@amplitude/ai 0.1.2 → 0.2.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.
Files changed (60) hide show
  1. package/AGENTS.md +3 -1
  2. package/README.md +44 -15
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +2 -1
  5. package/dist/client.js.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js +2 -2
  8. package/dist/integrations/anthropic-tools.js +2 -1
  9. package/dist/integrations/anthropic-tools.js.map +1 -1
  10. package/dist/integrations/langchain.d.ts.map +1 -1
  11. package/dist/integrations/langchain.js +35 -5
  12. package/dist/integrations/langchain.js.map +1 -1
  13. package/dist/integrations/llamaindex.d.ts.map +1 -1
  14. package/dist/integrations/llamaindex.js +27 -4
  15. package/dist/integrations/llamaindex.js.map +1 -1
  16. package/dist/integrations/openai-agents.js +5 -1
  17. package/dist/integrations/openai-agents.js.map +1 -1
  18. package/dist/integrations/opentelemetry.d.ts.map +1 -1
  19. package/dist/integrations/opentelemetry.js +2 -1
  20. package/dist/integrations/opentelemetry.js.map +1 -1
  21. package/dist/mcp/patterns.d.ts.map +1 -1
  22. package/dist/mcp/patterns.js +6 -0
  23. package/dist/mcp/patterns.js.map +1 -1
  24. package/dist/mcp/server.d.ts.map +1 -1
  25. package/dist/mcp/validate-file.js +1 -1
  26. package/dist/mcp/validate-file.js.map +1 -1
  27. package/dist/patching.d.ts.map +1 -1
  28. package/dist/patching.js +7 -1
  29. package/dist/patching.js.map +1 -1
  30. package/dist/providers/anthropic.d.ts.map +1 -1
  31. package/dist/providers/anthropic.js +7 -2
  32. package/dist/providers/anthropic.js.map +1 -1
  33. package/dist/providers/bedrock.d.ts.map +1 -1
  34. package/dist/providers/bedrock.js +4 -2
  35. package/dist/providers/bedrock.js.map +1 -1
  36. package/dist/providers/gemini.d.ts.map +1 -1
  37. package/dist/providers/gemini.js +4 -2
  38. package/dist/providers/gemini.js.map +1 -1
  39. package/dist/providers/mistral.d.ts.map +1 -1
  40. package/dist/providers/mistral.js +4 -2
  41. package/dist/providers/mistral.js.map +1 -1
  42. package/dist/providers/openai.d.ts.map +1 -1
  43. package/dist/providers/openai.js +14 -4
  44. package/dist/providers/openai.js.map +1 -1
  45. package/dist/session.d.ts +23 -0
  46. package/dist/session.d.ts.map +1 -1
  47. package/dist/session.js +44 -1
  48. package/dist/session.js.map +1 -1
  49. package/dist/utils/costs.d.ts +46 -5
  50. package/dist/utils/costs.d.ts.map +1 -1
  51. package/dist/utils/costs.js +115 -26
  52. package/dist/utils/costs.js.map +1 -1
  53. package/dist/utils/providers.d.ts +6 -1
  54. package/dist/utils/providers.d.ts.map +1 -1
  55. package/dist/utils/providers.js +9 -3
  56. package/dist/utils/providers.js.map +1 -1
  57. package/llms-full.txt +17 -1
  58. package/llms.txt +1 -1
  59. package/mcp.schema.json +1 -1
  60. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"llamaindex.js","names":["costUsd: number | undefined"],"sources":["../../src/integrations/llamaindex.ts"],"sourcesContent":["/**\n * LlamaIndex integration — AmplitudeLlamaIndexHandler.\n *\n * Tracks LLM calls, function calls, and embedding events\n * from LlamaIndex via its callback handler system.\n */\n\nimport type { AmplitudeAI } from '../client.js';\nimport { getActiveContext } from '../context.js';\nimport { calculateCost } from '../utils/costs.js';\n\nexport interface LlamaIndexHandlerOptions {\n amplitudeAI: AmplitudeAI;\n userId?: string;\n sessionId?: string;\n agentId?: string;\n env?: string;\n}\n\nexport class AmplitudeLlamaIndexHandler {\n private _ai: AmplitudeAI;\n private _userId: string | null;\n private _sessionId: string | null;\n private _agentId: string | null;\n private _env: string | null;\n private _startTimes: Map<string, number> = new Map();\n\n constructor(options: LlamaIndexHandlerOptions) {\n this._ai = options.amplitudeAI;\n this._userId = options.userId ?? null;\n this._sessionId = options.sessionId ?? null;\n this._agentId = options.agentId ?? null;\n this._env = options.env ?? null;\n }\n\n private _getContext() {\n const ctx = getActiveContext();\n return {\n userId: this._userId ?? ctx?.userId ?? 'unknown',\n sessionId: this._sessionId ?? ctx?.sessionId ?? undefined,\n agentId: this._agentId ?? ctx?.agentId ?? undefined,\n env: this._env ?? ctx?.env ?? undefined,\n traceId: ctx?.traceId ?? undefined,\n };\n }\n\n onLLMStart(eventId: string): void {\n this._startTimes.set(eventId, performance.now());\n }\n\n onLLMEnd(\n eventId: string,\n response: {\n content?: string;\n model?: string;\n inputTokens?: number;\n outputTokens?: number;\n },\n ): void {\n const startTime = this._startTimes.get(eventId) ?? performance.now();\n this._startTimes.delete(eventId);\n const latencyMs = performance.now() - startTime;\n\n const ctx = this._getContext();\n const normalized = _normalizeLlamaLlmResponse(response as unknown);\n\n let costUsd: number | undefined;\n if (\n normalized.model !== 'unknown' &&\n normalized.inputTokens != null &&\n normalized.outputTokens != null\n ) {\n try {\n const cost = calculateCost({\n modelName: normalized.model,\n inputTokens: normalized.inputTokens,\n outputTokens: normalized.outputTokens,\n });\n if (cost > 0) costUsd = cost;\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._ai.trackAiMessage({\n userId: ctx.userId,\n content: normalized.content,\n sessionId: ctx.sessionId ?? 'llamaindex-session',\n model: normalized.model,\n provider: 'llamaindex',\n latencyMs,\n traceId: ctx.traceId,\n agentId: ctx.agentId,\n env: ctx.env,\n inputTokens: normalized.inputTokens,\n outputTokens: normalized.outputTokens,\n totalCostUsd: costUsd,\n });\n }\n\n onEmbeddingStart(eventId: string): void {\n this._startTimes.set(eventId, performance.now());\n }\n\n onEmbeddingEnd(\n eventId: string,\n response: { model?: string; inputTokens?: number; dimensions?: number },\n ): void {\n const startTime = this._startTimes.get(eventId) ?? performance.now();\n this._startTimes.delete(eventId);\n const latencyMs = performance.now() - startTime;\n\n const ctx = this._getContext();\n const normalized = _normalizeLlamaEmbeddingResponse(response as unknown);\n this._ai.trackEmbedding({\n userId: ctx.userId,\n model: normalized.model,\n provider: 'llamaindex',\n latencyMs,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n agentId: ctx.agentId,\n env: ctx.env,\n inputTokens: normalized.inputTokens,\n dimensions: normalized.dimensions,\n });\n }\n\n onToolStart(eventId: string): void {\n this._startTimes.set(eventId, performance.now());\n }\n\n onToolEnd(\n eventId: string,\n response: { toolName?: string; output?: unknown; success?: boolean },\n ): void {\n const startTime = this._startTimes.get(eventId) ?? performance.now();\n this._startTimes.delete(eventId);\n const latencyMs = performance.now() - startTime;\n\n const ctx = this._getContext();\n const normalized = _normalizeLlamaToolResponse(response as unknown);\n this._ai.trackToolCall({\n userId: ctx.userId,\n toolName: normalized.toolName,\n latencyMs,\n success: normalized.success,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n agentId: ctx.agentId,\n env: ctx.env,\n output: normalized.output,\n });\n }\n}\n\nexport function createAmplitudeLlamaIndexHandler(\n options: LlamaIndexHandlerOptions,\n): AmplitudeLlamaIndexHandler {\n return new AmplitudeLlamaIndexHandler(options);\n}\n\nfunction _normalizeLlamaLlmResponse(response: unknown): {\n content: string;\n model: string;\n inputTokens?: number;\n outputTokens?: number;\n} {\n const resp =\n response != null && typeof response === 'object'\n ? (response as Record<string, unknown>)\n : {};\n const message = resp.message as Record<string, unknown> | undefined;\n const content =\n (typeof resp.content === 'string' ? resp.content : undefined) ??\n (typeof message?.content === 'string' ? (message.content as string) : '') ??\n '';\n const usage = (resp.usage as Record<string, unknown> | undefined) ?? {};\n return {\n content,\n model: String(resp.model ?? message?.model ?? 'unknown'),\n inputTokens: _toNumber(\n resp.inputTokens ?? usage.input_tokens ?? usage.prompt_tokens,\n ),\n outputTokens: _toNumber(\n resp.outputTokens ?? usage.output_tokens ?? usage.completion_tokens,\n ),\n };\n}\n\nfunction _normalizeLlamaEmbeddingResponse(response: unknown): {\n model: string;\n inputTokens?: number;\n dimensions?: number;\n} {\n const resp =\n response != null && typeof response === 'object'\n ? (response as Record<string, unknown>)\n : {};\n const usage = (resp.usage as Record<string, unknown> | undefined) ?? {};\n return {\n model: String(resp.model ?? 'unknown'),\n inputTokens: _toNumber(resp.inputTokens ?? usage.input_tokens),\n dimensions: _toNumber(\n resp.dimensions ?? resp.vectorSize ?? resp.embedding_dimensions,\n ),\n };\n}\n\nfunction _normalizeLlamaToolResponse(response: unknown): {\n toolName: string;\n success: boolean;\n output?: unknown;\n} {\n const resp =\n response != null && typeof response === 'object'\n ? (response as Record<string, unknown>)\n : {};\n return {\n toolName: String(resp.toolName ?? resp.name ?? 'llamaindex-tool'),\n success: Boolean(resp.success ?? resp.error == null),\n output: resp.output ?? resp.result,\n };\n}\n\nfunction _toNumber(value: unknown): number | undefined {\n return typeof value === 'number' ? value : undefined;\n}\n"],"mappings":";;;;AAmBA,IAAa,6BAAb,MAAwC;CACtC,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,8BAAmC,IAAI,KAAK;CAEpD,YAAY,SAAmC;AAC7C,OAAK,MAAM,QAAQ;AACnB,OAAK,UAAU,QAAQ,UAAU;AACjC,OAAK,aAAa,QAAQ,aAAa;AACvC,OAAK,WAAW,QAAQ,WAAW;AACnC,OAAK,OAAO,QAAQ,OAAO;;CAG7B,AAAQ,cAAc;EACpB,MAAM,MAAM,kBAAkB;AAC9B,SAAO;GACL,QAAQ,KAAK,WAAW,KAAK,UAAU;GACvC,WAAW,KAAK,cAAc,KAAK,aAAa;GAChD,SAAS,KAAK,YAAY,KAAK,WAAW;GAC1C,KAAK,KAAK,QAAQ,KAAK,OAAO;GAC9B,SAAS,KAAK,WAAW;GAC1B;;CAGH,WAAW,SAAuB;AAChC,OAAK,YAAY,IAAI,SAAS,YAAY,KAAK,CAAC;;CAGlD,SACE,SACA,UAMM;EACN,MAAM,YAAY,KAAK,YAAY,IAAI,QAAQ,IAAI,YAAY,KAAK;AACpE,OAAK,YAAY,OAAO,QAAQ;EAChC,MAAM,YAAY,YAAY,KAAK,GAAG;EAEtC,MAAM,MAAM,KAAK,aAAa;EAC9B,MAAM,aAAa,2BAA2B,SAAoB;EAElE,IAAIA;AACJ,MACE,WAAW,UAAU,aACrB,WAAW,eAAe,QAC1B,WAAW,gBAAgB,KAE3B,KAAI;GACF,MAAM,OAAO,cAAc;IACzB,WAAW,WAAW;IACtB,aAAa,WAAW;IACxB,cAAc,WAAW;IAC1B,CAAC;AACF,OAAI,OAAO,EAAG,WAAU;UAClB;AAKV,OAAK,IAAI,eAAe;GACtB,QAAQ,IAAI;GACZ,SAAS,WAAW;GACpB,WAAW,IAAI,aAAa;GAC5B,OAAO,WAAW;GAClB,UAAU;GACV;GACA,SAAS,IAAI;GACb,SAAS,IAAI;GACb,KAAK,IAAI;GACT,aAAa,WAAW;GACxB,cAAc,WAAW;GACzB,cAAc;GACf,CAAC;;CAGJ,iBAAiB,SAAuB;AACtC,OAAK,YAAY,IAAI,SAAS,YAAY,KAAK,CAAC;;CAGlD,eACE,SACA,UACM;EACN,MAAM,YAAY,KAAK,YAAY,IAAI,QAAQ,IAAI,YAAY,KAAK;AACpE,OAAK,YAAY,OAAO,QAAQ;EAChC,MAAM,YAAY,YAAY,KAAK,GAAG;EAEtC,MAAM,MAAM,KAAK,aAAa;EAC9B,MAAM,aAAa,iCAAiC,SAAoB;AACxE,OAAK,IAAI,eAAe;GACtB,QAAQ,IAAI;GACZ,OAAO,WAAW;GAClB,UAAU;GACV;GACA,WAAW,IAAI;GACf,SAAS,IAAI;GACb,SAAS,IAAI;GACb,KAAK,IAAI;GACT,aAAa,WAAW;GACxB,YAAY,WAAW;GACxB,CAAC;;CAGJ,YAAY,SAAuB;AACjC,OAAK,YAAY,IAAI,SAAS,YAAY,KAAK,CAAC;;CAGlD,UACE,SACA,UACM;EACN,MAAM,YAAY,KAAK,YAAY,IAAI,QAAQ,IAAI,YAAY,KAAK;AACpE,OAAK,YAAY,OAAO,QAAQ;EAChC,MAAM,YAAY,YAAY,KAAK,GAAG;EAEtC,MAAM,MAAM,KAAK,aAAa;EAC9B,MAAM,aAAa,4BAA4B,SAAoB;AACnE,OAAK,IAAI,cAAc;GACrB,QAAQ,IAAI;GACZ,UAAU,WAAW;GACrB;GACA,SAAS,WAAW;GACpB,WAAW,IAAI;GACf,SAAS,IAAI;GACb,SAAS,IAAI;GACb,KAAK,IAAI;GACT,QAAQ,WAAW;GACpB,CAAC;;;AAIN,SAAgB,iCACd,SAC4B;AAC5B,QAAO,IAAI,2BAA2B,QAAQ;;AAGhD,SAAS,2BAA2B,UAKlC;CACA,MAAM,OACJ,YAAY,QAAQ,OAAO,aAAa,WACnC,WACD,EAAE;CACR,MAAM,UAAU,KAAK;CACrB,MAAM,WACH,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,YAClD,OAAO,SAAS,YAAY,WAAY,QAAQ,UAAqB,OACtE;CACF,MAAM,QAAS,KAAK,SAAiD,EAAE;AACvE,QAAO;EACL;EACA,OAAO,OAAO,KAAK,SAAS,SAAS,SAAS,UAAU;EACxD,aAAa,UACX,KAAK,eAAe,MAAM,gBAAgB,MAAM,cACjD;EACD,cAAc,UACZ,KAAK,gBAAgB,MAAM,iBAAiB,MAAM,kBACnD;EACF;;AAGH,SAAS,iCAAiC,UAIxC;CACA,MAAM,OACJ,YAAY,QAAQ,OAAO,aAAa,WACnC,WACD,EAAE;CACR,MAAM,QAAS,KAAK,SAAiD,EAAE;AACvE,QAAO;EACL,OAAO,OAAO,KAAK,SAAS,UAAU;EACtC,aAAa,UAAU,KAAK,eAAe,MAAM,aAAa;EAC9D,YAAY,UACV,KAAK,cAAc,KAAK,cAAc,KAAK,qBAC5C;EACF;;AAGH,SAAS,4BAA4B,UAInC;CACA,MAAM,OACJ,YAAY,QAAQ,OAAO,aAAa,WACnC,WACD,EAAE;AACR,QAAO;EACL,UAAU,OAAO,KAAK,YAAY,KAAK,QAAQ,kBAAkB;EACjE,SAAS,QAAQ,KAAK,WAAW,KAAK,SAAS,KAAK;EACpD,QAAQ,KAAK,UAAU,KAAK;EAC7B;;AAGH,SAAS,UAAU,OAAoC;AACrD,QAAO,OAAO,UAAU,WAAW,QAAQ"}
1
+ {"version":3,"file":"llamaindex.js","names":["costUsd: number | undefined"],"sources":["../../src/integrations/llamaindex.ts"],"sourcesContent":["/**\n * LlamaIndex integration — AmplitudeLlamaIndexHandler.\n *\n * Tracks LLM calls, function calls, and embedding events\n * from LlamaIndex via its callback handler system.\n */\n\nimport type { AmplitudeAI } from '../client.js';\nimport { getActiveContext } from '../context.js';\nimport { calculateCost, inferProvider } from '../utils/costs.js';\n\nexport interface LlamaIndexHandlerOptions {\n amplitudeAI: AmplitudeAI;\n userId?: string;\n sessionId?: string;\n agentId?: string;\n env?: string;\n}\n\nexport class AmplitudeLlamaIndexHandler {\n private _ai: AmplitudeAI;\n private _userId: string | null;\n private _sessionId: string | null;\n private _agentId: string | null;\n private _env: string | null;\n private _startTimes: Map<string, number> = new Map();\n\n constructor(options: LlamaIndexHandlerOptions) {\n this._ai = options.amplitudeAI;\n this._userId = options.userId ?? null;\n this._sessionId = options.sessionId ?? null;\n this._agentId = options.agentId ?? null;\n this._env = options.env ?? null;\n }\n\n private _getContext() {\n const ctx = getActiveContext();\n return {\n userId: this._userId ?? ctx?.userId ?? 'unknown',\n sessionId: this._sessionId ?? ctx?.sessionId ?? undefined,\n agentId: this._agentId ?? ctx?.agentId ?? undefined,\n env: this._env ?? ctx?.env ?? undefined,\n traceId: ctx?.traceId ?? undefined,\n };\n }\n\n onLLMStart(eventId: string): void {\n this._startTimes.set(eventId, performance.now());\n }\n\n onLLMEnd(\n eventId: string,\n response: {\n content?: string;\n model?: string;\n inputTokens?: number;\n outputTokens?: number;\n },\n ): void {\n const startTime = this._startTimes.get(eventId) ?? performance.now();\n this._startTimes.delete(eventId);\n const latencyMs = performance.now() - startTime;\n\n const ctx = this._getContext();\n const normalized = _normalizeLlamaLlmResponse(response as unknown);\n\n let costUsd: number | undefined;\n if (\n normalized.model !== 'unknown' &&\n normalized.inputTokens != null &&\n normalized.outputTokens != null\n ) {\n try {\n const cost = calculateCost({\n modelName: normalized.model,\n inputTokens: normalized.inputTokens,\n outputTokens: normalized.outputTokens,\n cacheReadInputTokens: normalized.cacheReadTokens,\n cacheCreationInputTokens: normalized.cacheCreationTokens,\n defaultProvider: inferProvider(normalized.model),\n });\n if (cost > 0) costUsd = cost;\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._ai.trackAiMessage({\n userId: ctx.userId,\n content: normalized.content,\n sessionId: ctx.sessionId ?? 'llamaindex-session',\n model: normalized.model,\n provider: 'llamaindex',\n latencyMs,\n traceId: ctx.traceId,\n agentId: ctx.agentId,\n env: ctx.env,\n inputTokens: normalized.inputTokens,\n outputTokens: normalized.outputTokens,\n totalCostUsd: costUsd,\n });\n }\n\n onEmbeddingStart(eventId: string): void {\n this._startTimes.set(eventId, performance.now());\n }\n\n onEmbeddingEnd(\n eventId: string,\n response: { model?: string; inputTokens?: number; dimensions?: number },\n ): void {\n const startTime = this._startTimes.get(eventId) ?? performance.now();\n this._startTimes.delete(eventId);\n const latencyMs = performance.now() - startTime;\n\n const ctx = this._getContext();\n const normalized = _normalizeLlamaEmbeddingResponse(response as unknown);\n this._ai.trackEmbedding({\n userId: ctx.userId,\n model: normalized.model,\n provider: 'llamaindex',\n latencyMs,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n agentId: ctx.agentId,\n env: ctx.env,\n inputTokens: normalized.inputTokens,\n dimensions: normalized.dimensions,\n });\n }\n\n onToolStart(eventId: string): void {\n this._startTimes.set(eventId, performance.now());\n }\n\n onToolEnd(\n eventId: string,\n response: { toolName?: string; output?: unknown; success?: boolean },\n ): void {\n const startTime = this._startTimes.get(eventId) ?? performance.now();\n this._startTimes.delete(eventId);\n const latencyMs = performance.now() - startTime;\n\n const ctx = this._getContext();\n const normalized = _normalizeLlamaToolResponse(response as unknown);\n this._ai.trackToolCall({\n userId: ctx.userId,\n toolName: normalized.toolName,\n latencyMs,\n success: normalized.success,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n agentId: ctx.agentId,\n env: ctx.env,\n output: normalized.output,\n });\n }\n}\n\nexport function createAmplitudeLlamaIndexHandler(\n options: LlamaIndexHandlerOptions,\n): AmplitudeLlamaIndexHandler {\n return new AmplitudeLlamaIndexHandler(options);\n}\n\nfunction _normalizeLlamaLlmResponse(response: unknown): {\n content: string;\n model: string;\n inputTokens?: number;\n outputTokens?: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n} {\n const resp =\n response != null && typeof response === 'object'\n ? (response as Record<string, unknown>)\n : {};\n const message = resp.message as Record<string, unknown> | undefined;\n const content =\n (typeof resp.content === 'string' ? resp.content : undefined) ??\n (typeof message?.content === 'string' ? (message.content as string) : '') ??\n '';\n const usage = (resp.usage as Record<string, unknown> | undefined) ?? {};\n const outputTokens = _toNumber(\n resp.outputTokens ?? usage.output_tokens ?? usage.completion_tokens,\n );\n\n // OpenAI format: prompt_tokens is total (includes cached)\n const promptTokens = _toNumber(usage.prompt_tokens);\n if (promptTokens != null) {\n const details = usage.prompt_tokens_details as Record<string, unknown> | undefined;\n const cached = _toNumber(details?.cached_tokens) ?? 0;\n return {\n content,\n model: String(resp.model ?? message?.model ?? 'unknown'),\n inputTokens: _toNumber(resp.inputTokens) ?? promptTokens,\n outputTokens,\n cacheReadTokens: cached,\n cacheCreationTokens: 0,\n };\n }\n\n // Anthropic format: input_tokens is non-cached only; normalize to total\n const rawInput = _toNumber(usage.input_tokens);\n const cacheRead = _toNumber(usage.cache_read_input_tokens) ?? 0;\n const cacheCreation = _toNumber(usage.cache_creation_input_tokens) ?? 0;\n const hasCacheTokens = cacheRead > 0 || cacheCreation > 0;\n const totalInput = rawInput != null && hasCacheTokens\n ? rawInput + cacheRead + cacheCreation\n : _toNumber(resp.inputTokens) ?? rawInput;\n\n return {\n content,\n model: String(resp.model ?? message?.model ?? 'unknown'),\n inputTokens: totalInput,\n outputTokens,\n cacheReadTokens: cacheRead,\n cacheCreationTokens: cacheCreation,\n };\n}\n\nfunction _normalizeLlamaEmbeddingResponse(response: unknown): {\n model: string;\n inputTokens?: number;\n dimensions?: number;\n} {\n const resp =\n response != null && typeof response === 'object'\n ? (response as Record<string, unknown>)\n : {};\n const usage = (resp.usage as Record<string, unknown> | undefined) ?? {};\n return {\n model: String(resp.model ?? 'unknown'),\n inputTokens: _toNumber(resp.inputTokens ?? usage.input_tokens),\n dimensions: _toNumber(\n resp.dimensions ?? resp.vectorSize ?? resp.embedding_dimensions,\n ),\n };\n}\n\nfunction _normalizeLlamaToolResponse(response: unknown): {\n toolName: string;\n success: boolean;\n output?: unknown;\n} {\n const resp =\n response != null && typeof response === 'object'\n ? (response as Record<string, unknown>)\n : {};\n return {\n toolName: String(resp.toolName ?? resp.name ?? 'llamaindex-tool'),\n success: Boolean(resp.success ?? resp.error == null),\n output: resp.output ?? resp.result,\n };\n}\n\nfunction _toNumber(value: unknown): number | undefined {\n return typeof value === 'number' ? value : undefined;\n}\n"],"mappings":"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"}
@@ -107,12 +107,16 @@ var AmplitudeTracingProcessor = class {
107
107
  const inputTokens = this._safeNumber(usage.input_tokens ?? usage.prompt_tokens ?? null);
108
108
  const outputTokens = this._safeNumber(usage.output_tokens ?? usage.completion_tokens ?? null);
109
109
  const totalTokens = this._safeNumber(usage.total_tokens ?? null);
110
+ const promptDetails = usage.prompt_tokens_details;
111
+ const cachedTokens = this._safeNumber(promptDetails?.cached_tokens ?? promptDetails?.cachedTokens ?? null) ?? 0;
110
112
  let costUsd;
111
113
  if (model !== "unknown" && inputTokens != null && outputTokens != null) try {
112
114
  const cost = calculateCost({
113
115
  modelName: model,
114
116
  inputTokens,
115
- outputTokens
117
+ outputTokens,
118
+ cacheReadInputTokens: cachedTokens,
119
+ defaultProvider: inferProvider(model)
116
120
  });
117
121
  if (cost > 0) costUsd = cost;
118
122
  } catch {}
@@ -1 +1 @@
1
- {"version":3,"file":"openai-agents.js","names":["costUsd: number | undefined","result: Array<Record<string, unknown>>"],"sources":["../../src/integrations/openai-agents.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { AmplitudeAI } from '../client.js';\nimport { getActiveContext } from '../context.js';\nimport { calculateCost, inferProvider } from '../utils/costs.js';\n\nexport interface TracingProcessorOptions {\n amplitudeAI: AmplitudeAI;\n userId?: string;\n sessionId?: string;\n traceId?: string;\n agentId?: string;\n env?: string;\n}\n\nexport class AmplitudeTracingProcessor {\n private _ai: AmplitudeAI;\n private _defaults: {\n userId: string;\n sessionId: string;\n traceId: string;\n agentId: string | null;\n env: string | null;\n };\n private _turnId = 1;\n\n constructor(options: TracingProcessorOptions) {\n const ctx = getActiveContext();\n this._ai = options.amplitudeAI;\n this._defaults = {\n userId: options.userId ?? ctx?.userId ?? 'openai-agents-user',\n sessionId: options.sessionId ?? ctx?.sessionId ?? randomUUID(),\n traceId: options.traceId ?? ctx?.traceId ?? randomUUID(),\n agentId: options.agentId ?? ctx?.agentId ?? null,\n env: options.env ?? ctx?.env ?? null,\n };\n }\n\n onSpanStart(_span: Record<string, unknown>): void {\n // compatibility hook with tracing processors\n }\n\n onTraceStart(_trace: Record<string, unknown>): void {\n // compatibility hook with tracing processors\n }\n\n onTraceEnd(_trace: Record<string, unknown>): void {\n // compatibility hook with tracing processors\n }\n\n onSpanEnd(span: Record<string, unknown>): void {\n const spanData = this._getSpanData(span);\n if (spanData == null) return;\n\n const traceId = this._getTraceId(span);\n const latencyMs = this._getLatencyMs(span);\n const kind = this._inferKind(spanData);\n if (kind === 'generation') {\n this._handleGeneration(spanData, traceId, latencyMs);\n return;\n }\n if (kind === 'function') {\n this._handleFunction(spanData, traceId, latencyMs);\n return;\n }\n if (kind === 'handoff') {\n const fromAgent = String(spanData.from_agent ?? 'unknown');\n const toAgent = String(spanData.to_agent ?? 'unknown');\n this._ai.trackSpan({\n userId: this._defaults.userId,\n spanName: `handoff:${fromAgent}->${toAgent}`,\n traceId,\n latencyMs,\n sessionId: this._defaults.sessionId,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n inputState: { from_agent: fromAgent },\n outputState: { to_agent: toAgent },\n });\n return;\n }\n if (kind === 'guardrail') {\n const guardrail = String(spanData.name ?? 'guardrail');\n const triggered = Boolean(spanData.triggered);\n this._ai.trackSpan({\n userId: this._defaults.userId,\n spanName: `guardrail:${guardrail}`,\n traceId,\n latencyMs,\n sessionId: this._defaults.sessionId,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n outputState: { triggered },\n isError: triggered,\n });\n return;\n }\n\n const name = String(spanData.name ?? 'agent');\n this._ai.trackSpan({\n userId: this._defaults.userId,\n spanName: `agent:${name}`,\n traceId,\n latencyMs,\n sessionId: this._defaults.sessionId,\n agentId: this._defaults.agentId ?? name,\n env: this._defaults.env,\n outputState:\n spanData.output != null\n ? { output: String(spanData.output) }\n : undefined,\n });\n }\n\n shutdown(): void {\n this._ai.flush();\n }\n\n private _handleGeneration(\n data: Record<string, unknown>,\n traceId: string,\n latencyMs: number,\n ): void {\n const input = this._normalizeMessagesArray(data.input);\n for (const message of input) {\n const role = (message as Record<string, unknown>)?.role;\n const content = (message as Record<string, unknown>)?.content;\n if (\n role !== 'user' ||\n typeof content !== 'string' ||\n content.length === 0\n )\n continue;\n this._ai.trackUserMessage({\n userId: this._defaults.userId,\n content,\n sessionId: this._defaults.sessionId,\n traceId,\n turnId: this._turnId,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n });\n this._turnId += 1;\n }\n\n const output = this._normalizeMessagesArray(data.output);\n const responseText = this._extractAssistantText(output);\n const toolCalls = this._extractToolCalls(output);\n const model = String(data.model ?? 'unknown');\n const usage = (data.usage ?? {}) as Record<string, unknown>;\n const inputTokens = this._safeNumber(\n usage.input_tokens ?? usage.prompt_tokens ?? null,\n );\n const outputTokens = this._safeNumber(\n usage.output_tokens ?? usage.completion_tokens ?? null,\n );\n const totalTokens = this._safeNumber(usage.total_tokens ?? null);\n let costUsd: number | undefined;\n if (model !== 'unknown' && inputTokens != null && outputTokens != null) {\n try {\n const cost = calculateCost({\n modelName: model,\n inputTokens,\n outputTokens,\n });\n if (cost > 0) costUsd = cost;\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._ai.trackAiMessage({\n userId: this._defaults.userId,\n content: responseText,\n sessionId: this._defaults.sessionId,\n traceId,\n turnId: this._turnId,\n model,\n provider: inferProvider(model),\n latencyMs,\n inputTokens,\n outputTokens,\n totalTokens,\n totalCostUsd: costUsd,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n });\n this._turnId += 1;\n }\n\n private _handleFunction(\n data: Record<string, unknown>,\n traceId: string,\n latencyMs: number,\n ): void {\n const toolName = String(data.name ?? 'unknown');\n const errorMessage =\n data.error == null ? undefined : String(data.error ?? 'unknown error');\n const toolInput =\n data.input != null && typeof data.input === 'object'\n ? (data.input as Record<string, unknown>)\n : data.input != null\n ? { raw: String(data.input) }\n : undefined;\n const toolOutput =\n data.output == null ? undefined : String(data.output ?? undefined);\n\n this._ai.trackToolCall({\n userId: this._defaults.userId,\n toolName,\n success: errorMessage == null,\n latencyMs,\n sessionId: this._defaults.sessionId,\n traceId,\n turnId: this._turnId,\n input: toolInput,\n output: toolOutput,\n errorMessage,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n });\n }\n\n private _inferKind(\n data: Record<string, unknown>,\n ): 'generation' | 'function' | 'agent' | 'handoff' | 'guardrail' {\n if ('from_agent' in data || 'to_agent' in data) return 'handoff';\n if ('triggered' in data) return 'guardrail';\n if ('model' in data && ('input' in data || 'output' in data))\n return 'generation';\n if (\n 'name' in data &&\n ('input' in data || 'output' in data || 'error' in data)\n )\n return 'function';\n return 'agent';\n }\n\n private _getSpanData(\n span: Record<string, unknown>,\n ): Record<string, unknown> | null {\n const data = (span.span_data ?? span.data ?? span) as unknown;\n if (data == null || typeof data !== 'object') return null;\n return data as Record<string, unknown>;\n }\n\n private _getTraceId(span: Record<string, unknown>): string {\n const traceId = span.trace_id ?? span.traceId ?? this._defaults.traceId;\n return traceId == null ? randomUUID() : String(traceId);\n }\n\n private _getLatencyMs(span: Record<string, unknown>): number {\n if (typeof span.latency_ms === 'number') return span.latency_ms;\n if (typeof span.latencyMs === 'number') return span.latencyMs;\n const start = span.start_time_ms ?? span.startTimeMs;\n const end = span.end_time_ms ?? span.endTimeMs;\n if (typeof start === 'number' && typeof end === 'number' && end >= start)\n return end - start;\n return 0;\n }\n\n private _safeNumber(value: unknown): number | undefined {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n return undefined;\n }\n\n private _normalizeMessagesArray(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n if (value == null) return [];\n return [value];\n }\n\n private _extractAssistantText(output: unknown[]): string {\n let responseText = '';\n for (const block of output) {\n if (block == null || typeof block !== 'object') continue;\n const item = block as Record<string, unknown>;\n const isAssistantLike =\n item.role === 'assistant' ||\n item.type === 'message' ||\n item.type === 'output_text';\n if (!isAssistantLike) continue;\n if (typeof item.content === 'string') {\n responseText += item.content;\n continue;\n }\n if (Array.isArray(item.content)) {\n for (const part of item.content) {\n const p = part as Record<string, unknown>;\n if (p.type === 'text' && typeof p.text === 'string')\n responseText += p.text;\n if (typeof p.value === 'string') responseText += p.value;\n }\n }\n if (item.type === 'output_text' && typeof item.text === 'string') {\n responseText += item.text;\n }\n }\n return responseText;\n }\n\n private _extractToolCalls(output: unknown[]): Array<Record<string, unknown>> {\n const result: Array<Record<string, unknown>> = [];\n for (const block of output) {\n if (block == null || typeof block !== 'object') continue;\n const item = block as Record<string, unknown>;\n if (item.type === 'function_call') {\n result.push({\n type: 'function',\n id: String(item.id ?? ''),\n function: {\n name: String(item.name ?? ''),\n arguments: String(item.arguments ?? ''),\n },\n });\n continue;\n }\n if (!Array.isArray(item.tool_calls)) continue;\n for (const rawCall of item.tool_calls) {\n const call = rawCall as Record<string, unknown>;\n result.push({\n type: 'function',\n id: String(call.id ?? ''),\n function: {\n name: String(\n (call.function as Record<string, unknown> | undefined)?.name ??\n '',\n ),\n arguments: String(\n (call.function as Record<string, unknown> | undefined)\n ?.arguments ?? '',\n ),\n },\n });\n }\n }\n return result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"openai-agents.js","names":["costUsd: number | undefined","result: Array<Record<string, unknown>>"],"sources":["../../src/integrations/openai-agents.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport type { AmplitudeAI } from '../client.js';\nimport { getActiveContext } from '../context.js';\nimport { calculateCost, inferProvider } from '../utils/costs.js';\n\nexport interface TracingProcessorOptions {\n amplitudeAI: AmplitudeAI;\n userId?: string;\n sessionId?: string;\n traceId?: string;\n agentId?: string;\n env?: string;\n}\n\nexport class AmplitudeTracingProcessor {\n private _ai: AmplitudeAI;\n private _defaults: {\n userId: string;\n sessionId: string;\n traceId: string;\n agentId: string | null;\n env: string | null;\n };\n private _turnId = 1;\n\n constructor(options: TracingProcessorOptions) {\n const ctx = getActiveContext();\n this._ai = options.amplitudeAI;\n this._defaults = {\n userId: options.userId ?? ctx?.userId ?? 'openai-agents-user',\n sessionId: options.sessionId ?? ctx?.sessionId ?? randomUUID(),\n traceId: options.traceId ?? ctx?.traceId ?? randomUUID(),\n agentId: options.agentId ?? ctx?.agentId ?? null,\n env: options.env ?? ctx?.env ?? null,\n };\n }\n\n onSpanStart(_span: Record<string, unknown>): void {\n // compatibility hook with tracing processors\n }\n\n onTraceStart(_trace: Record<string, unknown>): void {\n // compatibility hook with tracing processors\n }\n\n onTraceEnd(_trace: Record<string, unknown>): void {\n // compatibility hook with tracing processors\n }\n\n onSpanEnd(span: Record<string, unknown>): void {\n const spanData = this._getSpanData(span);\n if (spanData == null) return;\n\n const traceId = this._getTraceId(span);\n const latencyMs = this._getLatencyMs(span);\n const kind = this._inferKind(spanData);\n if (kind === 'generation') {\n this._handleGeneration(spanData, traceId, latencyMs);\n return;\n }\n if (kind === 'function') {\n this._handleFunction(spanData, traceId, latencyMs);\n return;\n }\n if (kind === 'handoff') {\n const fromAgent = String(spanData.from_agent ?? 'unknown');\n const toAgent = String(spanData.to_agent ?? 'unknown');\n this._ai.trackSpan({\n userId: this._defaults.userId,\n spanName: `handoff:${fromAgent}->${toAgent}`,\n traceId,\n latencyMs,\n sessionId: this._defaults.sessionId,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n inputState: { from_agent: fromAgent },\n outputState: { to_agent: toAgent },\n });\n return;\n }\n if (kind === 'guardrail') {\n const guardrail = String(spanData.name ?? 'guardrail');\n const triggered = Boolean(spanData.triggered);\n this._ai.trackSpan({\n userId: this._defaults.userId,\n spanName: `guardrail:${guardrail}`,\n traceId,\n latencyMs,\n sessionId: this._defaults.sessionId,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n outputState: { triggered },\n isError: triggered,\n });\n return;\n }\n\n const name = String(spanData.name ?? 'agent');\n this._ai.trackSpan({\n userId: this._defaults.userId,\n spanName: `agent:${name}`,\n traceId,\n latencyMs,\n sessionId: this._defaults.sessionId,\n agentId: this._defaults.agentId ?? name,\n env: this._defaults.env,\n outputState:\n spanData.output != null\n ? { output: String(spanData.output) }\n : undefined,\n });\n }\n\n shutdown(): void {\n this._ai.flush();\n }\n\n private _handleGeneration(\n data: Record<string, unknown>,\n traceId: string,\n latencyMs: number,\n ): void {\n const input = this._normalizeMessagesArray(data.input);\n for (const message of input) {\n const role = (message as Record<string, unknown>)?.role;\n const content = (message as Record<string, unknown>)?.content;\n if (\n role !== 'user' ||\n typeof content !== 'string' ||\n content.length === 0\n )\n continue;\n this._ai.trackUserMessage({\n userId: this._defaults.userId,\n content,\n sessionId: this._defaults.sessionId,\n traceId,\n turnId: this._turnId,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n });\n this._turnId += 1;\n }\n\n const output = this._normalizeMessagesArray(data.output);\n const responseText = this._extractAssistantText(output);\n const toolCalls = this._extractToolCalls(output);\n const model = String(data.model ?? 'unknown');\n const usage = (data.usage ?? {}) as Record<string, unknown>;\n const inputTokens = this._safeNumber(\n usage.input_tokens ?? usage.prompt_tokens ?? null,\n );\n const outputTokens = this._safeNumber(\n usage.output_tokens ?? usage.completion_tokens ?? null,\n );\n const totalTokens = this._safeNumber(usage.total_tokens ?? null);\n const promptDetails = usage.prompt_tokens_details as Record<string, unknown> | undefined;\n const cachedTokens = this._safeNumber(\n promptDetails?.cached_tokens ?? promptDetails?.cachedTokens ?? null,\n ) ?? 0;\n let costUsd: number | undefined;\n if (model !== 'unknown' && inputTokens != null && outputTokens != null) {\n try {\n const cost = calculateCost({\n modelName: model,\n inputTokens,\n outputTokens,\n cacheReadInputTokens: cachedTokens,\n defaultProvider: inferProvider(model),\n });\n if (cost > 0) costUsd = cost;\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._ai.trackAiMessage({\n userId: this._defaults.userId,\n content: responseText,\n sessionId: this._defaults.sessionId,\n traceId,\n turnId: this._turnId,\n model,\n provider: inferProvider(model),\n latencyMs,\n inputTokens,\n outputTokens,\n totalTokens,\n totalCostUsd: costUsd,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n });\n this._turnId += 1;\n }\n\n private _handleFunction(\n data: Record<string, unknown>,\n traceId: string,\n latencyMs: number,\n ): void {\n const toolName = String(data.name ?? 'unknown');\n const errorMessage =\n data.error == null ? undefined : String(data.error ?? 'unknown error');\n const toolInput =\n data.input != null && typeof data.input === 'object'\n ? (data.input as Record<string, unknown>)\n : data.input != null\n ? { raw: String(data.input) }\n : undefined;\n const toolOutput =\n data.output == null ? undefined : String(data.output ?? undefined);\n\n this._ai.trackToolCall({\n userId: this._defaults.userId,\n toolName,\n success: errorMessage == null,\n latencyMs,\n sessionId: this._defaults.sessionId,\n traceId,\n turnId: this._turnId,\n input: toolInput,\n output: toolOutput,\n errorMessage,\n agentId: this._defaults.agentId,\n env: this._defaults.env,\n });\n }\n\n private _inferKind(\n data: Record<string, unknown>,\n ): 'generation' | 'function' | 'agent' | 'handoff' | 'guardrail' {\n if ('from_agent' in data || 'to_agent' in data) return 'handoff';\n if ('triggered' in data) return 'guardrail';\n if ('model' in data && ('input' in data || 'output' in data))\n return 'generation';\n if (\n 'name' in data &&\n ('input' in data || 'output' in data || 'error' in data)\n )\n return 'function';\n return 'agent';\n }\n\n private _getSpanData(\n span: Record<string, unknown>,\n ): Record<string, unknown> | null {\n const data = (span.span_data ?? span.data ?? span) as unknown;\n if (data == null || typeof data !== 'object') return null;\n return data as Record<string, unknown>;\n }\n\n private _getTraceId(span: Record<string, unknown>): string {\n const traceId = span.trace_id ?? span.traceId ?? this._defaults.traceId;\n return traceId == null ? randomUUID() : String(traceId);\n }\n\n private _getLatencyMs(span: Record<string, unknown>): number {\n if (typeof span.latency_ms === 'number') return span.latency_ms;\n if (typeof span.latencyMs === 'number') return span.latencyMs;\n const start = span.start_time_ms ?? span.startTimeMs;\n const end = span.end_time_ms ?? span.endTimeMs;\n if (typeof start === 'number' && typeof end === 'number' && end >= start)\n return end - start;\n return 0;\n }\n\n private _safeNumber(value: unknown): number | undefined {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n return undefined;\n }\n\n private _normalizeMessagesArray(value: unknown): unknown[] {\n if (Array.isArray(value)) return value;\n if (value == null) return [];\n return [value];\n }\n\n private _extractAssistantText(output: unknown[]): string {\n let responseText = '';\n for (const block of output) {\n if (block == null || typeof block !== 'object') continue;\n const item = block as Record<string, unknown>;\n const isAssistantLike =\n item.role === 'assistant' ||\n item.type === 'message' ||\n item.type === 'output_text';\n if (!isAssistantLike) continue;\n if (typeof item.content === 'string') {\n responseText += item.content;\n continue;\n }\n if (Array.isArray(item.content)) {\n for (const part of item.content) {\n const p = part as Record<string, unknown>;\n if (p.type === 'text' && typeof p.text === 'string')\n responseText += p.text;\n if (typeof p.value === 'string') responseText += p.value;\n }\n }\n if (item.type === 'output_text' && typeof item.text === 'string') {\n responseText += item.text;\n }\n }\n return responseText;\n }\n\n private _extractToolCalls(output: unknown[]): Array<Record<string, unknown>> {\n const result: Array<Record<string, unknown>> = [];\n for (const block of output) {\n if (block == null || typeof block !== 'object') continue;\n const item = block as Record<string, unknown>;\n if (item.type === 'function_call') {\n result.push({\n type: 'function',\n id: String(item.id ?? ''),\n function: {\n name: String(item.name ?? ''),\n arguments: String(item.arguments ?? ''),\n },\n });\n continue;\n }\n if (!Array.isArray(item.tool_calls)) continue;\n for (const rawCall of item.tool_calls) {\n const call = rawCall as Record<string, unknown>;\n result.push({\n type: 'function',\n id: String(call.id ?? ''),\n function: {\n name: String(\n (call.function as Record<string, unknown> | undefined)?.name ??\n '',\n ),\n arguments: String(\n (call.function as Record<string, unknown> | undefined)\n ?.arguments ?? '',\n ),\n },\n });\n }\n }\n return result;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"opentelemetry.d.ts","names":[],"sources":["../../src/integrations/opentelemetry.ts"],"sourcesContent":[],"mappings":";;;;AAkDc,UAvCG,eAAA,CAuCH;EAIE,WAAA,EA1CD,WA0CC;EAAO,aAAA,CAAA,EAAA,MAAA;AAgKvB;AACA,UAvMU,QAAA,CAuMG;;;eApME;;;;;;;;;;;;;cAQF,sBAAA;;;uBAIU;gBAMZ;;;cAaG;gBAIE;;;cAgKH,+BAAsB;cACtB,8BAAqB"}
1
+ {"version":3,"file":"opentelemetry.d.ts","names":[],"sources":["../../src/integrations/opentelemetry.ts"],"sourcesContent":[],"mappings":";;;;AAkDc,UAvCG,eAAA,CAuCH;EAIE,WAAA,EA1CD,WA0CC;EAAO,aAAA,CAAA,EAAA,MAAA;AAoKvB;AACA,UA3MU,QAAA,CA2MG;;;eAxME;;;;;;;;;;;;;cAQF,sBAAA;;;uBAIU;gBAMZ;;;cAaG;gBAIE;;;cAoKH,+BAAsB;cACtB,8BAAqB"}
@@ -85,7 +85,8 @@ var AmplitudeAgentExporter = class {
85
85
  inputTokens,
86
86
  outputTokens,
87
87
  cacheReadInputTokens: cacheReadTokens ?? 0,
88
- cacheCreationInputTokens: cacheCreationTokens ?? 0
88
+ cacheCreationInputTokens: cacheCreationTokens ?? 0,
89
+ defaultProvider: providerName && providerName !== "unknown" ? providerName : void 0
89
90
  });
90
91
  if (computed > 0) costUsd = computed;
91
92
  } catch {}
@@ -1 +1 @@
1
- {"version":3,"file":"opentelemetry.js","names":[],"sources":["../../src/integrations/opentelemetry.ts"],"sourcesContent":["/**\n * OpenTelemetry integration — maps GenAI OTEL spans to Amplitude events.\n *\n * Provides AmplitudeAgentExporter (SpanExporter) that converts\n * OpenTelemetry spans with GenAI semantic conventions into\n * Amplitude [Agent] events.\n */\n\nimport type { AmplitudeAI } from '../client.js';\nimport { calculateCost } from '../utils/costs.js';\n\nexport interface ExporterOptions {\n amplitudeAI: AmplitudeAI;\n defaultUserId?: string;\n}\n\ninterface OTELSpan {\n name: string;\n kind?: number;\n attributes?: Record<string, unknown>;\n startTimeUnixNano?: bigint | number;\n endTimeUnixNano?: bigint | number;\n status?: { code?: number; message?: string };\n parentSpanId?: string;\n spanContext?: () => { traceId: string; spanId: string };\n}\n\nexport class AmplitudeAgentExporter {\n private _ai: AmplitudeAI;\n private _defaultUserId: string;\n\n constructor(options: ExporterOptions) {\n this._ai = options.amplitudeAI;\n this._defaultUserId = options.defaultUserId ?? 'otel-user';\n }\n\n export(\n spans: OTELSpan[],\n resultCallback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n try {\n this._processSpan(span);\n } catch {\n // Skip individual span failures\n }\n }\n resultCallback({ code: 0 });\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n this._ai.flush();\n return Promise.resolve();\n }\n\n private _processSpan(span: OTELSpan): void {\n const attrs = span.attributes ?? {};\n const providerName =\n (attrs['gen_ai.provider.name'] as string | undefined) ??\n (attrs['gen_ai.system'] as string | undefined);\n\n if (!providerName) return; // Not a GenAI span\n\n const startNanos = Number(span.startTimeUnixNano ?? 0);\n const endNanos = Number(span.endTimeUnixNano ?? 0);\n const latencyMs = (endNanos - startNanos) / 1_000_000;\n const isError = span.status?.code === 2;\n const spanCtx = span.spanContext?.();\n const operation =\n (attrs['gen_ai.operation.name'] as string | undefined) ??\n (attrs['gen_ai.operation'] as string | undefined);\n const userId =\n _normalizeOtelString(attrs['amplitude.user_id']) ?? this._defaultUserId;\n const sessionId =\n _normalizeOtelString(attrs['amplitude.session_id']) ?? 'otel-session';\n\n // Prefer response.model (actual versioned model) over request.model\n const modelName = String(\n attrs['gen_ai.response.model'] ??\n attrs['gen_ai.request.model'] ??\n 'unknown',\n );\n\n if (\n operation === 'tool' ||\n operation === 'tool_call' ||\n operation === 'execute_tool'\n ) {\n this._ai.trackToolCall({\n userId,\n toolName: String(attrs['gen_ai.tool.name'] ?? span.name ?? 'tool'),\n latencyMs,\n success: !isError,\n sessionId,\n traceId: spanCtx?.traceId,\n input:\n attrs['gen_ai.tool.arguments'] ??\n attrs['gen_ai.request.prompt'] ??\n undefined,\n output: attrs['gen_ai.response.text'] ?? undefined,\n errorMessage: isError ? span.status?.message : undefined,\n });\n return;\n }\n\n if (operation === 'embedding' || operation === 'embeddings') {\n this._ai.trackEmbedding({\n userId,\n model: modelName,\n provider: providerName,\n latencyMs,\n sessionId,\n traceId: spanCtx?.traceId,\n inputTokens: attrs['gen_ai.usage.input_tokens'] as number | undefined,\n dimensions:\n (attrs['gen_ai.embedding.vector_size'] as number | undefined) ??\n (attrs['gen_ai.response.embedding_dimensions'] as\n | number\n | undefined) ??\n (attrs['gen_ai.embeddings.dimension.count'] as number | undefined),\n });\n return;\n }\n\n // Track user messages from gen_ai.input.messages when available\n const inputMessages = attrs['gen_ai.input.messages'];\n if (inputMessages != null) {\n const messages =\n typeof inputMessages === 'string'\n ? _tryParseJsonArray(inputMessages)\n : Array.isArray(inputMessages)\n ? inputMessages\n : [];\n for (const msg of messages) {\n const m = msg as Record<string, unknown> | undefined;\n if (\n m?.role === 'user' &&\n typeof m.content === 'string' &&\n m.content.length > 0\n ) {\n this._ai.trackUserMessage({\n userId,\n content: m.content,\n sessionId,\n traceId: spanCtx?.traceId,\n });\n }\n }\n }\n\n const inputTokens = _toOtelNumber(attrs['gen_ai.usage.input_tokens']);\n const outputTokens = _toOtelNumber(attrs['gen_ai.usage.output_tokens']);\n const totalTokens =\n inputTokens != null || outputTokens != null\n ? (inputTokens ?? 0) + (outputTokens ?? 0)\n : undefined;\n const cacheReadTokens = _toOtelNumber(\n attrs['gen_ai.usage.cache_read.input_tokens'],\n );\n const cacheCreationTokens = _toOtelNumber(\n attrs['gen_ai.usage.cache_creation.input_tokens'],\n );\n\n let costUsd = _toOtelNumber(attrs['gen_ai.usage.cost']);\n if (\n costUsd == null &&\n modelName !== 'unknown' &&\n inputTokens != null &&\n outputTokens != null\n ) {\n try {\n const computed = calculateCost({\n modelName,\n inputTokens,\n outputTokens,\n cacheReadInputTokens: cacheReadTokens ?? 0,\n cacheCreationInputTokens: cacheCreationTokens ?? 0,\n });\n if (computed > 0) costUsd = computed;\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._ai.trackAiMessage({\n userId,\n content: String(attrs['gen_ai.response.text'] ?? ''),\n sessionId,\n model: modelName,\n provider: providerName,\n latencyMs,\n traceId: spanCtx?.traceId,\n inputTokens,\n outputTokens,\n totalTokens,\n cacheReadTokens,\n cacheCreationTokens,\n totalCostUsd: costUsd,\n finishReason: _normalizeFinishReason(\n attrs['gen_ai.response.finish_reasons'],\n ),\n isError,\n errorMessage: isError ? span.status?.message : undefined,\n temperature: _toOtelNumber(attrs['gen_ai.request.temperature']),\n maxOutputTokens: _toOtelNumber(attrs['gen_ai.request.max_tokens']),\n topP: _toOtelNumber(attrs['gen_ai.request.top_p']),\n });\n }\n}\n\nexport const AmplitudeGenAIExporter = AmplitudeAgentExporter;\nexport const AmplitudeSpanExporter = AmplitudeAgentExporter;\n\nfunction _normalizeFinishReason(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (Array.isArray(value)) {\n const first = value[0];\n return typeof first === 'string' ? first : undefined;\n }\n return undefined;\n}\n\nfunction _toOtelNumber(value: unknown): number | undefined {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n return undefined;\n}\n\nfunction _tryParseJsonArray(value: string): unknown[] {\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nfunction _normalizeOtelString(value: unknown): string | undefined {\n if (typeof value === 'string' && value.length > 0) return value;\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n return undefined;\n}\n"],"mappings":";;;AA2BA,IAAa,yBAAb,MAAoC;CAClC,AAAQ;CACR,AAAQ;CAER,YAAY,SAA0B;AACpC,OAAK,MAAM,QAAQ;AACnB,OAAK,iBAAiB,QAAQ,iBAAiB;;CAGjD,OACE,OACA,gBACM;AACN,OAAK,MAAM,QAAQ,MACjB,KAAI;AACF,QAAK,aAAa,KAAK;UACjB;AAIV,iBAAe,EAAE,MAAM,GAAG,CAAC;;CAG7B,WAA0B;AACxB,SAAO,QAAQ,SAAS;;CAG1B,aAA4B;AAC1B,OAAK,IAAI,OAAO;AAChB,SAAO,QAAQ,SAAS;;CAG1B,AAAQ,aAAa,MAAsB;EACzC,MAAM,QAAQ,KAAK,cAAc,EAAE;EACnC,MAAM,eACH,MAAM,2BACN,MAAM;AAET,MAAI,CAAC,aAAc;EAEnB,MAAM,aAAa,OAAO,KAAK,qBAAqB,EAAE;EAEtD,MAAM,aADW,OAAO,KAAK,mBAAmB,EAAE,GACpB,cAAc;EAC5C,MAAM,UAAU,KAAK,QAAQ,SAAS;EACtC,MAAM,UAAU,KAAK,eAAe;EACpC,MAAM,YACH,MAAM,4BACN,MAAM;EACT,MAAM,SACJ,qBAAqB,MAAM,qBAAqB,IAAI,KAAK;EAC3D,MAAM,YACJ,qBAAqB,MAAM,wBAAwB,IAAI;EAGzD,MAAM,YAAY,OAChB,MAAM,4BACJ,MAAM,2BACN,UACH;AAED,MACE,cAAc,UACd,cAAc,eACd,cAAc,gBACd;AACA,QAAK,IAAI,cAAc;IACrB;IACA,UAAU,OAAO,MAAM,uBAAuB,KAAK,QAAQ,OAAO;IAClE;IACA,SAAS,CAAC;IACV;IACA,SAAS,SAAS;IAClB,OACE,MAAM,4BACN,MAAM,4BACN;IACF,QAAQ,MAAM,2BAA2B;IACzC,cAAc,UAAU,KAAK,QAAQ,UAAU;IAChD,CAAC;AACF;;AAGF,MAAI,cAAc,eAAe,cAAc,cAAc;AAC3D,QAAK,IAAI,eAAe;IACtB;IACA,OAAO;IACP,UAAU;IACV;IACA;IACA,SAAS,SAAS;IAClB,aAAa,MAAM;IACnB,YACG,MAAM,mCACN,MAAM,2CAGN,MAAM;IACV,CAAC;AACF;;EAIF,MAAM,gBAAgB,MAAM;AAC5B,MAAI,iBAAiB,MAAM;GACzB,MAAM,WACJ,OAAO,kBAAkB,WACrB,mBAAmB,cAAc,GACjC,MAAM,QAAQ,cAAc,GAC1B,gBACA,EAAE;AACV,QAAK,MAAM,OAAO,UAAU;IAC1B,MAAM,IAAI;AACV,QACE,GAAG,SAAS,UACZ,OAAO,EAAE,YAAY,YACrB,EAAE,QAAQ,SAAS,EAEnB,MAAK,IAAI,iBAAiB;KACxB;KACA,SAAS,EAAE;KACX;KACA,SAAS,SAAS;KACnB,CAAC;;;EAKR,MAAM,cAAc,cAAc,MAAM,6BAA6B;EACrE,MAAM,eAAe,cAAc,MAAM,8BAA8B;EACvE,MAAM,cACJ,eAAe,QAAQ,gBAAgB,QAClC,eAAe,MAAM,gBAAgB,KACtC;EACN,MAAM,kBAAkB,cACtB,MAAM,wCACP;EACD,MAAM,sBAAsB,cAC1B,MAAM,4CACP;EAED,IAAI,UAAU,cAAc,MAAM,qBAAqB;AACvD,MACE,WAAW,QACX,cAAc,aACd,eAAe,QACf,gBAAgB,KAEhB,KAAI;GACF,MAAM,WAAW,cAAc;IAC7B;IACA;IACA;IACA,sBAAsB,mBAAmB;IACzC,0BAA0B,uBAAuB;IAClD,CAAC;AACF,OAAI,WAAW,EAAG,WAAU;UACtB;AAKV,OAAK,IAAI,eAAe;GACtB;GACA,SAAS,OAAO,MAAM,2BAA2B,GAAG;GACpD;GACA,OAAO;GACP,UAAU;GACV;GACA,SAAS,SAAS;GAClB;GACA;GACA;GACA;GACA;GACA,cAAc;GACd,cAAc,uBACZ,MAAM,kCACP;GACD;GACA,cAAc,UAAU,KAAK,QAAQ,UAAU;GAC/C,aAAa,cAAc,MAAM,8BAA8B;GAC/D,iBAAiB,cAAc,MAAM,6BAA6B;GAClE,MAAM,cAAc,MAAM,wBAAwB;GACnD,CAAC;;;AAIN,MAAa,yBAAyB;AACtC,MAAa,wBAAwB;AAErC,SAAS,uBAAuB,OAAoC;AAClE,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,MAAM,QAAQ,MAAM,EAAE;EACxB,MAAM,QAAQ,MAAM;AACpB,SAAO,OAAO,UAAU,WAAW,QAAQ;;;AAK/C,SAAS,cAAc,OAAoC;AACzD,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,SAAS,OAAO,MAAM;AAC5B,SAAO,OAAO,MAAM,OAAO,GAAG,SAAY;;;AAK9C,SAAS,mBAAmB,OAA0B;AACpD,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,MAAM;AAChC,SAAO,MAAM,QAAQ,OAAO,GAAG,SAAS,EAAE;SACpC;AACN,SAAO,EAAE;;;AAIb,SAAS,qBAAqB,OAAoC;AAChE,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;AAC1D,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,OAAO,MAAM"}
1
+ {"version":3,"file":"opentelemetry.js","names":[],"sources":["../../src/integrations/opentelemetry.ts"],"sourcesContent":["/**\n * OpenTelemetry integration — maps GenAI OTEL spans to Amplitude events.\n *\n * Provides AmplitudeAgentExporter (SpanExporter) that converts\n * OpenTelemetry spans with GenAI semantic conventions into\n * Amplitude [Agent] events.\n */\n\nimport type { AmplitudeAI } from '../client.js';\nimport { calculateCost } from '../utils/costs.js';\n\nexport interface ExporterOptions {\n amplitudeAI: AmplitudeAI;\n defaultUserId?: string;\n}\n\ninterface OTELSpan {\n name: string;\n kind?: number;\n attributes?: Record<string, unknown>;\n startTimeUnixNano?: bigint | number;\n endTimeUnixNano?: bigint | number;\n status?: { code?: number; message?: string };\n parentSpanId?: string;\n spanContext?: () => { traceId: string; spanId: string };\n}\n\nexport class AmplitudeAgentExporter {\n private _ai: AmplitudeAI;\n private _defaultUserId: string;\n\n constructor(options: ExporterOptions) {\n this._ai = options.amplitudeAI;\n this._defaultUserId = options.defaultUserId ?? 'otel-user';\n }\n\n export(\n spans: OTELSpan[],\n resultCallback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n try {\n this._processSpan(span);\n } catch {\n // Skip individual span failures\n }\n }\n resultCallback({ code: 0 });\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n this._ai.flush();\n return Promise.resolve();\n }\n\n private _processSpan(span: OTELSpan): void {\n const attrs = span.attributes ?? {};\n const providerName =\n (attrs['gen_ai.provider.name'] as string | undefined) ??\n (attrs['gen_ai.system'] as string | undefined);\n\n if (!providerName) return; // Not a GenAI span\n\n const startNanos = Number(span.startTimeUnixNano ?? 0);\n const endNanos = Number(span.endTimeUnixNano ?? 0);\n const latencyMs = (endNanos - startNanos) / 1_000_000;\n const isError = span.status?.code === 2;\n const spanCtx = span.spanContext?.();\n const operation =\n (attrs['gen_ai.operation.name'] as string | undefined) ??\n (attrs['gen_ai.operation'] as string | undefined);\n const userId =\n _normalizeOtelString(attrs['amplitude.user_id']) ?? this._defaultUserId;\n const sessionId =\n _normalizeOtelString(attrs['amplitude.session_id']) ?? 'otel-session';\n\n // Prefer response.model (actual versioned model) over request.model\n const modelName = String(\n attrs['gen_ai.response.model'] ??\n attrs['gen_ai.request.model'] ??\n 'unknown',\n );\n\n if (\n operation === 'tool' ||\n operation === 'tool_call' ||\n operation === 'execute_tool'\n ) {\n this._ai.trackToolCall({\n userId,\n toolName: String(attrs['gen_ai.tool.name'] ?? span.name ?? 'tool'),\n latencyMs,\n success: !isError,\n sessionId,\n traceId: spanCtx?.traceId,\n input:\n attrs['gen_ai.tool.arguments'] ??\n attrs['gen_ai.request.prompt'] ??\n undefined,\n output: attrs['gen_ai.response.text'] ?? undefined,\n errorMessage: isError ? span.status?.message : undefined,\n });\n return;\n }\n\n if (operation === 'embedding' || operation === 'embeddings') {\n this._ai.trackEmbedding({\n userId,\n model: modelName,\n provider: providerName,\n latencyMs,\n sessionId,\n traceId: spanCtx?.traceId,\n inputTokens: attrs['gen_ai.usage.input_tokens'] as number | undefined,\n dimensions:\n (attrs['gen_ai.embedding.vector_size'] as number | undefined) ??\n (attrs['gen_ai.response.embedding_dimensions'] as\n | number\n | undefined) ??\n (attrs['gen_ai.embeddings.dimension.count'] as number | undefined),\n });\n return;\n }\n\n // Track user messages from gen_ai.input.messages when available\n const inputMessages = attrs['gen_ai.input.messages'];\n if (inputMessages != null) {\n const messages =\n typeof inputMessages === 'string'\n ? _tryParseJsonArray(inputMessages)\n : Array.isArray(inputMessages)\n ? inputMessages\n : [];\n for (const msg of messages) {\n const m = msg as Record<string, unknown> | undefined;\n if (\n m?.role === 'user' &&\n typeof m.content === 'string' &&\n m.content.length > 0\n ) {\n this._ai.trackUserMessage({\n userId,\n content: m.content,\n sessionId,\n traceId: spanCtx?.traceId,\n });\n }\n }\n }\n\n const inputTokens = _toOtelNumber(attrs['gen_ai.usage.input_tokens']);\n const outputTokens = _toOtelNumber(attrs['gen_ai.usage.output_tokens']);\n const totalTokens =\n inputTokens != null || outputTokens != null\n ? (inputTokens ?? 0) + (outputTokens ?? 0)\n : undefined;\n const cacheReadTokens = _toOtelNumber(\n attrs['gen_ai.usage.cache_read.input_tokens'],\n );\n const cacheCreationTokens = _toOtelNumber(\n attrs['gen_ai.usage.cache_creation.input_tokens'],\n );\n\n let costUsd = _toOtelNumber(attrs['gen_ai.usage.cost']);\n if (\n costUsd == null &&\n modelName !== 'unknown' &&\n inputTokens != null &&\n outputTokens != null\n ) {\n try {\n const computed = calculateCost({\n modelName,\n inputTokens,\n outputTokens,\n cacheReadInputTokens: cacheReadTokens ?? 0,\n cacheCreationInputTokens: cacheCreationTokens ?? 0,\n defaultProvider:\n providerName && providerName !== 'unknown'\n ? providerName\n : undefined,\n });\n if (computed > 0) costUsd = computed;\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._ai.trackAiMessage({\n userId,\n content: String(attrs['gen_ai.response.text'] ?? ''),\n sessionId,\n model: modelName,\n provider: providerName,\n latencyMs,\n traceId: spanCtx?.traceId,\n inputTokens,\n outputTokens,\n totalTokens,\n cacheReadTokens,\n cacheCreationTokens,\n totalCostUsd: costUsd,\n finishReason: _normalizeFinishReason(\n attrs['gen_ai.response.finish_reasons'],\n ),\n isError,\n errorMessage: isError ? span.status?.message : undefined,\n temperature: _toOtelNumber(attrs['gen_ai.request.temperature']),\n maxOutputTokens: _toOtelNumber(attrs['gen_ai.request.max_tokens']),\n topP: _toOtelNumber(attrs['gen_ai.request.top_p']),\n });\n }\n}\n\nexport const AmplitudeGenAIExporter = AmplitudeAgentExporter;\nexport const AmplitudeSpanExporter = AmplitudeAgentExporter;\n\nfunction _normalizeFinishReason(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (Array.isArray(value)) {\n const first = value[0];\n return typeof first === 'string' ? first : undefined;\n }\n return undefined;\n}\n\nfunction _toOtelNumber(value: unknown): number | undefined {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n return undefined;\n}\n\nfunction _tryParseJsonArray(value: string): unknown[] {\n try {\n const parsed = JSON.parse(value);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nfunction _normalizeOtelString(value: unknown): string | undefined {\n if (typeof value === 'string' && value.length > 0) return value;\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n return undefined;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.d.ts","names":[],"sources":["../../src/mcp/patterns.ts"],"sourcesContent":[],"mappings":";KAAK,kBAAA;EAAA,EAAA,EAAA,MAAA;EAgDC,KAAA,EAAA,MAAA;;;;cAAA,8BAA6B"}
1
+ {"version":3,"file":"patterns.d.ts","names":[],"sources":["../../src/mcp/patterns.ts"],"sourcesContent":[],"mappings":";KAAK,kBAAA;EAAA,EAAA,EAAA,MAAA;EAwDC,KAAA,EAAA,MAAA;;;;cAAA,8BAA6B"}
@@ -29,6 +29,12 @@ const INTEGRATION_PATTERNS = [
29
29
  title: "Express middleware propagation",
30
30
  whenToUse: "Server app needs per-request context propagation.",
31
31
  snippet: "import { createAmplitudeAIMiddleware } from \"@amplitude/ai\";\napp.use(createAmplitudeAIMiddleware());"
32
+ },
33
+ {
34
+ id: "multi-agent-runas",
35
+ title: "Multi-agent orchestration with session.runAs()",
36
+ whenToUse: "Parent agent delegates sub-tasks to child agents. Provider wrappers automatically pick up the child agent identity.",
37
+ snippet: "const orchestrator = ai.agent(\"orchestrator\", { userId: \"u1\" });\nconst researcher = orchestrator.child(\"researcher\");\nconst session = orchestrator.session({ sessionId: \"s1\" });\nawait session.run(async (s) => {\n const result = await s.runAs(researcher, async (cs) => {\n return openai.chat.completions.create({ model: \"gpt-4o\", messages: [...] });\n });\n});"
32
38
  }
33
39
  ];
34
40
  const getIntegrationPatterns = () => INTEGRATION_PATTERNS.map((pattern) => ({ ...pattern }));
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.js","names":["INTEGRATION_PATTERNS: IntegrationPattern[]"],"sources":["../../src/mcp/patterns.ts"],"sourcesContent":["type IntegrationPattern = {\n id: string;\n title: string;\n whenToUse: string;\n snippet: string;\n};\n\nconst INTEGRATION_PATTERNS: IntegrationPattern[] = [\n {\n id: 'wrap-openai',\n title: 'Recommended production default: wrap provider + session context',\n whenToUse:\n 'Best balance of effort and value: high-fidelity provider telemetry plus session lineage.',\n snippet:\n 'import { AmplitudeAI, OpenAI } from \"@amplitude/ai\";\\nconst ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY!, contentMode: \"full\", redactPii: true });\\nconst client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY!, amplitude: ai });\\nconst session = ai.agent(\"assistant\", { userId: \"u1\" }).session({ sessionId: \"s1\" });\\nawait session.run(async () => client.chat.completions.create({ model: \"gpt-4o\", messages: [{ role: \"user\", content: \"hi\" }] }));',\n },\n {\n id: 'bound-agent-session',\n title: 'Bound agent + session lifecycle',\n whenToUse:\n 'Need strong user/session lineage, multi-agent orchestration, and session-level analytics.',\n snippet:\n 'const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });\\nconst agent = ai.agent(\"assistant\", { userId: \"u1\" });\\nconst session = agent.session({ sessionId: \"s1\" });',\n },\n {\n id: 'zero-code',\n title: 'Zero-code patching (migration fallback)',\n whenToUse:\n 'Existing codebase; want immediate coverage before upgrading to wrapper + session context.',\n snippet:\n 'import { AmplitudeAI, patch } from \"@amplitude/ai\";\\nconst ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });\\npatch({ amplitudeAI: ai });',\n },\n {\n id: 'tool-decorator',\n title: 'Tool instrumentation',\n whenToUse: 'Need explicit visibility into tool calls and outcomes.',\n snippet:\n 'import { tool } from \"@amplitude/ai\";\\nconst searchProducts = tool(async (query: string) => ({ items: [] }), {\\n name: \"search_products\",\\n inputSchema: { type: \"object\", properties: { query: { type: \"string\" } }, required: [\"query\"] },\\n});',\n },\n {\n id: 'express-middleware',\n title: 'Express middleware propagation',\n whenToUse: 'Server app needs per-request context propagation.',\n snippet:\n 'import { createAmplitudeAIMiddleware } from \"@amplitude/ai\";\\napp.use(createAmplitudeAIMiddleware());',\n },\n];\n\nconst getIntegrationPatterns = (): IntegrationPattern[] =>\n INTEGRATION_PATTERNS.map((pattern) => ({ ...pattern }));\n\nexport { getIntegrationPatterns, type IntegrationPattern };\n"],"mappings":";AAOA,MAAMA,uBAA6C;CACjD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WAAW;EACX,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WAAW;EACX,SACE;EACH;CACF;AAED,MAAM,+BACJ,qBAAqB,KAAK,aAAa,EAAE,GAAG,SAAS,EAAE"}
1
+ {"version":3,"file":"patterns.js","names":["INTEGRATION_PATTERNS: IntegrationPattern[]"],"sources":["../../src/mcp/patterns.ts"],"sourcesContent":["type IntegrationPattern = {\n id: string;\n title: string;\n whenToUse: string;\n snippet: string;\n};\n\nconst INTEGRATION_PATTERNS: IntegrationPattern[] = [\n {\n id: 'wrap-openai',\n title: 'Recommended production default: wrap provider + session context',\n whenToUse:\n 'Best balance of effort and value: high-fidelity provider telemetry plus session lineage.',\n snippet:\n 'import { AmplitudeAI, OpenAI } from \"@amplitude/ai\";\\nconst ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY!, contentMode: \"full\", redactPii: true });\\nconst client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY!, amplitude: ai });\\nconst session = ai.agent(\"assistant\", { userId: \"u1\" }).session({ sessionId: \"s1\" });\\nawait session.run(async () => client.chat.completions.create({ model: \"gpt-4o\", messages: [{ role: \"user\", content: \"hi\" }] }));',\n },\n {\n id: 'bound-agent-session',\n title: 'Bound agent + session lifecycle',\n whenToUse:\n 'Need strong user/session lineage, multi-agent orchestration, and session-level analytics.',\n snippet:\n 'const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });\\nconst agent = ai.agent(\"assistant\", { userId: \"u1\" });\\nconst session = agent.session({ sessionId: \"s1\" });',\n },\n {\n id: 'zero-code',\n title: 'Zero-code patching (migration fallback)',\n whenToUse:\n 'Existing codebase; want immediate coverage before upgrading to wrapper + session context.',\n snippet:\n 'import { AmplitudeAI, patch } from \"@amplitude/ai\";\\nconst ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });\\npatch({ amplitudeAI: ai });',\n },\n {\n id: 'tool-decorator',\n title: 'Tool instrumentation',\n whenToUse: 'Need explicit visibility into tool calls and outcomes.',\n snippet:\n 'import { tool } from \"@amplitude/ai\";\\nconst searchProducts = tool(async (query: string) => ({ items: [] }), {\\n name: \"search_products\",\\n inputSchema: { type: \"object\", properties: { query: { type: \"string\" } }, required: [\"query\"] },\\n});',\n },\n {\n id: 'express-middleware',\n title: 'Express middleware propagation',\n whenToUse: 'Server app needs per-request context propagation.',\n snippet:\n 'import { createAmplitudeAIMiddleware } from \"@amplitude/ai\";\\napp.use(createAmplitudeAIMiddleware());',\n },\n {\n id: 'multi-agent-runas',\n title: 'Multi-agent orchestration with session.runAs()',\n whenToUse:\n 'Parent agent delegates sub-tasks to child agents. Provider wrappers automatically pick up the child agent identity.',\n snippet:\n 'const orchestrator = ai.agent(\"orchestrator\", { userId: \"u1\" });\\nconst researcher = orchestrator.child(\"researcher\");\\nconst session = orchestrator.session({ sessionId: \"s1\" });\\nawait session.run(async (s) => {\\n const result = await s.runAs(researcher, async (cs) => {\\n return openai.chat.completions.create({ model: \"gpt-4o\", messages: [...] });\\n });\\n});',\n },\n];\n\nconst getIntegrationPatterns = (): IntegrationPattern[] =>\n INTEGRATION_PATTERNS.map((pattern) => ({ ...pattern }));\n\nexport { getIntegrationPatterns, type IntegrationPattern };\n"],"mappings":";AAOA,MAAMA,uBAA6C;CACjD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WAAW;EACX,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WAAW;EACX,SACE;EACH;CACD;EACE,IAAI;EACJ,OAAO;EACP,WACE;EACF,SACE;EACH;CACF;AAED,MAAM,+BACJ,qBAAqB,KAAK,aAAa,EAAE,GAAG,SAAS,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/mcp/server.ts"],"sourcesContent":[],"mappings":";;;KAqCK,WAAA;cAaC,iFAGS;AAlDqD,cA2H9D,YAzFU,EAAA,GAAA,GAyFS,SAzFT;AAAA,cAqaV,YAxZA,EAAA,GAAA,GAwZyB,OAhW9B,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/mcp/server.ts"],"sourcesContent":[],"mappings":";;;KAqCK,WAAA;cAaC,iFAGS;AAlDqD,cA2H9D,YAzFU,EAAA,GAAA,GAyFS,SAzFT;AAAA,cAqaV,YAxZA,EAAA,GAAA,GAwZyB,OAhW9B,CAAA,IAAA,CArDc"}
@@ -64,7 +64,7 @@ function findWrappedConstructors(source) {
64
64
  }
65
65
  function analyzeFileInstrumentation(source) {
66
66
  const hasPatch = /\bpatch\s*\(\s*\{/.test(source);
67
- const hasSessionContext = /\.session\s*\(/.test(source) || /\bsession\.run\s*\(/.test(source) || /\bcreateAmplitudeAIMiddleware\s*\(/.test(source);
67
+ const hasSessionContext = /\.session\s*\(/.test(source) || /\bsession\.run\s*\(/.test(source) || /\.runAs\s*\(/.test(source) || /\bcreateAmplitudeAIMiddleware\s*\(/.test(source);
68
68
  const hasAmplitudeImport = /from\s+['"]@amplitude\/ai['"]/.test(source) || /require\s*\(\s*['"]@amplitude\/ai['"]\s*\)/.test(source) || /\bAmplitudeAI\b/.test(source);
69
69
  const wrappedClients = findWrappedConstructors(source);
70
70
  if (hasAmplitudeImport) for (const m of source.matchAll(/(?:const|let|var)\s+(\w+)\s*=\s*(?:\w+\.)*wrap\s*\(/g)) {
@@ -1 +1 @@
1
- {"version":3,"file":"validate-file.js","names":["callSites: CallSite[]","suggestions: string[]"],"sources":["../../src/mcp/validate-file.ts"],"sourcesContent":["export interface CallSite {\n line: number;\n provider: string;\n api: string;\n instrumented: boolean;\n}\n\nexport interface FileAnalysis {\n total_call_sites: number;\n instrumented: number;\n uninstrumented: number;\n has_amplitude_import: boolean;\n has_session_context: boolean;\n call_sites: CallSite[];\n suggestions: string[];\n}\n\nconst llmPatterns = [\n {\n pattern: /\\.chat\\.completions\\.create\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.chat\\.completions\\.create\\s*\\(/,\n provider: 'openai',\n api: 'chat.completions.create',\n },\n {\n pattern: /\\.chat\\.completions\\.parse\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.chat\\.completions\\.parse\\s*\\(/,\n provider: 'openai',\n api: 'chat.completions.parse',\n },\n {\n pattern: /\\.responses\\.create\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.responses\\.create\\s*\\(/,\n provider: 'openai',\n api: 'responses.create',\n },\n {\n pattern: /\\.messages\\.create\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.messages\\.create\\s*\\(/,\n provider: 'anthropic',\n api: 'messages.create',\n },\n {\n pattern: /\\.generateContent\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.generateContent\\s*\\(/,\n provider: 'gemini',\n api: 'generateContent',\n },\n {\n pattern: /\\.send\\s*\\(\\s*new\\s+InvokeModelCommand\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.send\\s*\\(/,\n provider: 'bedrock',\n api: 'invokeModel',\n },\n {\n pattern: /\\.send\\s*\\(\\s*new\\s+ConverseCommand\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.send\\s*\\(/,\n provider: 'bedrock',\n api: 'converse',\n },\n];\n\nfunction findWrappedConstructors(source: string): Set<string> {\n const result = new Set<string>();\n const declRe =\n /(?:const|let|var)\\s+(\\w+)\\s*=\\s*new\\s+(?:OpenAI|Anthropic|Gemini|AzureOpenAI|Bedrock|Mistral)\\s*\\(/g;\n for (const m of source.matchAll(declRe)) {\n const varName = m[1] ?? '';\n if (!varName) continue;\n let depth = 1;\n let i = (m.index ?? 0) + m[0].length;\n let argBlock = '';\n while (i < source.length && depth > 0) {\n const ch = source[i];\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n if (depth > 0) argBlock += ch;\n i++;\n }\n if (/\\bamplitude\\s*:/.test(argBlock)) {\n result.add(varName);\n }\n }\n return result;\n}\n\nexport function analyzeFileInstrumentation(source: string): FileAnalysis {\n const hasPatch = /\\bpatch\\s*\\(\\s*\\{/.test(source);\n const hasSessionContext =\n /\\.session\\s*\\(/.test(source) ||\n /\\bsession\\.run\\s*\\(/.test(source) ||\n /\\bcreateAmplitudeAIMiddleware\\s*\\(/.test(source);\n\n const hasAmplitudeImport =\n /from\\s+['\"]@amplitude\\/ai['\"]/.test(source) ||\n /require\\s*\\(\\s*['\"]@amplitude\\/ai['\"]\\s*\\)/.test(source) ||\n /\\bAmplitudeAI\\b/.test(source);\n\n const wrappedClients = findWrappedConstructors(source);\n if (hasAmplitudeImport) {\n const wrapRe = /(?:const|let|var)\\s+(\\w+)\\s*=\\s*(?:\\w+\\.)*wrap\\s*\\(/g;\n for (const m of source.matchAll(wrapRe)) {\n const name = m[1] ?? '';\n if (name) wrappedClients.add(name);\n }\n }\n\n const callSites: CallSite[] = [];\n const lines = source.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n for (const { pattern, receiverRe, provider, api } of llmPatterns) {\n pattern.lastIndex = 0;\n if (pattern.test(line)) {\n const rm = line.match(receiverRe);\n const receiver = rm?.[1] ?? '';\n const instrumented = hasPatch || wrappedClients.has(receiver);\n callSites.push({ line: i + 1, provider, api, instrumented });\n }\n }\n }\n\n const uninstrumentedSites = callSites.filter((s) => !s.instrumented);\n let suggestions: string[];\n if (uninstrumentedSites.length > 0) {\n suggestions = [\n 'Now: instrument provider calls with wrapper/swap import (for example, `new OpenAI({ amplitude: ai, apiKey })`).',\n 'Next: add session lineage with `const session = ai.agent(...).session(...)` and wrap calls in `session.run(...)`.',\n 'Why: session context unlocks session-level scoring, enrichments, and reliable product-to-AI funnels.',\n 'Content tiers: choose `contentMode` (`full`, `metadata_only`, or `customer_enriched`) and prefer `redactPii: true` when using `full`.',\n 'Fallback: use `patch({ amplitudeAI: ai })` for migration speed, then graduate to wrapper + session context.',\n ];\n } else if (callSites.length > 0 && !hasSessionContext) {\n suggestions = [\n 'Tracking is present, but session context is missing.',\n 'Now: add `const session = ai.agent(...).session(...)` and execute LLM calls inside `session.run(...)` (or use middleware).',\n 'Why: without session lineage you lose high-value outcomes like session enrichments, scoring, and dependable session funnels.',\n 'Content tiers: set `contentMode` intentionally and keep `redactPii: true` when using `full`.',\n ];\n } else if (callSites.length > 0) {\n suggestions = [\n 'File appears instrumented with session lineage.',\n 'For privacy-by-default, set `contentMode` intentionally and enable `redactPii: true` when using `full`.',\n ];\n } else {\n suggestions = [\n 'No supported LLM call sites detected in this file.',\n 'If this file should emit AI telemetry, add wrapped provider calls and session context.',\n ];\n }\n\n return {\n total_call_sites: callSites.length,\n instrumented: callSites.length - uninstrumentedSites.length,\n uninstrumented: uninstrumentedSites.length,\n has_amplitude_import: hasAmplitudeImport,\n has_session_context: hasSessionContext,\n call_sites: callSites,\n suggestions,\n };\n}\n"],"mappings":";AAiBA,MAAM,cAAc;CAClB;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACF;AAED,SAAS,wBAAwB,QAA6B;CAC5D,MAAM,yBAAS,IAAI,KAAa;AAGhC,MAAK,MAAM,KAAK,OAAO,SADrB,sGACqC,EAAE;EACvC,MAAM,UAAU,EAAE,MAAM;AACxB,MAAI,CAAC,QAAS;EACd,IAAI,QAAQ;EACZ,IAAI,KAAK,EAAE,SAAS,KAAK,EAAE,GAAG;EAC9B,IAAI,WAAW;AACf,SAAO,IAAI,OAAO,UAAU,QAAQ,GAAG;GACrC,MAAM,KAAK,OAAO;AAClB,OAAI,OAAO,IAAK;YACP,OAAO,IAAK;AACrB,OAAI,QAAQ,EAAG,aAAY;AAC3B;;AAEF,MAAI,kBAAkB,KAAK,SAAS,CAClC,QAAO,IAAI,QAAQ;;AAGvB,QAAO;;AAGT,SAAgB,2BAA2B,QAA8B;CACvE,MAAM,WAAW,oBAAoB,KAAK,OAAO;CACjD,MAAM,oBACJ,iBAAiB,KAAK,OAAO,IAC7B,sBAAsB,KAAK,OAAO,IAClC,qCAAqC,KAAK,OAAO;CAEnD,MAAM,qBACJ,gCAAgC,KAAK,OAAO,IAC5C,6CAA6C,KAAK,OAAO,IACzD,kBAAkB,KAAK,OAAO;CAEhC,MAAM,iBAAiB,wBAAwB,OAAO;AACtD,KAAI,mBAEF,MAAK,MAAM,KAAK,OAAO,SADR,uDACwB,EAAE;EACvC,MAAM,OAAO,EAAE,MAAM;AACrB,MAAI,KAAM,gBAAe,IAAI,KAAK;;CAItC,MAAMA,YAAwB,EAAE;CAChC,MAAM,QAAQ,OAAO,MAAM,KAAK;AAChC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM,MAAM;AACzB,OAAK,MAAM,EAAE,SAAS,YAAY,UAAU,SAAS,aAAa;AAChE,WAAQ,YAAY;AACpB,OAAI,QAAQ,KAAK,KAAK,EAAE;IAEtB,MAAM,WADK,KAAK,MAAM,WAAW,GACX,MAAM;IAC5B,MAAM,eAAe,YAAY,eAAe,IAAI,SAAS;AAC7D,cAAU,KAAK;KAAE,MAAM,IAAI;KAAG;KAAU;KAAK;KAAc,CAAC;;;;CAKlE,MAAM,sBAAsB,UAAU,QAAQ,MAAM,CAAC,EAAE,aAAa;CACpE,IAAIC;AACJ,KAAI,oBAAoB,SAAS,EAC/B,eAAc;EACZ;EACA;EACA;EACA;EACA;EACD;UACQ,UAAU,SAAS,KAAK,CAAC,kBAClC,eAAc;EACZ;EACA;EACA;EACA;EACD;UACQ,UAAU,SAAS,EAC5B,eAAc,CACZ,mDACA,0GACD;KAED,eAAc,CACZ,sDACA,yFACD;AAGH,QAAO;EACL,kBAAkB,UAAU;EAC5B,cAAc,UAAU,SAAS,oBAAoB;EACrD,gBAAgB,oBAAoB;EACpC,sBAAsB;EACtB,qBAAqB;EACrB,YAAY;EACZ;EACD"}
1
+ {"version":3,"file":"validate-file.js","names":["callSites: CallSite[]","suggestions: string[]"],"sources":["../../src/mcp/validate-file.ts"],"sourcesContent":["export interface CallSite {\n line: number;\n provider: string;\n api: string;\n instrumented: boolean;\n}\n\nexport interface FileAnalysis {\n total_call_sites: number;\n instrumented: number;\n uninstrumented: number;\n has_amplitude_import: boolean;\n has_session_context: boolean;\n call_sites: CallSite[];\n suggestions: string[];\n}\n\nconst llmPatterns = [\n {\n pattern: /\\.chat\\.completions\\.create\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.chat\\.completions\\.create\\s*\\(/,\n provider: 'openai',\n api: 'chat.completions.create',\n },\n {\n pattern: /\\.chat\\.completions\\.parse\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.chat\\.completions\\.parse\\s*\\(/,\n provider: 'openai',\n api: 'chat.completions.parse',\n },\n {\n pattern: /\\.responses\\.create\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.responses\\.create\\s*\\(/,\n provider: 'openai',\n api: 'responses.create',\n },\n {\n pattern: /\\.messages\\.create\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.messages\\.create\\s*\\(/,\n provider: 'anthropic',\n api: 'messages.create',\n },\n {\n pattern: /\\.generateContent\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.generateContent\\s*\\(/,\n provider: 'gemini',\n api: 'generateContent',\n },\n {\n pattern: /\\.send\\s*\\(\\s*new\\s+InvokeModelCommand\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.send\\s*\\(/,\n provider: 'bedrock',\n api: 'invokeModel',\n },\n {\n pattern: /\\.send\\s*\\(\\s*new\\s+ConverseCommand\\s*\\(/g,\n receiverRe: /(\\w+)(?:\\.\\w+)*\\.send\\s*\\(/,\n provider: 'bedrock',\n api: 'converse',\n },\n];\n\nfunction findWrappedConstructors(source: string): Set<string> {\n const result = new Set<string>();\n const declRe =\n /(?:const|let|var)\\s+(\\w+)\\s*=\\s*new\\s+(?:OpenAI|Anthropic|Gemini|AzureOpenAI|Bedrock|Mistral)\\s*\\(/g;\n for (const m of source.matchAll(declRe)) {\n const varName = m[1] ?? '';\n if (!varName) continue;\n let depth = 1;\n let i = (m.index ?? 0) + m[0].length;\n let argBlock = '';\n while (i < source.length && depth > 0) {\n const ch = source[i];\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n if (depth > 0) argBlock += ch;\n i++;\n }\n if (/\\bamplitude\\s*:/.test(argBlock)) {\n result.add(varName);\n }\n }\n return result;\n}\n\nexport function analyzeFileInstrumentation(source: string): FileAnalysis {\n const hasPatch = /\\bpatch\\s*\\(\\s*\\{/.test(source);\n const hasSessionContext =\n /\\.session\\s*\\(/.test(source) ||\n /\\bsession\\.run\\s*\\(/.test(source) ||\n /\\.runAs\\s*\\(/.test(source) ||\n /\\bcreateAmplitudeAIMiddleware\\s*\\(/.test(source);\n\n const hasAmplitudeImport =\n /from\\s+['\"]@amplitude\\/ai['\"]/.test(source) ||\n /require\\s*\\(\\s*['\"]@amplitude\\/ai['\"]\\s*\\)/.test(source) ||\n /\\bAmplitudeAI\\b/.test(source);\n\n const wrappedClients = findWrappedConstructors(source);\n if (hasAmplitudeImport) {\n const wrapRe = /(?:const|let|var)\\s+(\\w+)\\s*=\\s*(?:\\w+\\.)*wrap\\s*\\(/g;\n for (const m of source.matchAll(wrapRe)) {\n const name = m[1] ?? '';\n if (name) wrappedClients.add(name);\n }\n }\n\n const callSites: CallSite[] = [];\n const lines = source.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n for (const { pattern, receiverRe, provider, api } of llmPatterns) {\n pattern.lastIndex = 0;\n if (pattern.test(line)) {\n const rm = line.match(receiverRe);\n const receiver = rm?.[1] ?? '';\n const instrumented = hasPatch || wrappedClients.has(receiver);\n callSites.push({ line: i + 1, provider, api, instrumented });\n }\n }\n }\n\n const uninstrumentedSites = callSites.filter((s) => !s.instrumented);\n let suggestions: string[];\n if (uninstrumentedSites.length > 0) {\n suggestions = [\n 'Now: instrument provider calls with wrapper/swap import (for example, `new OpenAI({ amplitude: ai, apiKey })`).',\n 'Next: add session lineage with `const session = ai.agent(...).session(...)` and wrap calls in `session.run(...)`.',\n 'Why: session context unlocks session-level scoring, enrichments, and reliable product-to-AI funnels.',\n 'Content tiers: choose `contentMode` (`full`, `metadata_only`, or `customer_enriched`) and prefer `redactPii: true` when using `full`.',\n 'Fallback: use `patch({ amplitudeAI: ai })` for migration speed, then graduate to wrapper + session context.',\n ];\n } else if (callSites.length > 0 && !hasSessionContext) {\n suggestions = [\n 'Tracking is present, but session context is missing.',\n 'Now: add `const session = ai.agent(...).session(...)` and execute LLM calls inside `session.run(...)` (or use middleware).',\n 'Why: without session lineage you lose high-value outcomes like session enrichments, scoring, and dependable session funnels.',\n 'Content tiers: set `contentMode` intentionally and keep `redactPii: true` when using `full`.',\n ];\n } else if (callSites.length > 0) {\n suggestions = [\n 'File appears instrumented with session lineage.',\n 'For privacy-by-default, set `contentMode` intentionally and enable `redactPii: true` when using `full`.',\n ];\n } else {\n suggestions = [\n 'No supported LLM call sites detected in this file.',\n 'If this file should emit AI telemetry, add wrapped provider calls and session context.',\n ];\n }\n\n return {\n total_call_sites: callSites.length,\n instrumented: callSites.length - uninstrumentedSites.length,\n uninstrumented: uninstrumentedSites.length,\n has_amplitude_import: hasAmplitudeImport,\n has_session_context: hasSessionContext,\n call_sites: callSites,\n suggestions,\n };\n}\n"],"mappings":";AAiBA,MAAM,cAAc;CAClB;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACD;EACE,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CACF;AAED,SAAS,wBAAwB,QAA6B;CAC5D,MAAM,yBAAS,IAAI,KAAa;AAGhC,MAAK,MAAM,KAAK,OAAO,SADrB,sGACqC,EAAE;EACvC,MAAM,UAAU,EAAE,MAAM;AACxB,MAAI,CAAC,QAAS;EACd,IAAI,QAAQ;EACZ,IAAI,KAAK,EAAE,SAAS,KAAK,EAAE,GAAG;EAC9B,IAAI,WAAW;AACf,SAAO,IAAI,OAAO,UAAU,QAAQ,GAAG;GACrC,MAAM,KAAK,OAAO;AAClB,OAAI,OAAO,IAAK;YACP,OAAO,IAAK;AACrB,OAAI,QAAQ,EAAG,aAAY;AAC3B;;AAEF,MAAI,kBAAkB,KAAK,SAAS,CAClC,QAAO,IAAI,QAAQ;;AAGvB,QAAO;;AAGT,SAAgB,2BAA2B,QAA8B;CACvE,MAAM,WAAW,oBAAoB,KAAK,OAAO;CACjD,MAAM,oBACJ,iBAAiB,KAAK,OAAO,IAC7B,sBAAsB,KAAK,OAAO,IAClC,eAAe,KAAK,OAAO,IAC3B,qCAAqC,KAAK,OAAO;CAEnD,MAAM,qBACJ,gCAAgC,KAAK,OAAO,IAC5C,6CAA6C,KAAK,OAAO,IACzD,kBAAkB,KAAK,OAAO;CAEhC,MAAM,iBAAiB,wBAAwB,OAAO;AACtD,KAAI,mBAEF,MAAK,MAAM,KAAK,OAAO,SADR,uDACwB,EAAE;EACvC,MAAM,OAAO,EAAE,MAAM;AACrB,MAAI,KAAM,gBAAe,IAAI,KAAK;;CAItC,MAAMA,YAAwB,EAAE;CAChC,MAAM,QAAQ,OAAO,MAAM,KAAK;AAChC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM,MAAM;AACzB,OAAK,MAAM,EAAE,SAAS,YAAY,UAAU,SAAS,aAAa;AAChE,WAAQ,YAAY;AACpB,OAAI,QAAQ,KAAK,KAAK,EAAE;IAEtB,MAAM,WADK,KAAK,MAAM,WAAW,GACX,MAAM;IAC5B,MAAM,eAAe,YAAY,eAAe,IAAI,SAAS;AAC7D,cAAU,KAAK;KAAE,MAAM,IAAI;KAAG;KAAU;KAAK;KAAc,CAAC;;;;CAKlE,MAAM,sBAAsB,UAAU,QAAQ,MAAM,CAAC,EAAE,aAAa;CACpE,IAAIC;AACJ,KAAI,oBAAoB,SAAS,EAC/B,eAAc;EACZ;EACA;EACA;EACA;EACA;EACD;UACQ,UAAU,SAAS,KAAK,CAAC,kBAClC,eAAc;EACZ;EACA;EACA;EACA;EACD;UACQ,UAAU,SAAS,EAC5B,eAAc,CACZ,mDACA,0GACD;KAED,eAAc,CACZ,sDACA,yFACD;AAGH,QAAO;EACL,kBAAkB,UAAU;EAC5B,cAAc,UAAU,SAAS,oBAAoB;EACrD,gBAAgB,oBAAoB;EACpC,sBAAsB;EACtB,qBAAqB;EACrB,YAAY;EACZ;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"patching.d.ts","names":[],"sources":["../src/patching.ts"],"sourcesContent":[],"mappings":";;;;AAgLgB,iBAjJA,gBAAA,CAAA,CAkJD,EAAW,MAAA,EAAA;AAuHV,iBApNA,WAAA,CAqND,OAAA,EAAW;EA6CV,WAAA,EAjQD,WAiQa;EAgGZ,gBAAK,CAAA,EAAA,OACN;EAiEC,MAAA,CAAA,EAAA,OAAO;AAUvB,CAAA,CAAA,EAAgB,IAAA;AAIA,iBA7ZA,cAAA,CA6ZgB,OAAA,EAAA;EAIhB,WAAA,EAhaD,WAgac;EAIb,MAAA,CAAA,EAAA,OAAA;AAIhB,CAAA,CAAA,EAAgB,IAAA;AAIA,iBAhYA,gBAAA,CAgYkB,OAAA,EAAA;eA/XnB;;;iBAyBC,WAAA;eACD;;;iBAuHC,YAAA;eACD;;;iBA6CC,YAAA;eACD;;;;;;;;;;;;;;;;;;iBA+FC,KAAA;eACD;YACH;;iBAgEI,OAAA,CAAA;iBAUA,aAAA,CAAA;iBAIA,gBAAA,CAAA;iBAIA,aAAA,CAAA;iBAIA,cAAA,CAAA;iBAIA,cAAA,CAAA;iBAIA,kBAAA,CAAA"}
1
+ {"version":3,"file":"patching.d.ts","names":[],"sources":["../src/patching.ts"],"sourcesContent":[],"mappings":";;;;AAoLgB,iBAjJA,gBAAA,CAAA,CAkJU,EAAA,MAAA,EAAA;AAuHV,iBApNA,WAAA,CAqND,OAAW,EAAA;EA6CV,WAAA,EAjQD,WAiQa;EAgGZ,gBAAK,CAAA,EAAA,OACN;EAiEC,MAAA,CAAA,EAAA,OAAO;AAUvB,CAAA,CAAA,EAAgB,IAAA;AAIA,iBA7ZA,cAAA,CA6ZgB,OAAA,EAAA;EAIhB,WAAA,EAhaD,WAgac;EAIb,MAAA,CAAA,EAAA,OAAA;AAIhB,CAAA,CAAA,EAAgB,IAAA;AAIA,iBAhYA,gBAAA,CAgYkB,OAAA,EAAA;eA/XnB;;;iBAyBC,WAAA;eACD;;;iBAuHC,YAAA;eACD;;;iBA6CC,YAAA;eACD;;;;;;;;;;;;;;;;;;iBA+FC,KAAA;eACD;YACH;;iBAgEI,OAAA,CAAA;iBAUA,aAAA,CAAA;iBAIA,gBAAA,CAAA;iBAIA,aAAA,CAAA;iBAIA,cAAA,CAAA;iBAIA,cAAA,CAAA;iBAIA,kBAAA,CAAA"}
package/dist/patching.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { PROP_IDLE_TIMEOUT_MINUTES, PROP_SESSION_REPLAY_ID } from "./core/constants.js";
1
2
  import { getActiveContext } from "./context.js";
2
3
  import { ANTHROPIC_AVAILABLE, _AnthropicModule } from "./providers/anthropic.js";
3
4
  import { BEDROCK_AVAILABLE, _BedrockModule } from "./providers/bedrock.js";
@@ -877,13 +878,18 @@ function _extractResponsesFinishReason(response) {
877
878
  return typeof first?.status === "string" ? first.status : void 0;
878
879
  }
879
880
  function _contextExtras(ctx) {
880
- return {
881
+ const extras = {
881
882
  parentAgentId: ctx.parentAgentId ?? void 0,
882
883
  customerOrgId: ctx.customerOrgId ?? void 0,
883
884
  agentVersion: ctx.agentVersion ?? void 0,
884
885
  context: ctx.context ?? void 0,
885
886
  groups: ctx.groups ?? void 0
886
887
  };
888
+ const ep = {};
889
+ if (ctx.idleTimeoutMinutes != null) ep[PROP_IDLE_TIMEOUT_MINUTES] = ctx.idleTimeoutMinutes;
890
+ if (ctx.deviceId && ctx.browserSessionId) ep[PROP_SESSION_REPLAY_ID] = `${ctx.deviceId}/${ctx.browserSessionId}`;
891
+ if (Object.keys(ep).length > 0) extras.eventProperties = ep;
892
+ return extras;
887
893
  }
888
894
 
889
895
  //#endregion