risicare 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,210 @@
1
+ // src/ids.ts
2
+ import { randomBytes } from "crypto";
3
+
4
+ // src/noop.ts
5
+ var NOOP_SPAN = Object.freeze({
6
+ traceId: "00000000000000000000000000000000",
7
+ spanId: "0000000000000000",
8
+ parentSpanId: void 0,
9
+ name: "noop",
10
+ kind: "internal" /* INTERNAL */,
11
+ startTime: "",
12
+ startHrtime: 0,
13
+ endTime: void 0,
14
+ status: "unset" /* UNSET */,
15
+ statusMessage: void 0,
16
+ attributes: Object.freeze({}),
17
+ events: Object.freeze([]),
18
+ links: Object.freeze([]),
19
+ sessionId: void 0,
20
+ agentId: void 0,
21
+ agentName: void 0,
22
+ semanticPhase: void 0,
23
+ llmProvider: void 0,
24
+ llmModel: void 0,
25
+ llmPromptTokens: void 0,
26
+ llmCompletionTokens: void 0,
27
+ llmTotalTokens: void 0,
28
+ llmCostUsd: void 0,
29
+ toolName: void 0,
30
+ toolSuccess: void 0,
31
+ isEnded: true,
32
+ durationMs: 0,
33
+ setAttribute() {
34
+ return this;
35
+ },
36
+ setAttributes() {
37
+ return this;
38
+ },
39
+ setStatus() {
40
+ return this;
41
+ },
42
+ addEvent() {
43
+ return this;
44
+ },
45
+ addLink() {
46
+ return this;
47
+ },
48
+ recordException() {
49
+ return this;
50
+ },
51
+ setLlmFields() {
52
+ return this;
53
+ },
54
+ setToolFields() {
55
+ return this;
56
+ },
57
+ end() {
58
+ },
59
+ toPayload() {
60
+ return {
61
+ traceId: this.traceId,
62
+ spanId: this.spanId,
63
+ name: this.name,
64
+ kind: this.kind,
65
+ startTime: this.startTime,
66
+ status: this.status,
67
+ attributes: {},
68
+ events: [],
69
+ links: []
70
+ };
71
+ }
72
+ });
73
+
74
+ // src/context/storage.ts
75
+ import { AsyncLocalStorage } from "async_hooks";
76
+ var contextStorage = new AsyncLocalStorage();
77
+
78
+ // src/client.ts
79
+ var _tracer;
80
+ function requireTracer() {
81
+ if (!_tracer) {
82
+ throw new Error(
83
+ "Risicare SDK not initialized. Call init() before using tracing features."
84
+ );
85
+ }
86
+ return _tracer;
87
+ }
88
+
89
+ // src/utils/pricing.ts
90
+ var PRICING = {
91
+ // OpenAI
92
+ "gpt-4o": { input: 2.5, output: 10 },
93
+ "gpt-4o-mini": { input: 0.15, output: 0.6 },
94
+ "gpt-4-turbo": { input: 10, output: 30 },
95
+ "gpt-4": { input: 30, output: 60 },
96
+ "gpt-3.5-turbo": { input: 0.5, output: 1.5 },
97
+ "o1": { input: 15, output: 60 },
98
+ "o1-mini": { input: 3, output: 12 },
99
+ "o3-mini": { input: 1.1, output: 4.4 },
100
+ // Anthropic
101
+ "claude-opus-4-6": { input: 15, output: 75 },
102
+ "claude-sonnet-4-5-20250929": { input: 3, output: 15 },
103
+ "claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
104
+ "claude-3-5-sonnet-20241022": { input: 3, output: 15 },
105
+ "claude-3-haiku-20240307": { input: 0.25, output: 1.25 },
106
+ "claude-3-opus-20240229": { input: 15, output: 75 },
107
+ // Google
108
+ "gemini-2.0-flash": { input: 0.1, output: 0.4 },
109
+ "gemini-1.5-pro": { input: 1.25, output: 5 },
110
+ "gemini-1.5-flash": { input: 0.075, output: 0.3 },
111
+ // Groq
112
+ "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
113
+ "llama-3.1-8b-instant": { input: 0.05, output: 0.08 },
114
+ "mixtral-8x7b-32768": { input: 0.24, output: 0.24 },
115
+ // DeepSeek
116
+ "deepseek-chat": { input: 0.14, output: 0.28 },
117
+ "deepseek-reasoner": { input: 0.55, output: 2.19 },
118
+ // Together.ai (open-source models)
119
+ "meta-llama/llama-3.3-70b-instruct-turbo": { input: 0.88, output: 0.88 },
120
+ "meta-llama/meta-llama-3.1-8b-instruct-turbo": { input: 0.18, output: 0.18 },
121
+ "meta-llama/llama-3.2-3b-instruct-turbo": { input: 0.06, output: 0.06 },
122
+ "qwen/qwen2.5-7b-instruct-turbo": { input: 0.2, output: 0.2 },
123
+ "mistralai/mistral-small-24b-instruct-2501": { input: 0.2, output: 0.2 },
124
+ "mistralai/mixtral-8x7b-instruct-v0.1": { input: 0.6, output: 0.6 },
125
+ "deepseek-ai/deepseek-v3": { input: 0.27, output: 1.1 }
126
+ };
127
+ function calculateCost(model, promptTokens, completionTokens) {
128
+ const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];
129
+ if (!pricing) return void 0;
130
+ const inputCost = promptTokens / 1e6 * pricing.input;
131
+ const outputCost = completionTokens / 1e6 * pricing.output;
132
+ return inputCost + outputCost;
133
+ }
134
+
135
+ // src/providers/anthropic/patch.ts
136
+ function enrichSpanFromResponse(span, response) {
137
+ const model = response.model;
138
+ const usage = response.usage;
139
+ if (model) {
140
+ span.setLlmFields({ provider: "anthropic", model });
141
+ }
142
+ if (usage) {
143
+ const promptTokens = usage.input_tokens ?? 0;
144
+ const completionTokens = usage.output_tokens ?? 0;
145
+ const totalTokens = promptTokens + completionTokens;
146
+ const cost = model ? calculateCost(model, promptTokens, completionTokens) : void 0;
147
+ span.setLlmFields({
148
+ promptTokens,
149
+ completionTokens,
150
+ totalTokens,
151
+ costUsd: cost
152
+ });
153
+ }
154
+ }
155
+ function createMessagesProxy(originalCreate) {
156
+ return function patchedCreate(...args) {
157
+ let tracer;
158
+ try {
159
+ tracer = requireTracer();
160
+ } catch {
161
+ return originalCreate.apply(this, args);
162
+ }
163
+ const params = args[0] ?? {};
164
+ const model = params.model ?? "unknown";
165
+ const isStream = !!params.stream;
166
+ return tracer.startSpan(
167
+ {
168
+ name: `anthropic.messages.create`,
169
+ kind: "client" /* CLIENT */,
170
+ attributes: { "llm.request.model": model, "llm.stream": isStream }
171
+ },
172
+ (span) => {
173
+ span.setLlmFields({ provider: "anthropic", model });
174
+ const result = originalCreate.apply(this, args);
175
+ if (result && typeof result === "object" && typeof result.then === "function") {
176
+ return result.then((response) => {
177
+ if (!isStream && response) {
178
+ enrichSpanFromResponse(span, response);
179
+ }
180
+ return response;
181
+ });
182
+ }
183
+ return result;
184
+ }
185
+ );
186
+ };
187
+ }
188
+ function patchAnthropic(client) {
189
+ return new Proxy(client, {
190
+ get(target, prop, receiver) {
191
+ const value = Reflect.get(target, prop, receiver);
192
+ if (prop === "messages" && value && typeof value === "object") {
193
+ return new Proxy(value, {
194
+ get(msgTarget, msgProp, msgReceiver) {
195
+ const msgValue = Reflect.get(msgTarget, msgProp, msgReceiver);
196
+ if (msgProp === "create" && typeof msgValue === "function") {
197
+ return createMessagesProxy(msgValue.bind(msgTarget));
198
+ }
199
+ return msgValue;
200
+ }
201
+ });
202
+ }
203
+ return value;
204
+ }
205
+ });
206
+ }
207
+ export {
208
+ patchAnthropic
209
+ };
210
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ids.ts","../../../src/noop.ts","../../../src/context/storage.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/anthropic/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 * Anthropic SDK Proxy-based instrumentation.\n *\n * Wraps an Anthropic client instance using ES Proxy to intercept:\n * - messages.create (sync + streaming)\n *\n * Usage:\n * import Anthropic from '@anthropic-ai/sdk';\n * import { patchAnthropic } from 'risicare/anthropic';\n * const anthropic = patchAnthropic(new Anthropic({ 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\nfunction enrichSpanFromResponse(span: Span, response: Record<string, unknown>): 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: 'anthropic', model });\n }\n\n if (usage) {\n const promptTokens = usage.input_tokens ?? 0;\n const completionTokens = usage.output_tokens ?? 0;\n const totalTokens = promptTokens + completionTokens;\n const cost = model ? calculateCost(model, promptTokens, completionTokens) : undefined;\n\n span.setLlmFields({\n promptTokens,\n completionTokens,\n totalTokens,\n costUsd: cost,\n });\n }\n}\n\nfunction createMessagesProxy(originalCreate: Function): 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 {\n name: `anthropic.messages.create`,\n kind: SpanKind.CLIENT,\n attributes: { 'llm.request.model': model, 'llm.stream': isStream },\n },\n (span) => {\n span.setLlmFields({ provider: 'anthropic', 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 (!isStream && response) {\n enrichSpanFromResponse(span, response);\n }\n return response;\n });\n }\n\n return result;\n },\n );\n };\n}\n\n/**\n * Wrap an Anthropic client instance with tracing instrumentation.\n *\n * Returns a Proxy that intercepts messages.create.\n * The original client is NOT modified.\n *\n * @param client - An Anthropic client instance\n * @returns A proxied client with automatic tracing\n */\nexport function patchAnthropic<T extends object>(client: T): T {\n return new Proxy(client, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n if (prop === 'messages' && value && typeof value === 'object') {\n return new Proxy(value as object, {\n get(msgTarget, msgProp, msgReceiver) {\n const msgValue = Reflect.get(msgTarget, msgProp, msgReceiver);\n\n if (msgProp === 'create' && typeof msgValue === 'function') {\n return createMessagesProxy(msgValue.bind(msgTarget));\n }\n\n return msgValue;\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;;;AClDD,SAAS,yBAAyB;AAiClC,IAAM,iBAAiB,IAAI,kBAAgC;;;ACvB3D,IAAI;AA4JG,SAAS,gBAAwB;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC/KA,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,mBAAmB,EAAE,OAAO,IAAO,QAAQ,GAAM;AAAA,EACjD,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;;;ACrDA,SAAS,uBAAuB,MAAY,UAAyC;AACnF,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ,SAAS;AAEvB,MAAI,OAAO;AACT,SAAK,aAAa,EAAE,UAAU,aAAa,MAAM,CAAC;AAAA,EACpD;AAEA,MAAI,OAAO;AACT,UAAM,eAAe,MAAM,gBAAgB;AAC3C,UAAM,mBAAmB,MAAM,iBAAiB;AAChD,UAAM,cAAc,eAAe;AACnC,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,oBAAoB,gBAAoC;AAC/D,SAAO,SAAS,iBAAgC,MAAiB;AAC/D,QAAI;AACJ,QAAI;AACF,eAAS,cAAc;AAAA,IACzB,QAAQ;AACN,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;AAAA,QACE,MAAM;AAAA,QACN;AAAA,QACA,YAAY,EAAE,qBAAqB,OAAO,cAAc,SAAS;AAAA,MACnE;AAAA,MACA,CAAC,SAAS;AACR,aAAK,aAAa,EAAE,UAAU,aAAa,MAAM,CAAC;AAElD,cAAM,SAAS,eAAe,MAAM,MAAM,IAAI;AAE9C,YAAI,UAAU,OAAO,WAAW,YAAY,OAAQ,OAA4B,SAAS,YAAY;AACnG,iBAAQ,OAA4C,KAAK,CAAC,aAAa;AACrE,gBAAI,CAAC,YAAY,UAAU;AACzB,qCAAuB,MAAM,QAAQ;AAAA,YACvC;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAWO,SAAS,eAAiC,QAAc;AAC7D,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAEhD,UAAI,SAAS,cAAc,SAAS,OAAO,UAAU,UAAU;AAC7D,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,oBAAoB,SAAS,KAAK,SAAS,CAAC;AAAA,YACrD;AAEA,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/providers/openai/index.ts
21
+ var openai_exports = {};
22
+ __export(openai_exports, {
23
+ patchOpenAI: () => patchOpenAI
24
+ });
25
+ module.exports = __toCommonJS(openai_exports);
26
+
27
+ // src/ids.ts
28
+ var import_node_crypto = require("crypto");
29
+
30
+ // src/noop.ts
31
+ var NOOP_SPAN = Object.freeze({
32
+ traceId: "00000000000000000000000000000000",
33
+ spanId: "0000000000000000",
34
+ parentSpanId: void 0,
35
+ name: "noop",
36
+ kind: "internal" /* INTERNAL */,
37
+ startTime: "",
38
+ startHrtime: 0,
39
+ endTime: void 0,
40
+ status: "unset" /* UNSET */,
41
+ statusMessage: void 0,
42
+ attributes: Object.freeze({}),
43
+ events: Object.freeze([]),
44
+ links: Object.freeze([]),
45
+ sessionId: void 0,
46
+ agentId: void 0,
47
+ agentName: void 0,
48
+ semanticPhase: void 0,
49
+ llmProvider: void 0,
50
+ llmModel: void 0,
51
+ llmPromptTokens: void 0,
52
+ llmCompletionTokens: void 0,
53
+ llmTotalTokens: void 0,
54
+ llmCostUsd: void 0,
55
+ toolName: void 0,
56
+ toolSuccess: void 0,
57
+ isEnded: true,
58
+ durationMs: 0,
59
+ setAttribute() {
60
+ return this;
61
+ },
62
+ setAttributes() {
63
+ return this;
64
+ },
65
+ setStatus() {
66
+ return this;
67
+ },
68
+ addEvent() {
69
+ return this;
70
+ },
71
+ addLink() {
72
+ return this;
73
+ },
74
+ recordException() {
75
+ return this;
76
+ },
77
+ setLlmFields() {
78
+ return this;
79
+ },
80
+ setToolFields() {
81
+ return this;
82
+ },
83
+ end() {
84
+ },
85
+ toPayload() {
86
+ return {
87
+ traceId: this.traceId,
88
+ spanId: this.spanId,
89
+ name: this.name,
90
+ kind: this.kind,
91
+ startTime: this.startTime,
92
+ status: this.status,
93
+ attributes: {},
94
+ events: [],
95
+ links: []
96
+ };
97
+ }
98
+ });
99
+
100
+ // src/context/storage.ts
101
+ var import_node_async_hooks = require("async_hooks");
102
+ var contextStorage = new import_node_async_hooks.AsyncLocalStorage();
103
+
104
+ // src/client.ts
105
+ var _tracer;
106
+ function requireTracer() {
107
+ if (!_tracer) {
108
+ throw new Error(
109
+ "Risicare SDK not initialized. Call init() before using tracing features."
110
+ );
111
+ }
112
+ return _tracer;
113
+ }
114
+
115
+ // src/utils/pricing.ts
116
+ var PRICING = {
117
+ // OpenAI
118
+ "gpt-4o": { input: 2.5, output: 10 },
119
+ "gpt-4o-mini": { input: 0.15, output: 0.6 },
120
+ "gpt-4-turbo": { input: 10, output: 30 },
121
+ "gpt-4": { input: 30, output: 60 },
122
+ "gpt-3.5-turbo": { input: 0.5, output: 1.5 },
123
+ "o1": { input: 15, output: 60 },
124
+ "o1-mini": { input: 3, output: 12 },
125
+ "o3-mini": { input: 1.1, output: 4.4 },
126
+ // Anthropic
127
+ "claude-opus-4-6": { input: 15, output: 75 },
128
+ "claude-sonnet-4-5-20250929": { input: 3, output: 15 },
129
+ "claude-haiku-4-5-20251001": { input: 0.8, output: 4 },
130
+ "claude-3-5-sonnet-20241022": { input: 3, output: 15 },
131
+ "claude-3-haiku-20240307": { input: 0.25, output: 1.25 },
132
+ "claude-3-opus-20240229": { input: 15, output: 75 },
133
+ // Google
134
+ "gemini-2.0-flash": { input: 0.1, output: 0.4 },
135
+ "gemini-1.5-pro": { input: 1.25, output: 5 },
136
+ "gemini-1.5-flash": { input: 0.075, output: 0.3 },
137
+ // Groq
138
+ "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
139
+ "llama-3.1-8b-instant": { input: 0.05, output: 0.08 },
140
+ "mixtral-8x7b-32768": { input: 0.24, output: 0.24 },
141
+ // DeepSeek
142
+ "deepseek-chat": { input: 0.14, output: 0.28 },
143
+ "deepseek-reasoner": { input: 0.55, output: 2.19 },
144
+ // Together.ai (open-source models)
145
+ "meta-llama/llama-3.3-70b-instruct-turbo": { input: 0.88, output: 0.88 },
146
+ "meta-llama/meta-llama-3.1-8b-instruct-turbo": { input: 0.18, output: 0.18 },
147
+ "meta-llama/llama-3.2-3b-instruct-turbo": { input: 0.06, output: 0.06 },
148
+ "qwen/qwen2.5-7b-instruct-turbo": { input: 0.2, output: 0.2 },
149
+ "mistralai/mistral-small-24b-instruct-2501": { input: 0.2, output: 0.2 },
150
+ "mistralai/mixtral-8x7b-instruct-v0.1": { input: 0.6, output: 0.6 },
151
+ "deepseek-ai/deepseek-v3": { input: 0.27, output: 1.1 }
152
+ };
153
+ function calculateCost(model, promptTokens, completionTokens) {
154
+ const pricing = PRICING[model] ?? PRICING[model.toLowerCase()];
155
+ if (!pricing) return void 0;
156
+ const inputCost = promptTokens / 1e6 * pricing.input;
157
+ const outputCost = completionTokens / 1e6 * pricing.output;
158
+ return inputCost + outputCost;
159
+ }
160
+
161
+ // src/providers/openai/patch.ts
162
+ var COMPATIBLE_HOSTS = {
163
+ "api.deepseek.com": "deepseek",
164
+ "api.together.xyz": "together",
165
+ "api.groq.com": "groq"
166
+ };
167
+ function detectProvider(client) {
168
+ try {
169
+ const baseURL = client.baseURL;
170
+ if (!baseURL || typeof baseURL !== "string") return "openai";
171
+ const host = baseURL.split("//").pop()?.split("/")[0]?.split(":")[0]?.toLowerCase();
172
+ return (host && COMPATIBLE_HOSTS[host]) ?? "openai";
173
+ } catch {
174
+ return "openai";
175
+ }
176
+ }
177
+ function enrichSpanFromResponse(span, response, provider) {
178
+ const model = response.model;
179
+ const usage = response.usage;
180
+ if (model) {
181
+ span.setLlmFields({ provider, model });
182
+ }
183
+ if (usage) {
184
+ const promptTokens = usage.prompt_tokens ?? 0;
185
+ const completionTokens = usage.completion_tokens ?? 0;
186
+ const totalTokens = usage.total_tokens ?? promptTokens + completionTokens;
187
+ const cost = model ? calculateCost(model, promptTokens, completionTokens) : void 0;
188
+ span.setLlmFields({
189
+ promptTokens,
190
+ completionTokens,
191
+ totalTokens,
192
+ costUsd: cost
193
+ });
194
+ }
195
+ }
196
+ function createChatCompletionProxy(originalCreate, provider) {
197
+ return function patchedCreate(...args) {
198
+ let tracer;
199
+ try {
200
+ tracer = requireTracer();
201
+ } catch {
202
+ return originalCreate.apply(this, args);
203
+ }
204
+ const params = args[0] ?? {};
205
+ const model = params.model ?? "unknown";
206
+ const isStream = !!params.stream;
207
+ return tracer.startSpan(
208
+ { name: `openai.chat.completions.create`, kind: "llm_call" /* LLM_CALL */, attributes: { "llm.request.model": model, "llm.stream": isStream } },
209
+ (span) => {
210
+ span.setLlmFields({ provider, model });
211
+ const result = originalCreate.apply(this, args);
212
+ if (result && typeof result === "object" && typeof result.then === "function") {
213
+ return result.then((response) => {
214
+ if (!isStream && response) {
215
+ enrichSpanFromResponse(span, response, provider);
216
+ }
217
+ return response;
218
+ });
219
+ }
220
+ return result;
221
+ }
222
+ );
223
+ };
224
+ }
225
+ function createEmbeddingsProxy(originalCreate, provider) {
226
+ return function patchedCreate(...args) {
227
+ let tracer;
228
+ try {
229
+ tracer = requireTracer();
230
+ } catch {
231
+ return originalCreate.apply(this, args);
232
+ }
233
+ const params = args[0] ?? {};
234
+ const model = params.model ?? "unknown";
235
+ return tracer.startSpan(
236
+ { name: `openai.embeddings.create`, kind: "llm_call" /* LLM_CALL */, attributes: { "llm.request.model": model } },
237
+ (span) => {
238
+ span.setLlmFields({ provider, model });
239
+ const result = originalCreate.apply(this, args);
240
+ if (result && typeof result === "object" && typeof result.then === "function") {
241
+ return result.then((response) => {
242
+ if (response) {
243
+ enrichSpanFromResponse(span, response, provider);
244
+ }
245
+ return response;
246
+ });
247
+ }
248
+ return result;
249
+ }
250
+ );
251
+ };
252
+ }
253
+ function patchOpenAI(client) {
254
+ const provider = detectProvider(client);
255
+ return new Proxy(client, {
256
+ get(target, prop, receiver) {
257
+ const value = Reflect.get(target, prop, receiver);
258
+ if (prop === "chat" && value && typeof value === "object") {
259
+ return new Proxy(value, {
260
+ get(chatTarget, chatProp, chatReceiver) {
261
+ const chatValue = Reflect.get(chatTarget, chatProp, chatReceiver);
262
+ if (chatProp === "completions" && chatValue && typeof chatValue === "object") {
263
+ return new Proxy(chatValue, {
264
+ get(compTarget, compProp, compReceiver) {
265
+ const compValue = Reflect.get(compTarget, compProp, compReceiver);
266
+ if (compProp === "create" && typeof compValue === "function") {
267
+ return createChatCompletionProxy(compValue.bind(compTarget), provider);
268
+ }
269
+ return compValue;
270
+ }
271
+ });
272
+ }
273
+ return chatValue;
274
+ }
275
+ });
276
+ }
277
+ if (prop === "embeddings" && value && typeof value === "object") {
278
+ return new Proxy(value, {
279
+ get(embTarget, embProp, embReceiver) {
280
+ const embValue = Reflect.get(embTarget, embProp, embReceiver);
281
+ if (embProp === "create" && typeof embValue === "function") {
282
+ return createEmbeddingsProxy(embValue.bind(embTarget), provider);
283
+ }
284
+ return embValue;
285
+ }
286
+ });
287
+ }
288
+ return value;
289
+ }
290
+ });
291
+ }
292
+ // Annotate the CommonJS export names for ESM import in node:
293
+ 0 && (module.exports = {
294
+ patchOpenAI
295
+ });
296
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/openai/index.ts","../../../src/ids.ts","../../../src/noop.ts","../../../src/context/storage.ts","../../../src/client.ts","../../../src/utils/pricing.ts","../../../src/providers/openai/patch.ts"],"sourcesContent":["/**\n * OpenAI provider instrumentation.\n *\n * Entry point for `import { patchOpenAI } from 'risicare/openai'`.\n */\n\nexport { patchOpenAI } 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 * 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":[]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * OpenAI SDK Proxy-based instrumentation.
3
+ *
4
+ * Wraps an OpenAI client instance using ES Proxy to intercept:
5
+ * - chat.completions.create (sync + streaming)
6
+ * - embeddings.create
7
+ *
8
+ * The original client is NOT modified — Proxy creates a transparent wrapper
9
+ * that intercepts method calls and creates tracing spans.
10
+ *
11
+ * Usage:
12
+ * import OpenAI from 'openai';
13
+ * import { patchOpenAI } from 'risicare/openai';
14
+ * const openai = patchOpenAI(new OpenAI({ apiKey: '...' }));
15
+ */
16
+ /**
17
+ * Wrap an OpenAI client instance with tracing instrumentation.
18
+ *
19
+ * Returns a Proxy that intercepts chat.completions.create and embeddings.create.
20
+ * The original client is NOT modified.
21
+ *
22
+ * @param client - An OpenAI client instance
23
+ * @returns A proxied client with automatic tracing
24
+ */
25
+ declare function patchOpenAI<T extends object>(client: T): T;
26
+
27
+ export { patchOpenAI };