Skip to main content

Conditional Logic

PromptSmith provides conditional methods that allow you to build dynamic prompts based on runtime conditions, user permissions, feature flags, and environment configuration.

Conditional Tools with withToolIf()

The withToolIf() method adds a tool only when a condition is true, enabling dynamic tool availability based on runtime state.

Basic Usage

import { createPromptBuilder } from 'promptsmith';
import { z } from 'zod';

const config = {
  databaseEnabled: true,
  externalApiEnabled: false,
  premiumFeaturesEnabled: true
};

const agent = createPromptBuilder()
  .withIdentity('You are a data analysis assistant')
  // Database tool - only if database is enabled
  .withToolIf(config.databaseEnabled, {
    name: 'query_database',
    description: 'Query the customer database',
    schema: z.object({
      query: z.string().describe('SQL query to execute')
    }),
    execute: async ({ query }) => {
      // Execute query
      return { rows: [], count: 0 };
    }
  })
  // External API tool - only if API is enabled
  .withToolIf(config.externalApiEnabled, {
    name: 'fetch_external_data',
    description: 'Fetch data from external API',
    schema: z.object({
      endpoint: z.string().describe('API endpoint')
    }),
    execute: async ({ endpoint }) => {
      // Fetch from API
      return {};
    }
  })
  // Premium analytics - only for premium users
  .withToolIf(config.premiumFeaturesEnabled, {
    name: 'advanced_analytics',
    description: 'Run advanced analytics on dataset',
    schema: z.object({
      datasetId: z.string().describe('Dataset ID')
    })
  });

const summary = agent.getSummary();
console.log(summary.toolsCount); // 2 (database + premium, API disabled)

Real-World Example: Permission-Based Tools

import { createPromptBuilder } from 'promptsmith';
import { z } from 'zod';

interface UserPermissions {
  canReadCustomers: boolean;
  canWriteCustomers: boolean;
  canDeleteCustomers: boolean;
  canAccessFinancials: boolean;
  canExportData: boolean;
  isAdmin: boolean;
}

function buildAgentForUser(permissions: UserPermissions) {
  return createPromptBuilder()
    .withIdentity('You are a business operations assistant')
    .withCapabilities([
      'Answer questions about business operations',
      'Help with data analysis',
      'Assist with reporting'
    ])
    // Read-only customer tool
    .withToolIf(permissions.canReadCustomers, {
      name: 'get_customer',
      description: 'Retrieve customer information',
      schema: z.object({
        customerId: z.string().describe('Customer ID')
      }),
      execute: async ({ customerId }) => {
        return { id: customerId, name: 'John Doe' };
      }
    })
    // Customer update tool - requires write permission
    .withToolIf(permissions.canWriteCustomers, {
      name: 'update_customer',
      description: 'Update customer information',
      schema: z.object({
        customerId: z.string().describe('Customer ID'),
        data: z.record(z.unknown()).describe('Updated fields')
      }),
      execute: async ({ customerId, data }) => {
        return { success: true };
      }
    })
    // Delete tool - requires delete permission
    .withToolIf(permissions.canDeleteCustomers, {
      name: 'delete_customer',
      description: 'Delete a customer (use with caution)',
      schema: z.object({
        customerId: z.string().describe('Customer ID'),
        reason: z.string().describe('Reason for deletion')
      }),
      execute: async ({ customerId, reason }) => {
        return { deleted: true };
      }
    })
    // Financial data - restricted access
    .withToolIf(permissions.canAccessFinancials, {
      name: 'get_revenue_data',
      description: 'Retrieve revenue and financial metrics',
      schema: z.object({
        period: z.enum(['daily', 'weekly', 'monthly']).describe('Time period')
      }),
      execute: async ({ period }) => {
        return { revenue: 0, expenses: 0 };
      }
    })
    // Data export - compliance-sensitive
    .withToolIf(permissions.canExportData, {
      name: 'export_customer_data',
      description: 'Export customer data to CSV',
      schema: z.object({
        filters: z.object({}).optional().describe('Filter criteria')
      }),
      execute: async ({ filters }) => {
        return { downloadUrl: 'https://...' };
      }
    })
    // Admin-only tools
    .withToolIf(permissions.isAdmin, {
      name: 'manage_users',
      description: 'Manage user accounts and permissions',
      schema: z.object({
        action: z.enum(['create', 'update', 'delete']).describe('Action'),
        userId: z.string().describe('User ID')
      })
    })
    // Add constraints based on permissions
    .withConstraintIf(
      !permissions.canDeleteCustomers,
      'must_not',
      'Never suggest deleting customer records'
    )
    .withConstraintIf(
      permissions.canAccessFinancials,
      'must',
      'Treat all financial data as highly confidential'
    );
}

// Usage examples
const basicUser = buildAgentForUser({
  canReadCustomers: true,
  canWriteCustomers: false,
  canDeleteCustomers: false,
  canAccessFinancials: false,
  canExportData: false,
  isAdmin: false
});
console.log(basicUser.getSummary().toolsCount); // 1 (only read access)

const powerUser = buildAgentForUser({
  canReadCustomers: true,
  canWriteCustomers: true,
  canDeleteCustomers: false,
  canAccessFinancials: true,
  canExportData: true,
  isAdmin: false
});
console.log(powerUser.getSummary().toolsCount); // 4 (read, write, financials, export)

const adminUser = buildAgentForUser({
  canReadCustomers: true,
  canWriteCustomers: true,
  canDeleteCustomers: true,
  canAccessFinancials: true,
  canExportData: true,
  isAdmin: true
});
console.log(adminUser.getSummary().toolsCount); // 6 (all tools)

Conditional Constraints with withConstraintIf()

The withConstraintIf() method adds constraints based on conditions, enabling dynamic behavioral rules.

Environment-Based Constraints

import { createPromptBuilder } from 'promptsmith';

const env = process.env.NODE_ENV || 'development';
const isProd = env === 'production';
const isDev = env === 'development';

const agent = createPromptBuilder()
  .withIdentity('You are a system monitoring assistant')
  // Production-specific constraints
  .withConstraintIf(
    isProd,
    'must',
    'Log all errors and warnings to the monitoring system'
  )
  .withConstraintIf(
    isProd,
    'must',
    'Never expose internal system details in error messages'
  )
  .withConstraintIf(
    isProd,
    'must_not',
    'Never restart services without approval'
  )
  // Development-specific constraints
  .withConstraintIf(
    isDev,
    'should',
    'Provide verbose debugging information'
  )
  .withConstraintIf(
    isDev,
    'should',
    'Include stack traces in error responses'
  )
  // Different tone based on environment
  .withTone(
    isProd 
      ? 'Professional and cautious. Prioritize system stability.'
      : 'Technical and detailed. Help debug issues quickly.'
  );

Feature Flag Integration

1

Define feature flags

import { createPromptBuilder } from 'promptsmith';
import { z } from 'zod';

// Feature flags from your feature flag service
interface FeatureFlags {
  enableAISearch: boolean;
  enableImageGeneration: boolean;
  enableVoiceInput: boolean;
  enableMultiLanguage: boolean;
  enableBetaFeatures: boolean;
  enableAdvancedAnalytics: boolean;
}

// Fetch from feature flag service (LaunchDarkly, etc.)
async function getFeatureFlags(userId: string): Promise<FeatureFlags> {
  // In real app, fetch from feature flag service
  return {
    enableAISearch: true,
    enableImageGeneration: false,
    enableVoiceInput: true,
    enableMultiLanguage: false,
    enableBetaFeatures: false,
    enableAdvancedAnalytics: true
  };
}
2

Build agent based on flags

async function buildAgentWithFeatures(
  userId: string
): Promise<ReturnType<typeof createPromptBuilder>> {
  const flags = await getFeatureFlags(userId);
  
  const agent = createPromptBuilder()
    .withIdentity('You are a multi-modal AI assistant')
    .withCapabilities([
      'Answer questions',
      'Analyze documents',
      'Provide recommendations'
    ])
    // AI Search - feature flag controlled
    .withToolIf(flags.enableAISearch, {
      name: 'semantic_search',
      description: 'Search documents using AI-powered semantic search',
      schema: z.object({
        query: z.string().describe('Natural language search query'),
        limit: z.number().optional().describe('Max results')
      }),
      execute: async ({ query, limit }) => {
        return { results: [] };
      }
    })
    // Image generation - beta feature
    .withToolIf(flags.enableImageGeneration, {
      name: 'generate_image',
      description: 'Generate images from text descriptions',
      schema: z.object({
        prompt: z.string().describe('Image description'),
        style: z.enum(['realistic', 'artistic', 'cartoon']).optional()
      })
    })
    // Voice input - requires special handling
    .withToolIf(flags.enableVoiceInput, {
      name: 'transcribe_audio',
      description: 'Transcribe audio input to text',
      schema: z.object({
        audioUrl: z.string().describe('URL to audio file')
      })
    })
    // Advanced analytics
    .withToolIf(flags.enableAdvancedAnalytics, {
      name: 'run_analytics',
      description: 'Run advanced analytics on datasets',
      schema: z.object({
        analysisType: z.enum(['trend', 'forecast', 'anomaly']),
        datasetId: z.string()
      })
    })
    // Beta features disclaimer
    .withConstraintIf(
      flags.enableBetaFeatures,
      'must',
      'Inform users that beta features may have limited reliability'
    )
    // Multi-language support
    .withConstraintIf(
      flags.enableMultiLanguage,
      'should',
      'Detect user language and respond in the same language'
    )
    .withCapability(
      flags.enableMultiLanguage ? 'Communicate in multiple languages' : ''
    );
  
  return agent;
}

// Usage
const userAgent = await buildAgentWithFeatures('user-123');
const prompt = userAgent.build();

Subscription Tier-Based Configuration

import { createPromptBuilder } from 'promptsmith';
import { z } from 'zod';

type SubscriptionTier = 'free' | 'pro' | 'enterprise';

interface SubscriptionLimits {
  maxQueriesPerDay: number;
  hasAdvancedTools: boolean;
  hasPrioritySupport: boolean;
  hasCustomBranding: boolean;
}

function getTierLimits(tier: SubscriptionTier): SubscriptionLimits {
  switch (tier) {
    case 'free':
      return {
        maxQueriesPerDay: 10,
        hasAdvancedTools: false,
        hasPrioritySupport: false,
        hasCustomBranding: false
      };
    case 'pro':
      return {
        maxQueriesPerDay: 100,
        hasAdvancedTools: true,
        hasPrioritySupport: false,
        hasCustomBranding: false
      };
    case 'enterprise':
      return {
        maxQueriesPerDay: Infinity,
        hasAdvancedTools: true,
        hasPrioritySupport: true,
        hasCustomBranding: true
      };
  }
}

function buildAgentForTier(tier: SubscriptionTier, currentUsage: number) {
  const limits = getTierLimits(tier);
  const isNearLimit = currentUsage >= limits.maxQueriesPerDay * 0.8;
  const hasReachedLimit = currentUsage >= limits.maxQueriesPerDay;
  
  return createPromptBuilder()
    .withIdentity('You are a business intelligence assistant')
    .withCapabilities(['Analyze data', 'Generate reports', 'Answer questions'])
    // Basic tools - available to all tiers
    .withTool({
      name: 'basic_query',
      description: 'Run basic data queries',
      schema: z.object({
        query: z.string().describe('Query to run')
      })
    })
    // Advanced tools - Pro and Enterprise only
    .withToolIf(limits.hasAdvancedTools, {
      name: 'advanced_analytics',
      description: 'Run advanced analytics and forecasting',
      schema: z.object({
        analysisType: z.enum(['forecast', 'anomaly', 'clustering']),
        dataset: z.string()
      })
    })
    .withToolIf(limits.hasAdvancedTools, {
      name: 'export_report',
      description: 'Export reports in multiple formats',
      schema: z.object({
        format: z.enum(['pdf', 'excel', 'powerpoint']),
        reportId: z.string()
      })
    })
    // Enterprise-only features
    .withToolIf(limits.hasCustomBranding, {
      name: 'customize_branding',
      description: 'Customize report branding and styling',
      schema: z.object({
        logo: z.string().optional(),
        colors: z.object({}).optional()
      })
    })
    // Usage limit warnings
    .withConstraintIf(
      isNearLimit && !hasReachedLimit,
      'should',
      `Inform user they are approaching their daily limit (${currentUsage}/${limits.maxQueriesPerDay} queries used)`
    )
    .withConstraintIf(
      hasReachedLimit,
      'must',
      'Inform user they have reached their daily query limit and should upgrade'
    )
    // Priority support messaging
    .withConstraintIf(
      limits.hasPrioritySupport,
      'should',
      'Mention 24/7 priority support availability for complex issues'
    )
    .withConstraintIf(
      !limits.hasPrioritySupport,
      'should',
      'Suggest upgrading to Pro/Enterprise for priority support'
    )
    // Tier-specific tone
    .withTone(
      tier === 'enterprise'
        ? 'Professional and white-glove service oriented'
        : tier === 'pro'
        ? 'Helpful and proactive'
        : 'Friendly and encouraging (mention upgrade benefits)'
    );
}

// Usage
const freeUserAgent = buildAgentForTier('free', 8);
console.log(freeUserAgent.getSummary()); // 1 tool, warning about limit

const proUserAgent = buildAgentForTier('pro', 50);
console.log(proUserAgent.getSummary()); // 3 tools, advanced features

const enterpriseAgent = buildAgentForTier('enterprise', 500);
console.log(enterpriseAgent.getSummary()); // 4 tools, all features

Combining Multiple Conditions

Complex Permission Logic

import { createPromptBuilder } from 'promptsmith';
import { z } from 'zod';

interface UserContext {
  role: 'viewer' | 'editor' | 'admin';
  department: 'sales' | 'engineering' | 'hr' | 'finance';
  isProd: boolean;
  featureFlags: {
    enableAudit: boolean;
    enableExport: boolean;
  };
}

function buildContextualAgent(ctx: UserContext) {
  // Compute derived permissions
  const canWrite = ctx.role === 'editor' || ctx.role === 'admin';
  const canDelete = ctx.role === 'admin';
  const canAccessFinancials = ctx.department === 'finance' || ctx.role === 'admin';
  const canAccessHR = ctx.department === 'hr' || ctx.role === 'admin';
  const canExport = ctx.featureFlags.enableExport && canWrite;
  const canAudit = ctx.featureFlags.enableAudit && canDelete;
  
  return createPromptBuilder()
    .withIdentity(`You are a ${ctx.department} department assistant`)
    // Read tools - available to everyone
    .withTool({
      name: 'view_documents',
      description: 'View department documents',
      schema: z.object({
        documentId: z.string()
      })
    })
    // Write tools - editors and admins
    .withToolIf(canWrite, {
      name: 'create_document',
      description: 'Create a new document',
      schema: z.object({
        title: z.string(),
        content: z.string()
      })
    })
    .withToolIf(canWrite, {
      name: 'update_document',
      description: 'Update existing document',
      schema: z.object({
        documentId: z.string(),
        changes: z.object({})
      })
    })
    // Delete tools - admins only
    .withToolIf(canDelete, {
      name: 'delete_document',
      description: 'Delete a document (use with caution)',
      schema: z.object({
        documentId: z.string(),
        reason: z.string()
      })
    })
    // Financial data - finance department or admins
    .withToolIf(canAccessFinancials, {
      name: 'view_financials',
      description: 'View financial reports and data',
      schema: z.object({
        reportType: z.enum(['revenue', 'expenses', 'profit'])
      })
    })
    // HR data - HR department or admins
    .withToolIf(canAccessHR, {
      name: 'view_employee_data',
      description: 'View employee information',
      schema: z.object({
        employeeId: z.string()
      })
    })
    // Export - requires feature flag AND write permission
    .withToolIf(canExport, {
      name: 'export_data',
      description: 'Export data to various formats',
      schema: z.object({
        format: z.enum(['csv', 'json', 'excel'])
      })
    })
    // Audit - requires feature flag AND admin permission
    .withToolIf(canAudit, {
      name: 'view_audit_log',
      description: 'View system audit logs',
      schema: z.object({
        startDate: z.string(),
        endDate: z.string()
      })
    })
    // Constraints based on role
    .withConstraintIf(
      !canWrite,
      'must_not',
      'Never suggest modifying or creating documents'
    )
    .withConstraintIf(
      !canDelete,
      'must_not',
      'Never suggest deleting any data'
    )
    .withConstraintIf(
      ctx.isProd && canDelete,
      'must',
      'Always ask for confirmation before deleting anything in production'
    )
    // Department-specific constraints
    .withConstraintIf(
      ctx.department === 'finance',
      'must',
      'Handle all financial data with strict confidentiality'
    )
    .withConstraintIf(
      ctx.department === 'hr',
      'must',
      'Comply with employee privacy regulations (GDPR, etc.)'
    )
    // Production safety
    .withConstraintIf(
      ctx.isProd,
      'must',
      'Exercise extreme caution with any data modifications'
    )
    .withConstraintIf(
      !ctx.isProd,
      'should',
      'Feel free to experiment and test features'
    );
}

// Usage examples
const viewerAgent = buildContextualAgent({
  role: 'viewer',
  department: 'sales',
  isProd: true,
  featureFlags: { enableAudit: false, enableExport: false }
});
console.log(viewerAgent.getSummary().toolsCount); // 1 (view only)

const editorAgent = buildContextualAgent({
  role: 'editor',
  department: 'engineering',
  isProd: false,
  featureFlags: { enableAudit: false, enableExport: true }
});
console.log(editorAgent.getSummary().toolsCount); // 4 (view, create, update, export)

const adminAgent = buildContextualAgent({
  role: 'admin',
  department: 'finance',
  isProd: true,
  featureFlags: { enableAudit: true, enableExport: true }
});
console.log(adminAgent.getSummary().toolsCount); // 8 (all tools)

Best Practices

Security Consideration: Always validate conditions server-side. Never trust client-provided permission flags - fetch them from your authorization system.
// ❌ DON'T: Trust client-provided permissions
function buildAgent(clientPermissions: any) {
  return createPromptBuilder()
    .withToolIf(clientPermissions.isAdmin, adminTool); // Unsafe!
}

// ✅ DO: Fetch permissions from server
async function buildAgent(userId: string) {
  const permissions = await fetchUserPermissions(userId); // Server-side
  return createPromptBuilder()
    .withToolIf(permissions.isAdmin, adminTool); // Safe
}
Performance: Condition evaluation happens at build time, not at AI inference time. Feel free to use complex logic without worrying about runtime performance.

Use for permissions

Conditionally enable tools and constraints based on user roles and permissions

Use for feature flags

Gradually roll out new capabilities using feature flags

Use for tiers

Provide different capabilities based on subscription tiers

Use for environments

Adjust behavior based on dev/staging/prod environment

Build docs developers (and LLMs) love