chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
## Conditional tools
|
||||
|
||||
This demo shows how you can restrict tool access to only certain users and dynamically mutate the instructions and the available tools on the fly.
|
||||
|
||||
This approach guarantees safe and contextual execution and reduces the cognitive loads on prompts.
|
||||
|
||||
## 🎥 Demo
|
||||
|
||||

|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Example 03: Conditional Tool Access
|
||||
*
|
||||
* This example demonstrates dynamic tool access based on user authentication state.
|
||||
* It shows how to:
|
||||
* - Implement authentication with tools
|
||||
* - Dynamically provide different tools based on user identity
|
||||
* - Use function-based instructions and tools that evaluate at runtime
|
||||
* - Handle role-based access control (RBAC) patterns
|
||||
* - Manage stateful user sessions
|
||||
*
|
||||
* Key concepts:
|
||||
* - Dynamic tool filtering based on user state
|
||||
* - Authentication workflows with tools
|
||||
* - Runtime evaluation of instructions and tools
|
||||
* - Role-based access patterns
|
||||
* - Stateful execution context
|
||||
*/
|
||||
|
||||
import { Client } from '@botpress/client'
|
||||
import { z } from '@bpinternal/zui'
|
||||
import chalk from 'chalk'
|
||||
import { execute, Tool } from 'llmz'
|
||||
|
||||
import { CLIChat } from '../utils/cli-chat'
|
||||
import { printTrace } from '../utils/debug'
|
||||
|
||||
// Initialize Botpress client
|
||||
const client = new Client({
|
||||
botId: process.env.BOTPRESS_BOT_ID!,
|
||||
token: process.env.BOTPRESS_TOKEN!,
|
||||
})
|
||||
|
||||
// Display welcome message and instructions for the demo
|
||||
console.log('👋 Welcome to the Conditional Tool Example!')
|
||||
console.log("This example demonstrates how to conditionally show tools based on the user's identity.")
|
||||
console.log(`You can log in as an "${chalk.bold('admin')}" or a "${chalk.bold('customer')}" to access different tools.`)
|
||||
console.log(`The password for both is "${chalk.bold('password')}".`)
|
||||
console.log(`You can also ${chalk.bold('logout')} at any time to return to the login screen.`)
|
||||
console.log(chalk.dim('===========================\n\n'))
|
||||
|
||||
// Define user identity types for role-based access control
|
||||
type Identity = 'admin' | 'customer' | 'unknown'
|
||||
|
||||
// Track current user's authentication state
|
||||
// This state determines which tools and instructions are available
|
||||
let userId: Identity = 'unknown'
|
||||
|
||||
// Simple credential store for demo purposes
|
||||
// In production, this would integrate with proper authentication systems
|
||||
const Credentials = {
|
||||
admin: 'password',
|
||||
customer: 'password',
|
||||
} as const
|
||||
|
||||
// Authentication tool - allows users to log in with credentials
|
||||
// Demonstrates how tools can modify application state
|
||||
const login = new Tool({
|
||||
name: 'login',
|
||||
input: z.object({
|
||||
userId: z.enum(['admin', 'customer']),
|
||||
password: z.string(),
|
||||
}),
|
||||
output: z.object({
|
||||
userId: z.enum(['admin', 'customer']),
|
||||
success: z.boolean(),
|
||||
}),
|
||||
async handler(input) {
|
||||
// Validate credentials against our credential store
|
||||
if (!input.userId || Credentials[input.userId] !== input.password) {
|
||||
throw new Error('Invalid credentials')
|
||||
}
|
||||
|
||||
// Update global authentication state
|
||||
userId = input.userId
|
||||
|
||||
return {
|
||||
userId: input.userId,
|
||||
success: true,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// Admin-only tool for database operations
|
||||
// Only available when user is authenticated as admin
|
||||
const resetDatabase = new Tool({
|
||||
name: 'reset_database',
|
||||
description: 'Reset the database',
|
||||
async handler() {
|
||||
console.log('Database reset called')
|
||||
},
|
||||
})
|
||||
|
||||
// Another admin-only tool for sensitive operations
|
||||
const superSecretTool = new Tool({
|
||||
name: 'super_secret_tool',
|
||||
description: 'A super secret tool',
|
||||
async handler() {
|
||||
console.log('Super secret tool called')
|
||||
},
|
||||
})
|
||||
|
||||
// Logout tool - available to all authenticated users
|
||||
// Resets authentication state back to unknown
|
||||
const logout = new Tool({
|
||||
name: 'logout',
|
||||
description: 'Logout the user',
|
||||
async handler() {
|
||||
userId = 'unknown'
|
||||
console.log('User logged out')
|
||||
},
|
||||
})
|
||||
|
||||
// General tool available to both customers and admins
|
||||
const customerTool = new Tool({
|
||||
name: 'general_tool',
|
||||
description: 'A general purpose tool for customers and admins',
|
||||
async handler() {
|
||||
console.log('Customer tool called')
|
||||
},
|
||||
})
|
||||
|
||||
// Define tool sets for each user role
|
||||
// This creates a role-based access control (RBAC) system
|
||||
const ToolsForUser: Record<Identity, Tool[]> = {
|
||||
admin: [resetDatabase, superSecretTool, logout, customerTool], // Admins get all tools
|
||||
customer: [logout, customerTool], // Customers get limited tools
|
||||
unknown: [login], // Unauthenticated users can only login
|
||||
}
|
||||
|
||||
login.getTypings()
|
||||
|
||||
// Define dynamic instructions based on user state
|
||||
// Instructions change based on authentication status
|
||||
const InstructionsForUser: Record<Identity, string> = {
|
||||
unknown: 'The user is not authenticated. Please ask them to log in before proceeding. Use buttons to list users.',
|
||||
admin: `User is logged in as "${userId}". At every turn, send a <Message> to the user with the list of all the tools available for the user and ask them to choose one. Use buttons to list tools.`,
|
||||
customer: `User is logged in as "${userId}". At every turn, send a <Message> to the user with the list of all the tools available for the user and ask them to choose one. Use buttons to list tools.`,
|
||||
}
|
||||
|
||||
const chat = new CLIChat()
|
||||
|
||||
// Main execution loop with dynamic configuration
|
||||
while (await chat.iterate()) {
|
||||
await execute({
|
||||
client,
|
||||
chat,
|
||||
|
||||
// Use function-based instructions that evaluate at runtime
|
||||
// This allows instructions to change based on current user state
|
||||
instructions: () => InstructionsForUser[userId],
|
||||
|
||||
// Use function-based tools that evaluate at runtime
|
||||
// This enables dynamic tool availability based on user permissions
|
||||
tools: () => ToolsForUser[userId],
|
||||
|
||||
// Enable trace logging to see tool calls in action
|
||||
onTrace: ({ trace }) => printTrace(trace, ['tool_call']),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user