@digilogiclabs/platform-core 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents.ts","../src/interfaces/ILogger.ts","../src/agent/agent-loop.ts","../src/agent/agent-tracer.ts","../src/agent/agent-usage.ts","../src/agent/traced-ai.ts","../src/interfaces/IAI.ts","../src/adapters/ollama/OllamaAdapter.ts"],"sourcesContent":["/**\n * Agents Module\n *\n * Multi-turn tool-calling agent runtime with observability and cost tracking.\n *\n * This sub-path export (`@digilogiclabs/platform-core/agents`) is lightweight —\n * it only depends on IAI, ICache, ITracing, IAIUsage, and ILogger interfaces.\n * It does NOT pull in database, storage, email, payment, or AI provider adapters.\n *\n * @example\n * ```typescript\n * import { runAgentLoop, createTracedAI, createAgentTracer } from '@digilogiclabs/platform-core/agents';\n * ```\n */\n\n// Core agent loop\nexport {\n runAgentLoop,\n DEFAULT_AGENT_LOOP_OPTIONS,\n type AgentTool,\n type ToolExecutor,\n type AgentLoopOptions,\n type AgentLoopResult,\n type AgentStopReason,\n type AgentIterationEvent,\n type AgentIterationCallback,\n type AgentToolResult,\n type AgentCumulativeUsage,\n} from \"./agent/agent-loop\";\n\n// Agent tracing helpers\nexport {\n createAgentTracer,\n type AgentTracer,\n type AgentTracerOptions,\n} from \"./agent/agent-tracer\";\n\n// Agent usage tracking\nexport {\n createAgentUsageTracker,\n type AgentUsageTracker,\n type AgentUsageTrackerOptions,\n type AgentUsageRecordParams,\n type AgentBudget,\n} from \"./agent/agent-usage\";\n\n// Auto-instrumented AI wrapper\nexport { createTracedAI, type TracedAIOptions } from \"./agent/traced-ai\";\n\n// Local LLM adapter (zero API cost)\nexport {\n OllamaAdapter,\n type OllamaAdapterConfig,\n} from \"./adapters/ollama/OllamaAdapter\";\n","/**\n * Logger Interface\n * Standardizes logging across the platform\n */\n\n/**\n * Log levels in order of severity\n */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\n/**\n * Metadata that can be attached to log entries\n */\nexport interface LogMeta {\n /** Error object if logging an error */\n error?: Error;\n /** Duration in milliseconds (for timing operations) */\n duration?: number;\n /** Request ID for tracing */\n requestId?: string;\n /** User ID if applicable */\n userId?: string;\n /** Any additional key-value pairs */\n [key: string]: unknown;\n}\n\n/**\n * A structured log entry\n */\nexport interface LogEntry {\n /** Log level */\n level: LogLevel;\n /** Log message */\n message: string;\n /** When the log was created */\n timestamp: Date;\n /** Optional metadata */\n meta?: LogMeta;\n /** Logger context (e.g., service name) */\n context?: Record<string, unknown>;\n}\n\n/**\n * Logger interface for structured logging\n */\nexport interface ILogger {\n /**\n * Log a debug message (for development/troubleshooting)\n */\n debug(message: string, meta?: LogMeta): void;\n\n /**\n * Log an informational message\n */\n info(message: string, meta?: LogMeta): void;\n\n /**\n * Log a warning message\n */\n warn(message: string, meta?: LogMeta): void;\n\n /**\n * Log an error message\n */\n error(message: string, meta?: LogMeta): void;\n\n /**\n * Create a child logger with additional context\n * @param context Additional context to include in all logs from this child\n */\n child(context: Record<string, unknown>): ILogger;\n}\n\n/**\n * Configuration options for loggers\n */\nexport interface LoggerConfig {\n /** Minimum level to log */\n level?: LogLevel;\n /** Whether to pretty print (for development) */\n pretty?: boolean;\n /** Service name to include in logs */\n service?: string;\n /** Environment name */\n environment?: string;\n}\n\n/**\n * Console logger implementation (for development)\n */\nexport class ConsoleLogger implements ILogger {\n private context: Record<string, unknown>;\n private level: LogLevel;\n\n private static levelPriority: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n };\n\n constructor(config: LoggerConfig = {}) {\n this.context = {\n service: config.service,\n environment: config.environment,\n };\n this.level = config.level || \"info\";\n }\n\n private shouldLog(level: LogLevel): boolean {\n return (\n ConsoleLogger.levelPriority[level] >=\n ConsoleLogger.levelPriority[this.level]\n );\n }\n\n private log(level: LogLevel, message: string, meta?: LogMeta): void {\n if (!this.shouldLog(level)) return;\n\n const entry: LogEntry = {\n level,\n message,\n timestamp: new Date(),\n meta,\n context: this.context,\n };\n\n const prefix = `[${entry.timestamp.toISOString()}] [${level.toUpperCase()}]`;\n const contextStr =\n Object.keys(this.context).length > 0\n ? ` [${Object.entries(this.context)\n .map(([k, v]) => `${k}=${v}`)\n .join(\" \")}]`\n : \"\";\n\n switch (level) {\n case \"debug\":\n console.debug(`${prefix}${contextStr} ${message}`, meta || \"\");\n break;\n case \"info\":\n console.info(`${prefix}${contextStr} ${message}`, meta || \"\");\n break;\n case \"warn\":\n console.warn(`${prefix}${contextStr} ${message}`, meta || \"\");\n break;\n case \"error\":\n console.error(`${prefix}${contextStr} ${message}`, meta || \"\");\n if (meta?.error) {\n console.error(meta.error);\n }\n break;\n }\n }\n\n debug(message: string, meta?: LogMeta): void {\n this.log(\"debug\", message, meta);\n }\n\n info(message: string, meta?: LogMeta): void {\n this.log(\"info\", message, meta);\n }\n\n warn(message: string, meta?: LogMeta): void {\n this.log(\"warn\", message, meta);\n }\n\n error(message: string, meta?: LogMeta): void {\n this.log(\"error\", message, meta);\n }\n\n child(context: Record<string, unknown>): ILogger {\n const childLogger = new ConsoleLogger({ level: this.level });\n childLogger.context = { ...this.context, ...context };\n return childLogger;\n }\n}\n\n/**\n * No-op logger for testing or when logging is disabled\n */\nexport class NoopLogger implements ILogger {\n debug(): void {}\n info(): void {}\n warn(): void {}\n error(): void {}\n child(): ILogger {\n return this;\n }\n}\n","/**\n * Agent Loop — Multi-turn tool-calling orchestration\n *\n * Runs an IAI chat → tool execution → repeat loop until the AI stops,\n * a budget is hit, or the caller aborts.\n */\n\nimport type {\n IAI,\n AIMessage,\n AITool,\n AIToolCall,\n AIChatRequest,\n AIChatResponse,\n AIFinishReason,\n} from \"../interfaces/IAI\";\nimport type { ICache } from \"../interfaces/ICache\";\nimport type { ILogger } from \"../interfaces/ILogger\";\nimport { NoopLogger } from \"../interfaces/ILogger\";\n\n// ═══════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Function that executes a tool call and returns a string result.\n * Receives the parsed arguments (from JSON) and the raw tool call.\n */\nexport type ToolExecutor = (\n args: Record<string, unknown>,\n toolCall: AIToolCall,\n) => Promise<string>;\n\n/**\n * Tool definition paired with its executor.\n */\nexport interface AgentTool {\n /** The AI tool definition (JSON Schema for the function) */\n definition: AITool;\n /** The function that executes this tool */\n execute: ToolExecutor;\n}\n\n/**\n * Result of a single tool call execution.\n */\nexport interface AgentToolResult {\n /** The original tool call from the AI */\n toolCall: AIToolCall;\n /** The string result returned by the executor */\n result: string;\n /** Whether the tool execution succeeded */\n success: boolean;\n /** Error message if the tool failed */\n error?: string;\n /** Execution duration in ms */\n durationMs: number;\n /** Whether the result came from cache */\n cached: boolean;\n}\n\n/**\n * Cumulative usage across all iterations.\n */\nexport interface AgentCumulativeUsage {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n estimatedCostUsd: number;\n iterations: number;\n toolCallCount: number;\n}\n\n/**\n * Event emitted after each iteration of the agent loop.\n */\nexport interface AgentIterationEvent {\n /** 1-based iteration number */\n iteration: number;\n /** The AI response for this iteration */\n response: AIChatResponse;\n /** Tool calls made in this iteration (empty if no tool calls) */\n toolCalls: AgentToolResult[];\n /** Cumulative usage across all iterations */\n cumulativeUsage: AgentCumulativeUsage;\n /** Full conversation so far */\n messages: AIMessage[];\n}\n\n/**\n * Callback invoked after each iteration.\n * Return `false` to abort the loop.\n */\nexport type AgentIterationCallback = (\n event: AgentIterationEvent,\n) => void | false | Promise<void | false>;\n\n/**\n * Why the agent loop stopped.\n */\nexport type AgentStopReason =\n | \"stop\"\n | \"max_iterations\"\n | \"max_tokens\"\n | \"max_cost\"\n | \"aborted\"\n | \"callback_abort\"\n | \"length\"\n | \"content_filter\"\n | \"error\";\n\n/**\n * Options for runAgentLoop.\n */\nexport interface AgentLoopOptions {\n /** The IAI instance to call */\n ai: IAI;\n /** Initial messages (system + user prompt) */\n messages: AIMessage[];\n /** Tool definitions with executors */\n tools: AgentTool[];\n /** Maximum iterations before forced stop @default 10 */\n maxIterations: number;\n /** Maximum cumulative total tokens @default Infinity */\n maxTokens: number;\n /** Maximum cumulative cost in USD @default Infinity */\n maxCostUsd: number;\n /** Optional cache for tool results */\n cache?: ICache;\n /** Cache TTL in seconds for tool results @default 300 */\n cacheTtlSeconds: number;\n /** Cache key prefix @default \"agent:tool:\" */\n cacheKeyPrefix: string;\n /** Callback after each iteration */\n onIteration?: AgentIterationCallback;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n /** Logger for observability */\n logger?: ILogger;\n /** Additional fields for AIChatRequest (model, temperature, etc.) */\n chatRequestOverrides?: Partial<Omit<AIChatRequest, \"messages\" | \"tools\">>;\n}\n\n/**\n * Result of a completed agent loop.\n */\nexport interface AgentLoopResult {\n /** Final AI response */\n response: AIChatResponse;\n /** Complete conversation history including all tool messages */\n messages: AIMessage[];\n /** Why the loop stopped */\n stopReason: AgentStopReason;\n /** Per-iteration details */\n iterations: AgentIterationEvent[];\n /** Cumulative usage summary */\n usage: AgentCumulativeUsage;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// DEFAULTS\n// ═══════════════════════════════════════════════════════════════\n\nexport const DEFAULT_AGENT_LOOP_OPTIONS: Pick<\n AgentLoopOptions,\n | \"maxIterations\"\n | \"maxTokens\"\n | \"maxCostUsd\"\n | \"cacheTtlSeconds\"\n | \"cacheKeyPrefix\"\n> = {\n maxIterations: 10,\n maxTokens: Infinity,\n maxCostUsd: Infinity,\n cacheTtlSeconds: 300,\n cacheKeyPrefix: \"agent:tool:\",\n};\n\n// ═══════════════════════════════════════════════════════════════\n// HELPERS\n// ═══════════════════════════════════════════════════════════════\n\nfunction mapFinishReason(reason: AIFinishReason): AgentStopReason {\n switch (reason) {\n case \"stop\":\n return \"stop\";\n case \"length\":\n return \"length\";\n case \"content_filter\":\n return \"content_filter\";\n case \"tool_calls\":\n return \"stop\"; // shouldn't reach here in normal flow\n case \"error\":\n default:\n return \"error\";\n }\n}\n\nasync function executeToolCall(\n toolCall: AIToolCall,\n executorMap: Map<string, AgentTool>,\n cache: ICache | undefined,\n cacheKeyPrefix: string,\n cacheTtlSeconds: number,\n): Promise<AgentToolResult> {\n const start = Date.now();\n const executor = executorMap.get(toolCall.function.name);\n\n if (!executor) {\n return {\n toolCall,\n result: `Error: Unknown tool \"${toolCall.function.name}\"`,\n success: false,\n error: `Unknown tool: ${toolCall.function.name}`,\n durationMs: Date.now() - start,\n cached: false,\n };\n }\n\n // Check cache\n if (cache) {\n const cacheKey =\n cacheKeyPrefix +\n toolCall.function.name +\n \":\" +\n toolCall.function.arguments;\n try {\n const cached = await cache.get<string>(cacheKey);\n if (cached !== null) {\n return {\n toolCall,\n result: cached,\n success: true,\n durationMs: Date.now() - start,\n cached: true,\n };\n }\n } catch {\n // Cache read failure is non-fatal\n }\n }\n\n // Execute\n try {\n const parsed = JSON.parse(toolCall.function.arguments) as Record<\n string,\n unknown\n >;\n const result = await executor.execute(parsed, toolCall);\n\n // Store in cache\n if (cache) {\n const cacheKey =\n cacheKeyPrefix +\n toolCall.function.name +\n \":\" +\n toolCall.function.arguments;\n await cache.set(cacheKey, result, cacheTtlSeconds).catch(() => {});\n }\n\n return {\n toolCall,\n result,\n success: true,\n durationMs: Date.now() - start,\n cached: false,\n };\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n return {\n toolCall,\n result: `Error: ${errorMsg}`,\n success: false,\n error: errorMsg,\n durationMs: Date.now() - start,\n cached: false,\n };\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// MAIN FUNCTION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Run a multi-turn tool-calling agent loop.\n *\n * @example\n * ```typescript\n * const result = await runAgentLoop({\n * ai: platform.ai,\n * messages: [\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'What is the weather in London?' },\n * ],\n * tools: [{\n * definition: {\n * type: 'function',\n * function: {\n * name: 'get_weather',\n * description: 'Get the weather for a city',\n * parameters: { type: 'object', properties: { city: { type: 'string' } } },\n * },\n * },\n * execute: async (args) => {\n * const weather = await fetchWeather(args.city as string);\n * return JSON.stringify(weather);\n * },\n * }],\n * });\n *\n * console.log(result.stopReason); // \"stop\"\n * console.log(result.usage.totalTokens); // cumulative tokens\n * ```\n */\nexport async function runAgentLoop(\n options: Partial<AgentLoopOptions> &\n Pick<AgentLoopOptions, \"ai\" | \"messages\" | \"tools\">,\n): Promise<AgentLoopResult> {\n const opts = { ...DEFAULT_AGENT_LOOP_OPTIONS, ...options };\n const logger = opts.logger ?? new NoopLogger();\n\n // Build tool definitions and executor map\n const toolDefs: AITool[] = opts.tools.map((t) => t.definition);\n const executorMap = new Map<string, AgentTool>();\n for (const tool of opts.tools) {\n executorMap.set(tool.definition.function.name, tool);\n }\n\n // Mutable state\n const messages: AIMessage[] = [...opts.messages];\n const iterations: AgentIterationEvent[] = [];\n const cumulative: AgentCumulativeUsage = {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n estimatedCostUsd: 0,\n iterations: 0,\n toolCallCount: 0,\n };\n\n let lastResponse: AIChatResponse | undefined;\n let stopReason: AgentStopReason = \"stop\";\n\n for (let i = 1; i <= opts.maxIterations; i++) {\n // Check abort signal\n if (opts.signal?.aborted) {\n stopReason = \"aborted\";\n break;\n }\n\n // Check budgets before calling AI\n if (cumulative.totalTokens >= opts.maxTokens) {\n stopReason = \"max_tokens\";\n break;\n }\n if (cumulative.estimatedCostUsd >= opts.maxCostUsd) {\n stopReason = \"max_cost\";\n break;\n }\n\n // Call ai.chat()\n logger.debug(\"Agent loop iteration\", { iteration: i });\n const chatRequest: AIChatRequest = {\n ...opts.chatRequestOverrides,\n messages,\n tools: toolDefs.length > 0 ? toolDefs : undefined,\n };\n\n const response = await opts.ai.chat(chatRequest);\n lastResponse = response;\n\n // Accumulate usage\n cumulative.promptTokens += response.usage.promptTokens;\n cumulative.completionTokens += response.usage.completionTokens;\n cumulative.totalTokens += response.usage.totalTokens;\n cumulative.estimatedCostUsd += response.usage.estimatedCostUsd;\n cumulative.iterations = i;\n\n // Get assistant message\n const assistantMessage = response.choices[0]?.message;\n if (!assistantMessage) {\n stopReason = \"error\";\n break;\n }\n messages.push(assistantMessage);\n\n // If not tool_calls, we're done\n if (response.finishReason !== \"tool_calls\") {\n stopReason = mapFinishReason(response.finishReason);\n\n const event: AgentIterationEvent = {\n iteration: i,\n response,\n toolCalls: [],\n cumulativeUsage: { ...cumulative },\n messages: [...messages],\n };\n iterations.push(event);\n await opts.onIteration?.(event);\n break;\n }\n\n // Execute tool calls in parallel\n const toolCalls = assistantMessage.toolCalls ?? [];\n cumulative.toolCallCount += toolCalls.length;\n\n const toolResults: AgentToolResult[] = await Promise.all(\n toolCalls.map((tc) =>\n executeToolCall(\n tc,\n executorMap,\n opts.cache,\n opts.cacheKeyPrefix,\n opts.cacheTtlSeconds,\n ),\n ),\n );\n\n // Append tool result messages\n for (const tr of toolResults) {\n messages.push({\n role: \"tool\",\n content: tr.result,\n toolCallId: tr.toolCall.id,\n });\n }\n\n // Build iteration event\n const event: AgentIterationEvent = {\n iteration: i,\n response,\n toolCalls: toolResults,\n cumulativeUsage: { ...cumulative },\n messages: [...messages],\n };\n iterations.push(event);\n\n // Fire callback\n const callbackResult = await opts.onIteration?.(event);\n if (callbackResult === false) {\n stopReason = \"callback_abort\";\n break;\n }\n\n // Last iteration check\n if (i === opts.maxIterations) {\n stopReason = \"max_iterations\";\n }\n }\n\n return {\n response: lastResponse!,\n messages,\n stopReason,\n iterations,\n usage: cumulative,\n };\n}\n","/**\n * Agent Tracer — OTEL span helpers for agent observability\n *\n * Wraps ITracing with standardized agent-specific span attributes\n * following OpenTelemetry semantic conventions.\n */\n\nimport type { ITracing, ISpan } from \"../interfaces/ITracing\";\nimport type { AgentToolResult, AgentCumulativeUsage } from \"./agent-loop\";\n\n// ═══════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface AgentTracerOptions {\n /** The ITracing instance */\n tracing: ITracing;\n /** Agent identifier for span attributes */\n agentId: string;\n}\n\nexport interface AgentTracer {\n /**\n * Wrap an entire agent loop execution in a span.\n * Sets `agent.id` on the span.\n */\n traceLoop<T>(fn: (span: ISpan) => Promise<T>): Promise<T>;\n\n /**\n * Wrap a single iteration in a span.\n * Sets `agent.iteration` on the span.\n */\n traceIteration(\n iteration: number,\n fn: (span: ISpan) => Promise<void>,\n ): Promise<void>;\n\n /**\n * Wrap a single tool call execution in a span.\n * Sets `agent.tool.name`, `agent.tool.duration_ms`, `agent.tool.success`, `agent.tool.cached`.\n */\n traceToolCall(\n toolName: string,\n fn: (span: ISpan) => Promise<AgentToolResult>,\n ): Promise<AgentToolResult>;\n\n /**\n * Record a decision event on the current span.\n */\n traceDecision(\n span: ISpan,\n attrs: {\n finishReason: string;\n toolCallCount: number;\n cumulativeUsage: AgentCumulativeUsage;\n },\n ): void;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// IMPLEMENTATION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Create an agent tracer with standardized OTEL attributes.\n *\n * @example\n * ```typescript\n * const tracer = createAgentTracer({ tracing: platform.tracing, agentId: 'weather-agent' });\n *\n * const result = await tracer.traceLoop(async (span) => {\n * return await runAgentLoop({ ... });\n * });\n * ```\n */\nexport function createAgentTracer(options: AgentTracerOptions): AgentTracer {\n const { tracing, agentId } = options;\n\n return {\n async traceLoop<T>(fn: (span: ISpan) => Promise<T>): Promise<T> {\n return tracing.withSpanAsync(\n \"agent.loop\",\n async (span) => {\n span.setAttribute(\"agent.id\", agentId);\n return fn(span);\n },\n { kind: \"internal\" },\n );\n },\n\n async traceIteration(\n iteration: number,\n fn: (span: ISpan) => Promise<void>,\n ): Promise<void> {\n return tracing.withSpanAsync(\n \"agent.iteration\",\n async (span) => {\n span.setAttributes({\n \"agent.id\": agentId,\n \"agent.iteration\": iteration,\n });\n return fn(span);\n },\n { kind: \"internal\" },\n );\n },\n\n async traceToolCall(\n toolName: string,\n fn: (span: ISpan) => Promise<AgentToolResult>,\n ): Promise<AgentToolResult> {\n return tracing.withSpanAsync(\n `agent.tool.${toolName}`,\n async (span) => {\n span.setAttributes({\n \"agent.id\": agentId,\n \"agent.tool.name\": toolName,\n });\n const result = await fn(span);\n span.setAttributes({\n \"agent.tool.duration_ms\": result.durationMs,\n \"agent.tool.success\": result.success,\n \"agent.tool.cached\": result.cached,\n });\n if (result.error) {\n span.setAttribute(\"agent.tool.error\", result.error);\n }\n return result;\n },\n { kind: \"internal\" },\n );\n },\n\n traceDecision(\n span: ISpan,\n attrs: {\n finishReason: string;\n toolCallCount: number;\n cumulativeUsage: AgentCumulativeUsage;\n },\n ): void {\n span.setAttributes({\n \"agent.decision.finish_reason\": attrs.finishReason,\n \"agent.decision.tool_count\": attrs.toolCallCount,\n \"agent.decision.total_tokens\": attrs.cumulativeUsage.totalTokens,\n \"agent.decision.cost_usd\": attrs.cumulativeUsage.estimatedCostUsd,\n \"agent.decision.iterations\": attrs.cumulativeUsage.iterations,\n });\n span.addEvent(\"agent.decision\", {\n finish_reason: attrs.finishReason,\n tool_count: attrs.toolCallCount,\n });\n },\n };\n}\n","/**\n * Agent Usage Tracker — Per-agent cost tracking via IAIUsage\n *\n * Tags usage records with agentId in metadata for per-agent attribution.\n * No changes to IAIUsage interface required.\n */\n\nimport type {\n IAIUsage,\n UsageRecord,\n UsageCategory,\n} from \"../interfaces/IAIUsage\";\nimport type { AIUsageInfo } from \"../interfaces/IAI\";\nimport type { AgentIterationCallback, AgentIterationEvent } from \"./agent-loop\";\n\n// ═══════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface AgentUsageTrackerOptions {\n /** The IAIUsage instance */\n usage: IAIUsage;\n /** Agent identifier to tag on all records */\n agentId: string;\n /** Tenant ID for usage records */\n tenantId: string;\n /** Optional user ID */\n userId?: string;\n}\n\nexport interface AgentUsageRecordParams {\n /** AI provider name */\n provider: string;\n /** Model used */\n model: string;\n /** Token usage info from the AI response */\n usage: AIUsageInfo;\n /** Usage category @default \"chat\" */\n category?: UsageCategory;\n /** Whether the request succeeded @default true */\n success?: boolean;\n /** Request latency in ms */\n latencyMs?: number;\n /** Error message if failed */\n error?: string;\n}\n\nexport interface AgentBudget {\n /** Maximum cumulative cost in USD */\n maxCostUsd?: number;\n /** Maximum cumulative tokens */\n maxTokens?: number;\n}\n\nexport interface AgentUsageTracker {\n /**\n * Record a single AI call's usage, tagged with agentId in metadata.\n */\n recordIteration(params: AgentUsageRecordParams): Promise<UsageRecord>;\n\n /**\n * Returns an AgentIterationCallback suitable for runAgentLoop's onIteration.\n * Records usage for each iteration and returns false when budget is exceeded.\n */\n createBudgetCallback(budget?: AgentBudget): AgentIterationCallback;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// IMPLEMENTATION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Create a usage tracker that tags all records with an agent ID.\n *\n * @example\n * ```typescript\n * const tracker = createAgentUsageTracker({\n * usage: platform.aiUsage,\n * agentId: 'weather-agent',\n * tenantId: 'tenant_123',\n * });\n *\n * const result = await runAgentLoop({\n * ai, messages, tools,\n * onIteration: tracker.createBudgetCallback({ maxCostUsd: 0.50 }),\n * });\n * ```\n */\nexport function createAgentUsageTracker(\n options: AgentUsageTrackerOptions,\n): AgentUsageTracker {\n const { usage, agentId, tenantId, userId } = options;\n\n return {\n async recordIteration(\n params: AgentUsageRecordParams,\n ): Promise<UsageRecord> {\n return usage.record({\n tenantId,\n userId,\n category: params.category ?? \"chat\",\n provider: params.provider,\n model: params.model,\n inputTokens: params.usage.promptTokens,\n outputTokens: params.usage.completionTokens,\n totalTokens: params.usage.totalTokens,\n costUsd: params.usage.estimatedCostUsd,\n latencyMs: params.latencyMs ?? 0,\n success: params.success ?? true,\n error: params.error,\n metadata: { agentId },\n });\n },\n\n createBudgetCallback(budget?: AgentBudget): AgentIterationCallback {\n return async (event: AgentIterationEvent): Promise<void | false> => {\n // Record usage for this iteration\n const response = event.response;\n await usage.record({\n tenantId,\n userId,\n category: \"chat\",\n provider: response.provider,\n model: response.model,\n inputTokens: response.usage.promptTokens,\n outputTokens: response.usage.completionTokens,\n totalTokens: response.usage.totalTokens,\n costUsd: response.usage.estimatedCostUsd,\n latencyMs: 0,\n success: true,\n metadata: { agentId, iteration: event.iteration },\n });\n\n // Check budget\n if (budget) {\n if (\n budget.maxCostUsd !== undefined &&\n event.cumulativeUsage.estimatedCostUsd >= budget.maxCostUsd\n ) {\n return false;\n }\n if (\n budget.maxTokens !== undefined &&\n event.cumulativeUsage.totalTokens >= budget.maxTokens\n ) {\n return false;\n }\n }\n };\n },\n };\n}\n","/**\n * Traced AI — Auto-instrument any IAI with OTEL spans\n *\n * Wraps an IAI instance so that every chat() call is automatically\n * wrapped in an OTEL span capturing provider, model, tokens, cost,\n * latency, and finish reason.\n */\n\nimport type {\n IAI,\n AIChatRequest,\n AIChatResponse,\n AICompletionRequest,\n AICompletionResponse,\n AIEmbeddingRequest,\n AIEmbeddingResponse,\n AIStreamChunk,\n AIStreamCallback,\n AIModelConfig,\n AIModelType,\n AIProvider,\n} from \"../interfaces/IAI\";\nimport type { ITracing } from \"../interfaces/ITracing\";\n\n// ═══════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface TracedAIOptions {\n /** The IAI instance to wrap */\n ai: IAI;\n /** The ITracing instance */\n tracing: ITracing;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// IMPLEMENTATION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Wraps an IAI instance with automatic OTEL tracing on chat operations.\n *\n * @example\n * ```typescript\n * const tracedAI = createTracedAI({ ai: platform.ai, tracing: platform.tracing });\n *\n * // Every chat() call now creates an OTEL span with ai.* attributes\n * const response = await tracedAI.chat({ messages: [...] });\n * ```\n */\nexport function createTracedAI(options: TracedAIOptions): IAI {\n const { ai, tracing } = options;\n\n return {\n async chat(request: AIChatRequest): Promise<AIChatResponse> {\n return tracing.withSpanAsync(\n \"ai.chat\",\n async (span) => {\n const start = Date.now();\n if (request.model) {\n span.setAttribute(\"ai.model\", request.model);\n }\n\n try {\n const response = await ai.chat(request);\n span.setAttributes({\n \"ai.provider\": response.provider,\n \"ai.model\": response.model,\n \"ai.input_tokens\": response.usage.promptTokens,\n \"ai.output_tokens\": response.usage.completionTokens,\n \"ai.total_tokens\": response.usage.totalTokens,\n \"ai.cost_usd\": response.usage.estimatedCostUsd,\n \"ai.finish_reason\": response.finishReason,\n \"ai.latency_ms\": Date.now() - start,\n });\n span.setStatus({ code: \"ok\" });\n return response;\n } catch (err) {\n span.setAttributes({\n \"ai.latency_ms\": Date.now() - start,\n });\n if (err instanceof Error) {\n span.recordException(err);\n }\n span.setStatus({\n code: \"error\",\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n },\n { kind: \"client\" },\n );\n },\n\n async *chatStream(request: AIChatRequest): AsyncIterable<AIStreamChunk> {\n // Delegate directly — tracing streaming is complex and deferred to P4\n yield* ai.chatStream(request);\n },\n\n async chatWithCallback(\n request: AIChatRequest,\n callback: AIStreamCallback,\n ): Promise<AIChatResponse> {\n return tracing.withSpanAsync(\n \"ai.chatWithCallback\",\n async (span) => {\n const start = Date.now();\n if (request.model) {\n span.setAttribute(\"ai.model\", request.model);\n }\n\n try {\n const response = await ai.chatWithCallback(request, callback);\n span.setAttributes({\n \"ai.provider\": response.provider,\n \"ai.model\": response.model,\n \"ai.input_tokens\": response.usage.promptTokens,\n \"ai.output_tokens\": response.usage.completionTokens,\n \"ai.total_tokens\": response.usage.totalTokens,\n \"ai.cost_usd\": response.usage.estimatedCostUsd,\n \"ai.finish_reason\": response.finishReason,\n \"ai.latency_ms\": Date.now() - start,\n });\n span.setStatus({ code: \"ok\" });\n return response;\n } catch (err) {\n span.setAttributes({\n \"ai.latency_ms\": Date.now() - start,\n });\n if (err instanceof Error) {\n span.recordException(err);\n }\n span.setStatus({\n code: \"error\",\n message: err instanceof Error ? err.message : String(err),\n });\n throw err;\n }\n },\n { kind: \"client\" },\n );\n },\n\n // Pass-through methods (no tracing needed for non-chat operations in v1)\n async complete(\n request: AICompletionRequest,\n ): Promise<AICompletionResponse> {\n return ai.complete(request);\n },\n\n async *completeStream(\n request: AICompletionRequest,\n ): AsyncIterable<AIStreamChunk> {\n yield* ai.completeStream(request);\n },\n\n async embed(request: AIEmbeddingRequest): Promise<AIEmbeddingResponse> {\n return ai.embed(request);\n },\n\n async similarity(\n text1: string,\n text2: string,\n model?: string,\n ): Promise<number> {\n return ai.similarity(text1, text2, model);\n },\n\n async listModels(): Promise<AIModelConfig[]> {\n return ai.listModels();\n },\n\n async getModel(modelId: string): Promise<AIModelConfig | null> {\n return ai.getModel(modelId);\n },\n\n async supportsCapability(\n modelId: string,\n capability: AIModelType,\n ): Promise<boolean> {\n return ai.supportsCapability(modelId, capability);\n },\n\n async estimateTokens(text: string, model?: string): Promise<number> {\n return ai.estimateTokens(text, model);\n },\n\n async estimateCost(\n request: AIChatRequest | AICompletionRequest | AIEmbeddingRequest,\n ): Promise<number> {\n return ai.estimateCost(request);\n },\n\n async healthCheck(): Promise<{\n healthy: boolean;\n providers: Record<\n AIProvider,\n { available: boolean; latencyMs?: number; error?: string }\n >;\n }> {\n return ai.healthCheck();\n },\n };\n}\n","/**\n * IAI - Core AI abstraction interface for platform-core\n *\n * Provides a vendor-agnostic interface for AI operations including:\n * - Text generation (chat, completion)\n * - Embeddings generation\n * - Model routing and fallback\n * - Streaming support\n * - Token usage tracking\n */\n\n// ═══════════════════════════════════════════════════════════════\n// CORE TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport type AIProvider =\n | \"openai\"\n | \"anthropic\"\n | \"google\"\n | \"azure\"\n | \"bedrock\"\n | \"custom\";\n\nexport type AIModelType =\n | \"chat\"\n | \"completion\"\n | \"embedding\"\n | \"image\"\n | \"audio\"\n | \"video\";\n\nexport type AIRole = \"system\" | \"user\" | \"assistant\" | \"function\" | \"tool\";\n\nexport interface AIMessage {\n role: AIRole;\n content: string;\n name?: string;\n toolCallId?: string;\n toolCalls?: AIToolCall[];\n}\n\nexport interface AIToolCall {\n id: string;\n type: \"function\";\n function: {\n name: string;\n arguments: string;\n };\n}\n\nexport interface AITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// MODEL CONFIGURATION\n// ═══════════════════════════════════════════════════════════════\n\nexport interface AIModelConfig {\n /** Model identifier (e.g., 'gpt-4', 'claude-3-opus') */\n modelId: string;\n /** Provider for this model */\n provider: AIProvider;\n /** Model capabilities */\n capabilities: AIModelType[];\n /** Maximum context window in tokens */\n maxContextTokens: number;\n /** Maximum output tokens */\n maxOutputTokens: number;\n /** Cost per 1K input tokens in USD */\n inputCostPer1K: number;\n /** Cost per 1K output tokens in USD */\n outputCostPer1K: number;\n /** Supports streaming */\n supportsStreaming: boolean;\n /** Supports function/tool calling */\n supportsTools: boolean;\n /** Supports vision (image input) */\n supportsVision: boolean;\n /** Rate limits per minute */\n rateLimits?: {\n requestsPerMinute: number;\n tokensPerMinute: number;\n };\n /** Custom endpoint for self-hosted models */\n endpoint?: string;\n /** Additional provider-specific config */\n providerConfig?: Record<string, unknown>;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// REQUEST/RESPONSE TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface AIChatRequest {\n messages: AIMessage[];\n model?: string;\n temperature?: number;\n maxTokens?: number;\n topP?: number;\n frequencyPenalty?: number;\n presencePenalty?: number;\n stop?: string | string[];\n tools?: AITool[];\n toolChoice?:\n | \"auto\"\n | \"none\"\n | \"required\"\n | { type: \"function\"; function: { name: string } };\n stream?: boolean;\n user?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AICompletionRequest {\n prompt: string;\n model?: string;\n temperature?: number;\n maxTokens?: number;\n topP?: number;\n frequencyPenalty?: number;\n presencePenalty?: number;\n stop?: string | string[];\n stream?: boolean;\n user?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AIEmbeddingRequest {\n input: string | string[];\n model?: string;\n dimensions?: number;\n user?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AIChatResponse {\n id: string;\n model: string;\n provider: AIProvider;\n choices: AIChatChoice[];\n usage: AIUsageInfo;\n created: Date;\n finishReason: AIFinishReason;\n}\n\nexport interface AIChatChoice {\n index: number;\n message: AIMessage;\n finishReason: AIFinishReason;\n}\n\nexport type AIFinishReason =\n | \"stop\"\n | \"length\"\n | \"tool_calls\"\n | \"content_filter\"\n | \"error\";\n\nexport interface AICompletionResponse {\n id: string;\n model: string;\n provider: AIProvider;\n text: string;\n usage: AIUsageInfo;\n created: Date;\n finishReason: AIFinishReason;\n}\n\nexport interface AIEmbeddingResponse {\n id: string;\n model: string;\n provider: AIProvider;\n embeddings: number[][];\n usage: AIUsageInfo;\n created: Date;\n}\n\nexport interface AIUsageInfo {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n estimatedCostUsd: number;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// STREAMING TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport interface AIStreamChunk {\n id: string;\n model: string;\n provider: AIProvider;\n delta: {\n content?: string;\n toolCalls?: AIToolCall[];\n role?: AIRole;\n };\n finishReason?: AIFinishReason;\n usage?: AIUsageInfo;\n}\n\nexport type AIStreamCallback = (chunk: AIStreamChunk) => void | Promise<void>;\n\n// ═══════════════════════════════════════════════════════════════\n// ROUTING CONFIGURATION\n// ═══════════════════════════════════════════════════════════════\n\nexport type RoutingStrategy =\n | \"priority\"\n | \"round-robin\"\n | \"least-latency\"\n | \"cost-optimized\"\n | \"random\";\n\nexport interface AIRouterConfig {\n /** Primary model to use */\n primaryModel: string;\n /** Fallback models in order of preference */\n fallbackModels?: string[];\n /** Routing strategy for load balancing */\n strategy?: RoutingStrategy;\n /** Maximum retries before failing */\n maxRetries?: number;\n /** Timeout in milliseconds */\n timeoutMs?: number;\n /** Enable automatic fallback on errors */\n autoFallback?: boolean;\n /** Cost budget per request in USD (for cost-optimized routing) */\n costBudget?: number;\n /** Custom routing function */\n customRouter?: (request: AIChatRequest | AICompletionRequest) => string;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// ERROR TYPES\n// ═══════════════════════════════════════════════════════════════\n\nexport type AIErrorCode =\n | \"invalid_request\"\n | \"authentication_error\"\n | \"rate_limit_exceeded\"\n | \"quota_exceeded\"\n | \"model_not_found\"\n | \"context_length_exceeded\"\n | \"content_filter\"\n | \"server_error\"\n | \"timeout\"\n | \"network_error\"\n | \"provider_unavailable\"\n | \"unknown\";\n\nexport interface AIError extends Error {\n code: AIErrorCode;\n provider?: AIProvider;\n model?: string;\n statusCode?: number;\n retryable: boolean;\n retryAfterMs?: number;\n}\n\nexport const AIErrorMessages: Record<AIErrorCode, string> = {\n invalid_request: \"The request was invalid or malformed\",\n authentication_error: \"Authentication failed - check your API key\",\n rate_limit_exceeded: \"Rate limit exceeded - try again later\",\n quota_exceeded: \"Usage quota exceeded for this billing period\",\n model_not_found: \"The specified model was not found\",\n context_length_exceeded: \"Input exceeds the model context length\",\n content_filter: \"Content was filtered due to policy violations\",\n server_error: \"The AI provider encountered an internal error\",\n timeout: \"The request timed out\",\n network_error: \"Network error connecting to AI provider\",\n provider_unavailable: \"The AI provider is currently unavailable\",\n unknown: \"An unknown error occurred\",\n};\n\nexport function createAIError(\n code: AIErrorCode,\n message?: string,\n options?: {\n provider?: AIProvider;\n model?: string;\n statusCode?: number;\n retryable?: boolean;\n retryAfterMs?: number;\n cause?: Error;\n },\n): AIError {\n const error = new Error(message || AIErrorMessages[code]) as AIError;\n error.name = \"AIError\";\n error.code = code;\n error.provider = options?.provider;\n error.model = options?.model;\n error.statusCode = options?.statusCode;\n error.retryable =\n options?.retryable ??\n [\n \"rate_limit_exceeded\",\n \"server_error\",\n \"timeout\",\n \"network_error\",\n ].includes(code);\n error.retryAfterMs = options?.retryAfterMs;\n if (options?.cause) {\n error.cause = options.cause;\n }\n return error;\n}\n\nexport function isAIError(error: unknown): error is AIError {\n return (\n error instanceof Error &&\n \"code\" in error &&\n typeof (error as AIError).retryable === \"boolean\"\n );\n}\n\n// ═══════════════════════════════════════════════════════════════\n// AI CONFIGURATION\n// ═══════════════════════════════════════════════════════════════\n\nexport interface AIConfig {\n /** Default model for chat requests */\n defaultChatModel?: string;\n /** Default model for completion requests */\n defaultCompletionModel?: string;\n /** Default model for embedding requests */\n defaultEmbeddingModel?: string;\n /** Router configuration */\n router?: AIRouterConfig;\n /** Available model configurations */\n models?: AIModelConfig[];\n /** API keys for providers */\n apiKeys?: Partial<Record<AIProvider, string>>;\n /** Default timeout in milliseconds */\n defaultTimeoutMs?: number;\n /** Enable request/response logging */\n enableLogging?: boolean;\n /** Cache embeddings */\n cacheEmbeddings?: boolean;\n /** Embedding cache TTL in seconds */\n embeddingCacheTtlSeconds?: number;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// MAIN INTERFACE\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * IAI - Core AI interface\n *\n * @example\n * ```typescript\n * const ai = platform.ai;\n *\n * // Chat completion\n * const response = await ai.chat({\n * messages: [\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Hello!' }\n * ]\n * });\n *\n * // Streaming chat\n * for await (const chunk of ai.chatStream({\n * messages: [{ role: 'user', content: 'Tell me a story' }]\n * })) {\n * process.stdout.write(chunk.delta.content || '');\n * }\n *\n * // Embeddings\n * const embeddings = await ai.embed({\n * input: ['Hello world', 'Goodbye world']\n * });\n * ```\n */\nexport interface IAI {\n // ─────────────────────────────────────────────────────────────\n // Chat Operations\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Send a chat completion request\n */\n chat(request: AIChatRequest): Promise<AIChatResponse>;\n\n /**\n * Send a streaming chat completion request\n */\n chatStream(request: AIChatRequest): AsyncIterable<AIStreamChunk>;\n\n /**\n * Send a chat completion request with callback-based streaming\n */\n chatWithCallback(\n request: AIChatRequest,\n callback: AIStreamCallback,\n ): Promise<AIChatResponse>;\n\n // ─────────────────────────────────────────────────────────────\n // Completion Operations\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Send a text completion request\n */\n complete(request: AICompletionRequest): Promise<AICompletionResponse>;\n\n /**\n * Send a streaming completion request\n */\n completeStream(request: AICompletionRequest): AsyncIterable<AIStreamChunk>;\n\n // ─────────────────────────────────────────────────────────────\n // Embedding Operations\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Generate embeddings for text\n */\n embed(request: AIEmbeddingRequest): Promise<AIEmbeddingResponse>;\n\n /**\n * Calculate similarity between two texts\n */\n similarity(text1: string, text2: string, model?: string): Promise<number>;\n\n // ─────────────────────────────────────────────────────────────\n // Model Management\n // ─────────────────────────────────────────────────────────────\n\n /**\n * List available models\n */\n listModels(): Promise<AIModelConfig[]>;\n\n /**\n * Get model configuration\n */\n getModel(modelId: string): Promise<AIModelConfig | null>;\n\n /**\n * Check if a model supports a capability\n */\n supportsCapability(\n modelId: string,\n capability: AIModelType,\n ): Promise<boolean>;\n\n /**\n * Estimate tokens for text\n */\n estimateTokens(text: string, model?: string): Promise<number>;\n\n /**\n * Estimate cost for a request\n */\n estimateCost(\n request: AIChatRequest | AICompletionRequest | AIEmbeddingRequest,\n ): Promise<number>;\n\n // ─────────────────────────────────────────────────────────────\n // Health & Status\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Check if AI service is healthy\n */\n healthCheck(): Promise<{\n healthy: boolean;\n providers: Record<\n AIProvider,\n { available: boolean; latencyMs?: number; error?: string }\n >;\n }>;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// MEMORY IMPLEMENTATION\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * MemoryAI - In-memory implementation for testing\n */\nexport class MemoryAI implements IAI {\n private models: AIModelConfig[] = [];\n private responses: Map<string, AIChatResponse | AICompletionResponse> =\n new Map();\n private embeddings: Map<string, number[]> = new Map();\n private requestLog: Array<{\n type: string;\n request: unknown;\n timestamp: Date;\n }> = [];\n\n constructor(private config: AIConfig = {}) {\n // Initialize with default models\n this.models = config.models || [\n {\n modelId: \"gpt-4\",\n provider: \"openai\",\n capabilities: [\"chat\", \"completion\"],\n maxContextTokens: 128000,\n maxOutputTokens: 4096,\n inputCostPer1K: 0.03,\n outputCostPer1K: 0.06,\n supportsStreaming: true,\n supportsTools: true,\n supportsVision: true,\n },\n {\n modelId: \"claude-3-opus\",\n provider: \"anthropic\",\n capabilities: [\"chat\"],\n maxContextTokens: 200000,\n maxOutputTokens: 4096,\n inputCostPer1K: 0.015,\n outputCostPer1K: 0.075,\n supportsStreaming: true,\n supportsTools: true,\n supportsVision: true,\n },\n {\n modelId: \"text-embedding-3-small\",\n provider: \"openai\",\n capabilities: [\"embedding\"],\n maxContextTokens: 8191,\n maxOutputTokens: 0,\n inputCostPer1K: 0.00002,\n outputCostPer1K: 0,\n supportsStreaming: false,\n supportsTools: false,\n supportsVision: false,\n },\n ];\n }\n\n // ─────────────────────────────────────────────────────────────\n // Test Helpers\n // ─────────────────────────────────────────────────────────────\n\n setResponse(\n key: string,\n response: AIChatResponse | AICompletionResponse,\n ): void {\n this.responses.set(key, response);\n }\n\n setEmbedding(text: string, embedding: number[]): void {\n this.embeddings.set(text, embedding);\n }\n\n getRequestLog(): Array<{ type: string; request: unknown; timestamp: Date }> {\n return [...this.requestLog];\n }\n\n clearRequestLog(): void {\n this.requestLog = [];\n }\n\n // ─────────────────────────────────────────────────────────────\n // Chat Operations\n // ─────────────────────────────────────────────────────────────\n\n async chat(request: AIChatRequest): Promise<AIChatResponse> {\n this.requestLog.push({ type: \"chat\", request, timestamp: new Date() });\n\n const model = request.model || this.config.defaultChatModel || \"gpt-4\";\n const lastMessage = request.messages[request.messages.length - 1];\n const key = `${model}:${lastMessage?.content}`;\n\n if (this.responses.has(key)) {\n return this.responses.get(key) as AIChatResponse;\n }\n\n // Generate mock response\n const response: AIChatResponse = {\n id: `chatcmpl-${Date.now()}`,\n model,\n provider: \"openai\",\n choices: [\n {\n index: 0,\n message: {\n role: \"assistant\",\n content: `Mock response to: ${lastMessage?.content || \"empty\"}`,\n },\n finishReason: \"stop\",\n },\n ],\n usage: {\n promptTokens: this.estimateTokensSync(\n request.messages.map((m) => m.content).join(\" \"),\n ),\n completionTokens: 20,\n totalTokens: 0,\n estimatedCostUsd: 0,\n },\n created: new Date(),\n finishReason: \"stop\",\n };\n\n response.usage.totalTokens =\n response.usage.promptTokens + response.usage.completionTokens;\n response.usage.estimatedCostUsd = this.calculateCost(model, response.usage);\n\n return response;\n }\n\n async *chatStream(request: AIChatRequest): AsyncIterable<AIStreamChunk> {\n this.requestLog.push({\n type: \"chatStream\",\n request,\n timestamp: new Date(),\n });\n\n const model = request.model || this.config.defaultChatModel || \"gpt-4\";\n const lastMessage = request.messages[request.messages.length - 1];\n const responseText = `Mock streaming response to: ${lastMessage?.content || \"empty\"}`;\n const words = responseText.split(\" \");\n\n for (let i = 0; i < words.length; i++) {\n yield {\n id: `chatcmpl-${Date.now()}`,\n model,\n provider: \"openai\",\n delta: {\n content: (i > 0 ? \" \" : \"\") + words[i],\n role: i === 0 ? \"assistant\" : undefined,\n },\n finishReason: i === words.length - 1 ? \"stop\" : undefined,\n };\n }\n }\n\n async chatWithCallback(\n request: AIChatRequest,\n callback: AIStreamCallback,\n ): Promise<AIChatResponse> {\n let fullContent = \"\";\n\n for await (const chunk of this.chatStream(request)) {\n await callback(chunk);\n if (chunk.delta.content) {\n fullContent += chunk.delta.content;\n }\n }\n\n const model = request.model || this.config.defaultChatModel || \"gpt-4\";\n\n return {\n id: `chatcmpl-${Date.now()}`,\n model,\n provider: \"openai\",\n choices: [\n {\n index: 0,\n message: { role: \"assistant\", content: fullContent },\n finishReason: \"stop\",\n },\n ],\n usage: {\n promptTokens: this.estimateTokensSync(\n request.messages.map((m) => m.content).join(\" \"),\n ),\n completionTokens: this.estimateTokensSync(fullContent),\n totalTokens: 0,\n estimatedCostUsd: 0,\n },\n created: new Date(),\n finishReason: \"stop\",\n };\n }\n\n // ─────────────────────────────────────────────────────────────\n // Completion Operations\n // ─────────────────────────────────────────────────────────────\n\n async complete(request: AICompletionRequest): Promise<AICompletionResponse> {\n this.requestLog.push({ type: \"complete\", request, timestamp: new Date() });\n\n const model =\n request.model || this.config.defaultCompletionModel || \"gpt-4\";\n const key = `completion:${model}:${request.prompt}`;\n\n if (this.responses.has(key)) {\n return this.responses.get(key) as AICompletionResponse;\n }\n\n const response: AICompletionResponse = {\n id: `cmpl-${Date.now()}`,\n model,\n provider: \"openai\",\n text: `Mock completion of: ${request.prompt.substring(0, 50)}...`,\n usage: {\n promptTokens: this.estimateTokensSync(request.prompt),\n completionTokens: 20,\n totalTokens: 0,\n estimatedCostUsd: 0,\n },\n created: new Date(),\n finishReason: \"stop\",\n };\n\n response.usage.totalTokens =\n response.usage.promptTokens + response.usage.completionTokens;\n response.usage.estimatedCostUsd = this.calculateCost(model, response.usage);\n\n return response;\n }\n\n async *completeStream(\n request: AICompletionRequest,\n ): AsyncIterable<AIStreamChunk> {\n this.requestLog.push({\n type: \"completeStream\",\n request,\n timestamp: new Date(),\n });\n\n const model =\n request.model || this.config.defaultCompletionModel || \"gpt-4\";\n const responseText = `Mock streaming completion of: ${request.prompt.substring(0, 30)}...`;\n const words = responseText.split(\" \");\n\n for (let i = 0; i < words.length; i++) {\n yield {\n id: `cmpl-${Date.now()}`,\n model,\n provider: \"openai\",\n delta: {\n content: (i > 0 ? \" \" : \"\") + words[i],\n },\n finishReason: i === words.length - 1 ? \"stop\" : undefined,\n };\n }\n }\n\n // ─────────────────────────────────────────────────────────────\n // Embedding Operations\n // ─────────────────────────────────────────────────────────────\n\n async embed(request: AIEmbeddingRequest): Promise<AIEmbeddingResponse> {\n this.requestLog.push({ type: \"embed\", request, timestamp: new Date() });\n\n const model =\n request.model ||\n this.config.defaultEmbeddingModel ||\n \"text-embedding-3-small\";\n const inputs = Array.isArray(request.input)\n ? request.input\n : [request.input];\n const dimensions = request.dimensions || 1536;\n\n const embeddings = inputs.map((text) => {\n if (this.embeddings.has(text)) {\n return this.embeddings.get(text)!;\n }\n // Generate deterministic mock embedding based on text hash\n return this.generateMockEmbedding(text, dimensions);\n });\n\n return {\n id: `emb-${Date.now()}`,\n model,\n provider: \"openai\",\n embeddings,\n usage: {\n promptTokens: inputs.reduce(\n (sum, t) => sum + this.estimateTokensSync(t),\n 0,\n ),\n completionTokens: 0,\n totalTokens: 0,\n estimatedCostUsd: 0,\n },\n created: new Date(),\n };\n }\n\n async similarity(\n text1: string,\n text2: string,\n model?: string,\n ): Promise<number> {\n const response = await this.embed({ input: [text1, text2], model });\n const [emb1, emb2] = response.embeddings;\n return this.cosineSimilarity(emb1!, emb2!);\n }\n\n // ─────────────────────────────────────────────────────────────\n // Model Management\n // ─────────────────────────────────────────────────────────────\n\n async listModels(): Promise<AIModelConfig[]> {\n return [...this.models];\n }\n\n async getModel(modelId: string): Promise<AIModelConfig | null> {\n return this.models.find((m) => m.modelId === modelId) || null;\n }\n\n async supportsCapability(\n modelId: string,\n capability: AIModelType,\n ): Promise<boolean> {\n const model = await this.getModel(modelId);\n return model?.capabilities.includes(capability) ?? false;\n }\n\n async estimateTokens(text: string, _model?: string): Promise<number> {\n return this.estimateTokensSync(text);\n }\n\n async estimateCost(\n request: AIChatRequest | AICompletionRequest | AIEmbeddingRequest,\n ): Promise<number> {\n let model: string;\n let inputTokens: number;\n\n if (\"messages\" in request) {\n model = request.model || this.config.defaultChatModel || \"gpt-4\";\n inputTokens = this.estimateTokensSync(\n request.messages.map((m) => m.content).join(\" \"),\n );\n } else if (\"prompt\" in request) {\n model = request.model || this.config.defaultCompletionModel || \"gpt-4\";\n inputTokens = this.estimateTokensSync(request.prompt);\n } else {\n model =\n request.model ||\n this.config.defaultEmbeddingModel ||\n \"text-embedding-3-small\";\n const inputs = Array.isArray(request.input)\n ? request.input\n : [request.input];\n inputTokens = inputs.reduce(\n (sum, t) => sum + this.estimateTokensSync(t),\n 0,\n );\n }\n\n const modelConfig = await this.getModel(model);\n if (!modelConfig) return 0;\n\n const estimatedOutputTokens =\n \"messages\" in request || \"prompt\" in request ? 100 : 0;\n return (\n (inputTokens / 1000) * modelConfig.inputCostPer1K +\n (estimatedOutputTokens / 1000) * modelConfig.outputCostPer1K\n );\n }\n\n // ─────────────────────────────────────────────────────────────\n // Health & Status\n // ─────────────────────────────────────────────────────────────\n\n async healthCheck(): Promise<{\n healthy: boolean;\n providers: Record<\n AIProvider,\n { available: boolean; latencyMs?: number; error?: string }\n >;\n }> {\n return {\n healthy: true,\n providers: {\n openai: { available: true, latencyMs: 50 },\n anthropic: { available: true, latencyMs: 60 },\n google: { available: true, latencyMs: 55 },\n azure: { available: false, error: \"Not configured\" },\n bedrock: { available: false, error: \"Not configured\" },\n custom: { available: false, error: \"Not configured\" },\n },\n };\n }\n\n // ─────────────────────────────────────────────────────────────\n // Private Helpers\n // ─────────────────────────────────────────────────────────────\n\n private estimateTokensSync(text: string): number {\n // Rough approximation: ~4 characters per token\n return Math.ceil(text.length / 4);\n }\n\n private calculateCost(modelId: string, usage: AIUsageInfo): number {\n const model = this.models.find((m) => m.modelId === modelId);\n if (!model) return 0;\n\n return (\n (usage.promptTokens / 1000) * model.inputCostPer1K +\n (usage.completionTokens / 1000) * model.outputCostPer1K\n );\n }\n\n private generateMockEmbedding(text: string, dimensions: number): number[] {\n // Generate deterministic embedding based on text hash\n const embedding: number[] = [];\n let hash = 0;\n for (let i = 0; i < text.length; i++) {\n hash = (hash << 5) - hash + text.charCodeAt(i);\n hash = hash & hash;\n }\n\n for (let i = 0; i < dimensions; i++) {\n // Generate pseudo-random values based on hash and index\n const seed = hash + i * 31;\n embedding.push(Math.sin(seed) * 0.5);\n }\n\n // Normalize to unit vector\n const magnitude = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));\n return embedding.map((v) => v / magnitude);\n }\n\n private cosineSimilarity(a: number[], b: number[]): number {\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n\n return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));\n }\n}\n","/**\n * Ollama Adapter — Local LLM inference via Ollama REST API\n *\n * Zero API cost. Runs against a local Ollama instance.\n * Supports chat, completion, and embedding operations.\n *\n * @example\n * ```typescript\n * import { OllamaAdapter } from '@digilogiclabs/platform-core';\n *\n * const ai = new OllamaAdapter({ baseUrl: 'http://localhost:11434' });\n * const response = await ai.chat({\n * messages: [{ role: 'user', content: 'Hello' }],\n * model: 'qwen2.5:3b',\n * });\n * ```\n */\n\nimport type {\n IAI,\n AIConfig,\n AIChatRequest,\n AIChatResponse,\n AICompletionRequest,\n AICompletionResponse,\n AIEmbeddingRequest,\n AIEmbeddingResponse,\n AIStreamChunk,\n AIStreamCallback,\n AIModelConfig,\n AIModelType,\n AIProvider,\n AIUsageInfo,\n AIMessage,\n} from \"../../interfaces/IAI\";\nimport { createAIError } from \"../../interfaces/IAI\";\n\n// ═══════════════════════════════════════════════════════════════\n// CONFIGURATION\n// ═══════════════════════════════════════════════════════════════\n\nexport interface OllamaAdapterConfig {\n /** Ollama API base URL @default \"http://localhost:11434\" */\n baseUrl?: string;\n /** Default chat model @default \"qwen2.5:3b\" */\n defaultModel?: string;\n /** Default embedding model @default \"nomic-embed-text\" */\n defaultEmbeddingModel?: string;\n /** Request timeout in ms @default 120000 */\n timeoutMs?: number;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// OLLAMA API TYPES\n// ═══════════════════════════════════════════════════════════════\n\ninterface OllamaChatRequest {\n model: string;\n messages: Array<{ role: string; content: string }>;\n stream: boolean;\n options?: {\n temperature?: number;\n num_predict?: number;\n top_p?: number;\n stop?: string[];\n };\n}\n\ninterface OllamaChatResponse {\n message: { role: string; content: string };\n prompt_eval_count?: number;\n eval_count?: number;\n total_duration?: number;\n model: string;\n}\n\ninterface OllamaEmbedRequest {\n model: string;\n input: string | string[];\n}\n\ninterface OllamaEmbedResponse {\n embeddings: number[][];\n}\n\ninterface OllamaTagsResponse {\n models: Array<{\n name: string;\n size: number;\n details?: { parameter_size?: string };\n }>;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// ADAPTER\n// ═══════════════════════════════════════════════════════════════\n\nexport class OllamaAdapter implements IAI {\n private readonly baseUrl: string;\n private readonly defaultModel: string;\n private readonly defaultEmbeddingModel: string;\n private readonly timeoutMs: number;\n\n constructor(config?: OllamaAdapterConfig) {\n this.baseUrl = (config?.baseUrl ?? \"http://localhost:11434\").replace(\n /\\/$/,\n \"\",\n );\n this.defaultModel = config?.defaultModel ?? \"qwen2.5:3b\";\n this.defaultEmbeddingModel =\n config?.defaultEmbeddingModel ?? \"nomic-embed-text\";\n this.timeoutMs = config?.timeoutMs ?? 120000;\n }\n\n async chat(request: AIChatRequest): Promise<AIChatResponse> {\n const model = request.model ?? this.defaultModel;\n\n const ollamaReq: OllamaChatRequest = {\n model,\n messages: request.messages.map((m) => ({\n role: m.role === \"tool\" ? \"assistant\" : m.role,\n content: m.content,\n })),\n stream: false,\n options: {\n temperature: request.temperature,\n num_predict: request.maxTokens,\n top_p: request.topP,\n stop: request.stop\n ? Array.isArray(request.stop)\n ? request.stop\n : [request.stop]\n : undefined,\n },\n };\n\n const response = await this.fetch<OllamaChatResponse>(\n \"/api/chat\",\n ollamaReq,\n );\n\n const usage: AIUsageInfo = {\n promptTokens: response.prompt_eval_count ?? 0,\n completionTokens: response.eval_count ?? 0,\n totalTokens:\n (response.prompt_eval_count ?? 0) + (response.eval_count ?? 0),\n estimatedCostUsd: 0, // Local — zero cost\n };\n\n return {\n id: `ollama-${Date.now()}`,\n model: response.model ?? model,\n provider: \"custom\",\n choices: [\n {\n index: 0,\n message: {\n role: \"assistant\",\n content: response.message.content,\n },\n finishReason: \"stop\",\n },\n ],\n usage,\n created: new Date(),\n finishReason: \"stop\",\n };\n }\n\n async *chatStream(request: AIChatRequest): AsyncIterable<AIStreamChunk> {\n const model = request.model ?? this.defaultModel;\n\n const ollamaReq: OllamaChatRequest = {\n model,\n messages: request.messages.map((m) => ({\n role: m.role === \"tool\" ? \"assistant\" : m.role,\n content: m.content,\n })),\n stream: true,\n options: {\n temperature: request.temperature,\n num_predict: request.maxTokens,\n top_p: request.topP,\n },\n };\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const res = await fetch(`${this.baseUrl}/api/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(ollamaReq),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n throw createAIError(\"server_error\", `Ollama error: ${res.status}`);\n }\n\n const reader = res.body?.getReader();\n if (!reader) return;\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n const chunk = JSON.parse(line) as {\n message?: { content: string };\n done: boolean;\n };\n\n yield {\n id: `ollama-stream-${Date.now()}`,\n model,\n provider: \"custom\",\n delta: {\n content: chunk.message?.content ?? \"\",\n },\n finishReason: chunk.done ? \"stop\" : undefined,\n };\n }\n }\n } finally {\n clearTimeout(timer);\n }\n }\n\n async chatWithCallback(\n request: AIChatRequest,\n callback: AIStreamCallback,\n ): Promise<AIChatResponse> {\n let fullContent = \"\";\n\n for await (const chunk of this.chatStream(request)) {\n await callback(chunk);\n if (chunk.delta.content) {\n fullContent += chunk.delta.content;\n }\n }\n\n const model = request.model ?? this.defaultModel;\n\n return {\n id: `ollama-${Date.now()}`,\n model,\n provider: \"custom\",\n choices: [\n {\n index: 0,\n message: { role: \"assistant\", content: fullContent },\n finishReason: \"stop\",\n },\n ],\n usage: {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n estimatedCostUsd: 0,\n },\n created: new Date(),\n finishReason: \"stop\",\n };\n }\n\n async complete(request: AICompletionRequest): Promise<AICompletionResponse> {\n const response = await this.chat({\n messages: [{ role: \"user\", content: request.prompt }],\n model: request.model,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n });\n\n return {\n id: response.id,\n model: response.model,\n provider: \"custom\",\n text: response.choices[0]?.message.content ?? \"\",\n usage: response.usage,\n created: response.created,\n finishReason: response.finishReason,\n };\n }\n\n async *completeStream(\n request: AICompletionRequest,\n ): AsyncIterable<AIStreamChunk> {\n yield* this.chatStream({\n messages: [{ role: \"user\", content: request.prompt }],\n model: request.model,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n });\n }\n\n async embed(request: AIEmbeddingRequest): Promise<AIEmbeddingResponse> {\n const model = request.model ?? this.defaultEmbeddingModel;\n\n const response = await this.fetch<OllamaEmbedResponse>(\"/api/embed\", {\n model,\n input: request.input,\n });\n\n return {\n id: `ollama-emb-${Date.now()}`,\n model,\n provider: \"custom\",\n embeddings: response.embeddings,\n usage: {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n estimatedCostUsd: 0,\n },\n created: new Date(),\n };\n }\n\n async similarity(\n text1: string,\n text2: string,\n model?: string,\n ): Promise<number> {\n const response = await this.embed({ input: [text1, text2], model });\n const [a, b] = response.embeddings;\n if (!a || !b) return 0;\n\n let dot = 0,\n normA = 0,\n normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n }\n\n async listModels(): Promise<AIModelConfig[]> {\n try {\n const response = await this.fetch<OllamaTagsResponse>(\n \"/api/tags\",\n null,\n \"GET\",\n );\n return response.models.map((m) => ({\n modelId: m.name,\n provider: \"custom\" as AIProvider,\n capabilities: [\"chat\", \"completion\"] as AIModelType[],\n maxContextTokens: 4096,\n maxOutputTokens: 2048,\n inputCostPer1K: 0,\n outputCostPer1K: 0,\n supportsStreaming: true,\n supportsTools: false,\n supportsVision: false,\n }));\n } catch {\n return [];\n }\n }\n\n async getModel(modelId: string): Promise<AIModelConfig | null> {\n const models = await this.listModels();\n return models.find((m) => m.modelId === modelId) ?? null;\n }\n\n async supportsCapability(\n modelId: string,\n capability: AIModelType,\n ): Promise<boolean> {\n const model = await this.getModel(modelId);\n return model?.capabilities.includes(capability) ?? false;\n }\n\n async estimateTokens(text: string, _model?: string): Promise<number> {\n return Math.ceil(text.length / 4);\n }\n\n async estimateCost(): Promise<number> {\n return 0; // Local inference — always free\n }\n\n async healthCheck(): Promise<{\n healthy: boolean;\n providers: Record<\n AIProvider,\n { available: boolean; latencyMs?: number; error?: string }\n >;\n }> {\n try {\n const start = Date.now();\n await this.fetch<OllamaTagsResponse>(\"/api/tags\", null, \"GET\");\n const latency = Date.now() - start;\n\n return {\n healthy: true,\n providers: {\n custom: { available: true, latencyMs: latency },\n openai: { available: false, error: \"Not configured\" },\n anthropic: { available: false, error: \"Not configured\" },\n google: { available: false, error: \"Not configured\" },\n azure: { available: false, error: \"Not configured\" },\n bedrock: { available: false, error: \"Not configured\" },\n },\n };\n } catch (err) {\n return {\n healthy: false,\n providers: {\n custom: {\n available: false,\n error: err instanceof Error ? err.message : \"Ollama unavailable\",\n },\n openai: { available: false, error: \"Not configured\" },\n anthropic: { available: false, error: \"Not configured\" },\n google: { available: false, error: \"Not configured\" },\n azure: { available: false, error: \"Not configured\" },\n bedrock: { available: false, error: \"Not configured\" },\n },\n };\n }\n }\n\n private async fetch<T>(\n path: string,\n body: unknown,\n method: string = \"POST\",\n ): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers: body ? { \"Content-Type\": \"application/json\" } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw createAIError(\n \"server_error\",\n `Ollama ${method} ${path}: ${res.status} ${text}`,\n );\n }\n\n return (await res.json()) as T;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n"],"mappings":"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","names":["event"]}