@directive-run/ai 0.2.0 → 0.3.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.
- package/README.md +26 -31
- package/dist/anthropic.cjs +1 -1
- package/dist/anthropic.cjs.map +1 -1
- package/dist/anthropic.d.cts +5 -9
- package/dist/anthropic.d.ts +5 -9
- package/dist/anthropic.js +1 -1
- package/dist/anthropic.js.map +1 -1
- package/dist/gemini.cjs +3 -0
- package/dist/gemini.cjs.map +1 -0
- package/dist/gemini.d.cts +93 -0
- package/dist/gemini.d.ts +93 -0
- package/dist/gemini.js +3 -0
- package/dist/gemini.js.map +1 -0
- package/dist/index.cjs +117 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1376 -2106
- package/dist/index.d.ts +1376 -2106
- package/dist/index.js +117 -45
- package/dist/index.js.map +1 -1
- package/dist/multi-agent-orchestrator-CxL8ycw_.d.cts +2290 -0
- package/dist/multi-agent-orchestrator-uMp8bLfV.d.ts +2290 -0
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts +3 -2
- package/dist/ollama.d.ts +3 -2
- package/dist/ollama.js.map +1 -1
- package/dist/openai.cjs +2 -2
- package/dist/openai.cjs.map +1 -1
- package/dist/openai.d.cts +4 -8
- package/dist/openai.d.ts +4 -8
- package/dist/openai.js +2 -2
- package/dist/openai.js.map +1 -1
- package/dist/semantic-cache-F0psCRuz.d.cts +271 -0
- package/dist/semantic-cache-F0psCRuz.d.ts +271 -0
- package/dist/testing.cjs +42 -7
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +365 -5
- package/dist/testing.d.ts +365 -5
- package/dist/testing.js +42 -7
- package/dist/testing.js.map +1 -1
- package/dist/types-Co4BzMiH.d.cts +1373 -0
- package/dist/types-Co4BzMiH.d.ts +1373 -0
- package/package.json +7 -2
- package/dist/types-Bbar7yKz.d.cts +0 -304
- package/dist/types-Bbar7yKz.d.ts +0 -304
package/dist/openai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/helpers.ts","../src/adapters/openai.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","OPENAI_PRICING","createOpenAIRunner","apiKey","model","maxTokens","timeoutMs","_input","m","res","data","inputTokens","outputTokens","createOpenAIEmbedder","dimensions","entry","createOpenAIStreamingRunner","callbacks","reader","decoder","buf","fullText","promptTokens","completionTokens","done","value","lines","line","event","delta","parseErr","assistantMsg","totalTokens"],"mappings":"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","file":"openai.js","sourcesContent":["/**\n * Helper functions for AI adapter — createRunner, estimateCost, state queries, validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n RunResult,\n RunOptions,\n Message,\n TokenUsage,\n AgentState,\n ApprovalState,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/** Check if agent is currently running. */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/** Check if there are pending approvals. */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Get total cost estimate based on token usage.\n *\n * @param tokenUsage - Total token count\n * @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)\n * @returns Estimated cost in dollars\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a baseURL uses http or https.\n * Throws immediately at adapter creation time (not at call time) to catch config errors early.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[]\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[]\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner from buildRequest/parseResponse helpers.\n * Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = { role: \"assistant\", content: parsed.text };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/openai\n *\n * OpenAI adapter for Directive AI. Provides runners and embedders\n * for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * @example\n * ```typescript\n * import { createOpenAIRunner, createOpenAIEmbedder } from '@directive-run/ai/openai';\n *\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../helpers.js\";\nimport type { AdapterHooks, AgentRunner, Message, TokenUsage } from \"../types.js\";\nimport type { StreamingCallbackRunner } from \"../stack.js\";\nimport type { EmbedderFn, Embedding } from \"../guardrails/semantic-cache.js\";\n\n// ============================================================================\n// Pricing Constants\n// ============================================================================\n\n/**\n * OpenAI model pricing (USD per million tokens).\n *\n * Use with `estimateCost()` for per-call cost tracking:\n * ```typescript\n * import { estimateCost } from '@directive-run/ai';\n * import { OPENAI_PRICING } from '@directive-run/ai/openai';\n *\n * const cost =\n * estimateCost(result.tokenUsage!.inputTokens, OPENAI_PRICING[\"gpt-4o\"].input) +\n * estimateCost(result.tokenUsage!.outputTokens, OPENAI_PRICING[\"gpt-4o\"].output);\n * ```\n *\n * **Note:** Pricing changes over time. These values are provided as a convenience\n * and may not reflect the latest rates. Always verify at https://openai.com/pricing\n */\nexport const OPENAI_PRICING: Record<string, { input: number; output: number }> = {\n\t\"gpt-4o\": { input: 2.5, output: 10 },\n\t\"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n\t\"gpt-4-turbo\": { input: 10, output: 30 },\n\t\"o3-mini\": { input: 1.1, output: 4.4 },\n};\n\n// ============================================================================\n// OpenAI Runner\n// ============================================================================\n\n/** Options for createOpenAIRunner */\nexport interface OpenAIRunnerOptions {\n\tapiKey: string;\n\tmodel?: string;\n\tmaxTokens?: number;\n\tbaseURL?: string;\n\tfetch?: typeof globalThis.fetch;\n\t/** @default undefined */\n\ttimeoutMs?: number;\n\t/** Lifecycle hooks for tracing, logging, and metrics */\n\thooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * // OpenAI\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n *\n * // Azure OpenAI\n * const azure = createOpenAIRunner({\n * apiKey: process.env.AZURE_KEY!,\n * baseURL: \"https://your-resource.openai.azure.com/v1\",\n * });\n *\n * // Together.ai (OpenAI-compatible)\n * const together = createOpenAIRunner({\n * apiKey: process.env.TOGETHER_KEY!,\n * baseURL: \"https://api.together.xyz/v1\",\n * });\n * ```\n */\nexport function createOpenAIRunner(options: OpenAIRunnerOptions): AgentRunner {\n\tconst {\n\t\tapiKey,\n\t\tmodel = \"gpt-4o\",\n\t\tmaxTokens,\n\t\tbaseURL = \"https://api.openai.com/v1\",\n\t\tfetch: fetchFn = globalThis.fetch,\n\t\ttimeoutMs,\n\t\thooks,\n\t} = options;\n\n\tvalidateBaseURL(baseURL);\n\n\tif (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && !apiKey) {\n\t\tconsole.warn(\"[Directive] createOpenAIRunner: apiKey is empty. API calls will fail.\");\n\t}\n\n\treturn createRunner({\n\t\tfetch: fetchFn,\n\t\thooks,\n\t\tbuildRequest: (agent, _input, messages) => ({\n\t\t\turl: `${baseURL}/chat/completions`,\n\t\t\tinit: {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: agent.model ?? model,\n\t\t\t\t\t...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n\t\t\t\t\tmessages: [\n\t\t\t\t\t\t...(agent.instructions\n\t\t\t\t\t\t\t? [{ role: \"system\", content: agent.instructions }]\n\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t...messages.map((m) => ({ role: m.role, content: m.content })),\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\t...(timeoutMs != null ? { signal: AbortSignal.timeout(timeoutMs) } : {}),\n\t\t\t},\n\t\t}),\n\t\tparseResponse: async (res) => {\n\t\t\tconst data = await res.json();\n\t\t\tconst text = data.choices?.[0]?.message?.content ?? \"\";\n\t\t\tconst inputTokens = data.usage?.prompt_tokens ?? 0;\n\t\t\tconst outputTokens = data.usage?.completion_tokens ?? 0;\n\n\t\t\treturn {\n\t\t\t\ttext,\n\t\t\t\ttotalTokens: inputTokens + outputTokens,\n\t\t\t\tinputTokens,\n\t\t\t\toutputTokens,\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ============================================================================\n// OpenAI Embedder\n// ============================================================================\n\n/** Options for createOpenAIEmbedder */\nexport interface OpenAIEmbedderOptions {\n\tapiKey: string;\n\tmodel?: string;\n\tdimensions?: number;\n\tbaseURL?: string;\n\tfetch?: typeof globalThis.fetch;\n\t/** @default 30000 */\n\ttimeoutMs?: number;\n}\n\n/**\n * Create an EmbedderFn that calls the OpenAI embeddings API.\n *\n * @example\n * ```typescript\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedding = await embedder('How do constraints work?');\n * ```\n */\nexport function createOpenAIEmbedder(\n\toptions: OpenAIEmbedderOptions,\n): EmbedderFn {\n\tconst {\n\t\tapiKey,\n\t\tmodel = \"text-embedding-3-small\",\n\t\tdimensions = 1536,\n\t\tbaseURL = \"https://api.openai.com/v1\",\n\t\tfetch: fetchFn = globalThis.fetch,\n\t\ttimeoutMs,\n\t} = options;\n\n\tvalidateBaseURL(baseURL);\n\n\tif (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && !apiKey) {\n\t\tconsole.warn(\"[Directive] createOpenAIEmbedder: apiKey is empty. API calls will fail.\");\n\t}\n\n\treturn async (text: string): Promise<Embedding> => {\n\t\tconst response = await fetchFn(`${baseURL}/embeddings`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify({ model, input: text, dimensions }),\n\t\t\tsignal: AbortSignal.timeout(timeoutMs ?? 30_000),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errBody = await response.text().catch(() => \"\");\n\n\t\t\tthrow new Error(\n\t\t\t\t`[Directive] OpenAI embedding failed: ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n\t\t\t);\n\t\t}\n\n\t\tconst data = (await response.json()) as {\n\t\t\tdata: Array<{ embedding: number[] }>;\n\t\t};\n\n\t\tconst entry = data.data[0];\n\t\tif (!entry) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[Directive] OpenAI embedding response contained no data entries\",\n\t\t\t);\n\t\t}\n\n\t\treturn entry.embedding;\n\t};\n}\n\n// ============================================================================\n// OpenAI Streaming Runner\n// ============================================================================\n\n/** Options for createOpenAIStreamingRunner */\nexport interface OpenAIStreamingRunnerOptions {\n\tapiKey: string;\n\tmodel?: string;\n\tmaxTokens?: number;\n\tbaseURL?: string;\n\tfetch?: typeof globalThis.fetch;\n\t/** Lifecycle hooks for tracing, logging, and metrics */\n\thooks?: AdapterHooks;\n}\n\n/**\n * Create a StreamingCallbackRunner for OpenAI-compatible chat completions\n * with server-sent events. Can be used standalone or paired with `createOpenAIRunner`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * const streamingRunner = createOpenAIStreamingRunner({\n * apiKey: process.env.OPENAI_API_KEY!,\n * });\n * const stack = createAgentStack({\n * runner: createOpenAIRunner({ apiKey }),\n * streaming: { runner: streamingRunner },\n * agents: { ... },\n * });\n * ```\n */\nexport function createOpenAIStreamingRunner(\n\toptions: OpenAIStreamingRunnerOptions,\n): StreamingCallbackRunner {\n\tconst {\n\t\tapiKey,\n\t\tmodel = \"gpt-4o\",\n\t\tmaxTokens,\n\t\tbaseURL = \"https://api.openai.com/v1\",\n\t\tfetch: fetchFn = globalThis.fetch,\n\t\thooks,\n\t} = options;\n\n\tvalidateBaseURL(baseURL);\n\n\tif (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && !apiKey) {\n\t\tconsole.warn(\"[Directive] createOpenAIStreamingRunner: apiKey is empty. API calls will fail.\");\n\t}\n\n\treturn async (agent, input, callbacks) => {\n\t\tconst startTime = Date.now();\n\t\thooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n\t\ttry {\n\t\t\tconst response = await fetchFn(`${baseURL}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: agent.model ?? model,\n\t\t\t\t\t...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n\t\t\t\t\tmessages: [\n\t\t\t\t\t\t...(agent.instructions\n\t\t\t\t\t\t\t? [{ role: \"system\", content: agent.instructions }]\n\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t{ role: \"user\", content: input },\n\t\t\t\t\t],\n\t\t\t\t\tstream: true,\n\t\t\t\t\tstream_options: { include_usage: true },\n\t\t\t\t}),\n\t\t\t\tsignal: callbacks.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errBody = await response.text().catch(() => \"\");\n\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[Directive] OpenAI streaming error ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader();\n\t\t\tif (!reader) {\n\t\t\t\tthrow new Error(\"[Directive] No response body\");\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buf = \"\";\n\t\t\tlet fullText = \"\";\n\t\t\tlet promptTokens = 0;\n\t\t\tlet completionTokens = 0;\n\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\t\tif (done) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tbuf += decoder.decode(value, { stream: true });\n\t\t\t\t\tconst lines = buf.split(\"\\n\");\n\t\t\t\t\tbuf = lines.pop() ?? \"\";\n\n\t\t\t\t\tfor (const line of lines) {\n\t\t\t\t\t\tif (!line.startsWith(\"data: \")) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data === \"[DONE]\") {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst event = JSON.parse(data);\n\n\t\t\t\t\t\t\t// Extract token content from delta\n\t\t\t\t\t\t\tconst delta = event.choices?.[0]?.delta;\n\t\t\t\t\t\t\tif (delta?.content) {\n\t\t\t\t\t\t\t\tfullText += delta.content;\n\t\t\t\t\t\t\t\tcallbacks.onToken?.(delta.content);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Extract usage from the final chunk (stream_options: include_usage)\n\t\t\t\t\t\t\tif (event.usage) {\n\t\t\t\t\t\t\t\tpromptTokens = event.usage.prompt_tokens ?? 0;\n\t\t\t\t\t\t\t\tcompletionTokens = event.usage.completion_tokens ?? 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (parseErr) {\n\t\t\t\t\t\t\tif (parseErr instanceof SyntaxError) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\ttypeof process !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\tprocess.env?.NODE_ENV === \"development\"\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t\t\"[Directive] Malformed SSE event from OpenAI:\",\n\t\t\t\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow parseErr;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treader.cancel().catch(() => {});\n\t\t\t}\n\n\t\t\tconst assistantMsg: Message = { role: \"assistant\", content: fullText };\n\t\t\tcallbacks.onMessage?.(assistantMsg);\n\n\t\t\tconst tokenUsage: TokenUsage = {\n\t\t\t\tinputTokens: promptTokens,\n\t\t\t\toutputTokens: completionTokens,\n\t\t\t};\n\t\t\tconst totalTokens = promptTokens + completionTokens;\n\n\t\t\thooks?.onAfterCall?.({\n\t\t\t\tagent,\n\t\t\t\tinput,\n\t\t\t\toutput: fullText,\n\t\t\t\ttotalTokens,\n\t\t\t\ttokenUsage,\n\t\t\t\tdurationMs: Date.now() - startTime,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\toutput: fullText,\n\t\t\t\tmessages: [{ role: \"user\" as const, content: input }, assistantMsg],\n\t\t\t\ttoolCalls: [],\n\t\t\t\ttotalTokens,\n\t\t\t\ttokenUsage,\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof Error) {\n\t\t\t\thooks?.onError?.({\n\t\t\t\t\tagent,\n\t\t\t\t\tinput,\n\t\t\t\t\terror: err,\n\t\t\t\t\tdurationMs: Date.now() - startTime,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent-utils.ts","../src/adapters/openai.ts"],"names":["ALLOWED_PROTOCOLS","validateBaseURL","baseURL","url","err","createRunner","options","fetchFn","buildRequest","parseResponse","parseOutput","hooks","parse","text","agent","input","runOptions","startTime","messages","init","fetchInit","response","errBody","parsed","tokenUsage","assistantMessage","allMessages","durationMs","OPENAI_PRICING","createOpenAIRunner","apiKey","model","maxTokens","timeoutMs","_input","m","res","data","inputTokens","outputTokens","createOpenAIEmbedder","dimensions","entry","createOpenAIStreamingRunner","callbacks","reader","decoder","buf","fullText","promptTokens","completionTokens","done","value","lines","line","event","delta","parseErr","assistantMsg","totalTokens"],"mappings":"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","file":"openai.js","sourcesContent":["/**\n * Agent utilities — createRunner, estimateCost, state queries, URL validation.\n */\n\nimport type {\n AdapterHooks,\n AgentLike,\n AgentRunner,\n RunResult,\n RunOptions,\n Message,\n TokenUsage,\n AgentState,\n ApprovalState,\n} from \"./types.js\";\n\n// ============================================================================\n// State Query Helpers\n// ============================================================================\n\n/** Check if agent is currently running. */\nexport function isAgentRunning(state: AgentState): boolean {\n return state.status === \"running\";\n}\n\n/** Check if there are pending approvals. */\nexport function hasPendingApprovals(state: ApprovalState): boolean {\n return state.pending.length > 0;\n}\n\n// ============================================================================\n// Cost Estimation\n// ============================================================================\n\n/**\n * Get total cost estimate based on token usage.\n *\n * @param tokenUsage - Total token count\n * @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)\n * @returns Estimated cost in dollars\n */\nexport function estimateCost(\n tokenUsage: number,\n ratePerMillionTokens: number\n): number {\n return (tokenUsage / 1_000_000) * ratePerMillionTokens;\n}\n\n// ============================================================================\n// Validation Helpers\n// ============================================================================\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\"]);\n\n/**\n * Validate that a baseURL uses http or https.\n * Throws immediately at adapter creation time (not at call time) to catch config errors early.\n */\nexport function validateBaseURL(baseURL: string): void {\n try {\n const url = new URL(baseURL);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) {\n throw new Error(\n `[Directive] Invalid baseURL protocol \"${url.protocol}\" – only http: and https: are allowed`,\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"[Directive]\")) {\n throw err;\n }\n\n throw new Error(\n `[Directive] Invalid baseURL \"${baseURL}\" – must be a valid URL (e.g. \"https://api.openai.com/v1\")`,\n );\n }\n}\n\n// ============================================================================\n// createRunner Helper\n// ============================================================================\n\n/** Parsed response from an LLM provider */\nexport interface ParsedResponse {\n text: string;\n totalTokens: number;\n /** Input token count, when available from the provider */\n inputTokens?: number;\n /** Output token count, when available from the provider */\n outputTokens?: number;\n}\n\n/** Options for creating an AgentRunner from buildRequest/parseResponse */\nexport interface CreateRunnerOptions {\n fetch?: typeof globalThis.fetch;\n buildRequest: (\n agent: AgentLike,\n input: string,\n messages: Message[]\n ) => { url: string; init: RequestInit };\n parseResponse: (\n response: Response,\n messages: Message[]\n ) => Promise<ParsedResponse>;\n parseOutput?: <T>(text: string) => T;\n /** Lifecycle hooks for tracing, logging, and metrics */\n hooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner from buildRequest/parseResponse helpers.\n * Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.\n *\n * Supports lifecycle hooks for observability:\n * - `onBeforeCall` fires before each API request\n * - `onAfterCall` fires after a successful response (includes token breakdown)\n * - `onError` fires when the request fails\n *\n * @example\n * ```typescript\n * const runClaude = createRunner({\n * buildRequest: (agent, input) => ({\n * url: \"/api/claude\",\n * init: {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify({\n * model: agent.model ?? \"claude-haiku-4-5-20251001\",\n * system: agent.instructions ?? \"\",\n * messages: [{ role: \"user\", content: input }],\n * }),\n * },\n * }),\n * parseResponse: async (res) => {\n * const data = await res.json();\n * const inputTokens = data.usage?.input_tokens ?? 0;\n * const outputTokens = data.usage?.output_tokens ?? 0;\n * return {\n * text: data.content?.[0]?.text ?? \"\",\n * totalTokens: inputTokens + outputTokens,\n * inputTokens,\n * outputTokens,\n * };\n * },\n * hooks: {\n * onAfterCall: ({ durationMs, tokenUsage }) => {\n * console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);\n * },\n * },\n * });\n * ```\n */\nexport function createRunner(options: CreateRunnerOptions): AgentRunner {\n const {\n fetch: fetchFn = globalThis.fetch,\n buildRequest,\n parseResponse,\n parseOutput,\n hooks,\n } = options;\n\n const defaultParseOutput = <T>(text: string): T => {\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n };\n\n const parse = parseOutput ?? defaultParseOutput;\n\n return async <T = unknown>(\n agent: AgentLike,\n input: string,\n runOptions?: RunOptions\n ): Promise<RunResult<T>> => {\n const startTime = Date.now();\n hooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n const messages: Message[] = [{ role: \"user\", content: input }];\n\n try {\n const { url, init } = buildRequest(agent, input, messages);\n\n const fetchInit: RequestInit = runOptions?.signal\n ? { ...init, signal: runOptions.signal }\n : init;\n\n const response = await fetchFn(url, fetchInit);\n\n if (!response.ok) {\n const errBody = await response.text().catch(() => \"\");\n\n throw new Error(\n `[Directive] AgentRunner request failed: ${response.status} ${response.statusText}${errBody ? ` – ${errBody.slice(0, 300)}` : \"\"}`,\n );\n }\n\n const parsed = await parseResponse(response, messages);\n const tokenUsage: TokenUsage = {\n inputTokens: parsed.inputTokens ?? 0,\n outputTokens: parsed.outputTokens ?? 0,\n };\n\n const assistantMessage: Message = { role: \"assistant\", content: parsed.text };\n const allMessages: Message[] = [...messages, assistantMessage];\n\n runOptions?.onMessage?.(assistantMessage);\n\n const durationMs = Date.now() - startTime;\n hooks?.onAfterCall?.({\n agent,\n input,\n output: parsed.text,\n totalTokens: parsed.totalTokens,\n tokenUsage,\n durationMs,\n timestamp: Date.now(),\n });\n\n return {\n output: parse<T>(parsed.text),\n messages: allMessages,\n toolCalls: [],\n totalTokens: parsed.totalTokens,\n tokenUsage,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n if (err instanceof Error) {\n hooks?.onError?.({\n agent,\n input,\n error: err,\n durationMs,\n timestamp: Date.now(),\n });\n }\n\n throw err;\n }\n };\n}\n","/**\n * @directive-run/ai/openai\n *\n * OpenAI adapter for Directive AI. Provides runners and embedders\n * for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * @example\n * ```typescript\n * import { createOpenAIRunner, createOpenAIEmbedder } from '@directive-run/ai/openai';\n *\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * ```\n */\n\nimport { createRunner, validateBaseURL } from \"../agent-utils.js\";\nimport type { AdapterHooks, AgentRunner, Message, TokenUsage } from \"../types.js\";\nimport type { StreamingCallbackRunner } from \"../types.js\";\nimport type { EmbedderFn, Embedding } from \"../guardrails/semantic-cache.js\";\n\n// ============================================================================\n// Pricing Constants\n// ============================================================================\n\n/**\n * OpenAI model pricing (USD per million tokens).\n *\n * Use with `estimateCost()` for per-call cost tracking:\n * ```typescript\n * import { estimateCost } from '@directive-run/ai';\n * import { OPENAI_PRICING } from '@directive-run/ai/openai';\n *\n * const cost =\n * estimateCost(result.tokenUsage!.inputTokens, OPENAI_PRICING[\"gpt-4o\"].input) +\n * estimateCost(result.tokenUsage!.outputTokens, OPENAI_PRICING[\"gpt-4o\"].output);\n * ```\n *\n * **Note:** Pricing changes over time. These values are provided as a convenience\n * and may not reflect the latest rates. Always verify at https://openai.com/pricing\n */\nexport const OPENAI_PRICING: Record<string, { input: number; output: number }> = {\n\t\"gpt-4o\": { input: 2.5, output: 10 },\n\t\"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n\t\"gpt-4-turbo\": { input: 10, output: 30 },\n\t\"o3-mini\": { input: 1.1, output: 4.4 },\n};\n\n// ============================================================================\n// OpenAI Runner\n// ============================================================================\n\n/** Options for createOpenAIRunner */\nexport interface OpenAIRunnerOptions {\n\tapiKey: string;\n\tmodel?: string;\n\tmaxTokens?: number;\n\tbaseURL?: string;\n\tfetch?: typeof globalThis.fetch;\n\t/** @default undefined */\n\ttimeoutMs?: number;\n\t/** Lifecycle hooks for tracing, logging, and metrics */\n\thooks?: AdapterHooks;\n}\n\n/**\n * Create an AgentRunner for OpenAI-compatible APIs (OpenAI, Azure, Together, etc.)\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * // OpenAI\n * const runner = createOpenAIRunner({ apiKey: process.env.OPENAI_API_KEY! });\n *\n * // Azure OpenAI\n * const azure = createOpenAIRunner({\n * apiKey: process.env.AZURE_KEY!,\n * baseURL: \"https://your-resource.openai.azure.com/v1\",\n * });\n *\n * // Together.ai (OpenAI-compatible)\n * const together = createOpenAIRunner({\n * apiKey: process.env.TOGETHER_KEY!,\n * baseURL: \"https://api.together.xyz/v1\",\n * });\n * ```\n */\nexport function createOpenAIRunner(options: OpenAIRunnerOptions): AgentRunner {\n\tconst {\n\t\tapiKey,\n\t\tmodel = \"gpt-4o\",\n\t\tmaxTokens,\n\t\tbaseURL = \"https://api.openai.com/v1\",\n\t\tfetch: fetchFn = globalThis.fetch,\n\t\ttimeoutMs,\n\t\thooks,\n\t} = options;\n\n\tvalidateBaseURL(baseURL);\n\n\tif (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && !apiKey) {\n\t\tconsole.warn(\"[Directive] createOpenAIRunner: apiKey is empty. API calls will fail.\");\n\t}\n\n\treturn createRunner({\n\t\tfetch: fetchFn,\n\t\thooks,\n\t\tbuildRequest: (agent, _input, messages) => ({\n\t\t\turl: `${baseURL}/chat/completions`,\n\t\t\tinit: {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: agent.model ?? model,\n\t\t\t\t\t...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n\t\t\t\t\tmessages: [\n\t\t\t\t\t\t...(agent.instructions\n\t\t\t\t\t\t\t? [{ role: \"system\", content: agent.instructions }]\n\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t...messages.map((m) => ({ role: m.role, content: m.content })),\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\t...(timeoutMs != null ? { signal: AbortSignal.timeout(timeoutMs) } : {}),\n\t\t\t},\n\t\t}),\n\t\tparseResponse: async (res) => {\n\t\t\tconst data = await res.json();\n\t\t\tconst text = data.choices?.[0]?.message?.content ?? \"\";\n\t\t\tconst inputTokens = data.usage?.prompt_tokens ?? 0;\n\t\t\tconst outputTokens = data.usage?.completion_tokens ?? 0;\n\n\t\t\treturn {\n\t\t\t\ttext,\n\t\t\t\ttotalTokens: inputTokens + outputTokens,\n\t\t\t\tinputTokens,\n\t\t\t\toutputTokens,\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ============================================================================\n// OpenAI Embedder\n// ============================================================================\n\n/** Options for createOpenAIEmbedder */\nexport interface OpenAIEmbedderOptions {\n\tapiKey: string;\n\tmodel?: string;\n\tdimensions?: number;\n\tbaseURL?: string;\n\tfetch?: typeof globalThis.fetch;\n\t/** @default 30000 */\n\ttimeoutMs?: number;\n}\n\n/**\n * Create an EmbedderFn that calls the OpenAI embeddings API.\n *\n * @example\n * ```typescript\n * const embedder = createOpenAIEmbedder({ apiKey: process.env.OPENAI_API_KEY! });\n * const embedding = await embedder('How do constraints work?');\n * ```\n */\nexport function createOpenAIEmbedder(\n\toptions: OpenAIEmbedderOptions,\n): EmbedderFn {\n\tconst {\n\t\tapiKey,\n\t\tmodel = \"text-embedding-3-small\",\n\t\tdimensions = 1536,\n\t\tbaseURL = \"https://api.openai.com/v1\",\n\t\tfetch: fetchFn = globalThis.fetch,\n\t\ttimeoutMs,\n\t} = options;\n\n\tvalidateBaseURL(baseURL);\n\n\tif (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && !apiKey) {\n\t\tconsole.warn(\"[Directive] createOpenAIEmbedder: apiKey is empty. API calls will fail.\");\n\t}\n\n\treturn async (text: string): Promise<Embedding> => {\n\t\tconst response = await fetchFn(`${baseURL}/embeddings`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify({ model, input: text, dimensions }),\n\t\t\tsignal: AbortSignal.timeout(timeoutMs ?? 30_000),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errBody = await response.text().catch(() => \"\");\n\n\t\t\tthrow new Error(\n\t\t\t\t`[Directive] OpenAI embedding failed: ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n\t\t\t);\n\t\t}\n\n\t\tconst data = (await response.json()) as {\n\t\t\tdata: Array<{ embedding: number[] }>;\n\t\t};\n\n\t\tconst entry = data.data[0];\n\t\tif (!entry) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[Directive] OpenAI embedding response contained no data entries\",\n\t\t\t);\n\t\t}\n\n\t\treturn entry.embedding;\n\t};\n}\n\n// ============================================================================\n// OpenAI Streaming Runner\n// ============================================================================\n\n/** Options for createOpenAIStreamingRunner */\nexport interface OpenAIStreamingRunnerOptions {\n\tapiKey: string;\n\tmodel?: string;\n\tmaxTokens?: number;\n\tbaseURL?: string;\n\tfetch?: typeof globalThis.fetch;\n\t/** Lifecycle hooks for tracing, logging, and metrics */\n\thooks?: AdapterHooks;\n}\n\n/**\n * Create a StreamingCallbackRunner for OpenAI-compatible chat completions\n * with server-sent events. Can be used standalone or paired with `createOpenAIRunner`.\n *\n * Returns `tokenUsage` with input/output breakdown for cost tracking.\n *\n * @example\n * ```typescript\n * const streamingRunner = createOpenAIStreamingRunner({\n * apiKey: process.env.OPENAI_API_KEY!,\n * });\n * const streamRunner = createStreamingRunner(streamingRunner);\n * const { stream, result } = streamRunner(agent, input);\n * ```\n */\nexport function createOpenAIStreamingRunner(\n\toptions: OpenAIStreamingRunnerOptions,\n): StreamingCallbackRunner {\n\tconst {\n\t\tapiKey,\n\t\tmodel = \"gpt-4o\",\n\t\tmaxTokens,\n\t\tbaseURL = \"https://api.openai.com/v1\",\n\t\tfetch: fetchFn = globalThis.fetch,\n\t\thooks,\n\t} = options;\n\n\tvalidateBaseURL(baseURL);\n\n\tif (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && !apiKey) {\n\t\tconsole.warn(\"[Directive] createOpenAIStreamingRunner: apiKey is empty. API calls will fail.\");\n\t}\n\n\treturn async (agent, input, callbacks) => {\n\t\tconst startTime = Date.now();\n\t\thooks?.onBeforeCall?.({ agent, input, timestamp: startTime });\n\n\t\ttry {\n\t\t\tconst response = await fetchFn(`${baseURL}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel: agent.model ?? model,\n\t\t\t\t\t...(maxTokens != null ? { max_tokens: maxTokens } : {}),\n\t\t\t\t\tmessages: [\n\t\t\t\t\t\t...(agent.instructions\n\t\t\t\t\t\t\t? [{ role: \"system\", content: agent.instructions }]\n\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t{ role: \"user\", content: input },\n\t\t\t\t\t],\n\t\t\t\t\tstream: true,\n\t\t\t\t\tstream_options: { include_usage: true },\n\t\t\t\t}),\n\t\t\t\tsignal: callbacks.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errBody = await response.text().catch(() => \"\");\n\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[Directive] OpenAI streaming error ${response.status}${errBody ? ` – ${errBody.slice(0, 200)}` : \"\"}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader();\n\t\t\tif (!reader) {\n\t\t\t\tthrow new Error(\"[Directive] No response body\");\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buf = \"\";\n\t\t\tlet fullText = \"\";\n\t\t\tlet promptTokens = 0;\n\t\t\tlet completionTokens = 0;\n\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\t\tif (done) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tbuf += decoder.decode(value, { stream: true });\n\t\t\t\t\tconst lines = buf.split(\"\\n\");\n\t\t\t\t\tbuf = lines.pop() ?? \"\";\n\n\t\t\t\t\tfor (const line of lines) {\n\t\t\t\t\t\tif (!line.startsWith(\"data: \")) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data === \"[DONE]\") {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst event = JSON.parse(data);\n\n\t\t\t\t\t\t\t// Extract token content from delta\n\t\t\t\t\t\t\tconst delta = event.choices?.[0]?.delta;\n\t\t\t\t\t\t\tif (delta?.content) {\n\t\t\t\t\t\t\t\tfullText += delta.content;\n\t\t\t\t\t\t\t\tcallbacks.onToken?.(delta.content);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Extract usage from the final chunk (stream_options: include_usage)\n\t\t\t\t\t\t\tif (event.usage) {\n\t\t\t\t\t\t\t\tpromptTokens = event.usage.prompt_tokens ?? 0;\n\t\t\t\t\t\t\t\tcompletionTokens = event.usage.completion_tokens ?? 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (parseErr) {\n\t\t\t\t\t\t\tif (parseErr instanceof SyntaxError) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\ttypeof process !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\tprocess.env?.NODE_ENV === \"development\"\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t\t\"[Directive] Malformed SSE event from OpenAI:\",\n\t\t\t\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow parseErr;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treader.cancel().catch(() => {});\n\t\t\t}\n\n\t\t\tconst assistantMsg: Message = { role: \"assistant\", content: fullText };\n\t\t\tcallbacks.onMessage?.(assistantMsg);\n\n\t\t\tconst tokenUsage: TokenUsage = {\n\t\t\t\tinputTokens: promptTokens,\n\t\t\t\toutputTokens: completionTokens,\n\t\t\t};\n\t\t\tconst totalTokens = promptTokens + completionTokens;\n\n\t\t\thooks?.onAfterCall?.({\n\t\t\t\tagent,\n\t\t\t\tinput,\n\t\t\t\toutput: fullText,\n\t\t\t\ttotalTokens,\n\t\t\t\ttokenUsage,\n\t\t\t\tdurationMs: Date.now() - startTime,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\toutput: fullText,\n\t\t\t\tmessages: [{ role: \"user\" as const, content: input }, assistantMsg],\n\t\t\t\ttoolCalls: [],\n\t\t\t\ttotalTokens,\n\t\t\t\ttokenUsage,\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof Error) {\n\t\t\t\thooks?.onError?.({\n\t\t\t\t\tagent,\n\t\t\t\t\tinput,\n\t\t\t\t\terror: err,\n\t\t\t\t\tdurationMs: Date.now() - startTime,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t};\n}\n"]}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Caching Guardrail
|
|
3
|
+
*
|
|
4
|
+
* Caches agent responses based on semantic similarity to reduce redundant LLM calls.
|
|
5
|
+
* Uses vector embeddings to find semantically similar previous queries.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createSemanticCacheGuardrail } from '@directive-run/ai';
|
|
10
|
+
*
|
|
11
|
+
* const cacheGuardrail = createSemanticCacheGuardrail({
|
|
12
|
+
* embedder: async (text) => {
|
|
13
|
+
* // Use your embedding model (OpenAI, local model, etc.)
|
|
14
|
+
* return await getEmbedding(text);
|
|
15
|
+
* },
|
|
16
|
+
* similarityThreshold: 0.95,
|
|
17
|
+
* maxCacheSize: 1000,
|
|
18
|
+
* ttlMs: 3600000, // 1 hour
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* const orchestrator = createAgentOrchestrator({
|
|
22
|
+
* guardrails: {
|
|
23
|
+
* input: [cacheGuardrail],
|
|
24
|
+
* },
|
|
25
|
+
* runner: run,
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
/** Vector embedding (array of numbers) */
|
|
30
|
+
type Embedding = number[];
|
|
31
|
+
/** Function to generate embeddings for text */
|
|
32
|
+
type EmbedderFn = (text: string) => Promise<Embedding>;
|
|
33
|
+
/** Cached response entry */
|
|
34
|
+
interface CacheEntry {
|
|
35
|
+
id: string;
|
|
36
|
+
query: string;
|
|
37
|
+
queryEmbedding: Embedding;
|
|
38
|
+
response: string;
|
|
39
|
+
metadata: Record<string, unknown>;
|
|
40
|
+
createdAt: number;
|
|
41
|
+
accessedAt: number;
|
|
42
|
+
accessCount: number;
|
|
43
|
+
agentName?: string;
|
|
44
|
+
}
|
|
45
|
+
/** Cache lookup result */
|
|
46
|
+
interface CacheLookupResult {
|
|
47
|
+
hit: boolean;
|
|
48
|
+
entry?: CacheEntry;
|
|
49
|
+
similarity?: number;
|
|
50
|
+
latencyMs: number;
|
|
51
|
+
}
|
|
52
|
+
/** Semantic cache configuration */
|
|
53
|
+
interface SemanticCacheConfig {
|
|
54
|
+
/** Function to generate embeddings */
|
|
55
|
+
embedder: EmbedderFn;
|
|
56
|
+
/** Similarity threshold (0.0 to 1.0) for cache hits */
|
|
57
|
+
similarityThreshold?: number;
|
|
58
|
+
/** Maximum number of entries to cache */
|
|
59
|
+
maxCacheSize?: number;
|
|
60
|
+
/** Time-to-live in milliseconds for cache entries */
|
|
61
|
+
ttlMs?: number;
|
|
62
|
+
/** Cache namespace for multi-tenant scenarios */
|
|
63
|
+
namespace?: string;
|
|
64
|
+
/** Custom storage backend (defaults to in-memory) */
|
|
65
|
+
storage?: SemanticCacheStorage;
|
|
66
|
+
/** Callback when cache hit occurs */
|
|
67
|
+
onHit?: (entry: CacheEntry, similarity: number) => void;
|
|
68
|
+
/** Callback when cache miss occurs */
|
|
69
|
+
onMiss?: (query: string) => void;
|
|
70
|
+
/** Callback when cache lookup encounters an error */
|
|
71
|
+
onError?: (error: Error) => void;
|
|
72
|
+
/** Whether to include agent name in cache key */
|
|
73
|
+
perAgent?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/** Storage interface for cache backends */
|
|
76
|
+
interface SemanticCacheStorage {
|
|
77
|
+
/** Get all entries for a namespace */
|
|
78
|
+
getEntries(namespace: string): Promise<CacheEntry[]>;
|
|
79
|
+
/** Add an entry to the cache */
|
|
80
|
+
addEntry(namespace: string, entry: CacheEntry): Promise<void>;
|
|
81
|
+
/** Update an entry (e.g., access count) */
|
|
82
|
+
updateEntry(namespace: string, id: string, updates: Partial<CacheEntry>): Promise<void>;
|
|
83
|
+
/** Remove an entry */
|
|
84
|
+
removeEntry(namespace: string, id: string): Promise<void>;
|
|
85
|
+
/** Clear all entries in a namespace */
|
|
86
|
+
clear(namespace: string): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
/** Semantic cache instance */
|
|
89
|
+
interface SemanticCache {
|
|
90
|
+
/** Look up a query in the cache */
|
|
91
|
+
lookup(query: string, agentName?: string): Promise<CacheLookupResult>;
|
|
92
|
+
/** Store a response in the cache */
|
|
93
|
+
store(query: string, response: string, agentName?: string, metadata?: Record<string, unknown>): Promise<void>;
|
|
94
|
+
/** Invalidate cache entries matching a predicate */
|
|
95
|
+
invalidate(predicate: (entry: CacheEntry) => boolean): Promise<number>;
|
|
96
|
+
/** Clear all cache entries */
|
|
97
|
+
clear(): Promise<void>;
|
|
98
|
+
/** Get cache statistics */
|
|
99
|
+
getStats(): CacheStats;
|
|
100
|
+
/** Export cache entries (for persistence) */
|
|
101
|
+
export(): Promise<CacheEntry[]>;
|
|
102
|
+
/** Import cache entries (from persistence) */
|
|
103
|
+
import(entries: CacheEntry[]): Promise<void>;
|
|
104
|
+
}
|
|
105
|
+
/** Cache statistics */
|
|
106
|
+
interface CacheStats {
|
|
107
|
+
totalEntries: number;
|
|
108
|
+
totalHits: number;
|
|
109
|
+
totalMisses: number;
|
|
110
|
+
hitRate: number;
|
|
111
|
+
avgSimilarityOnHit: number;
|
|
112
|
+
oldestEntry: number | null;
|
|
113
|
+
newestEntry: number | null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Create an in-memory cache storage backend.
|
|
117
|
+
*/
|
|
118
|
+
declare function createInMemoryStorage(): SemanticCacheStorage;
|
|
119
|
+
/**
|
|
120
|
+
* Create a semantic cache instance.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const cache = createSemanticCache({
|
|
125
|
+
* embedder: async (text) => {
|
|
126
|
+
* const response = await openai.embeddings.create({
|
|
127
|
+
* model: 'text-embedding-3-small',
|
|
128
|
+
* input: text,
|
|
129
|
+
* });
|
|
130
|
+
* return response.data[0].embedding;
|
|
131
|
+
* },
|
|
132
|
+
* similarityThreshold: 0.92,
|
|
133
|
+
* maxCacheSize: 500,
|
|
134
|
+
* ttlMs: 3600000, // 1 hour
|
|
135
|
+
* });
|
|
136
|
+
*
|
|
137
|
+
* // Check cache before calling agent
|
|
138
|
+
* const result = await cache.lookup(userQuery);
|
|
139
|
+
* if (result.hit) {
|
|
140
|
+
* return result.entry!.response;
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* // Call agent and cache response
|
|
144
|
+
* const response = await runAgent(userQuery);
|
|
145
|
+
* await cache.store(userQuery, response);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function createSemanticCache(config: SemanticCacheConfig): SemanticCache;
|
|
149
|
+
/** Input guardrail data for semantic cache */
|
|
150
|
+
interface SemanticCacheGuardrailData {
|
|
151
|
+
input: string;
|
|
152
|
+
agentName?: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Result of semantic cache guardrail.
|
|
156
|
+
*
|
|
157
|
+
* **Important semantics:**
|
|
158
|
+
* - `passed: false` + `cacheHit: true` = Short-circuit with cached response (not an error!)
|
|
159
|
+
* - `passed: true` + `cacheHit: false` = No cache hit, proceed with agent call
|
|
160
|
+
*
|
|
161
|
+
* The `passed: false` follows guardrail convention where "not passing" stops the flow,
|
|
162
|
+
* but in this case stopping is desirable (returning cached data is good).
|
|
163
|
+
*/
|
|
164
|
+
interface SemanticCacheGuardrailResult {
|
|
165
|
+
/**
|
|
166
|
+
* Whether to proceed with the agent call.
|
|
167
|
+
* `false` means short-circuit with cached response (this is good, not an error).
|
|
168
|
+
* `true` means no cache hit, proceed with agent.
|
|
169
|
+
*/
|
|
170
|
+
passed: boolean;
|
|
171
|
+
/** Indicates whether this was a cache hit */
|
|
172
|
+
cacheHit: boolean;
|
|
173
|
+
/** Reason for the result */
|
|
174
|
+
reason?: string;
|
|
175
|
+
/** The cached response (only present on cache hit) */
|
|
176
|
+
cachedResponse?: string;
|
|
177
|
+
/** Similarity score (0-1) of the cache hit */
|
|
178
|
+
similarity?: number;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Create a semantic caching input guardrail.
|
|
182
|
+
*
|
|
183
|
+
* **How it works:**
|
|
184
|
+
* - On cache HIT: Returns `{ passed: false, cacheHit: true, cachedResponse: "..." }`
|
|
185
|
+
* The orchestrator should detect `cacheHit: true` and return the cached response.
|
|
186
|
+
* - On cache MISS: Returns `{ passed: true, cacheHit: false }`
|
|
187
|
+
* Proceed with normal agent execution.
|
|
188
|
+
*
|
|
189
|
+
* **Important:** `passed: false` with `cacheHit: true` is SUCCESS, not failure.
|
|
190
|
+
* The guardrail "short-circuits" the flow to return cached data efficiently.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* const cacheGuardrail = createSemanticCacheGuardrail({
|
|
195
|
+
* cache: mySemanticCache,
|
|
196
|
+
* });
|
|
197
|
+
*
|
|
198
|
+
* const orchestrator = createAgentOrchestrator({
|
|
199
|
+
* guardrails: {
|
|
200
|
+
* input: [
|
|
201
|
+
* {
|
|
202
|
+
* name: 'semantic-cache',
|
|
203
|
+
* fn: cacheGuardrail,
|
|
204
|
+
* },
|
|
205
|
+
* ],
|
|
206
|
+
* },
|
|
207
|
+
* runner: run,
|
|
208
|
+
* });
|
|
209
|
+
*
|
|
210
|
+
* // In your orchestrator wrapper, check for cache hits:
|
|
211
|
+
* const guardrailResult = await cacheGuardrail({ input: userQuery });
|
|
212
|
+
* if (guardrailResult.cacheHit) {
|
|
213
|
+
* return guardrailResult.cachedResponse; // Fast path!
|
|
214
|
+
* }
|
|
215
|
+
* // Otherwise proceed with agent call...
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
declare function createSemanticCacheGuardrail(config: {
|
|
219
|
+
cache: SemanticCache;
|
|
220
|
+
}): (data: SemanticCacheGuardrailData) => Promise<SemanticCacheGuardrailResult>;
|
|
221
|
+
/**
|
|
222
|
+
* Create a simple hash-based "embedder" for testing.
|
|
223
|
+
* NOT suitable for production - use a real embedding model.
|
|
224
|
+
*/
|
|
225
|
+
declare function createTestEmbedder(dimensions?: number): EmbedderFn;
|
|
226
|
+
/** Batched embedder instance with dispose capability */
|
|
227
|
+
interface BatchedEmbedder {
|
|
228
|
+
/** Embed a single text (batched internally) */
|
|
229
|
+
embed: EmbedderFn;
|
|
230
|
+
/** Flush any pending batch immediately */
|
|
231
|
+
flush(): Promise<void>;
|
|
232
|
+
/** Dispose of the embedder, clearing timers and rejecting pending requests */
|
|
233
|
+
dispose(): void;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Create a batched embedder that groups multiple texts into single API calls.
|
|
237
|
+
*
|
|
238
|
+
* **BREAKING CHANGE:** Previously returned `EmbedderFn` directly. Now returns
|
|
239
|
+
* a `BatchedEmbedder` object with `embed`, `flush`, and `dispose` methods.
|
|
240
|
+
*
|
|
241
|
+
* To migrate: `const embed = createBatchedEmbedder(...)` becomes
|
|
242
|
+
* `const { embed } = createBatchedEmbedder(...)`.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* const batchedEmbedder = createBatchedEmbedder({
|
|
247
|
+
* batchSize: 20,
|
|
248
|
+
* embedBatch: async (texts) => {
|
|
249
|
+
* const response = await openai.embeddings.create({
|
|
250
|
+
* model: 'text-embedding-3-small',
|
|
251
|
+
* input: texts,
|
|
252
|
+
* });
|
|
253
|
+
* return response.data.map(d => d.embedding);
|
|
254
|
+
* },
|
|
255
|
+
* maxWaitMs: 50,
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* // Use the embedder
|
|
259
|
+
* const embedding = await batchedEmbedder.embed("Hello world");
|
|
260
|
+
*
|
|
261
|
+
* // Clean up when done
|
|
262
|
+
* batchedEmbedder.dispose();
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
declare function createBatchedEmbedder(config: {
|
|
266
|
+
batchSize?: number;
|
|
267
|
+
embedBatch: (texts: string[]) => Promise<Embedding[]>;
|
|
268
|
+
maxWaitMs?: number;
|
|
269
|
+
}): BatchedEmbedder;
|
|
270
|
+
|
|
271
|
+
export { type BatchedEmbedder as B, type CacheEntry as C, type Embedding as E, type SemanticCache as S, type EmbedderFn as a, type CacheLookupResult as b, type CacheStats as c, type SemanticCacheConfig as d, type SemanticCacheStorage as e, createBatchedEmbedder as f, createInMemoryStorage as g, createSemanticCache as h, createSemanticCacheGuardrail as i, createTestEmbedder as j };
|