UNPKG

ai-functions

Version:

Core AI primitives for building intelligent applications

284 lines 8.76 kB
/** * Execution Context for AI Functions * * Standalone context module with batch processing and budget tracking features. * Settings flow from environment -> global context -> local context. * * @example * ```ts * // Set global defaults (from environment or initialization) * configure({ * provider: 'anthropic', * model: 'claude-sonnet-4-20250514', * batchMode: 'auto', // 'auto' | 'immediate' | 'deferred' * }) * * // Or use execution context for specific operations * await withContext({ provider: 'openai', model: 'gpt-4o' }, async () => { * const titles = await list`10 blog titles` * return titles.map(title => write`blog post: ${title}`) * }) * ``` * * @packageDocumentation */ // ============================================================================ // Global Context // ============================================================================ let globalContext = {}; /** * Configure global defaults for AI functions * * @example * ```ts * configure({ * model: 'claude-sonnet-4-20250514', * provider: 'anthropic', * batchMode: 'auto', * batchThreshold: 5, * }) * ``` */ export function configure(context) { globalContext = { ...globalContext, ...context }; } /** * Get the current global context */ export function getGlobalContext() { return { ...globalContext }; } /** * Reset global context to defaults */ export function resetContext() { globalContext = {}; } // ============================================================================ // Async Local Storage for Execution Context // ============================================================================ // Use AsyncLocalStorage if available (Node.js), otherwise fallback to global let asyncLocalStorage = null; // Lazy initialization of AsyncLocalStorage let asyncLocalStorageInitialized = false; // Initialize synchronously if possible (for Node.js environments) if (typeof process !== 'undefined' && process.versions?.node) { import('async_hooks') .then(({ AsyncLocalStorage }) => { asyncLocalStorage = new AsyncLocalStorage(); asyncLocalStorageInitialized = true; }) .catch(() => { asyncLocalStorageInitialized = true; }); } // ============================================================================ // Environment Defaults // ============================================================================ function getEnvContext() { if (typeof process === 'undefined') return {}; const context = {}; // Model defaults if (process.env['AI_MODEL']) { context.model = process.env['AI_MODEL']; } // Provider defaults if (process.env['AI_PROVIDER']) { context.provider = process.env['AI_PROVIDER']; } else if (process.env['ANTHROPIC_API_KEY'] && !process.env['OPENAI_API_KEY']) { context.provider = 'anthropic'; } else if (process.env['OPENAI_API_KEY']) { context.provider = 'openai'; } else if (process.env['CLOUDFLARE_API_TOKEN']) { context.provider = 'cloudflare'; } else if (process.env['AWS_ACCESS_KEY_ID']) { context.provider = 'bedrock'; } return context; } function getEnvBatchContext() { if (typeof process === 'undefined') return {}; const context = {}; // Batch mode if (process.env['AI_BATCH_MODE']) { context.batchMode = process.env['AI_BATCH_MODE']; } // Flex threshold (when to start using flex processing) if (process.env['AI_FLEX_THRESHOLD']) { context.flexThreshold = parseInt(process.env['AI_FLEX_THRESHOLD'], 10); } // Batch threshold (when to switch from flex to full batch) if (process.env['AI_BATCH_THRESHOLD']) { context.batchThreshold = parseInt(process.env['AI_BATCH_THRESHOLD'], 10); } // Webhook URL if (process.env['AI_BATCH_WEBHOOK_URL']) { context.webhookUrl = process.env['AI_BATCH_WEBHOOK_URL']; } return context; } // ============================================================================ // Context Functions // ============================================================================ /** * Get the current execution context * Merges: environment defaults -> global context -> local context */ export function getContext() { const envContext = getEnvContext(); const envBatchContext = getEnvBatchContext(); const localContext = asyncLocalStorage?.getStore(); return { ...envContext, ...envBatchContext, ...globalContext, ...localContext, }; } /** * Run a function with a specific execution context * * @example * ```ts * const posts = await withContext({ provider: 'openai', batchMode: 'deferred' }, async () => { * const titles = await list`10 blog titles` * return titles.map(title => write`blog post: ${title}`) * }) * ``` */ export function withContext(context, fn) { const mergedContext = { ...getContext(), ...context }; if (asyncLocalStorage) { return asyncLocalStorage.run(mergedContext, fn); } // Fallback: temporarily modify global context const previousContext = globalContext; globalContext = mergedContext; try { return fn(); } finally { globalContext = previousContext; } } // ============================================================================ // Context Helpers // ============================================================================ /** * Get the effective model from context */ export function getModel() { const ctx = getContext(); return ctx.model || 'sonnet'; } /** * Get the effective provider from context (typed as BatchProvider) */ export function getProvider() { const ctx = getContext(); return (ctx.provider || 'openai'); } // ============================================================================ // Batch-Specific Context Helpers // ============================================================================ /** * Get the effective batch mode from context */ export function getBatchMode() { const ctx = getContext(); return ctx.batchMode || 'auto'; } /** * Get the flex threshold from context (minimum items to use flex) * Default: 5 items */ export function getFlexThreshold() { const ctx = getContext(); return ctx.flexThreshold || 5; } /** * Get the batch threshold from context (minimum items to use full batch) * Default: 500 items */ export function getBatchThreshold() { const ctx = getContext(); return ctx.batchThreshold || 500; } /** * Determine the execution tier for a given number of items * * Auto mode tiers: * - immediate: < flexThreshold (default 5) - concurrent requests, full price * - flex: flexThreshold to batchThreshold (5-500) - ~50% discount, minutes * - batch: >= batchThreshold (500+) - 50% discount, up to 24hr * * @example * ```ts * getExecutionTier(3) // 'immediate' (< 5) * getExecutionTier(50) // 'flex' (5-500) * getExecutionTier(1000) // 'batch' (500+) * ``` */ export function getExecutionTier(itemCount) { const mode = getBatchMode(); switch (mode) { case 'immediate': return 'immediate'; case 'flex': return 'flex'; case 'deferred': return 'batch'; case 'auto': default: { const flexThreshold = getFlexThreshold(); const batchThreshold = getBatchThreshold(); if (itemCount < flexThreshold) { return 'immediate'; } else if (itemCount < batchThreshold) { return 'flex'; } else { return 'batch'; } } } } /** * Check if we should use the batch API for a given number of items * * @deprecated Use {@link getExecutionTier} instead for more granular control. * This function will be removed in a future major version. * * @param itemCount - Number of items to process * @returns true if batch or flex tier should be used, false for immediate * * @example * ```ts * // Deprecated usage: * if (shouldUseBatchAPI(items.length)) { ... } * * // Recommended: * const tier = getExecutionTier(items.length) * if (tier === 'batch' || tier === 'flex') { ... } * ``` */ export function shouldUseBatchAPI(itemCount) { const tier = getExecutionTier(itemCount); return tier === 'flex' || tier === 'batch'; } /** * Check if flex processing is available for the current provider * Only OpenAI and AWS Bedrock support flex processing currently */ export function isFlexAvailable() { const provider = getProvider(); return provider === 'openai' || provider === 'bedrock' || provider === 'google'; } //# sourceMappingURL=context.js.map