risicare 0.1.0 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ids.ts","../../../src/noop.ts","../../../src/context/storage.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/openai/patch.ts"],"sourcesContent":["/**\n * ID generation for traces and spans.\n *\n * Trace IDs: 32 lowercase hex characters (16 random bytes)\n * Span IDs: 16 lowercase hex characters (8 random bytes)\n *\n * Uses crypto.randomBytes for cryptographically secure randomness.\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst HEX_REGEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_REGEX_16 = /^[0-9a-f]{16}$/;\n\nexport function generateTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\nexport function generateSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nexport function generateAgentId(prefix?: string): string {\n const suffix = randomBytes(6).toString('hex');\n return prefix ? `${prefix}-${suffix}` : suffix;\n}\n\nexport function validateTraceId(id: string): boolean {\n return HEX_REGEX_32.test(id);\n}\n\nexport function validateSpanId(id: string): boolean {\n return HEX_REGEX_16.test(id);\n}\n","/**\n * No-op implementations for the disabled path.\n *\n * When tracing is disabled, all operations return these no-op objects\n * to maintain zero overhead. No allocations, no side effects.\n */\n\nimport { SpanKind, SpanStatus, type SpanPayload } from './types.js';\n\n/**\n * A frozen no-op span that silently ignores all operations.\n * Used when SDK is disabled to avoid overhead.\n */\nexport const NOOP_SPAN = Object.freeze({\n traceId: '00000000000000000000000000000000',\n spanId: '0000000000000000',\n parentSpanId: undefined,\n name: 'noop',\n kind: SpanKind.INTERNAL,\n startTime: '',\n startHrtime: 0,\n endTime: undefined,\n status: SpanStatus.UNSET,\n statusMessage: undefined,\n attributes: Object.freeze({}) as Record<string, unknown>,\n events: Object.freeze([]) as readonly [],\n links: Object.freeze([]) as readonly [],\n sessionId: undefined,\n agentId: undefined,\n agentName: undefined,\n semanticPhase: undefined,\n llmProvider: undefined,\n llmModel: undefined,\n llmPromptTokens: undefined,\n llmCompletionTokens: undefined,\n llmTotalTokens: undefined,\n llmCostUsd: undefined,\n toolName: undefined,\n toolSuccess: undefined,\n isEnded: true,\n durationMs: 0,\n\n setAttribute() { return this; },\n setAttributes() { return this; },\n setStatus() { return this; },\n addEvent() { return this; },\n addLink() { return this; },\n recordException() { return this; },\n setLlmFields() { return this; },\n setToolFields() { return this; },\n end() {},\n toPayload(): SpanPayload {\n return {\n traceId: this.traceId,\n spanId: this.spanId,\n name: this.name,\n kind: this.kind,\n startTime: this.startTime,\n status: this.status,\n attributes: {},\n events: [],\n links: [],\n };\n },\n});\n\nexport type NoopSpan = typeof NOOP_SPAN;\n","/**\n * AsyncLocalStorage-based context propagation.\n *\n * Uses a single AsyncLocalStorage instance with a composite state object.\n * This is simpler and more performant than multiple separate stores.\n *\n * Node.js AsyncLocalStorage automatically propagates through:\n * - Promise / async-await\n * - setTimeout / setImmediate\n * - EventEmitter callbacks\n * - process.nextTick\n * - async generators (unlike Python's contextvars!)\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Span } from '../span.js';\nimport type { SemanticPhase } from '../types.js';\n\n// ─── Context Types ───────────────────────────────────────────────────────────\n\nexport interface SessionContext {\n sessionId: string;\n userId?: string;\n metadata?: Record<string, unknown>;\n parentSessionId?: string;\n turnNumber?: number;\n}\n\nexport interface AgentContext {\n agentId: string;\n agentName?: string;\n agentRole?: string;\n agentType?: string;\n parentAgentId?: string;\n version?: number;\n metadata?: Record<string, unknown>;\n}\n\nexport interface ContextState {\n session?: SessionContext;\n agent?: AgentContext;\n span?: Span;\n phase?: SemanticPhase;\n}\n\n// ─── Storage Instance ────────────────────────────────────────────────────────\n\nconst contextStorage = new AsyncLocalStorage<ContextState>();\n\n// ─── Core Operations ─────────────────────────────────────────────────────────\n\n/**\n * Get the current context state, or empty object if outside any context.\n */\nexport function getContext(): ContextState {\n return contextStorage.getStore() ?? {};\n}\n\n/**\n * Run a callback within a new context scope.\n * The new scope inherits from the parent, with overrides applied.\n */\nexport function runWithContext<T>(overrides: Partial<ContextState>, fn: () => T): T {\n const parent = getContext();\n const merged: ContextState = { ...parent, ...overrides };\n return contextStorage.run(merged, fn);\n}\n\n/**\n * Run an async callback within a new context scope.\n */\nexport function runWithContextAsync<T>(overrides: Partial<ContextState>, fn: () => Promise<T>): Promise<T> {\n const parent = getContext();\n const merged: ContextState = { ...parent, ...overrides };\n return contextStorage.run(merged, fn);\n}\n\n// ─── Context Accessors ───────────────────────────────────────────────────────\n\nexport function getCurrentSession(): SessionContext | undefined {\n return getContext().session;\n}\n\nexport function getCurrentAgent(): AgentContext | undefined {\n return getContext().agent;\n}\n\nexport function getCurrentSpan(): Span | undefined {\n return getContext().span;\n}\n\nexport function getCurrentPhase(): SemanticPhase | undefined {\n return getContext().phase;\n}\n\nexport function getCurrentSessionId(): string | undefined {\n return getContext().session?.sessionId;\n}\n\nexport function getCurrentAgentId(): string | undefined {\n return getContext().agent?.agentId;\n}\n\nexport function getCurrentTraceId(): string | undefined {\n return getContext().span?.traceId;\n}\n\nexport function getCurrentSpanId(): string | undefined {\n return getContext().span?.spanId;\n}\n\nexport function getCurrentParentSpanId(): string | undefined {\n return getContext().span?.parentSpanId;\n}\n\n/**\n * Get all current context as a plain object (for debugging/serialization).\n */\nexport function getCurrentContext(): Record<string, unknown> {\n const ctx = getContext();\n return {\n session: ctx.session ? { sessionId: ctx.session.sessionId, userId: ctx.session.userId } : null,\n agent: ctx.agent ? { agentId: ctx.agent.agentId, agentName: ctx.agent.agentName, agentRole: ctx.agent.agentRole } : null,\n span: ctx.span ? { spanId: ctx.span.spanId, traceId: ctx.span.traceId } : null,\n phase: ctx.phase ?? null,\n };\n}\n","/**\n * RisicareClient — singleton client managing SDK lifecycle.\n *\n * Handles initialization, shutdown, and the connection between\n * the Tracer and the export pipeline (batch processor + HTTP exporter).\n *\n * Usage:\n * import { init, shutdown } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n * // ... instrument code ...\n * await shutdown(); // flush remaining spans\n */\n\nimport { type RisicareConfig, resolveConfig } from './config.js';\nimport { Tracer } from './tracer.js';\nimport { BatchSpanProcessor } from './exporters/batch.js';\nimport { HttpExporter } from './exporters/http.js';\nimport { ConsoleExporter } from './exporters/console.js';\nimport type { SpanExporter } from './exporters/base.js';\nimport { setDebug, debug } from './utils/log.js';\n\n// ─── Singleton State ────────────────────────────────────────────────────────\n\nlet _client: RisicareClient | undefined;\nlet _tracer: Tracer | undefined;\n\n// ─── Client Class ───────────────────────────────────────────────────────────\n\nclass RisicareClient {\n readonly config: ReturnType<typeof resolveConfig>;\n readonly processor: BatchSpanProcessor;\n readonly tracer: Tracer;\n private _shutdownCalled = false;\n\n constructor(config?: Partial<RisicareConfig>) {\n this.config = resolveConfig(config);\n\n // Build exporter chain\n let exporter: SpanExporter;\n if (this.config.debug && !this.config.apiKey) {\n exporter = new ConsoleExporter();\n } else if (this.config.apiKey) {\n exporter = new HttpExporter({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n projectId: this.config.projectId || undefined,\n environment: this.config.environment || undefined,\n });\n } else {\n // No API key and not debug — use console as fallback\n exporter = new ConsoleExporter();\n }\n\n this.processor = new BatchSpanProcessor({\n exporters: [exporter],\n batchSize: this.config.batchSize,\n batchTimeoutMs: this.config.batchTimeoutMs,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n });\n\n this.tracer = new Tracer({\n onSpanEnd: (span) => this.processor.onSpanEnd(span),\n sampleRate: this.config.sampleRate,\n enabled: this.config.enabled,\n });\n\n // Start the batch processor (enables span queuing and periodic flushing)\n this.processor.start();\n\n // Register shutdown hooks\n this._registerShutdownHooks();\n\n // Enable internal debug logging if configured\n setDebug(this.config.debug);\n debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);\n }\n\n get enabled(): boolean {\n return this.tracer.enabled;\n }\n\n set enabled(value: boolean) {\n this.tracer.enabled = value;\n }\n\n async shutdown(): Promise<void> {\n if (this._shutdownCalled) return;\n this._shutdownCalled = true;\n\n debug('Shutting down...');\n\n await this.processor.shutdown();\n }\n\n async flush(): Promise<void> {\n await this.processor.flush();\n }\n\n private _registerShutdownHooks(): void {\n const onShutdown = () => {\n this.shutdown().catch(() => {});\n };\n\n // These handlers use unref'd timers internally, so they won't prevent exit\n process.once('beforeExit', onShutdown);\n process.once('SIGTERM', onShutdown);\n process.once('SIGINT', onShutdown);\n }\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Initialize the Risicare SDK. Call once at application startup.\n *\n * @example\n * import { init } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n */\nexport function init(config?: Partial<RisicareConfig>): void {\n if (_client) {\n debug('Already initialized. Call shutdown() first to re-initialize.');\n return;\n }\n\n _client = new RisicareClient(config);\n _tracer = _client.tracer;\n}\n\n/**\n * Gracefully shut down the SDK. Flushes pending spans before resolving.\n */\nexport async function shutdown(): Promise<void> {\n if (!_client) return;\n await _client.shutdown();\n _client = undefined;\n _tracer = undefined;\n}\n\n/**\n * Flush all pending spans without shutting down.\n */\nexport async function flush(): Promise<void> {\n if (!_client) return;\n await _client.flush();\n}\n\n/**\n * Enable tracing at runtime.\n */\nexport function enable(): void {\n if (_client) _client.enabled = true;\n}\n\n/**\n * Disable tracing at runtime. Spans will not be created or exported.\n */\nexport function disable(): void {\n if (_client) _client.enabled = false;\n}\n\n/**\n * Check whether tracing is currently enabled.\n */\nexport function isEnabled(): boolean {\n return _client?.enabled ?? false;\n}\n\n/**\n * Get the global tracer instance. Returns undefined if not initialized.\n */\nexport function getTracer(): Tracer | undefined {\n return _tracer;\n}\n\n/**\n * Get the global tracer, or throw if not initialized.\n * @internal Used by decorators that require an active tracer.\n */\nexport function requireTracer(): Tracer {\n if (!_tracer) {\n throw new Error(\n 'Risicare SDK not initialized. Call init() before using tracing features.',\n );\n }\n return _tracer;\n}\n","/**\n * Token cost calculation table.\n *\n * Prices are per 1M tokens. Update monthly.\n * Source: provider pricing pages.\n */\n\ninterface ModelPricing {\n input: number; // USD per 1M input tokens\n output: number; // USD per 1M output tokens\n}\n\nconst PRICING: Record<string, ModelPricing> = {\n // OpenAI\n 'gpt-4o': { input: 2.50, output: 10.00 },\n 'gpt-4o-mini': { input: 0.15, output: 0.60 },\n 'gpt-4-turbo': { input: 10.00, output: 30.00 },\n 'gpt-4': { input: 30.00, output: 60.00 },\n 'gpt-3.5-turbo': { input: 0.50, output: 1.50 },\n 'o1': { input: 15.00, output: 60.00 },\n 'o1-mini': { input: 3.00, output: 12.00 },\n 'o3-mini': { input: 1.10, output: 4.40 },\n\n // Anthropic\n 'claude-opus-4-6': { input: 15.00, output: 75.00 },\n 'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },\n 'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },\n 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },\n 'claude-3-opus-20240229': { input: 15.00, output: 75.00 },\n\n // Google\n 'gemini-2.0-flash': { input: 0.10, output: 0.40 },\n 'gemini-1.5-pro': { input: 1.25, output: 5.00 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.30 },\n\n // Groq\n 'llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },\n 'llama-3.1-8b-instant': { input: 0.05, output: 0.08 },\n 'mixtral-8x7b-32768': { input: 0.24, output: 0.24 },\n\n // DeepSeek\n 'deepseek-chat': { input: 0.14, output: 0.28 },\n 'deepseek-reasoner': { input: 0.55, output: 2.19 },\n\n // Together.ai (open-source models)\n 'meta-llama/llama-3.3-70b-instruct-turbo': { input: 0.88, output: 0.88 },\n 'meta-llama/meta-llama-3.1-8b-instruct-turbo': { input: 0.18, output: 0.18 },\n 'meta-llama/llama-3.2-3b-instruct-turbo': { input: 0.06, output: 0.06 },\n 'qwen/qwen2.5-7b-instruct-turbo': { input: 0.20, output: 0.20 },\n 'mistralai/mistral-small-24b-instruct-2501': { input: 0.20, output: 0.20 },\n 'mistralai/mixtral-8x7b-instruct-v0.1': { input: 0.60, output: 0.60 },\n 'deepseek-ai/deepseek-v3': { input: 0.27, output: 1.10 },\n};\n\n/**\n * Calculate cost in USD for a model's token usage.\n * Returns undefined if model is not in pricing table.\n */\nexport function calculateCost(\n model: string,\n promptTokens: number,\n completionTokens: number,\n): number | undefined {\n const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];\n if (!pricing) return undefined;\n\n const inputCost = (promptTokens / 1_000_000) * pricing.input;\n const outputCost = (completionTokens / 1_000_000) * pricing.output;\n return inputCost + outputCost;\n}\n\n/**\n * Check if a model has pricing data.\n */\nexport function hasPricing(model: string): boolean {\n return model in PRICING || model.toLowerCase() in PRICING;\n}\n","/**\n * OpenAI SDK Proxy-based instrumentation.\n *\n * Wraps an OpenAI client instance using ES Proxy to intercept:\n * - chat.completions.create (sync + streaming)\n * - embeddings.create\n *\n * The original client is NOT modified — Proxy creates a transparent wrapper\n * that intercepts method calls and creates tracing spans.\n *\n * Usage:\n * import OpenAI from 'openai';\n * import { patchOpenAI } from 'risicare/openai';\n * const openai = patchOpenAI(new OpenAI({ apiKey: '...' }));\n */\n\nimport { requireTracer } from '../../client.js';\nimport { SpanKind } from '../../types.js';\nimport { calculateCost } from '../../utils/pricing.js';\nimport type { Span } from '../../span.js';\n\n// Known OpenAI-compatible hosts for provider detection\nconst COMPATIBLE_HOSTS: Record<string, string> = {\n 'api.deepseek.com': 'deepseek',\n 'api.together.xyz': 'together',\n 'api.groq.com': 'groq',\n};\n\nfunction detectProvider(client: unknown): string {\n try {\n const baseURL = (client as Record<string, unknown>).baseURL;\n if (!baseURL || typeof baseURL !== 'string') return 'openai';\n const host = baseURL.split('//').pop()?.split('/')[0]?.split(':')[0]?.toLowerCase();\n return (host && COMPATIBLE_HOSTS[host]) ?? 'openai';\n } catch {\n return 'openai';\n }\n}\n\nfunction enrichSpanFromResponse(span: Span, response: Record<string, unknown>, provider: string): void {\n const model = response.model as string | undefined;\n const usage = response.usage as Record<string, number> | undefined;\n\n if (model) {\n span.setLlmFields({ provider, model });\n }\n\n if (usage) {\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const totalTokens = usage.total_tokens ?? (promptTokens + completionTokens);\n const cost = model ? calculateCost(model, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens: totalTokens,\n costUsd: cost,\n });\n }\n}\n\nfunction createChatCompletionProxy(originalCreate: Function, provider: string): Function {\n return function patchedCreate(this: unknown, ...args: unknown[]) {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return originalCreate.apply(this, args);\n }\n\n const params = (args[0] ?? {}) as Record<string, unknown>;\n const model = (params.model as string) ?? 'unknown';\n const isStream = !!params.stream;\n\n return tracer.startSpan(\n { name: `openai.chat.completions.create`, kind: SpanKind.LLM_CALL, attributes: { 'llm.request.model': model, 'llm.stream': isStream } },\n (span) => {\n span.setLlmFields({ provider, model });\n\n const result = originalCreate.apply(this, args);\n\n if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then === 'function') {\n // Don't auto-end span here — the tracer.startSpan handles it\n // but we need to enrich before it ends. Return a Promise that\n // enriches then resolves.\n return (result as Promise<Record<string, unknown>>).then((response) => {\n if (!isStream && response) {\n enrichSpanFromResponse(span, response, provider);\n }\n return response;\n });\n }\n\n return result;\n },\n );\n };\n}\n\nfunction createEmbeddingsProxy(originalCreate: Function, provider: string): Function {\n return function patchedCreate(this: unknown, ...args: unknown[]) {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return originalCreate.apply(this, args);\n }\n\n const params = (args[0] ?? {}) as Record<string, unknown>;\n const model = (params.model as string) ?? 'unknown';\n\n return tracer.startSpan(\n { name: `openai.embeddings.create`, kind: SpanKind.LLM_CALL, attributes: { 'llm.request.model': model } },\n (span) => {\n span.setLlmFields({ provider, model });\n\n const result = originalCreate.apply(this, args);\n\n if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<Record<string, unknown>>).then((response) => {\n if (response) {\n enrichSpanFromResponse(span, response, provider);\n }\n return response;\n });\n }\n\n return result;\n },\n );\n };\n}\n\n/**\n * Wrap an OpenAI client instance with tracing instrumentation.\n *\n * Returns a Proxy that intercepts chat.completions.create and embeddings.create.\n * The original client is NOT modified.\n *\n * @param client - An OpenAI client instance\n * @returns A proxied client with automatic tracing\n */\nexport function patchOpenAI<T extends object>(client: T): T {\n const provider = detectProvider(client);\n\n // Proxy the top-level client\n return new Proxy(client, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n // Intercept `.chat` access to proxy `.chat.completions`\n if (prop === 'chat' && value && typeof value === 'object') {\n return new Proxy(value as object, {\n get(chatTarget, chatProp, chatReceiver) {\n const chatValue = Reflect.get(chatTarget, chatProp, chatReceiver);\n\n if (chatProp === 'completions' && chatValue && typeof chatValue === 'object') {\n return new Proxy(chatValue as object, {\n get(compTarget, compProp, compReceiver) {\n const compValue = Reflect.get(compTarget, compProp, compReceiver);\n\n if (compProp === 'create' && typeof compValue === 'function') {\n return createChatCompletionProxy(compValue.bind(compTarget), provider);\n }\n\n return compValue;\n },\n });\n }\n\n return chatValue;\n },\n });\n }\n\n // Intercept `.embeddings` access\n if (prop === 'embeddings' && value && typeof value === 'object') {\n return new Proxy(value as object, {\n get(embTarget, embProp, embReceiver) {\n const embValue = Reflect.get(embTarget, embProp, embReceiver);\n\n if (embProp === 'create' && typeof embValue === 'function') {\n return createEmbeddingsProxy(embValue.bind(embTarget), provider);\n }\n\n return embValue;\n },\n });\n }\n\n return value;\n },\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ids.ts","../../../src/noop.ts","../../../src/globals.ts","../../../src/utils/log.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/openai/patch.ts"],"sourcesContent":["/**\n * ID generation for traces and spans.\n *\n * Trace IDs: 32 lowercase hex characters (16 random bytes)\n * Span IDs: 16 lowercase hex characters (8 random bytes)\n *\n * Uses crypto.randomBytes for cryptographically secure randomness.\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst HEX_REGEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_REGEX_16 = /^[0-9a-f]{16}$/;\n\nexport function generateTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\nexport function generateSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nexport function generateAgentId(prefix?: string): string {\n const suffix = randomBytes(8).toString('hex');\n return prefix ? `${prefix}-${suffix}` : suffix;\n}\n\nexport function validateTraceId(id: string): boolean {\n return HEX_REGEX_32.test(id);\n}\n\nexport function validateSpanId(id: string): boolean {\n return HEX_REGEX_16.test(id);\n}\n","/**\n * No-op implementations for the disabled path.\n *\n * When tracing is disabled, all operations return these no-op objects\n * to maintain zero overhead. No allocations, no side effects.\n */\n\nimport { SpanKind, SpanStatus, type SpanPayload } from './types.js';\n\n/**\n * A frozen no-op span that silently ignores all operations.\n * Used when SDK is disabled to avoid overhead.\n */\nexport const NOOP_SPAN = Object.freeze({\n traceId: '00000000000000000000000000000000',\n spanId: '0000000000000000',\n parentSpanId: undefined,\n name: 'noop',\n kind: SpanKind.INTERNAL,\n startTime: '',\n startHrtime: 0,\n endTime: undefined,\n status: SpanStatus.UNSET,\n statusMessage: undefined,\n attributes: Object.freeze({}) as Record<string, unknown>,\n events: Object.freeze([]) as readonly [],\n links: Object.freeze([]) as readonly [],\n sessionId: undefined,\n agentId: undefined,\n agentName: undefined,\n semanticPhase: undefined,\n llmProvider: undefined,\n llmModel: undefined,\n llmPromptTokens: undefined,\n llmCompletionTokens: undefined,\n llmTotalTokens: undefined,\n llmCostUsd: undefined,\n toolName: undefined,\n toolSuccess: undefined,\n isEnded: true,\n durationMs: 0,\n\n setAttribute() { return this; },\n setAttributes() { return this; },\n setStatus() { return this; },\n addEvent() { return this; },\n addLink() { return this; },\n recordException() { return this; },\n setLlmFields() { return this; },\n setToolFields() { return this; },\n end() {},\n toPayload(): SpanPayload {\n return {\n traceId: this.traceId,\n spanId: this.spanId,\n name: this.name,\n kind: this.kind,\n startTime: this.startTime,\n status: this.status,\n attributes: {},\n events: [],\n links: [],\n };\n },\n});\n\nexport type NoopSpan = typeof NOOP_SPAN;\n","/**\n * Shared state via globalThis — ensures all entry point bundles share\n * the same singleton instances.\n *\n * Problem: tsup with `splitting: false` gives each entry point (index,\n * openai, anthropic, vercel-ai) its own copy of module-level variables.\n * This means `init()` from 'risicare' sets a tracer that 'risicare/openai'\n * can't see — breaking all provider instrumentation silently.\n *\n * Solution: Store all mutable singletons on globalThis with a namespaced\n * prefix. Every bundle reads/writes the same global slots.\n *\n * This pattern is used by React, OpenTelemetry, and other SDKs that must\n * share state across independently bundled entry points.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst G = globalThis as any;\nconst PREFIX = '__risicare_';\n\n// ─── Client & Tracer ────────────────────────────────────────────────────────\n\nexport function getClient(): unknown {\n return G[PREFIX + 'client'];\n}\n\nexport function setClient(client: unknown): void {\n G[PREFIX + 'client'] = client;\n}\n\nexport function getTracer(): unknown {\n return G[PREFIX + 'tracer'];\n}\n\nexport function setTracer(tracer: unknown): void {\n G[PREFIX + 'tracer'] = tracer;\n}\n\n// ─── Context Storage ────────────────────────────────────────────────────────\n\nexport function getContextStorage(): AsyncLocalStorage<unknown> {\n if (!G[PREFIX + 'ctx']) {\n G[PREFIX + 'ctx'] = new AsyncLocalStorage();\n }\n return G[PREFIX + 'ctx'];\n}\n\n// ─── Span Registry ──────────────────────────────────────────────────────────\n\nexport function getRegistry(): Map<string, unknown> {\n if (!G[PREFIX + 'registry']) {\n G[PREFIX + 'registry'] = new Map();\n }\n return G[PREFIX + 'registry'];\n}\n\nexport function getOpCount(): number {\n return G[PREFIX + 'opcount'] ?? 0;\n}\n\nexport function setOpCount(n: number): void {\n G[PREFIX + 'opcount'] = n;\n}\n\n// ─── Debug Flag ─────────────────────────────────────────────────────────────\n\nexport function getDebug(): boolean {\n return G[PREFIX + 'debug'] ?? false;\n}\n\nexport function setDebugFlag(enabled: boolean): void {\n G[PREFIX + 'debug'] = enabled;\n}\n","/**\n * Internal logger for the Risicare SDK.\n *\n * Centralizes all diagnostic output so that:\n * - Debug messages are gated by a single flag (zero-cost when disabled)\n * - Warnings always fire (operational alerts like queue full)\n * - All output goes to stderr with a consistent [risicare] prefix\n * - A future custom logger callback can be added in one place\n */\n\nimport { getDebug, setDebugFlag } from '../globals.js';\n\n/**\n * Enable or disable debug logging. Called once during init().\n * @internal\n */\nexport function setDebug(enabled: boolean): void {\n setDebugFlag(enabled);\n}\n\n/**\n * Log a debug message. Only outputs when debug mode is enabled.\n * @internal\n */\nexport function debug(msg: string): void {\n if (getDebug()) {\n process.stderr.write(`[risicare] ${msg}\\n`);\n }\n}\n\n/**\n * Log a warning. Always outputs regardless of debug mode.\n * Use sparingly — only for operational issues the user should see.\n * @internal\n */\nexport function warn(msg: string): void {\n process.stderr.write(`[risicare] WARNING: ${msg}\\n`);\n}\n","/**\n * RisicareClient — singleton client managing SDK lifecycle.\n *\n * Handles initialization, shutdown, and the connection between\n * the Tracer and the export pipeline (batch processor + HTTP exporter).\n *\n * Usage:\n * import { init, shutdown } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n * // ... instrument code ...\n * await shutdown(); // flush remaining spans\n */\n\nimport { type RisicareConfig, resolveConfig } from './config.js';\nimport { Tracer } from './tracer.js';\nimport { BatchSpanProcessor } from './exporters/batch.js';\nimport { HttpExporter } from './exporters/http.js';\nimport { ConsoleExporter } from './exporters/console.js';\nimport type { SpanExporter } from './exporters/base.js';\nimport { setDebug, debug } from './utils/log.js';\nimport {\n getClient as getGlobalClient,\n setClient as setGlobalClient,\n getTracer as getGlobalTracer,\n setTracer as setGlobalTracer,\n} from './globals.js';\n\n// ─── Client Class ───────────────────────────────────────────────────────────\n\nclass RisicareClient {\n readonly config: ReturnType<typeof resolveConfig>;\n readonly processor: BatchSpanProcessor;\n readonly tracer: Tracer;\n private _shutdownPromise: Promise<void> | undefined;\n private _shutdownHandlers: { signal: string; handler: () => void }[] = [];\n\n constructor(config?: Partial<RisicareConfig>) {\n this.config = resolveConfig(config);\n\n // API key format validation\n if (this.config.apiKey && !this.config.apiKey.startsWith('rsk-')) {\n debug('Warning: API key should start with \"rsk-\". Got: ' + this.config.apiKey.slice(0, 4) + '...');\n }\n\n // Build exporter chain\n let exporter: SpanExporter;\n if (this.config.debug && !this.config.apiKey) {\n exporter = new ConsoleExporter();\n } else if (this.config.apiKey) {\n exporter = new HttpExporter({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n projectId: this.config.projectId || undefined,\n environment: this.config.environment || undefined,\n compress: this.config.compress,\n });\n } else {\n // No API key and not debug — use console as fallback\n exporter = new ConsoleExporter();\n }\n\n this.processor = new BatchSpanProcessor({\n exporters: [exporter],\n batchSize: this.config.batchSize,\n batchTimeoutMs: this.config.batchTimeoutMs,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n });\n\n this.tracer = new Tracer({\n onSpanEnd: (span) => this.processor.onSpanEnd(span),\n sampleRate: this.config.sampleRate,\n enabled: this.config.enabled,\n traceContent: this.config.traceContent,\n });\n\n // Start the batch processor (enables span queuing and periodic flushing)\n this.processor.start();\n\n // Register shutdown hooks\n this._registerShutdownHooks();\n\n // Enable internal debug logging if configured\n setDebug(this.config.debug);\n debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);\n }\n\n get enabled(): boolean {\n return this.tracer.enabled;\n }\n\n set enabled(value: boolean) {\n this.tracer.enabled = value;\n }\n\n // Audit #6: Promise-based shutdown dedup (fixes TOCTOU race condition)\n async shutdown(): Promise<void> {\n if (this._shutdownPromise) return this._shutdownPromise;\n this._shutdownPromise = this._doShutdown();\n return this._shutdownPromise;\n }\n\n private async _doShutdown(): Promise<void> {\n debug('Shutting down...');\n\n // Audit #3: Remove process listeners to prevent leak\n for (const { signal, handler } of this._shutdownHandlers) {\n process.removeListener(signal, handler);\n }\n this._shutdownHandlers = [];\n\n await this.processor.shutdown();\n }\n\n async flush(): Promise<void> {\n await this.processor.flush();\n }\n\n private _registerShutdownHooks(): void {\n const onShutdown = () => {\n // Audit #3: Add 5s timeout to prevent hanging on signal\n const timeout = setTimeout(() => process.exit(1), 5000);\n timeout.unref();\n this.shutdown().catch(() => {}).finally(() => clearTimeout(timeout));\n };\n\n const signals = ['beforeExit', 'SIGTERM', 'SIGINT'];\n for (const signal of signals) {\n process.once(signal, onShutdown);\n this._shutdownHandlers.push({ signal, handler: onShutdown });\n }\n }\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Initialize the Risicare SDK. Call once at application startup.\n *\n * @example\n * import { init } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n */\nexport function init(config?: Partial<RisicareConfig>): void {\n if (getGlobalClient()) {\n debug('Already initialized. Call shutdown() first to re-initialize.');\n return;\n }\n\n const client = new RisicareClient(config);\n setGlobalClient(client);\n setGlobalTracer(client.tracer);\n}\n\n/**\n * Gracefully shut down the SDK. Flushes pending spans before resolving.\n */\nexport async function shutdown(): Promise<void> {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (!client) return;\n await client.shutdown();\n setGlobalClient(undefined);\n setGlobalTracer(undefined);\n}\n\n/**\n * Flush all pending spans without shutting down.\n */\nexport async function flush(): Promise<void> {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (!client) return;\n await client.flush();\n}\n\n/**\n * Enable tracing at runtime.\n */\nexport function enable(): void {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (client) client.enabled = true;\n}\n\n/**\n * Disable tracing at runtime. Spans will not be created or exported.\n */\nexport function disable(): void {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (client) client.enabled = false;\n}\n\n/**\n * Check whether tracing is currently enabled.\n */\nexport function isEnabled(): boolean {\n const client = getGlobalClient() as RisicareClient | undefined;\n return client?.enabled ?? false;\n}\n\n/**\n * Get the global tracer instance. Returns undefined if not initialized.\n */\nexport function getTracer(): Tracer | undefined {\n return getGlobalTracer() as Tracer | undefined;\n}\n\n/**\n * Get the global tracer, or throw if not initialized.\n * @internal Used by decorators and providers that require an active tracer.\n */\nexport function requireTracer(): Tracer {\n const tracer = getGlobalTracer() as Tracer | undefined;\n if (!tracer) {\n throw new Error(\n 'Risicare SDK not initialized. Call init() before using tracing features.',\n );\n }\n return tracer;\n}\n\n/**\n * Check whether content tracing (prompt/completion capture) is enabled.\n */\nexport function getTraceContent(): boolean {\n const tracer = getGlobalTracer() as Tracer | undefined;\n return tracer?.traceContent ?? true;\n}\n\n/**\n * Get SDK metrics: exported spans, dropped spans, failed exports, queue stats.\n * Returns zero-valued metrics if SDK is not initialized.\n */\nexport function getMetrics() {\n const client = getGlobalClient() as RisicareClient | undefined;\n return client?.processor.getMetrics() ?? {\n exportedSpans: 0,\n droppedSpans: 0,\n failedExports: 0,\n queueSize: 0,\n queueCapacity: 0,\n queueUtilization: 0,\n };\n}\n","/**\n * Token cost calculation table.\n *\n * Prices are per 1M tokens. Update monthly.\n * Source: provider pricing pages.\n */\n\ninterface ModelPricing {\n input: number; // USD per 1M input tokens\n output: number; // USD per 1M output tokens\n}\n\nconst PRICING: Record<string, ModelPricing> = {\n // OpenAI\n 'gpt-4o': { input: 2.50, output: 10.00 },\n 'gpt-4o-mini': { input: 0.15, output: 0.60 },\n 'gpt-4-turbo': { input: 10.00, output: 30.00 },\n 'gpt-4': { input: 30.00, output: 60.00 },\n 'gpt-3.5-turbo': { input: 0.50, output: 1.50 },\n 'o1': { input: 15.00, output: 60.00 },\n 'o1-mini': { input: 3.00, output: 12.00 },\n 'o3-mini': { input: 1.10, output: 4.40 },\n\n // Anthropic\n 'claude-opus-4-5-20251101': { input: 15.00, output: 75.00 },\n 'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },\n 'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },\n 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },\n 'claude-3-opus-20240229': { input: 15.00, output: 75.00 },\n\n // Google\n 'gemini-2.0-flash': { input: 0.10, output: 0.40 },\n 'gemini-1.5-pro': { input: 1.25, output: 5.00 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.30 },\n\n // Groq\n 'llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },\n 'llama-3.1-8b-instant': { input: 0.05, output: 0.08 },\n 'mixtral-8x7b-32768': { input: 0.24, output: 0.24 },\n\n // DeepSeek\n 'deepseek-chat': { input: 0.14, output: 0.28 },\n 'deepseek-reasoner': { input: 0.55, output: 2.19 },\n\n // Together.ai (open-source models)\n 'meta-llama/llama-3.3-70b-instruct-turbo': { input: 0.88, output: 0.88 },\n 'meta-llama/meta-llama-3.1-8b-instruct-turbo': { input: 0.18, output: 0.18 },\n 'meta-llama/llama-3.2-3b-instruct-turbo': { input: 0.06, output: 0.06 },\n 'qwen/qwen2.5-7b-instruct-turbo': { input: 0.20, output: 0.20 },\n 'mistralai/mistral-small-24b-instruct-2501': { input: 0.20, output: 0.20 },\n 'mistralai/mixtral-8x7b-instruct-v0.1': { input: 0.60, output: 0.60 },\n 'deepseek-ai/deepseek-v3': { input: 0.27, output: 1.10 },\n};\n\n/**\n * Calculate cost in USD for a model's token usage.\n * Returns undefined if model is not in pricing table.\n */\nexport function calculateCost(\n model: string,\n promptTokens: number,\n completionTokens: number,\n): number | undefined {\n const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];\n if (!pricing) return undefined;\n\n const inputCost = (promptTokens / 1_000_000) * pricing.input;\n const outputCost = (completionTokens / 1_000_000) * pricing.output;\n return inputCost + outputCost;\n}\n\n/**\n * Check if a model has pricing data.\n */\nexport function hasPricing(model: string): boolean {\n return model in PRICING || model.toLowerCase() in PRICING;\n}\n","/**\n * OpenAI SDK Proxy-based instrumentation.\n *\n * Wraps an OpenAI client instance using ES Proxy to intercept:\n * - chat.completions.create (sync + streaming)\n * - embeddings.create\n *\n * The original client is NOT modified — Proxy creates a transparent wrapper\n * that intercepts method calls and creates tracing spans.\n *\n * Usage:\n * import OpenAI from 'openai';\n * import { patchOpenAI } from 'risicare/openai';\n * const openai = patchOpenAI(new OpenAI({ apiKey: '...' }));\n */\n\nimport { requireTracer } from '../../client.js';\nimport { SpanKind } from '../../types.js';\nimport { calculateCost } from '../../utils/pricing.js';\nimport { debug } from '../../utils/log.js';\nimport type { Span } from '../../span.js';\n\n// Known OpenAI-compatible hosts for provider detection\nconst COMPATIBLE_HOSTS: Record<string, string> = {\n 'api.deepseek.com': 'deepseek',\n 'api.together.xyz': 'together',\n 'api.groq.com': 'groq',\n};\n\nfunction detectProvider(client: unknown): string {\n try {\n const baseURL = (client as Record<string, unknown>).baseURL;\n if (!baseURL || typeof baseURL !== 'string') return 'openai';\n const host = baseURL.split('//').pop()?.split('/')[0]?.split(':')[0]?.toLowerCase();\n return (host && COMPATIBLE_HOSTS[host]) ?? 'openai';\n } catch {\n return 'openai';\n }\n}\n\nfunction enrichSpanFromResponse(span: Span, response: Record<string, unknown>, provider: string): void {\n const model = response.model as string | undefined;\n const usage = response.usage as Record<string, number> | undefined;\n\n if (model) {\n span.setLlmFields({ provider, model });\n }\n\n if (usage) {\n const promptTokens = usage.prompt_tokens ?? 0;\n const completionTokens = usage.completion_tokens ?? 0;\n const totalTokens = usage.total_tokens ?? (promptTokens + completionTokens);\n const cost = model ? calculateCost(model, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens: totalTokens,\n costUsd: cost,\n });\n }\n}\n\nfunction createChatCompletionProxy(originalCreate: Function, provider: string): Function {\n return function patchedCreate(this: unknown, ...args: unknown[]) {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchOpenAI()');\n return originalCreate.apply(this, args);\n }\n\n const params = (args[0] ?? {}) as Record<string, unknown>;\n const model = (params.model as string) ?? 'unknown';\n const isStream = !!params.stream;\n\n return tracer.startSpan(\n { name: `openai.chat.completions.create`, kind: SpanKind.LLM_CALL, attributes: { 'llm.request.model': model, 'llm.stream': isStream } },\n (span) => {\n span.setLlmFields({ provider, model });\n\n const result = originalCreate.apply(this, args);\n\n if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then === 'function') {\n // Don't auto-end span here — the tracer.startSpan handles it\n // but we need to enrich before it ends. Return a Promise that\n // enriches then resolves.\n return (result as Promise<Record<string, unknown>>).then((response) => {\n if (!isStream && response) {\n enrichSpanFromResponse(span, response, provider);\n }\n return response;\n });\n }\n\n return result;\n },\n );\n };\n}\n\nfunction createEmbeddingsProxy(originalCreate: Function, provider: string): Function {\n return function patchedCreate(this: unknown, ...args: unknown[]) {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchOpenAI()');\n return originalCreate.apply(this, args);\n }\n\n const params = (args[0] ?? {}) as Record<string, unknown>;\n const model = (params.model as string) ?? 'unknown';\n\n return tracer.startSpan(\n { name: `openai.embeddings.create`, kind: SpanKind.LLM_CALL, attributes: { 'llm.request.model': model } },\n (span) => {\n span.setLlmFields({ provider, model });\n\n const result = originalCreate.apply(this, args);\n\n if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<Record<string, unknown>>).then((response) => {\n if (response) {\n enrichSpanFromResponse(span, response, provider);\n }\n return response;\n });\n }\n\n return result;\n },\n );\n };\n}\n\n/**\n * Wrap an OpenAI client instance with tracing instrumentation.\n *\n * Returns a Proxy that intercepts chat.completions.create and embeddings.create.\n * The original client is NOT modified.\n *\n * @param client - An OpenAI client instance\n * @returns A proxied client with automatic tracing\n */\nexport function patchOpenAI<T extends object>(client: T): T {\n const provider = detectProvider(client);\n\n // Proxy the top-level client\n return new Proxy(client, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n // Intercept `.chat` access to proxy `.chat.completions`\n if (prop === 'chat' && value && typeof value === 'object') {\n return new Proxy(value as object, {\n get(chatTarget, chatProp, chatReceiver) {\n const chatValue = Reflect.get(chatTarget, chatProp, chatReceiver);\n\n if (chatProp === 'completions' && chatValue && typeof chatValue === 'object') {\n return new Proxy(chatValue as object, {\n get(compTarget, compProp, compReceiver) {\n const compValue = Reflect.get(compTarget, compProp, compReceiver);\n\n if (compProp === 'create' && typeof compValue === 'function') {\n return createChatCompletionProxy(compValue.bind(compTarget), provider);\n }\n\n return compValue;\n },\n });\n }\n\n return chatValue;\n },\n });\n }\n\n // Intercept `.embeddings` access\n if (prop === 'embeddings' && value && typeof value === 'object') {\n return new Proxy(value as object, {\n get(embTarget, embProp, embReceiver) {\n const embValue = Reflect.get(embTarget, embProp, embReceiver);\n\n if (embProp === 'create' && typeof embValue === 'function') {\n return createEmbeddingsProxy(embValue.bind(embTarget), provider);\n }\n\n return embValue;\n },\n });\n }\n\n return value;\n },\n });\n}\n"],"mappings":";AASA,SAAS,mBAAmB;;;ACIrB,IAAM,YAAY,OAAO,OAAO;AAAA,EACrC,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,MAAM;AAAA,EACN;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AAAA,EACb,SAAS;AAAA,EACT;AAAA,EACA,eAAe;AAAA,EACf,YAAY,OAAO,OAAO,CAAC,CAAC;AAAA,EAC5B,QAAQ,OAAO,OAAO,CAAC,CAAC;AAAA,EACxB,OAAO,OAAO,OAAO,CAAC,CAAC;AAAA,EACvB,WAAW;AAAA,EACX,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe;AAAA,EACf,aAAa;AAAA,EACb,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,YAAY;AAAA,EAEZ,eAAe;AAAE,WAAO;AAAA,EAAM;AAAA,EAC9B,gBAAgB;AAAE,WAAO;AAAA,EAAM;AAAA,EAC/B,YAAY;AAAE,WAAO;AAAA,EAAM;AAAA,EAC3B,WAAW;AAAE,WAAO;AAAA,EAAM;AAAA,EAC1B,UAAU;AAAE,WAAO;AAAA,EAAM;AAAA,EACzB,kBAAkB;AAAE,WAAO;AAAA,EAAM;AAAA,EACjC,eAAe;AAAE,WAAO;AAAA,EAAM;AAAA,EAC9B,gBAAgB;AAAE,WAAO;AAAA,EAAM;AAAA,EAC/B,MAAM;AAAA,EAAC;AAAA,EACP,YAAyB;AACvB,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,QAAQ,KAAK;AAAA,MACb,YAAY,CAAC;AAAA,MACb,QAAQ,CAAC;AAAA,MACT,OAAO,CAAC;AAAA,IACV;AAAA,EACF;AACF,CAAC;;;AC9CD,SAAS,yBAAyB;AAGlC,IAAM,IAAI;AACV,IAAM,SAAS;AAYR,SAAS,YAAqB;AACnC,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAkCO,SAAS,WAAoB;AAClC,SAAO,EAAE,SAAS,OAAO,KAAK;AAChC;;;AChDO,SAAS,MAAM,KAAmB;AACvC,MAAI,SAAS,GAAG;AACd,YAAQ,OAAO,MAAM,cAAc,GAAG;AAAA,CAAI;AAAA,EAC5C;AACF;;;ACqLO,SAAS,gBAAwB;AACtC,QAAM,SAAS,UAAgB;AAC/B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC7MA,IAAM,UAAwC;AAAA;AAAA,EAE5C,UAAU,EAAE,OAAO,KAAM,QAAQ,GAAM;AAAA,EACvC,eAAe,EAAE,OAAO,MAAM,QAAQ,IAAK;AAAA,EAC3C,eAAe,EAAE,OAAO,IAAO,QAAQ,GAAM;AAAA,EAC7C,SAAS,EAAE,OAAO,IAAO,QAAQ,GAAM;AAAA,EACvC,iBAAiB,EAAE,OAAO,KAAM,QAAQ,IAAK;AAAA,EAC7C,MAAM,EAAE,OAAO,IAAO,QAAQ,GAAM;AAAA,EACpC,WAAW,EAAE,OAAO,GAAM,QAAQ,GAAM;AAAA,EACxC,WAAW,EAAE,OAAO,KAAM,QAAQ,IAAK;AAAA;AAAA,EAGvC,4BAA4B,EAAE,OAAO,IAAO,QAAQ,GAAM;AAAA,EAC1D,8BAA8B,EAAE,OAAO,GAAM,QAAQ,GAAM;AAAA,EAC3D,6BAA6B,EAAE,OAAO,KAAM,QAAQ,EAAK;AAAA,EACzD,8BAA8B,EAAE,OAAO,GAAM,QAAQ,GAAM;AAAA,EAC3D,2BAA2B,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACvD,0BAA0B,EAAE,OAAO,IAAO,QAAQ,GAAM;AAAA;AAAA,EAGxD,oBAAoB,EAAE,OAAO,KAAM,QAAQ,IAAK;AAAA,EAChD,kBAAkB,EAAE,OAAO,MAAM,QAAQ,EAAK;AAAA,EAC9C,oBAAoB,EAAE,OAAO,OAAO,QAAQ,IAAK;AAAA;AAAA,EAGjD,2BAA2B,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACvD,wBAAwB,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACpD,sBAAsB,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA;AAAA,EAGlD,iBAAiB,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC7C,qBAAqB,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA;AAAA,EAGjD,2CAA2C,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACvE,+CAA+C,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC3E,0CAA0C,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EACtE,kCAAkC,EAAE,OAAO,KAAM,QAAQ,IAAK;AAAA,EAC9D,6CAA6C,EAAE,OAAO,KAAM,QAAQ,IAAK;AAAA,EACzE,wCAAwC,EAAE,OAAO,KAAM,QAAQ,IAAK;AAAA,EACpE,2BAA2B,EAAE,OAAO,MAAM,QAAQ,IAAK;AACzD;AAMO,SAAS,cACd,OACA,cACA,kBACoB;AACpB,QAAM,UAAU,QAAQ,KAAK,KAAK,QAAQ,MAAM,YAAY,CAAC;AAC7D,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,YAAa,eAAe,MAAa,QAAQ;AACvD,QAAM,aAAc,mBAAmB,MAAa,QAAQ;AAC5D,SAAO,YAAY;AACrB;;;AC/CA,IAAM,mBAA2C;AAAA,EAC/C,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,gBAAgB;AAClB;AAEA,SAAS,eAAe,QAAyB;AAC/C,MAAI;AACF,UAAM,UAAW,OAAmC;AACpD,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,UAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY;AAClF,YAAQ,QAAQ,iBAAiB,IAAI,MAAM;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,uBAAuB,MAAY,UAAmC,UAAwB;AACrG,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ,SAAS;AAEvB,MAAI,OAAO;AACT,SAAK,aAAa,EAAE,UAAU,MAAM,CAAC;AAAA,EACvC;AAEA,MAAI,OAAO;AACT,UAAM,eAAe,MAAM,iBAAiB;AAC5C,UAAM,mBAAmB,MAAM,qBAAqB;AACpD,UAAM,cAAc,MAAM,gBAAiB,eAAe;AAC1D,UAAM,OAAO,QAAQ,cAAc,OAAO,cAAc,gBAAgB,IAAI;AAE5E,SAAK,aAAa;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,SAAS,0BAA0B,gBAA0B,UAA4B;AACvF,SAAO,SAAS,iBAAgC,MAAiB;AAC/D,QAAI;AACJ,QAAI;AACF,eAAS,cAAc;AAAA,IACzB,QAAQ;AACN,YAAM,sEAAiE;AACvE,aAAO,eAAe,MAAM,MAAM,IAAI;AAAA,IACxC;AAEA,UAAM,SAAU,KAAK,CAAC,KAAK,CAAC;AAC5B,UAAM,QAAS,OAAO,SAAoB;AAC1C,UAAM,WAAW,CAAC,CAAC,OAAO;AAE1B,WAAO,OAAO;AAAA,MACZ,EAAE,MAAM,kCAAkC,iCAAyB,YAAY,EAAE,qBAAqB,OAAO,cAAc,SAAS,EAAE;AAAA,MACtI,CAAC,SAAS;AACR,aAAK,aAAa,EAAE,UAAU,MAAM,CAAC;AAErC,cAAM,SAAS,eAAe,MAAM,MAAM,IAAI;AAE9C,YAAI,UAAU,OAAO,WAAW,YAAY,OAAQ,OAA4B,SAAS,YAAY;AAInG,iBAAQ,OAA4C,KAAK,CAAC,aAAa;AACrE,gBAAI,CAAC,YAAY,UAAU;AACzB,qCAAuB,MAAM,UAAU,QAAQ;AAAA,YACjD;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,gBAA0B,UAA4B;AACnF,SAAO,SAAS,iBAAgC,MAAiB;AAC/D,QAAI;AACJ,QAAI;AACF,eAAS,cAAc;AAAA,IACzB,QAAQ;AACN,YAAM,sEAAiE;AACvE,aAAO,eAAe,MAAM,MAAM,IAAI;AAAA,IACxC;AAEA,UAAM,SAAU,KAAK,CAAC,KAAK,CAAC;AAC5B,UAAM,QAAS,OAAO,SAAoB;AAE1C,WAAO,OAAO;AAAA,MACZ,EAAE,MAAM,4BAA4B,iCAAyB,YAAY,EAAE,qBAAqB,MAAM,EAAE;AAAA,MACxG,CAAC,SAAS;AACR,aAAK,aAAa,EAAE,UAAU,MAAM,CAAC;AAErC,cAAM,SAAS,eAAe,MAAM,MAAM,IAAI;AAE9C,YAAI,UAAU,OAAO,WAAW,YAAY,OAAQ,OAA4B,SAAS,YAAY;AACnG,iBAAQ,OAA4C,KAAK,CAAC,aAAa;AACrE,gBAAI,UAAU;AACZ,qCAAuB,MAAM,UAAU,QAAQ;AAAA,YACjD;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAWO,SAAS,YAA8B,QAAc;AAC1D,QAAM,WAAW,eAAe,MAAM;AAGtC,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAGhD,UAAI,SAAS,UAAU,SAAS,OAAO,UAAU,UAAU;AACzD,eAAO,IAAI,MAAM,OAAiB;AAAA,UAChC,IAAI,YAAY,UAAU,cAAc;AACtC,kBAAM,YAAY,QAAQ,IAAI,YAAY,UAAU,YAAY;AAEhE,gBAAI,aAAa,iBAAiB,aAAa,OAAO,cAAc,UAAU;AAC5E,qBAAO,IAAI,MAAM,WAAqB;AAAA,gBACpC,IAAI,YAAY,UAAU,cAAc;AACtC,wBAAM,YAAY,QAAQ,IAAI,YAAY,UAAU,YAAY;AAEhE,sBAAI,aAAa,YAAY,OAAO,cAAc,YAAY;AAC5D,2BAAO,0BAA0B,UAAU,KAAK,UAAU,GAAG,QAAQ;AAAA,kBACvE;AAEA,yBAAO;AAAA,gBACT;AAAA,cACF,CAAC;AAAA,YACH;AAEA,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AAGA,UAAI,SAAS,gBAAgB,SAAS,OAAO,UAAU,UAAU;AAC/D,eAAO,IAAI,MAAM,OAAiB;AAAA,UAChC,IAAI,WAAW,SAAS,aAAa;AACnC,kBAAM,WAAW,QAAQ,IAAI,WAAW,SAAS,WAAW;AAE5D,gBAAI,YAAY,YAAY,OAAO,aAAa,YAAY;AAC1D,qBAAO,sBAAsB,SAAS,KAAK,SAAS,GAAG,QAAQ;AAAA,YACjE;AAEA,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -97,19 +97,34 @@ var NOOP_SPAN = Object.freeze({
97
97
  }
98
98
  });
99
99
 
100
- // src/context/storage.ts
100
+ // src/globals.ts
101
101
  var import_node_async_hooks = require("async_hooks");
102
- var contextStorage = new import_node_async_hooks.AsyncLocalStorage();
102
+ var G = globalThis;
103
+ var PREFIX = "__risicare_";
104
+ function getTracer() {
105
+ return G[PREFIX + "tracer"];
106
+ }
107
+ function getDebug() {
108
+ return G[PREFIX + "debug"] ?? false;
109
+ }
110
+
111
+ // src/utils/log.ts
112
+ function debug(msg) {
113
+ if (getDebug()) {
114
+ process.stderr.write(`[risicare] ${msg}
115
+ `);
116
+ }
117
+ }
103
118
 
104
119
  // src/client.ts
105
- var _tracer;
106
120
  function requireTracer() {
107
- if (!_tracer) {
121
+ const tracer = getTracer();
122
+ if (!tracer) {
108
123
  throw new Error(
109
124
  "Risicare SDK not initialized. Call init() before using tracing features."
110
125
  );
111
126
  }
112
- return _tracer;
127
+ return tracer;
113
128
  }
114
129
 
115
130
  // src/utils/pricing.ts
@@ -124,7 +139,7 @@ var PRICING = {
124
139
  "o1-mini": { input: 3, output: 12 },
125
140
  "o3-mini": { input: 1.1, output: 4.4 },
126
141
  // Anthropic
127
- "claude-opus-4-6": { input: 15, output: 75 },
142
+ "claude-opus-4-5-20251101": { input: 15, output: 75 },
128
143
  "claude-sonnet-4-5-20250929": { input: 3, output: 15 },
129
144
  "claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
130
145
  "claude-3-5-sonnet-20241022": { input: 3, output: 15 },
@@ -187,10 +202,11 @@ function patchVercelAI() {
187
202
  try {
188
203
  tracer = requireTracer();
189
204
  } catch {
205
+ debug("Tracer not initialized \u2014 call init() before using patchVercelAI()");
190
206
  return fn(params);
191
207
  }
192
208
  return tracer.startSpan(
193
- { name: "vercel-ai.generateText", kind: "client" /* CLIENT */ },
209
+ { name: "vercel-ai.generateText", kind: "llm_call" /* LLM_CALL */ },
194
210
  async (span) => {
195
211
  span.setLlmFields({ provider: "vercel-ai" });
196
212
  const result = await fn(params);
@@ -206,10 +222,11 @@ function patchVercelAI() {
206
222
  try {
207
223
  tracer = requireTracer();
208
224
  } catch {
225
+ debug("Tracer not initialized \u2014 call init() before using patchVercelAI()");
209
226
  return fn(params);
210
227
  }
211
228
  return tracer.startSpan(
212
- { name: "vercel-ai.streamText", kind: "client" /* CLIENT */ },
229
+ { name: "vercel-ai.streamText", kind: "llm_call" /* LLM_CALL */ },
213
230
  (span) => {
214
231
  span.setLlmFields({ provider: "vercel-ai" });
215
232
  return fn(params);
@@ -223,10 +240,11 @@ function patchVercelAI() {
223
240
  try {
224
241
  tracer = requireTracer();
225
242
  } catch {
243
+ debug("Tracer not initialized \u2014 call init() before using patchVercelAI()");
226
244
  return fn(params);
227
245
  }
228
246
  return tracer.startSpan(
229
- { name: "vercel-ai.generateObject", kind: "client" /* CLIENT */ },
247
+ { name: "vercel-ai.generateObject", kind: "llm_call" /* LLM_CALL */ },
230
248
  async (span) => {
231
249
  span.setLlmFields({ provider: "vercel-ai" });
232
250
  const result = await fn(params);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/providers/vercel-ai/index.ts","../../../src/ids.ts","../../../src/noop.ts","../../../src/context/storage.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/vercel-ai/patch.ts"],"sourcesContent":["/**\n * Vercel AI SDK instrumentation.\n *\n * Entry point for `import { patchVercelAI } from 'risicare/vercel-ai'`.\n */\n\nexport { patchVercelAI } from './patch.js';\n","/**\n * ID generation for traces and spans.\n *\n * Trace IDs: 32 lowercase hex characters (16 random bytes)\n * Span IDs: 16 lowercase hex characters (8 random bytes)\n *\n * Uses crypto.randomBytes for cryptographically secure randomness.\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst HEX_REGEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_REGEX_16 = /^[0-9a-f]{16}$/;\n\nexport function generateTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\nexport function generateSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nexport function generateAgentId(prefix?: string): string {\n const suffix = randomBytes(6).toString('hex');\n return prefix ? `${prefix}-${suffix}` : suffix;\n}\n\nexport function validateTraceId(id: string): boolean {\n return HEX_REGEX_32.test(id);\n}\n\nexport function validateSpanId(id: string): boolean {\n return HEX_REGEX_16.test(id);\n}\n","/**\n * No-op implementations for the disabled path.\n *\n * When tracing is disabled, all operations return these no-op objects\n * to maintain zero overhead. No allocations, no side effects.\n */\n\nimport { SpanKind, SpanStatus, type SpanPayload } from './types.js';\n\n/**\n * A frozen no-op span that silently ignores all operations.\n * Used when SDK is disabled to avoid overhead.\n */\nexport const NOOP_SPAN = Object.freeze({\n traceId: '00000000000000000000000000000000',\n spanId: '0000000000000000',\n parentSpanId: undefined,\n name: 'noop',\n kind: SpanKind.INTERNAL,\n startTime: '',\n startHrtime: 0,\n endTime: undefined,\n status: SpanStatus.UNSET,\n statusMessage: undefined,\n attributes: Object.freeze({}) as Record<string, unknown>,\n events: Object.freeze([]) as readonly [],\n links: Object.freeze([]) as readonly [],\n sessionId: undefined,\n agentId: undefined,\n agentName: undefined,\n semanticPhase: undefined,\n llmProvider: undefined,\n llmModel: undefined,\n llmPromptTokens: undefined,\n llmCompletionTokens: undefined,\n llmTotalTokens: undefined,\n llmCostUsd: undefined,\n toolName: undefined,\n toolSuccess: undefined,\n isEnded: true,\n durationMs: 0,\n\n setAttribute() { return this; },\n setAttributes() { return this; },\n setStatus() { return this; },\n addEvent() { return this; },\n addLink() { return this; },\n recordException() { return this; },\n setLlmFields() { return this; },\n setToolFields() { return this; },\n end() {},\n toPayload(): SpanPayload {\n return {\n traceId: this.traceId,\n spanId: this.spanId,\n name: this.name,\n kind: this.kind,\n startTime: this.startTime,\n status: this.status,\n attributes: {},\n events: [],\n links: [],\n };\n },\n});\n\nexport type NoopSpan = typeof NOOP_SPAN;\n","/**\n * AsyncLocalStorage-based context propagation.\n *\n * Uses a single AsyncLocalStorage instance with a composite state object.\n * This is simpler and more performant than multiple separate stores.\n *\n * Node.js AsyncLocalStorage automatically propagates through:\n * - Promise / async-await\n * - setTimeout / setImmediate\n * - EventEmitter callbacks\n * - process.nextTick\n * - async generators (unlike Python's contextvars!)\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Span } from '../span.js';\nimport type { SemanticPhase } from '../types.js';\n\n// ─── Context Types ───────────────────────────────────────────────────────────\n\nexport interface SessionContext {\n sessionId: string;\n userId?: string;\n metadata?: Record<string, unknown>;\n parentSessionId?: string;\n turnNumber?: number;\n}\n\nexport interface AgentContext {\n agentId: string;\n agentName?: string;\n agentRole?: string;\n agentType?: string;\n parentAgentId?: string;\n version?: number;\n metadata?: Record<string, unknown>;\n}\n\nexport interface ContextState {\n session?: SessionContext;\n agent?: AgentContext;\n span?: Span;\n phase?: SemanticPhase;\n}\n\n// ─── Storage Instance ────────────────────────────────────────────────────────\n\nconst contextStorage = new AsyncLocalStorage<ContextState>();\n\n// ─── Core Operations ─────────────────────────────────────────────────────────\n\n/**\n * Get the current context state, or empty object if outside any context.\n */\nexport function getContext(): ContextState {\n return contextStorage.getStore() ?? {};\n}\n\n/**\n * Run a callback within a new context scope.\n * The new scope inherits from the parent, with overrides applied.\n */\nexport function runWithContext<T>(overrides: Partial<ContextState>, fn: () => T): T {\n const parent = getContext();\n const merged: ContextState = { ...parent, ...overrides };\n return contextStorage.run(merged, fn);\n}\n\n/**\n * Run an async callback within a new context scope.\n */\nexport function runWithContextAsync<T>(overrides: Partial<ContextState>, fn: () => Promise<T>): Promise<T> {\n const parent = getContext();\n const merged: ContextState = { ...parent, ...overrides };\n return contextStorage.run(merged, fn);\n}\n\n// ─── Context Accessors ───────────────────────────────────────────────────────\n\nexport function getCurrentSession(): SessionContext | undefined {\n return getContext().session;\n}\n\nexport function getCurrentAgent(): AgentContext | undefined {\n return getContext().agent;\n}\n\nexport function getCurrentSpan(): Span | undefined {\n return getContext().span;\n}\n\nexport function getCurrentPhase(): SemanticPhase | undefined {\n return getContext().phase;\n}\n\nexport function getCurrentSessionId(): string | undefined {\n return getContext().session?.sessionId;\n}\n\nexport function getCurrentAgentId(): string | undefined {\n return getContext().agent?.agentId;\n}\n\nexport function getCurrentTraceId(): string | undefined {\n return getContext().span?.traceId;\n}\n\nexport function getCurrentSpanId(): string | undefined {\n return getContext().span?.spanId;\n}\n\nexport function getCurrentParentSpanId(): string | undefined {\n return getContext().span?.parentSpanId;\n}\n\n/**\n * Get all current context as a plain object (for debugging/serialization).\n */\nexport function getCurrentContext(): Record<string, unknown> {\n const ctx = getContext();\n return {\n session: ctx.session ? { sessionId: ctx.session.sessionId, userId: ctx.session.userId } : null,\n agent: ctx.agent ? { agentId: ctx.agent.agentId, agentName: ctx.agent.agentName, agentRole: ctx.agent.agentRole } : null,\n span: ctx.span ? { spanId: ctx.span.spanId, traceId: ctx.span.traceId } : null,\n phase: ctx.phase ?? null,\n };\n}\n","/**\n * RisicareClient — singleton client managing SDK lifecycle.\n *\n * Handles initialization, shutdown, and the connection between\n * the Tracer and the export pipeline (batch processor + HTTP exporter).\n *\n * Usage:\n * import { init, shutdown } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n * // ... instrument code ...\n * await shutdown(); // flush remaining spans\n */\n\nimport { type RisicareConfig, resolveConfig } from './config.js';\nimport { Tracer } from './tracer.js';\nimport { BatchSpanProcessor } from './exporters/batch.js';\nimport { HttpExporter } from './exporters/http.js';\nimport { ConsoleExporter } from './exporters/console.js';\nimport type { SpanExporter } from './exporters/base.js';\nimport { setDebug, debug } from './utils/log.js';\n\n// ─── Singleton State ────────────────────────────────────────────────────────\n\nlet _client: RisicareClient | undefined;\nlet _tracer: Tracer | undefined;\n\n// ─── Client Class ───────────────────────────────────────────────────────────\n\nclass RisicareClient {\n readonly config: ReturnType<typeof resolveConfig>;\n readonly processor: BatchSpanProcessor;\n readonly tracer: Tracer;\n private _shutdownCalled = false;\n\n constructor(config?: Partial<RisicareConfig>) {\n this.config = resolveConfig(config);\n\n // Build exporter chain\n let exporter: SpanExporter;\n if (this.config.debug && !this.config.apiKey) {\n exporter = new ConsoleExporter();\n } else if (this.config.apiKey) {\n exporter = new HttpExporter({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n projectId: this.config.projectId || undefined,\n environment: this.config.environment || undefined,\n });\n } else {\n // No API key and not debug — use console as fallback\n exporter = new ConsoleExporter();\n }\n\n this.processor = new BatchSpanProcessor({\n exporters: [exporter],\n batchSize: this.config.batchSize,\n batchTimeoutMs: this.config.batchTimeoutMs,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n });\n\n this.tracer = new Tracer({\n onSpanEnd: (span) => this.processor.onSpanEnd(span),\n sampleRate: this.config.sampleRate,\n enabled: this.config.enabled,\n });\n\n // Start the batch processor (enables span queuing and periodic flushing)\n this.processor.start();\n\n // Register shutdown hooks\n this._registerShutdownHooks();\n\n // Enable internal debug logging if configured\n setDebug(this.config.debug);\n debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);\n }\n\n get enabled(): boolean {\n return this.tracer.enabled;\n }\n\n set enabled(value: boolean) {\n this.tracer.enabled = value;\n }\n\n async shutdown(): Promise<void> {\n if (this._shutdownCalled) return;\n this._shutdownCalled = true;\n\n debug('Shutting down...');\n\n await this.processor.shutdown();\n }\n\n async flush(): Promise<void> {\n await this.processor.flush();\n }\n\n private _registerShutdownHooks(): void {\n const onShutdown = () => {\n this.shutdown().catch(() => {});\n };\n\n // These handlers use unref'd timers internally, so they won't prevent exit\n process.once('beforeExit', onShutdown);\n process.once('SIGTERM', onShutdown);\n process.once('SIGINT', onShutdown);\n }\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Initialize the Risicare SDK. Call once at application startup.\n *\n * @example\n * import { init } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n */\nexport function init(config?: Partial<RisicareConfig>): void {\n if (_client) {\n debug('Already initialized. Call shutdown() first to re-initialize.');\n return;\n }\n\n _client = new RisicareClient(config);\n _tracer = _client.tracer;\n}\n\n/**\n * Gracefully shut down the SDK. Flushes pending spans before resolving.\n */\nexport async function shutdown(): Promise<void> {\n if (!_client) return;\n await _client.shutdown();\n _client = undefined;\n _tracer = undefined;\n}\n\n/**\n * Flush all pending spans without shutting down.\n */\nexport async function flush(): Promise<void> {\n if (!_client) return;\n await _client.flush();\n}\n\n/**\n * Enable tracing at runtime.\n */\nexport function enable(): void {\n if (_client) _client.enabled = true;\n}\n\n/**\n * Disable tracing at runtime. Spans will not be created or exported.\n */\nexport function disable(): void {\n if (_client) _client.enabled = false;\n}\n\n/**\n * Check whether tracing is currently enabled.\n */\nexport function isEnabled(): boolean {\n return _client?.enabled ?? false;\n}\n\n/**\n * Get the global tracer instance. Returns undefined if not initialized.\n */\nexport function getTracer(): Tracer | undefined {\n return _tracer;\n}\n\n/**\n * Get the global tracer, or throw if not initialized.\n * @internal Used by decorators that require an active tracer.\n */\nexport function requireTracer(): Tracer {\n if (!_tracer) {\n throw new Error(\n 'Risicare SDK not initialized. Call init() before using tracing features.',\n );\n }\n return _tracer;\n}\n","/**\n * Token cost calculation table.\n *\n * Prices are per 1M tokens. Update monthly.\n * Source: provider pricing pages.\n */\n\ninterface ModelPricing {\n input: number; // USD per 1M input tokens\n output: number; // USD per 1M output tokens\n}\n\nconst PRICING: Record<string, ModelPricing> = {\n // OpenAI\n 'gpt-4o': { input: 2.50, output: 10.00 },\n 'gpt-4o-mini': { input: 0.15, output: 0.60 },\n 'gpt-4-turbo': { input: 10.00, output: 30.00 },\n 'gpt-4': { input: 30.00, output: 60.00 },\n 'gpt-3.5-turbo': { input: 0.50, output: 1.50 },\n 'o1': { input: 15.00, output: 60.00 },\n 'o1-mini': { input: 3.00, output: 12.00 },\n 'o3-mini': { input: 1.10, output: 4.40 },\n\n // Anthropic\n 'claude-opus-4-6': { input: 15.00, output: 75.00 },\n 'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },\n 'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },\n 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },\n 'claude-3-opus-20240229': { input: 15.00, output: 75.00 },\n\n // Google\n 'gemini-2.0-flash': { input: 0.10, output: 0.40 },\n 'gemini-1.5-pro': { input: 1.25, output: 5.00 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.30 },\n\n // Groq\n 'llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },\n 'llama-3.1-8b-instant': { input: 0.05, output: 0.08 },\n 'mixtral-8x7b-32768': { input: 0.24, output: 0.24 },\n\n // DeepSeek\n 'deepseek-chat': { input: 0.14, output: 0.28 },\n 'deepseek-reasoner': { input: 0.55, output: 2.19 },\n\n // Together.ai (open-source models)\n 'meta-llama/llama-3.3-70b-instruct-turbo': { input: 0.88, output: 0.88 },\n 'meta-llama/meta-llama-3.1-8b-instruct-turbo': { input: 0.18, output: 0.18 },\n 'meta-llama/llama-3.2-3b-instruct-turbo': { input: 0.06, output: 0.06 },\n 'qwen/qwen2.5-7b-instruct-turbo': { input: 0.20, output: 0.20 },\n 'mistralai/mistral-small-24b-instruct-2501': { input: 0.20, output: 0.20 },\n 'mistralai/mixtral-8x7b-instruct-v0.1': { input: 0.60, output: 0.60 },\n 'deepseek-ai/deepseek-v3': { input: 0.27, output: 1.10 },\n};\n\n/**\n * Calculate cost in USD for a model's token usage.\n * Returns undefined if model is not in pricing table.\n */\nexport function calculateCost(\n model: string,\n promptTokens: number,\n completionTokens: number,\n): number | undefined {\n const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];\n if (!pricing) return undefined;\n\n const inputCost = (promptTokens / 1_000_000) * pricing.input;\n const outputCost = (completionTokens / 1_000_000) * pricing.output;\n return inputCost + outputCost;\n}\n\n/**\n * Check if a model has pricing data.\n */\nexport function hasPricing(model: string): boolean {\n return model in PRICING || model.toLowerCase() in PRICING;\n}\n","/**\n * Vercel AI SDK instrumentation.\n *\n * The Vercel AI SDK (package: \"ai\") supports telemetry via configuration.\n * This module provides a helper that wraps generateText/streamText/generateObject\n * calls with Risicare tracing.\n *\n * Unlike OpenAI/Anthropic, we don't use Proxy here — the Vercel AI SDK\n * uses top-level functions (not a client class). We provide wrapper functions.\n *\n * Usage:\n * import { generateText } from 'ai';\n * import { patchVercelAI } from 'risicare/vercel-ai';\n * const { tracedGenerateText, tracedStreamText } = patchVercelAI();\n *\n * const result = await tracedGenerateText({\n * model: openai('gpt-4o'),\n * prompt: 'Hello',\n * });\n */\n\nimport { requireTracer } from '../../client.js';\nimport { SpanKind } from '../../types.js';\nimport { calculateCost } from '../../utils/pricing.js';\nimport type { Span } from '../../span.js';\n\nfunction enrichSpanFromResult(span: Span, result: Record<string, unknown>): void {\n const usage = result.usage as Record<string, number> | undefined;\n // Vercel AI SDK exposes model info via result.response.modelId or similar\n const response = result.response as Record<string, unknown> | undefined;\n const modelId = (response?.modelId ?? result.modelId) as string | undefined;\n\n if (modelId) {\n span.setLlmFields({ model: modelId });\n }\n\n if (usage) {\n const promptTokens = usage.promptTokens ?? 0;\n const completionTokens = usage.completionTokens ?? 0;\n const totalTokens = usage.totalTokens ?? (promptTokens + completionTokens);\n const cost = modelId ? calculateCost(modelId, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens,\n costUsd: cost,\n });\n }\n}\n\n/**\n * Create traced wrappers for Vercel AI SDK functions.\n *\n * Returns wrapped versions of generateText, streamText, and generateObject\n * that automatically create tracing spans.\n */\nexport function patchVercelAI(): {\n tracedGenerateText: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n tracedStreamText: <T>(fn: (params: T) => unknown) => (params: T) => unknown;\n tracedGenerateObject: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n} {\n return {\n tracedGenerateText<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateText', kind: SpanKind.CLIENT },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n\n tracedStreamText<T>(fn: (params: T) => unknown) {\n return (params: T): unknown => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return fn(params);\n }\n\n // For streaming, we create a span but can't easily await the stream.\n // Create a manual span that the user closes, or wrap the stream result.\n return tracer.startSpan(\n { name: 'vercel-ai.streamText', kind: SpanKind.CLIENT },\n (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n return fn(params);\n },\n );\n };\n },\n\n tracedGenerateObject<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateObject', kind: SpanKind.CLIENT },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/providers/vercel-ai/index.ts","../../../src/ids.ts","../../../src/noop.ts","../../../src/globals.ts","../../../src/utils/log.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/vercel-ai/patch.ts"],"sourcesContent":["/**\n * Vercel AI SDK instrumentation.\n *\n * Entry point for `import { patchVercelAI } from 'risicare/vercel-ai'`.\n */\n\nexport { patchVercelAI } from './patch.js';\n","/**\n * ID generation for traces and spans.\n *\n * Trace IDs: 32 lowercase hex characters (16 random bytes)\n * Span IDs: 16 lowercase hex characters (8 random bytes)\n *\n * Uses crypto.randomBytes for cryptographically secure randomness.\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst HEX_REGEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_REGEX_16 = /^[0-9a-f]{16}$/;\n\nexport function generateTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\nexport function generateSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nexport function generateAgentId(prefix?: string): string {\n const suffix = randomBytes(8).toString('hex');\n return prefix ? `${prefix}-${suffix}` : suffix;\n}\n\nexport function validateTraceId(id: string): boolean {\n return HEX_REGEX_32.test(id);\n}\n\nexport function validateSpanId(id: string): boolean {\n return HEX_REGEX_16.test(id);\n}\n","/**\n * No-op implementations for the disabled path.\n *\n * When tracing is disabled, all operations return these no-op objects\n * to maintain zero overhead. No allocations, no side effects.\n */\n\nimport { SpanKind, SpanStatus, type SpanPayload } from './types.js';\n\n/**\n * A frozen no-op span that silently ignores all operations.\n * Used when SDK is disabled to avoid overhead.\n */\nexport const NOOP_SPAN = Object.freeze({\n traceId: '00000000000000000000000000000000',\n spanId: '0000000000000000',\n parentSpanId: undefined,\n name: 'noop',\n kind: SpanKind.INTERNAL,\n startTime: '',\n startHrtime: 0,\n endTime: undefined,\n status: SpanStatus.UNSET,\n statusMessage: undefined,\n attributes: Object.freeze({}) as Record<string, unknown>,\n events: Object.freeze([]) as readonly [],\n links: Object.freeze([]) as readonly [],\n sessionId: undefined,\n agentId: undefined,\n agentName: undefined,\n semanticPhase: undefined,\n llmProvider: undefined,\n llmModel: undefined,\n llmPromptTokens: undefined,\n llmCompletionTokens: undefined,\n llmTotalTokens: undefined,\n llmCostUsd: undefined,\n toolName: undefined,\n toolSuccess: undefined,\n isEnded: true,\n durationMs: 0,\n\n setAttribute() { return this; },\n setAttributes() { return this; },\n setStatus() { return this; },\n addEvent() { return this; },\n addLink() { return this; },\n recordException() { return this; },\n setLlmFields() { return this; },\n setToolFields() { return this; },\n end() {},\n toPayload(): SpanPayload {\n return {\n traceId: this.traceId,\n spanId: this.spanId,\n name: this.name,\n kind: this.kind,\n startTime: this.startTime,\n status: this.status,\n attributes: {},\n events: [],\n links: [],\n };\n },\n});\n\nexport type NoopSpan = typeof NOOP_SPAN;\n","/**\n * Shared state via globalThis — ensures all entry point bundles share\n * the same singleton instances.\n *\n * Problem: tsup with `splitting: false` gives each entry point (index,\n * openai, anthropic, vercel-ai) its own copy of module-level variables.\n * This means `init()` from 'risicare' sets a tracer that 'risicare/openai'\n * can't see — breaking all provider instrumentation silently.\n *\n * Solution: Store all mutable singletons on globalThis with a namespaced\n * prefix. Every bundle reads/writes the same global slots.\n *\n * This pattern is used by React, OpenTelemetry, and other SDKs that must\n * share state across independently bundled entry points.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst G = globalThis as any;\nconst PREFIX = '__risicare_';\n\n// ─── Client & Tracer ────────────────────────────────────────────────────────\n\nexport function getClient(): unknown {\n return G[PREFIX + 'client'];\n}\n\nexport function setClient(client: unknown): void {\n G[PREFIX + 'client'] = client;\n}\n\nexport function getTracer(): unknown {\n return G[PREFIX + 'tracer'];\n}\n\nexport function setTracer(tracer: unknown): void {\n G[PREFIX + 'tracer'] = tracer;\n}\n\n// ─── Context Storage ────────────────────────────────────────────────────────\n\nexport function getContextStorage(): AsyncLocalStorage<unknown> {\n if (!G[PREFIX + 'ctx']) {\n G[PREFIX + 'ctx'] = new AsyncLocalStorage();\n }\n return G[PREFIX + 'ctx'];\n}\n\n// ─── Span Registry ──────────────────────────────────────────────────────────\n\nexport function getRegistry(): Map<string, unknown> {\n if (!G[PREFIX + 'registry']) {\n G[PREFIX + 'registry'] = new Map();\n }\n return G[PREFIX + 'registry'];\n}\n\nexport function getOpCount(): number {\n return G[PREFIX + 'opcount'] ?? 0;\n}\n\nexport function setOpCount(n: number): void {\n G[PREFIX + 'opcount'] = n;\n}\n\n// ─── Debug Flag ─────────────────────────────────────────────────────────────\n\nexport function getDebug(): boolean {\n return G[PREFIX + 'debug'] ?? false;\n}\n\nexport function setDebugFlag(enabled: boolean): void {\n G[PREFIX + 'debug'] = enabled;\n}\n","/**\n * Internal logger for the Risicare SDK.\n *\n * Centralizes all diagnostic output so that:\n * - Debug messages are gated by a single flag (zero-cost when disabled)\n * - Warnings always fire (operational alerts like queue full)\n * - All output goes to stderr with a consistent [risicare] prefix\n * - A future custom logger callback can be added in one place\n */\n\nimport { getDebug, setDebugFlag } from '../globals.js';\n\n/**\n * Enable or disable debug logging. Called once during init().\n * @internal\n */\nexport function setDebug(enabled: boolean): void {\n setDebugFlag(enabled);\n}\n\n/**\n * Log a debug message. Only outputs when debug mode is enabled.\n * @internal\n */\nexport function debug(msg: string): void {\n if (getDebug()) {\n process.stderr.write(`[risicare] ${msg}\\n`);\n }\n}\n\n/**\n * Log a warning. Always outputs regardless of debug mode.\n * Use sparingly — only for operational issues the user should see.\n * @internal\n */\nexport function warn(msg: string): void {\n process.stderr.write(`[risicare] WARNING: ${msg}\\n`);\n}\n","/**\n * RisicareClient — singleton client managing SDK lifecycle.\n *\n * Handles initialization, shutdown, and the connection between\n * the Tracer and the export pipeline (batch processor + HTTP exporter).\n *\n * Usage:\n * import { init, shutdown } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n * // ... instrument code ...\n * await shutdown(); // flush remaining spans\n */\n\nimport { type RisicareConfig, resolveConfig } from './config.js';\nimport { Tracer } from './tracer.js';\nimport { BatchSpanProcessor } from './exporters/batch.js';\nimport { HttpExporter } from './exporters/http.js';\nimport { ConsoleExporter } from './exporters/console.js';\nimport type { SpanExporter } from './exporters/base.js';\nimport { setDebug, debug } from './utils/log.js';\nimport {\n getClient as getGlobalClient,\n setClient as setGlobalClient,\n getTracer as getGlobalTracer,\n setTracer as setGlobalTracer,\n} from './globals.js';\n\n// ─── Client Class ───────────────────────────────────────────────────────────\n\nclass RisicareClient {\n readonly config: ReturnType<typeof resolveConfig>;\n readonly processor: BatchSpanProcessor;\n readonly tracer: Tracer;\n private _shutdownPromise: Promise<void> | undefined;\n private _shutdownHandlers: { signal: string; handler: () => void }[] = [];\n\n constructor(config?: Partial<RisicareConfig>) {\n this.config = resolveConfig(config);\n\n // API key format validation\n if (this.config.apiKey && !this.config.apiKey.startsWith('rsk-')) {\n debug('Warning: API key should start with \"rsk-\". Got: ' + this.config.apiKey.slice(0, 4) + '...');\n }\n\n // Build exporter chain\n let exporter: SpanExporter;\n if (this.config.debug && !this.config.apiKey) {\n exporter = new ConsoleExporter();\n } else if (this.config.apiKey) {\n exporter = new HttpExporter({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n projectId: this.config.projectId || undefined,\n environment: this.config.environment || undefined,\n compress: this.config.compress,\n });\n } else {\n // No API key and not debug — use console as fallback\n exporter = new ConsoleExporter();\n }\n\n this.processor = new BatchSpanProcessor({\n exporters: [exporter],\n batchSize: this.config.batchSize,\n batchTimeoutMs: this.config.batchTimeoutMs,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n });\n\n this.tracer = new Tracer({\n onSpanEnd: (span) => this.processor.onSpanEnd(span),\n sampleRate: this.config.sampleRate,\n enabled: this.config.enabled,\n traceContent: this.config.traceContent,\n });\n\n // Start the batch processor (enables span queuing and periodic flushing)\n this.processor.start();\n\n // Register shutdown hooks\n this._registerShutdownHooks();\n\n // Enable internal debug logging if configured\n setDebug(this.config.debug);\n debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);\n }\n\n get enabled(): boolean {\n return this.tracer.enabled;\n }\n\n set enabled(value: boolean) {\n this.tracer.enabled = value;\n }\n\n // Audit #6: Promise-based shutdown dedup (fixes TOCTOU race condition)\n async shutdown(): Promise<void> {\n if (this._shutdownPromise) return this._shutdownPromise;\n this._shutdownPromise = this._doShutdown();\n return this._shutdownPromise;\n }\n\n private async _doShutdown(): Promise<void> {\n debug('Shutting down...');\n\n // Audit #3: Remove process listeners to prevent leak\n for (const { signal, handler } of this._shutdownHandlers) {\n process.removeListener(signal, handler);\n }\n this._shutdownHandlers = [];\n\n await this.processor.shutdown();\n }\n\n async flush(): Promise<void> {\n await this.processor.flush();\n }\n\n private _registerShutdownHooks(): void {\n const onShutdown = () => {\n // Audit #3: Add 5s timeout to prevent hanging on signal\n const timeout = setTimeout(() => process.exit(1), 5000);\n timeout.unref();\n this.shutdown().catch(() => {}).finally(() => clearTimeout(timeout));\n };\n\n const signals = ['beforeExit', 'SIGTERM', 'SIGINT'];\n for (const signal of signals) {\n process.once(signal, onShutdown);\n this._shutdownHandlers.push({ signal, handler: onShutdown });\n }\n }\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Initialize the Risicare SDK. Call once at application startup.\n *\n * @example\n * import { init } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n */\nexport function init(config?: Partial<RisicareConfig>): void {\n if (getGlobalClient()) {\n debug('Already initialized. Call shutdown() first to re-initialize.');\n return;\n }\n\n const client = new RisicareClient(config);\n setGlobalClient(client);\n setGlobalTracer(client.tracer);\n}\n\n/**\n * Gracefully shut down the SDK. Flushes pending spans before resolving.\n */\nexport async function shutdown(): Promise<void> {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (!client) return;\n await client.shutdown();\n setGlobalClient(undefined);\n setGlobalTracer(undefined);\n}\n\n/**\n * Flush all pending spans without shutting down.\n */\nexport async function flush(): Promise<void> {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (!client) return;\n await client.flush();\n}\n\n/**\n * Enable tracing at runtime.\n */\nexport function enable(): void {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (client) client.enabled = true;\n}\n\n/**\n * Disable tracing at runtime. Spans will not be created or exported.\n */\nexport function disable(): void {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (client) client.enabled = false;\n}\n\n/**\n * Check whether tracing is currently enabled.\n */\nexport function isEnabled(): boolean {\n const client = getGlobalClient() as RisicareClient | undefined;\n return client?.enabled ?? false;\n}\n\n/**\n * Get the global tracer instance. Returns undefined if not initialized.\n */\nexport function getTracer(): Tracer | undefined {\n return getGlobalTracer() as Tracer | undefined;\n}\n\n/**\n * Get the global tracer, or throw if not initialized.\n * @internal Used by decorators and providers that require an active tracer.\n */\nexport function requireTracer(): Tracer {\n const tracer = getGlobalTracer() as Tracer | undefined;\n if (!tracer) {\n throw new Error(\n 'Risicare SDK not initialized. Call init() before using tracing features.',\n );\n }\n return tracer;\n}\n\n/**\n * Check whether content tracing (prompt/completion capture) is enabled.\n */\nexport function getTraceContent(): boolean {\n const tracer = getGlobalTracer() as Tracer | undefined;\n return tracer?.traceContent ?? true;\n}\n\n/**\n * Get SDK metrics: exported spans, dropped spans, failed exports, queue stats.\n * Returns zero-valued metrics if SDK is not initialized.\n */\nexport function getMetrics() {\n const client = getGlobalClient() as RisicareClient | undefined;\n return client?.processor.getMetrics() ?? {\n exportedSpans: 0,\n droppedSpans: 0,\n failedExports: 0,\n queueSize: 0,\n queueCapacity: 0,\n queueUtilization: 0,\n };\n}\n","/**\n * Token cost calculation table.\n *\n * Prices are per 1M tokens. Update monthly.\n * Source: provider pricing pages.\n */\n\ninterface ModelPricing {\n input: number; // USD per 1M input tokens\n output: number; // USD per 1M output tokens\n}\n\nconst PRICING: Record<string, ModelPricing> = {\n // OpenAI\n 'gpt-4o': { input: 2.50, output: 10.00 },\n 'gpt-4o-mini': { input: 0.15, output: 0.60 },\n 'gpt-4-turbo': { input: 10.00, output: 30.00 },\n 'gpt-4': { input: 30.00, output: 60.00 },\n 'gpt-3.5-turbo': { input: 0.50, output: 1.50 },\n 'o1': { input: 15.00, output: 60.00 },\n 'o1-mini': { input: 3.00, output: 12.00 },\n 'o3-mini': { input: 1.10, output: 4.40 },\n\n // Anthropic\n 'claude-opus-4-5-20251101': { input: 15.00, output: 75.00 },\n 'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },\n 'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },\n 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },\n 'claude-3-opus-20240229': { input: 15.00, output: 75.00 },\n\n // Google\n 'gemini-2.0-flash': { input: 0.10, output: 0.40 },\n 'gemini-1.5-pro': { input: 1.25, output: 5.00 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.30 },\n\n // Groq\n 'llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },\n 'llama-3.1-8b-instant': { input: 0.05, output: 0.08 },\n 'mixtral-8x7b-32768': { input: 0.24, output: 0.24 },\n\n // DeepSeek\n 'deepseek-chat': { input: 0.14, output: 0.28 },\n 'deepseek-reasoner': { input: 0.55, output: 2.19 },\n\n // Together.ai (open-source models)\n 'meta-llama/llama-3.3-70b-instruct-turbo': { input: 0.88, output: 0.88 },\n 'meta-llama/meta-llama-3.1-8b-instruct-turbo': { input: 0.18, output: 0.18 },\n 'meta-llama/llama-3.2-3b-instruct-turbo': { input: 0.06, output: 0.06 },\n 'qwen/qwen2.5-7b-instruct-turbo': { input: 0.20, output: 0.20 },\n 'mistralai/mistral-small-24b-instruct-2501': { input: 0.20, output: 0.20 },\n 'mistralai/mixtral-8x7b-instruct-v0.1': { input: 0.60, output: 0.60 },\n 'deepseek-ai/deepseek-v3': { input: 0.27, output: 1.10 },\n};\n\n/**\n * Calculate cost in USD for a model's token usage.\n * Returns undefined if model is not in pricing table.\n */\nexport function calculateCost(\n model: string,\n promptTokens: number,\n completionTokens: number,\n): number | undefined {\n const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];\n if (!pricing) return undefined;\n\n const inputCost = (promptTokens / 1_000_000) * pricing.input;\n const outputCost = (completionTokens / 1_000_000) * pricing.output;\n return inputCost + outputCost;\n}\n\n/**\n * Check if a model has pricing data.\n */\nexport function hasPricing(model: string): boolean {\n return model in PRICING || model.toLowerCase() in PRICING;\n}\n","/**\n * Vercel AI SDK instrumentation.\n *\n * The Vercel AI SDK (package: \"ai\") supports telemetry via configuration.\n * This module provides a helper that wraps generateText/streamText/generateObject\n * calls with Risicare tracing.\n *\n * Unlike OpenAI/Anthropic, we don't use Proxy here — the Vercel AI SDK\n * uses top-level functions (not a client class). We provide wrapper functions.\n *\n * Usage:\n * import { generateText } from 'ai';\n * import { patchVercelAI } from 'risicare/vercel-ai';\n * const { tracedGenerateText, tracedStreamText } = patchVercelAI();\n *\n * const result = await tracedGenerateText({\n * model: openai('gpt-4o'),\n * prompt: 'Hello',\n * });\n */\n\nimport { requireTracer } from '../../client.js';\nimport { SpanKind } from '../../types.js';\nimport { calculateCost } from '../../utils/pricing.js';\nimport { debug } from '../../utils/log.js';\nimport type { Span } from '../../span.js';\n\nfunction enrichSpanFromResult(span: Span, result: Record<string, unknown>): void {\n const usage = result.usage as Record<string, number> | undefined;\n // Vercel AI SDK exposes model info via result.response.modelId or similar\n const response = result.response as Record<string, unknown> | undefined;\n const modelId = (response?.modelId ?? result.modelId) as string | undefined;\n\n if (modelId) {\n span.setLlmFields({ model: modelId });\n }\n\n if (usage) {\n const promptTokens = usage.promptTokens ?? 0;\n const completionTokens = usage.completionTokens ?? 0;\n const totalTokens = usage.totalTokens ?? (promptTokens + completionTokens);\n const cost = modelId ? calculateCost(modelId, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens,\n costUsd: cost,\n });\n }\n}\n\n/**\n * Create traced wrappers for Vercel AI SDK functions.\n *\n * Returns wrapped versions of generateText, streamText, and generateObject\n * that automatically create tracing spans.\n */\nexport function patchVercelAI(): {\n tracedGenerateText: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n tracedStreamText: <T>(fn: (params: T) => unknown) => (params: T) => unknown;\n tracedGenerateObject: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n} {\n return {\n tracedGenerateText<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchVercelAI()');\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateText', kind: SpanKind.LLM_CALL },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n\n tracedStreamText<T>(fn: (params: T) => unknown) {\n return (params: T): unknown => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchVercelAI()');\n return fn(params);\n }\n\n // For streaming, we create a span but can't easily await the stream.\n // Create a manual span that the user closes, or wrap the stream result.\n return tracer.startSpan(\n { name: 'vercel-ai.streamText', kind: SpanKind.LLM_CALL },\n (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n return fn(params);\n },\n );\n };\n },\n\n tracedGenerateObject<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchVercelAI()');\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateObject', kind: SpanKind.LLM_CALL },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -71,19 +71,34 @@ var NOOP_SPAN = Object.freeze({
71
71
  }
72
72
  });
73
73
 
74
- // src/context/storage.ts
74
+ // src/globals.ts
75
75
  import { AsyncLocalStorage } from "async_hooks";
76
- var contextStorage = new AsyncLocalStorage();
76
+ var G = globalThis;
77
+ var PREFIX = "__risicare_";
78
+ function getTracer() {
79
+ return G[PREFIX + "tracer"];
80
+ }
81
+ function getDebug() {
82
+ return G[PREFIX + "debug"] ?? false;
83
+ }
84
+
85
+ // src/utils/log.ts
86
+ function debug(msg) {
87
+ if (getDebug()) {
88
+ process.stderr.write(`[risicare] ${msg}
89
+ `);
90
+ }
91
+ }
77
92
 
78
93
  // src/client.ts
79
- var _tracer;
80
94
  function requireTracer() {
81
- if (!_tracer) {
95
+ const tracer = getTracer();
96
+ if (!tracer) {
82
97
  throw new Error(
83
98
  "Risicare SDK not initialized. Call init() before using tracing features."
84
99
  );
85
100
  }
86
- return _tracer;
101
+ return tracer;
87
102
  }
88
103
 
89
104
  // src/utils/pricing.ts
@@ -98,7 +113,7 @@ var PRICING = {
98
113
  "o1-mini": { input: 3, output: 12 },
99
114
  "o3-mini": { input: 1.1, output: 4.4 },
100
115
  // Anthropic
101
- "claude-opus-4-6": { input: 15, output: 75 },
116
+ "claude-opus-4-5-20251101": { input: 15, output: 75 },
102
117
  "claude-sonnet-4-5-20250929": { input: 3, output: 15 },
103
118
  "claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
104
119
  "claude-3-5-sonnet-20241022": { input: 3, output: 15 },
@@ -161,10 +176,11 @@ function patchVercelAI() {
161
176
  try {
162
177
  tracer = requireTracer();
163
178
  } catch {
179
+ debug("Tracer not initialized \u2014 call init() before using patchVercelAI()");
164
180
  return fn(params);
165
181
  }
166
182
  return tracer.startSpan(
167
- { name: "vercel-ai.generateText", kind: "client" /* CLIENT */ },
183
+ { name: "vercel-ai.generateText", kind: "llm_call" /* LLM_CALL */ },
168
184
  async (span) => {
169
185
  span.setLlmFields({ provider: "vercel-ai" });
170
186
  const result = await fn(params);
@@ -180,10 +196,11 @@ function patchVercelAI() {
180
196
  try {
181
197
  tracer = requireTracer();
182
198
  } catch {
199
+ debug("Tracer not initialized \u2014 call init() before using patchVercelAI()");
183
200
  return fn(params);
184
201
  }
185
202
  return tracer.startSpan(
186
- { name: "vercel-ai.streamText", kind: "client" /* CLIENT */ },
203
+ { name: "vercel-ai.streamText", kind: "llm_call" /* LLM_CALL */ },
187
204
  (span) => {
188
205
  span.setLlmFields({ provider: "vercel-ai" });
189
206
  return fn(params);
@@ -197,10 +214,11 @@ function patchVercelAI() {
197
214
  try {
198
215
  tracer = requireTracer();
199
216
  } catch {
217
+ debug("Tracer not initialized \u2014 call init() before using patchVercelAI()");
200
218
  return fn(params);
201
219
  }
202
220
  return tracer.startSpan(
203
- { name: "vercel-ai.generateObject", kind: "client" /* CLIENT */ },
221
+ { name: "vercel-ai.generateObject", kind: "llm_call" /* LLM_CALL */ },
204
222
  async (span) => {
205
223
  span.setLlmFields({ provider: "vercel-ai" });
206
224
  const result = await fn(params);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ids.ts","../../../src/noop.ts","../../../src/context/storage.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/vercel-ai/patch.ts"],"sourcesContent":["/**\n * ID generation for traces and spans.\n *\n * Trace IDs: 32 lowercase hex characters (16 random bytes)\n * Span IDs: 16 lowercase hex characters (8 random bytes)\n *\n * Uses crypto.randomBytes for cryptographically secure randomness.\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst HEX_REGEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_REGEX_16 = /^[0-9a-f]{16}$/;\n\nexport function generateTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\nexport function generateSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nexport function generateAgentId(prefix?: string): string {\n const suffix = randomBytes(6).toString('hex');\n return prefix ? `${prefix}-${suffix}` : suffix;\n}\n\nexport function validateTraceId(id: string): boolean {\n return HEX_REGEX_32.test(id);\n}\n\nexport function validateSpanId(id: string): boolean {\n return HEX_REGEX_16.test(id);\n}\n","/**\n * No-op implementations for the disabled path.\n *\n * When tracing is disabled, all operations return these no-op objects\n * to maintain zero overhead. No allocations, no side effects.\n */\n\nimport { SpanKind, SpanStatus, type SpanPayload } from './types.js';\n\n/**\n * A frozen no-op span that silently ignores all operations.\n * Used when SDK is disabled to avoid overhead.\n */\nexport const NOOP_SPAN = Object.freeze({\n traceId: '00000000000000000000000000000000',\n spanId: '0000000000000000',\n parentSpanId: undefined,\n name: 'noop',\n kind: SpanKind.INTERNAL,\n startTime: '',\n startHrtime: 0,\n endTime: undefined,\n status: SpanStatus.UNSET,\n statusMessage: undefined,\n attributes: Object.freeze({}) as Record<string, unknown>,\n events: Object.freeze([]) as readonly [],\n links: Object.freeze([]) as readonly [],\n sessionId: undefined,\n agentId: undefined,\n agentName: undefined,\n semanticPhase: undefined,\n llmProvider: undefined,\n llmModel: undefined,\n llmPromptTokens: undefined,\n llmCompletionTokens: undefined,\n llmTotalTokens: undefined,\n llmCostUsd: undefined,\n toolName: undefined,\n toolSuccess: undefined,\n isEnded: true,\n durationMs: 0,\n\n setAttribute() { return this; },\n setAttributes() { return this; },\n setStatus() { return this; },\n addEvent() { return this; },\n addLink() { return this; },\n recordException() { return this; },\n setLlmFields() { return this; },\n setToolFields() { return this; },\n end() {},\n toPayload(): SpanPayload {\n return {\n traceId: this.traceId,\n spanId: this.spanId,\n name: this.name,\n kind: this.kind,\n startTime: this.startTime,\n status: this.status,\n attributes: {},\n events: [],\n links: [],\n };\n },\n});\n\nexport type NoopSpan = typeof NOOP_SPAN;\n","/**\n * AsyncLocalStorage-based context propagation.\n *\n * Uses a single AsyncLocalStorage instance with a composite state object.\n * This is simpler and more performant than multiple separate stores.\n *\n * Node.js AsyncLocalStorage automatically propagates through:\n * - Promise / async-await\n * - setTimeout / setImmediate\n * - EventEmitter callbacks\n * - process.nextTick\n * - async generators (unlike Python's contextvars!)\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Span } from '../span.js';\nimport type { SemanticPhase } from '../types.js';\n\n// ─── Context Types ───────────────────────────────────────────────────────────\n\nexport interface SessionContext {\n sessionId: string;\n userId?: string;\n metadata?: Record<string, unknown>;\n parentSessionId?: string;\n turnNumber?: number;\n}\n\nexport interface AgentContext {\n agentId: string;\n agentName?: string;\n agentRole?: string;\n agentType?: string;\n parentAgentId?: string;\n version?: number;\n metadata?: Record<string, unknown>;\n}\n\nexport interface ContextState {\n session?: SessionContext;\n agent?: AgentContext;\n span?: Span;\n phase?: SemanticPhase;\n}\n\n// ─── Storage Instance ────────────────────────────────────────────────────────\n\nconst contextStorage = new AsyncLocalStorage<ContextState>();\n\n// ─── Core Operations ─────────────────────────────────────────────────────────\n\n/**\n * Get the current context state, or empty object if outside any context.\n */\nexport function getContext(): ContextState {\n return contextStorage.getStore() ?? {};\n}\n\n/**\n * Run a callback within a new context scope.\n * The new scope inherits from the parent, with overrides applied.\n */\nexport function runWithContext<T>(overrides: Partial<ContextState>, fn: () => T): T {\n const parent = getContext();\n const merged: ContextState = { ...parent, ...overrides };\n return contextStorage.run(merged, fn);\n}\n\n/**\n * Run an async callback within a new context scope.\n */\nexport function runWithContextAsync<T>(overrides: Partial<ContextState>, fn: () => Promise<T>): Promise<T> {\n const parent = getContext();\n const merged: ContextState = { ...parent, ...overrides };\n return contextStorage.run(merged, fn);\n}\n\n// ─── Context Accessors ───────────────────────────────────────────────────────\n\nexport function getCurrentSession(): SessionContext | undefined {\n return getContext().session;\n}\n\nexport function getCurrentAgent(): AgentContext | undefined {\n return getContext().agent;\n}\n\nexport function getCurrentSpan(): Span | undefined {\n return getContext().span;\n}\n\nexport function getCurrentPhase(): SemanticPhase | undefined {\n return getContext().phase;\n}\n\nexport function getCurrentSessionId(): string | undefined {\n return getContext().session?.sessionId;\n}\n\nexport function getCurrentAgentId(): string | undefined {\n return getContext().agent?.agentId;\n}\n\nexport function getCurrentTraceId(): string | undefined {\n return getContext().span?.traceId;\n}\n\nexport function getCurrentSpanId(): string | undefined {\n return getContext().span?.spanId;\n}\n\nexport function getCurrentParentSpanId(): string | undefined {\n return getContext().span?.parentSpanId;\n}\n\n/**\n * Get all current context as a plain object (for debugging/serialization).\n */\nexport function getCurrentContext(): Record<string, unknown> {\n const ctx = getContext();\n return {\n session: ctx.session ? { sessionId: ctx.session.sessionId, userId: ctx.session.userId } : null,\n agent: ctx.agent ? { agentId: ctx.agent.agentId, agentName: ctx.agent.agentName, agentRole: ctx.agent.agentRole } : null,\n span: ctx.span ? { spanId: ctx.span.spanId, traceId: ctx.span.traceId } : null,\n phase: ctx.phase ?? null,\n };\n}\n","/**\n * RisicareClient — singleton client managing SDK lifecycle.\n *\n * Handles initialization, shutdown, and the connection between\n * the Tracer and the export pipeline (batch processor + HTTP exporter).\n *\n * Usage:\n * import { init, shutdown } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n * // ... instrument code ...\n * await shutdown(); // flush remaining spans\n */\n\nimport { type RisicareConfig, resolveConfig } from './config.js';\nimport { Tracer } from './tracer.js';\nimport { BatchSpanProcessor } from './exporters/batch.js';\nimport { HttpExporter } from './exporters/http.js';\nimport { ConsoleExporter } from './exporters/console.js';\nimport type { SpanExporter } from './exporters/base.js';\nimport { setDebug, debug } from './utils/log.js';\n\n// ─── Singleton State ────────────────────────────────────────────────────────\n\nlet _client: RisicareClient | undefined;\nlet _tracer: Tracer | undefined;\n\n// ─── Client Class ───────────────────────────────────────────────────────────\n\nclass RisicareClient {\n readonly config: ReturnType<typeof resolveConfig>;\n readonly processor: BatchSpanProcessor;\n readonly tracer: Tracer;\n private _shutdownCalled = false;\n\n constructor(config?: Partial<RisicareConfig>) {\n this.config = resolveConfig(config);\n\n // Build exporter chain\n let exporter: SpanExporter;\n if (this.config.debug && !this.config.apiKey) {\n exporter = new ConsoleExporter();\n } else if (this.config.apiKey) {\n exporter = new HttpExporter({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n projectId: this.config.projectId || undefined,\n environment: this.config.environment || undefined,\n });\n } else {\n // No API key and not debug — use console as fallback\n exporter = new ConsoleExporter();\n }\n\n this.processor = new BatchSpanProcessor({\n exporters: [exporter],\n batchSize: this.config.batchSize,\n batchTimeoutMs: this.config.batchTimeoutMs,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n });\n\n this.tracer = new Tracer({\n onSpanEnd: (span) => this.processor.onSpanEnd(span),\n sampleRate: this.config.sampleRate,\n enabled: this.config.enabled,\n });\n\n // Start the batch processor (enables span queuing and periodic flushing)\n this.processor.start();\n\n // Register shutdown hooks\n this._registerShutdownHooks();\n\n // Enable internal debug logging if configured\n setDebug(this.config.debug);\n debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);\n }\n\n get enabled(): boolean {\n return this.tracer.enabled;\n }\n\n set enabled(value: boolean) {\n this.tracer.enabled = value;\n }\n\n async shutdown(): Promise<void> {\n if (this._shutdownCalled) return;\n this._shutdownCalled = true;\n\n debug('Shutting down...');\n\n await this.processor.shutdown();\n }\n\n async flush(): Promise<void> {\n await this.processor.flush();\n }\n\n private _registerShutdownHooks(): void {\n const onShutdown = () => {\n this.shutdown().catch(() => {});\n };\n\n // These handlers use unref'd timers internally, so they won't prevent exit\n process.once('beforeExit', onShutdown);\n process.once('SIGTERM', onShutdown);\n process.once('SIGINT', onShutdown);\n }\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Initialize the Risicare SDK. Call once at application startup.\n *\n * @example\n * import { init } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n */\nexport function init(config?: Partial<RisicareConfig>): void {\n if (_client) {\n debug('Already initialized. Call shutdown() first to re-initialize.');\n return;\n }\n\n _client = new RisicareClient(config);\n _tracer = _client.tracer;\n}\n\n/**\n * Gracefully shut down the SDK. Flushes pending spans before resolving.\n */\nexport async function shutdown(): Promise<void> {\n if (!_client) return;\n await _client.shutdown();\n _client = undefined;\n _tracer = undefined;\n}\n\n/**\n * Flush all pending spans without shutting down.\n */\nexport async function flush(): Promise<void> {\n if (!_client) return;\n await _client.flush();\n}\n\n/**\n * Enable tracing at runtime.\n */\nexport function enable(): void {\n if (_client) _client.enabled = true;\n}\n\n/**\n * Disable tracing at runtime. Spans will not be created or exported.\n */\nexport function disable(): void {\n if (_client) _client.enabled = false;\n}\n\n/**\n * Check whether tracing is currently enabled.\n */\nexport function isEnabled(): boolean {\n return _client?.enabled ?? false;\n}\n\n/**\n * Get the global tracer instance. Returns undefined if not initialized.\n */\nexport function getTracer(): Tracer | undefined {\n return _tracer;\n}\n\n/**\n * Get the global tracer, or throw if not initialized.\n * @internal Used by decorators that require an active tracer.\n */\nexport function requireTracer(): Tracer {\n if (!_tracer) {\n throw new Error(\n 'Risicare SDK not initialized. Call init() before using tracing features.',\n );\n }\n return _tracer;\n}\n","/**\n * Token cost calculation table.\n *\n * Prices are per 1M tokens. Update monthly.\n * Source: provider pricing pages.\n */\n\ninterface ModelPricing {\n input: number; // USD per 1M input tokens\n output: number; // USD per 1M output tokens\n}\n\nconst PRICING: Record<string, ModelPricing> = {\n // OpenAI\n 'gpt-4o': { input: 2.50, output: 10.00 },\n 'gpt-4o-mini': { input: 0.15, output: 0.60 },\n 'gpt-4-turbo': { input: 10.00, output: 30.00 },\n 'gpt-4': { input: 30.00, output: 60.00 },\n 'gpt-3.5-turbo': { input: 0.50, output: 1.50 },\n 'o1': { input: 15.00, output: 60.00 },\n 'o1-mini': { input: 3.00, output: 12.00 },\n 'o3-mini': { input: 1.10, output: 4.40 },\n\n // Anthropic\n 'claude-opus-4-6': { input: 15.00, output: 75.00 },\n 'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },\n 'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },\n 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },\n 'claude-3-opus-20240229': { input: 15.00, output: 75.00 },\n\n // Google\n 'gemini-2.0-flash': { input: 0.10, output: 0.40 },\n 'gemini-1.5-pro': { input: 1.25, output: 5.00 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.30 },\n\n // Groq\n 'llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },\n 'llama-3.1-8b-instant': { input: 0.05, output: 0.08 },\n 'mixtral-8x7b-32768': { input: 0.24, output: 0.24 },\n\n // DeepSeek\n 'deepseek-chat': { input: 0.14, output: 0.28 },\n 'deepseek-reasoner': { input: 0.55, output: 2.19 },\n\n // Together.ai (open-source models)\n 'meta-llama/llama-3.3-70b-instruct-turbo': { input: 0.88, output: 0.88 },\n 'meta-llama/meta-llama-3.1-8b-instruct-turbo': { input: 0.18, output: 0.18 },\n 'meta-llama/llama-3.2-3b-instruct-turbo': { input: 0.06, output: 0.06 },\n 'qwen/qwen2.5-7b-instruct-turbo': { input: 0.20, output: 0.20 },\n 'mistralai/mistral-small-24b-instruct-2501': { input: 0.20, output: 0.20 },\n 'mistralai/mixtral-8x7b-instruct-v0.1': { input: 0.60, output: 0.60 },\n 'deepseek-ai/deepseek-v3': { input: 0.27, output: 1.10 },\n};\n\n/**\n * Calculate cost in USD for a model's token usage.\n * Returns undefined if model is not in pricing table.\n */\nexport function calculateCost(\n model: string,\n promptTokens: number,\n completionTokens: number,\n): number | undefined {\n const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];\n if (!pricing) return undefined;\n\n const inputCost = (promptTokens / 1_000_000) * pricing.input;\n const outputCost = (completionTokens / 1_000_000) * pricing.output;\n return inputCost + outputCost;\n}\n\n/**\n * Check if a model has pricing data.\n */\nexport function hasPricing(model: string): boolean {\n return model in PRICING || model.toLowerCase() in PRICING;\n}\n","/**\n * Vercel AI SDK instrumentation.\n *\n * The Vercel AI SDK (package: \"ai\") supports telemetry via configuration.\n * This module provides a helper that wraps generateText/streamText/generateObject\n * calls with Risicare tracing.\n *\n * Unlike OpenAI/Anthropic, we don't use Proxy here — the Vercel AI SDK\n * uses top-level functions (not a client class). We provide wrapper functions.\n *\n * Usage:\n * import { generateText } from 'ai';\n * import { patchVercelAI } from 'risicare/vercel-ai';\n * const { tracedGenerateText, tracedStreamText } = patchVercelAI();\n *\n * const result = await tracedGenerateText({\n * model: openai('gpt-4o'),\n * prompt: 'Hello',\n * });\n */\n\nimport { requireTracer } from '../../client.js';\nimport { SpanKind } from '../../types.js';\nimport { calculateCost } from '../../utils/pricing.js';\nimport type { Span } from '../../span.js';\n\nfunction enrichSpanFromResult(span: Span, result: Record<string, unknown>): void {\n const usage = result.usage as Record<string, number> | undefined;\n // Vercel AI SDK exposes model info via result.response.modelId or similar\n const response = result.response as Record<string, unknown> | undefined;\n const modelId = (response?.modelId ?? result.modelId) as string | undefined;\n\n if (modelId) {\n span.setLlmFields({ model: modelId });\n }\n\n if (usage) {\n const promptTokens = usage.promptTokens ?? 0;\n const completionTokens = usage.completionTokens ?? 0;\n const totalTokens = usage.totalTokens ?? (promptTokens + completionTokens);\n const cost = modelId ? calculateCost(modelId, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens,\n costUsd: cost,\n });\n }\n}\n\n/**\n * Create traced wrappers for Vercel AI SDK functions.\n *\n * Returns wrapped versions of generateText, streamText, and generateObject\n * that automatically create tracing spans.\n */\nexport function patchVercelAI(): {\n tracedGenerateText: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n tracedStreamText: <T>(fn: (params: T) => unknown) => (params: T) => unknown;\n tracedGenerateObject: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n} {\n return {\n tracedGenerateText<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateText', kind: SpanKind.CLIENT },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n\n tracedStreamText<T>(fn: (params: T) => unknown) {\n return (params: T): unknown => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return fn(params);\n }\n\n // For streaming, we create a span but can't easily await the stream.\n // Create a manual span that the user closes, or wrap the stream result.\n return tracer.startSpan(\n { name: 'vercel-ai.streamText', kind: SpanKind.CLIENT },\n (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n return fn(params);\n },\n );\n };\n },\n\n tracedGenerateObject<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateObject', kind: SpanKind.CLIENT },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ids.ts","../../../src/noop.ts","../../../src/globals.ts","../../../src/utils/log.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/vercel-ai/patch.ts"],"sourcesContent":["/**\n * ID generation for traces and spans.\n *\n * Trace IDs: 32 lowercase hex characters (16 random bytes)\n * Span IDs: 16 lowercase hex characters (8 random bytes)\n *\n * Uses crypto.randomBytes for cryptographically secure randomness.\n */\n\nimport { randomBytes } from 'node:crypto';\n\nconst HEX_REGEX_32 = /^[0-9a-f]{32}$/;\nconst HEX_REGEX_16 = /^[0-9a-f]{16}$/;\n\nexport function generateTraceId(): string {\n return randomBytes(16).toString('hex');\n}\n\nexport function generateSpanId(): string {\n return randomBytes(8).toString('hex');\n}\n\nexport function generateAgentId(prefix?: string): string {\n const suffix = randomBytes(8).toString('hex');\n return prefix ? `${prefix}-${suffix}` : suffix;\n}\n\nexport function validateTraceId(id: string): boolean {\n return HEX_REGEX_32.test(id);\n}\n\nexport function validateSpanId(id: string): boolean {\n return HEX_REGEX_16.test(id);\n}\n","/**\n * No-op implementations for the disabled path.\n *\n * When tracing is disabled, all operations return these no-op objects\n * to maintain zero overhead. No allocations, no side effects.\n */\n\nimport { SpanKind, SpanStatus, type SpanPayload } from './types.js';\n\n/**\n * A frozen no-op span that silently ignores all operations.\n * Used when SDK is disabled to avoid overhead.\n */\nexport const NOOP_SPAN = Object.freeze({\n traceId: '00000000000000000000000000000000',\n spanId: '0000000000000000',\n parentSpanId: undefined,\n name: 'noop',\n kind: SpanKind.INTERNAL,\n startTime: '',\n startHrtime: 0,\n endTime: undefined,\n status: SpanStatus.UNSET,\n statusMessage: undefined,\n attributes: Object.freeze({}) as Record<string, unknown>,\n events: Object.freeze([]) as readonly [],\n links: Object.freeze([]) as readonly [],\n sessionId: undefined,\n agentId: undefined,\n agentName: undefined,\n semanticPhase: undefined,\n llmProvider: undefined,\n llmModel: undefined,\n llmPromptTokens: undefined,\n llmCompletionTokens: undefined,\n llmTotalTokens: undefined,\n llmCostUsd: undefined,\n toolName: undefined,\n toolSuccess: undefined,\n isEnded: true,\n durationMs: 0,\n\n setAttribute() { return this; },\n setAttributes() { return this; },\n setStatus() { return this; },\n addEvent() { return this; },\n addLink() { return this; },\n recordException() { return this; },\n setLlmFields() { return this; },\n setToolFields() { return this; },\n end() {},\n toPayload(): SpanPayload {\n return {\n traceId: this.traceId,\n spanId: this.spanId,\n name: this.name,\n kind: this.kind,\n startTime: this.startTime,\n status: this.status,\n attributes: {},\n events: [],\n links: [],\n };\n },\n});\n\nexport type NoopSpan = typeof NOOP_SPAN;\n","/**\n * Shared state via globalThis — ensures all entry point bundles share\n * the same singleton instances.\n *\n * Problem: tsup with `splitting: false` gives each entry point (index,\n * openai, anthropic, vercel-ai) its own copy of module-level variables.\n * This means `init()` from 'risicare' sets a tracer that 'risicare/openai'\n * can't see — breaking all provider instrumentation silently.\n *\n * Solution: Store all mutable singletons on globalThis with a namespaced\n * prefix. Every bundle reads/writes the same global slots.\n *\n * This pattern is used by React, OpenTelemetry, and other SDKs that must\n * share state across independently bundled entry points.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst G = globalThis as any;\nconst PREFIX = '__risicare_';\n\n// ─── Client & Tracer ────────────────────────────────────────────────────────\n\nexport function getClient(): unknown {\n return G[PREFIX + 'client'];\n}\n\nexport function setClient(client: unknown): void {\n G[PREFIX + 'client'] = client;\n}\n\nexport function getTracer(): unknown {\n return G[PREFIX + 'tracer'];\n}\n\nexport function setTracer(tracer: unknown): void {\n G[PREFIX + 'tracer'] = tracer;\n}\n\n// ─── Context Storage ────────────────────────────────────────────────────────\n\nexport function getContextStorage(): AsyncLocalStorage<unknown> {\n if (!G[PREFIX + 'ctx']) {\n G[PREFIX + 'ctx'] = new AsyncLocalStorage();\n }\n return G[PREFIX + 'ctx'];\n}\n\n// ─── Span Registry ──────────────────────────────────────────────────────────\n\nexport function getRegistry(): Map<string, unknown> {\n if (!G[PREFIX + 'registry']) {\n G[PREFIX + 'registry'] = new Map();\n }\n return G[PREFIX + 'registry'];\n}\n\nexport function getOpCount(): number {\n return G[PREFIX + 'opcount'] ?? 0;\n}\n\nexport function setOpCount(n: number): void {\n G[PREFIX + 'opcount'] = n;\n}\n\n// ─── Debug Flag ─────────────────────────────────────────────────────────────\n\nexport function getDebug(): boolean {\n return G[PREFIX + 'debug'] ?? false;\n}\n\nexport function setDebugFlag(enabled: boolean): void {\n G[PREFIX + 'debug'] = enabled;\n}\n","/**\n * Internal logger for the Risicare SDK.\n *\n * Centralizes all diagnostic output so that:\n * - Debug messages are gated by a single flag (zero-cost when disabled)\n * - Warnings always fire (operational alerts like queue full)\n * - All output goes to stderr with a consistent [risicare] prefix\n * - A future custom logger callback can be added in one place\n */\n\nimport { getDebug, setDebugFlag } from '../globals.js';\n\n/**\n * Enable or disable debug logging. Called once during init().\n * @internal\n */\nexport function setDebug(enabled: boolean): void {\n setDebugFlag(enabled);\n}\n\n/**\n * Log a debug message. Only outputs when debug mode is enabled.\n * @internal\n */\nexport function debug(msg: string): void {\n if (getDebug()) {\n process.stderr.write(`[risicare] ${msg}\\n`);\n }\n}\n\n/**\n * Log a warning. Always outputs regardless of debug mode.\n * Use sparingly — only for operational issues the user should see.\n * @internal\n */\nexport function warn(msg: string): void {\n process.stderr.write(`[risicare] WARNING: ${msg}\\n`);\n}\n","/**\n * RisicareClient — singleton client managing SDK lifecycle.\n *\n * Handles initialization, shutdown, and the connection between\n * the Tracer and the export pipeline (batch processor + HTTP exporter).\n *\n * Usage:\n * import { init, shutdown } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n * // ... instrument code ...\n * await shutdown(); // flush remaining spans\n */\n\nimport { type RisicareConfig, resolveConfig } from './config.js';\nimport { Tracer } from './tracer.js';\nimport { BatchSpanProcessor } from './exporters/batch.js';\nimport { HttpExporter } from './exporters/http.js';\nimport { ConsoleExporter } from './exporters/console.js';\nimport type { SpanExporter } from './exporters/base.js';\nimport { setDebug, debug } from './utils/log.js';\nimport {\n getClient as getGlobalClient,\n setClient as setGlobalClient,\n getTracer as getGlobalTracer,\n setTracer as setGlobalTracer,\n} from './globals.js';\n\n// ─── Client Class ───────────────────────────────────────────────────────────\n\nclass RisicareClient {\n readonly config: ReturnType<typeof resolveConfig>;\n readonly processor: BatchSpanProcessor;\n readonly tracer: Tracer;\n private _shutdownPromise: Promise<void> | undefined;\n private _shutdownHandlers: { signal: string; handler: () => void }[] = [];\n\n constructor(config?: Partial<RisicareConfig>) {\n this.config = resolveConfig(config);\n\n // API key format validation\n if (this.config.apiKey && !this.config.apiKey.startsWith('rsk-')) {\n debug('Warning: API key should start with \"rsk-\". Got: ' + this.config.apiKey.slice(0, 4) + '...');\n }\n\n // Build exporter chain\n let exporter: SpanExporter;\n if (this.config.debug && !this.config.apiKey) {\n exporter = new ConsoleExporter();\n } else if (this.config.apiKey) {\n exporter = new HttpExporter({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n projectId: this.config.projectId || undefined,\n environment: this.config.environment || undefined,\n compress: this.config.compress,\n });\n } else {\n // No API key and not debug — use console as fallback\n exporter = new ConsoleExporter();\n }\n\n this.processor = new BatchSpanProcessor({\n exporters: [exporter],\n batchSize: this.config.batchSize,\n batchTimeoutMs: this.config.batchTimeoutMs,\n maxQueueSize: this.config.maxQueueSize,\n debug: this.config.debug,\n });\n\n this.tracer = new Tracer({\n onSpanEnd: (span) => this.processor.onSpanEnd(span),\n sampleRate: this.config.sampleRate,\n enabled: this.config.enabled,\n traceContent: this.config.traceContent,\n });\n\n // Start the batch processor (enables span queuing and periodic flushing)\n this.processor.start();\n\n // Register shutdown hooks\n this._registerShutdownHooks();\n\n // Enable internal debug logging if configured\n setDebug(this.config.debug);\n debug(`Initialized: enabled=${this.config.enabled}, endpoint=${this.config.endpoint}`);\n }\n\n get enabled(): boolean {\n return this.tracer.enabled;\n }\n\n set enabled(value: boolean) {\n this.tracer.enabled = value;\n }\n\n // Audit #6: Promise-based shutdown dedup (fixes TOCTOU race condition)\n async shutdown(): Promise<void> {\n if (this._shutdownPromise) return this._shutdownPromise;\n this._shutdownPromise = this._doShutdown();\n return this._shutdownPromise;\n }\n\n private async _doShutdown(): Promise<void> {\n debug('Shutting down...');\n\n // Audit #3: Remove process listeners to prevent leak\n for (const { signal, handler } of this._shutdownHandlers) {\n process.removeListener(signal, handler);\n }\n this._shutdownHandlers = [];\n\n await this.processor.shutdown();\n }\n\n async flush(): Promise<void> {\n await this.processor.flush();\n }\n\n private _registerShutdownHooks(): void {\n const onShutdown = () => {\n // Audit #3: Add 5s timeout to prevent hanging on signal\n const timeout = setTimeout(() => process.exit(1), 5000);\n timeout.unref();\n this.shutdown().catch(() => {}).finally(() => clearTimeout(timeout));\n };\n\n const signals = ['beforeExit', 'SIGTERM', 'SIGINT'];\n for (const signal of signals) {\n process.once(signal, onShutdown);\n this._shutdownHandlers.push({ signal, handler: onShutdown });\n }\n }\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Initialize the Risicare SDK. Call once at application startup.\n *\n * @example\n * import { init } from 'risicare';\n * init({ apiKey: 'rsk-...', projectId: 'my-project' });\n */\nexport function init(config?: Partial<RisicareConfig>): void {\n if (getGlobalClient()) {\n debug('Already initialized. Call shutdown() first to re-initialize.');\n return;\n }\n\n const client = new RisicareClient(config);\n setGlobalClient(client);\n setGlobalTracer(client.tracer);\n}\n\n/**\n * Gracefully shut down the SDK. Flushes pending spans before resolving.\n */\nexport async function shutdown(): Promise<void> {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (!client) return;\n await client.shutdown();\n setGlobalClient(undefined);\n setGlobalTracer(undefined);\n}\n\n/**\n * Flush all pending spans without shutting down.\n */\nexport async function flush(): Promise<void> {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (!client) return;\n await client.flush();\n}\n\n/**\n * Enable tracing at runtime.\n */\nexport function enable(): void {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (client) client.enabled = true;\n}\n\n/**\n * Disable tracing at runtime. Spans will not be created or exported.\n */\nexport function disable(): void {\n const client = getGlobalClient() as RisicareClient | undefined;\n if (client) client.enabled = false;\n}\n\n/**\n * Check whether tracing is currently enabled.\n */\nexport function isEnabled(): boolean {\n const client = getGlobalClient() as RisicareClient | undefined;\n return client?.enabled ?? false;\n}\n\n/**\n * Get the global tracer instance. Returns undefined if not initialized.\n */\nexport function getTracer(): Tracer | undefined {\n return getGlobalTracer() as Tracer | undefined;\n}\n\n/**\n * Get the global tracer, or throw if not initialized.\n * @internal Used by decorators and providers that require an active tracer.\n */\nexport function requireTracer(): Tracer {\n const tracer = getGlobalTracer() as Tracer | undefined;\n if (!tracer) {\n throw new Error(\n 'Risicare SDK not initialized. Call init() before using tracing features.',\n );\n }\n return tracer;\n}\n\n/**\n * Check whether content tracing (prompt/completion capture) is enabled.\n */\nexport function getTraceContent(): boolean {\n const tracer = getGlobalTracer() as Tracer | undefined;\n return tracer?.traceContent ?? true;\n}\n\n/**\n * Get SDK metrics: exported spans, dropped spans, failed exports, queue stats.\n * Returns zero-valued metrics if SDK is not initialized.\n */\nexport function getMetrics() {\n const client = getGlobalClient() as RisicareClient | undefined;\n return client?.processor.getMetrics() ?? {\n exportedSpans: 0,\n droppedSpans: 0,\n failedExports: 0,\n queueSize: 0,\n queueCapacity: 0,\n queueUtilization: 0,\n };\n}\n","/**\n * Token cost calculation table.\n *\n * Prices are per 1M tokens. Update monthly.\n * Source: provider pricing pages.\n */\n\ninterface ModelPricing {\n input: number; // USD per 1M input tokens\n output: number; // USD per 1M output tokens\n}\n\nconst PRICING: Record<string, ModelPricing> = {\n // OpenAI\n 'gpt-4o': { input: 2.50, output: 10.00 },\n 'gpt-4o-mini': { input: 0.15, output: 0.60 },\n 'gpt-4-turbo': { input: 10.00, output: 30.00 },\n 'gpt-4': { input: 30.00, output: 60.00 },\n 'gpt-3.5-turbo': { input: 0.50, output: 1.50 },\n 'o1': { input: 15.00, output: 60.00 },\n 'o1-mini': { input: 3.00, output: 12.00 },\n 'o3-mini': { input: 1.10, output: 4.40 },\n\n // Anthropic\n 'claude-opus-4-5-20251101': { input: 15.00, output: 75.00 },\n 'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },\n 'claude-3-5-sonnet-20241022': { input: 3.00, output: 15.00 },\n 'claude-3-haiku-20240307': { input: 0.25, output: 1.25 },\n 'claude-3-opus-20240229': { input: 15.00, output: 75.00 },\n\n // Google\n 'gemini-2.0-flash': { input: 0.10, output: 0.40 },\n 'gemini-1.5-pro': { input: 1.25, output: 5.00 },\n 'gemini-1.5-flash': { input: 0.075, output: 0.30 },\n\n // Groq\n 'llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },\n 'llama-3.1-8b-instant': { input: 0.05, output: 0.08 },\n 'mixtral-8x7b-32768': { input: 0.24, output: 0.24 },\n\n // DeepSeek\n 'deepseek-chat': { input: 0.14, output: 0.28 },\n 'deepseek-reasoner': { input: 0.55, output: 2.19 },\n\n // Together.ai (open-source models)\n 'meta-llama/llama-3.3-70b-instruct-turbo': { input: 0.88, output: 0.88 },\n 'meta-llama/meta-llama-3.1-8b-instruct-turbo': { input: 0.18, output: 0.18 },\n 'meta-llama/llama-3.2-3b-instruct-turbo': { input: 0.06, output: 0.06 },\n 'qwen/qwen2.5-7b-instruct-turbo': { input: 0.20, output: 0.20 },\n 'mistralai/mistral-small-24b-instruct-2501': { input: 0.20, output: 0.20 },\n 'mistralai/mixtral-8x7b-instruct-v0.1': { input: 0.60, output: 0.60 },\n 'deepseek-ai/deepseek-v3': { input: 0.27, output: 1.10 },\n};\n\n/**\n * Calculate cost in USD for a model's token usage.\n * Returns undefined if model is not in pricing table.\n */\nexport function calculateCost(\n model: string,\n promptTokens: number,\n completionTokens: number,\n): number | undefined {\n const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];\n if (!pricing) return undefined;\n\n const inputCost = (promptTokens / 1_000_000) * pricing.input;\n const outputCost = (completionTokens / 1_000_000) * pricing.output;\n return inputCost + outputCost;\n}\n\n/**\n * Check if a model has pricing data.\n */\nexport function hasPricing(model: string): boolean {\n return model in PRICING || model.toLowerCase() in PRICING;\n}\n","/**\n * Vercel AI SDK instrumentation.\n *\n * The Vercel AI SDK (package: \"ai\") supports telemetry via configuration.\n * This module provides a helper that wraps generateText/streamText/generateObject\n * calls with Risicare tracing.\n *\n * Unlike OpenAI/Anthropic, we don't use Proxy here — the Vercel AI SDK\n * uses top-level functions (not a client class). We provide wrapper functions.\n *\n * Usage:\n * import { generateText } from 'ai';\n * import { patchVercelAI } from 'risicare/vercel-ai';\n * const { tracedGenerateText, tracedStreamText } = patchVercelAI();\n *\n * const result = await tracedGenerateText({\n * model: openai('gpt-4o'),\n * prompt: 'Hello',\n * });\n */\n\nimport { requireTracer } from '../../client.js';\nimport { SpanKind } from '../../types.js';\nimport { calculateCost } from '../../utils/pricing.js';\nimport { debug } from '../../utils/log.js';\nimport type { Span } from '../../span.js';\n\nfunction enrichSpanFromResult(span: Span, result: Record<string, unknown>): void {\n const usage = result.usage as Record<string, number> | undefined;\n // Vercel AI SDK exposes model info via result.response.modelId or similar\n const response = result.response as Record<string, unknown> | undefined;\n const modelId = (response?.modelId ?? result.modelId) as string | undefined;\n\n if (modelId) {\n span.setLlmFields({ model: modelId });\n }\n\n if (usage) {\n const promptTokens = usage.promptTokens ?? 0;\n const completionTokens = usage.completionTokens ?? 0;\n const totalTokens = usage.totalTokens ?? (promptTokens + completionTokens);\n const cost = modelId ? calculateCost(modelId, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens,\n costUsd: cost,\n });\n }\n}\n\n/**\n * Create traced wrappers for Vercel AI SDK functions.\n *\n * Returns wrapped versions of generateText, streamText, and generateObject\n * that automatically create tracing spans.\n */\nexport function patchVercelAI(): {\n tracedGenerateText: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n tracedStreamText: <T>(fn: (params: T) => unknown) => (params: T) => unknown;\n tracedGenerateObject: <T>(fn: (params: T) => Promise<Record<string, unknown>>) => (params: T) => Promise<Record<string, unknown>>;\n} {\n return {\n tracedGenerateText<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchVercelAI()');\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateText', kind: SpanKind.LLM_CALL },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n\n tracedStreamText<T>(fn: (params: T) => unknown) {\n return (params: T): unknown => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchVercelAI()');\n return fn(params);\n }\n\n // For streaming, we create a span but can't easily await the stream.\n // Create a manual span that the user closes, or wrap the stream result.\n return tracer.startSpan(\n { name: 'vercel-ai.streamText', kind: SpanKind.LLM_CALL },\n (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n return fn(params);\n },\n );\n };\n },\n\n tracedGenerateObject<T>(fn: (params: T) => Promise<Record<string, unknown>>) {\n return (params: T): Promise<Record<string, unknown>> => {\n let tracer;\n try {\n tracer = requireTracer();\n } catch {\n debug('Tracer not initialized — call init() before using patchVercelAI()');\n return fn(params);\n }\n\n return tracer.startSpan(\n { name: 'vercel-ai.generateObject', kind: SpanKind.LLM_CALL },\n async (span) => {\n span.setLlmFields({ provider: 'vercel-ai' });\n const result = await fn(params);\n enrichSpanFromResult(span, result);\n return result;\n },\n ) as Promise<Record<string, unknown>>;\n };\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "risicare",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "JavaScript/TypeScript SDK for Risicare — Agent Self-Healing Infrastructure",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",