/** * 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 = { 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 = { 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 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 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']), }) }