@posthog/ai 7.4.0 → 7.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { uuidv7 } from '@posthog/core';
6
6
  import AnthropicOriginal from '@anthropic-ai/sdk';
7
7
  import { GoogleGenAI } from '@google/genai';
8
8
 
9
- var version = "7.4.0";
9
+ var version = "7.4.2";
10
10
 
11
11
  // Type guards for safer type checking
12
12
  const isString = value => {
@@ -1937,6 +1937,10 @@ class WrappedEmbeddings extends AzureOpenAI.Embeddings {
1937
1937
  }
1938
1938
  }
1939
1939
 
1940
+ // Type guards
1941
+ function isV3Model(model) {
1942
+ return model.specificationVersion === 'v3';
1943
+ }
1940
1944
  const mapVercelParams = params => {
1941
1945
  return {
1942
1946
  temperature: params.temperature,
@@ -2157,6 +2161,19 @@ const extractAdditionalTokenValues = providerMetadata => {
2157
2161
  }
2158
2162
  return {};
2159
2163
  };
2164
+ // For Anthropic providers in V3, inputTokens.total is the sum of all tokens (uncached + cache read + cache write).
2165
+ // Our cost calculation expects inputTokens to be only the uncached portion for Anthropic.
2166
+ // This helper subtracts cache tokens from inputTokens for Anthropic V3 models.
2167
+ const adjustAnthropicV3CacheTokens = (model, provider, usage) => {
2168
+ if (isV3Model(model) && provider.toLowerCase().includes('anthropic')) {
2169
+ const cacheReadTokens = usage.cacheReadInputTokens || 0;
2170
+ const cacheWriteTokens = usage.cacheCreationInputTokens || 0;
2171
+ const cacheTokens = cacheReadTokens + cacheWriteTokens;
2172
+ if (usage.inputTokens && cacheTokens > 0) {
2173
+ usage.inputTokens = Math.max(usage.inputTokens - cacheTokens, 0);
2174
+ }
2175
+ }
2176
+ };
2160
2177
  // Helper to extract numeric token value from V2 (number) or V3 (object with .total) usage formats
2161
2178
  const extractTokenCount = value => {
2162
2179
  if (typeof value === 'number') {
@@ -2207,234 +2224,246 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2207
2224
  $ai_framework_version: model.specificationVersion === 'v3' ? '6' : '5'
2208
2225
  }
2209
2226
  };
2210
- // Create wrapped model that preserves the original type
2211
- const wrappedModel = {
2212
- ...model,
2213
- doGenerate: async params => {
2214
- const startTime = Date.now();
2215
- const mergedParams = {
2216
- ...mergedOptions,
2217
- ...mapVercelParams(params)
2218
- };
2219
- const availableTools = extractAvailableToolCalls('vercel', params);
2220
- try {
2221
- const result = await model.doGenerate(params);
2222
- const modelId = mergedOptions.posthogModelOverride ?? (result.response?.modelId ? result.response.modelId : model.modelId);
2223
- const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
2224
- const baseURL = ''; // cannot currently get baseURL from vercel
2225
- const content = mapVercelOutput(result.content);
2226
- const latency = (Date.now() - startTime) / 1000;
2227
- const providerMetadata = result.providerMetadata;
2228
- const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
2229
- const webSearchCount = extractWebSearchCount(providerMetadata, result.usage);
2230
- // V2 usage has simple numbers, V3 has objects with .total - normalize both
2231
- const usageObj = result.usage;
2232
- const usage = {
2233
- inputTokens: extractTokenCount(result.usage.inputTokens),
2234
- outputTokens: extractTokenCount(result.usage.outputTokens),
2235
- reasoningTokens: extractReasoningTokens(usageObj),
2236
- cacheReadInputTokens: extractCacheReadTokens(usageObj),
2237
- webSearchCount,
2238
- ...additionalTokenValues
2227
+ // Create wrapped model using Object.create to preserve the prototype chain
2228
+ // This automatically inherits all properties (including getters) from the model
2229
+ const wrappedModel = Object.create(model, {
2230
+ doGenerate: {
2231
+ value: async params => {
2232
+ const startTime = Date.now();
2233
+ const mergedParams = {
2234
+ ...mergedOptions,
2235
+ ...mapVercelParams(params)
2239
2236
  };
2240
- await sendEventToPosthog({
2241
- client: phClient,
2242
- distinctId: mergedOptions.posthogDistinctId,
2243
- traceId: mergedOptions.posthogTraceId ?? v4(),
2244
- model: modelId,
2245
- provider: provider,
2246
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2247
- output: content,
2248
- latency,
2249
- baseURL,
2250
- params: mergedParams,
2251
- httpStatus: 200,
2252
- usage,
2253
- tools: availableTools,
2254
- captureImmediate: mergedOptions.posthogCaptureImmediate
2255
- });
2256
- return result;
2257
- } catch (error) {
2258
- const modelId = model.modelId;
2259
- const enrichedError = await sendEventWithErrorToPosthog({
2260
- client: phClient,
2261
- distinctId: mergedOptions.posthogDistinctId,
2262
- traceId: mergedOptions.posthogTraceId ?? v4(),
2263
- model: modelId,
2264
- provider: model.provider,
2265
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2266
- output: [],
2267
- latency: 0,
2268
- baseURL: '',
2269
- params: mergedParams,
2270
- usage: {
2271
- inputTokens: 0,
2272
- outputTokens: 0
2273
- },
2274
- error: error,
2275
- tools: availableTools,
2276
- captureImmediate: mergedOptions.posthogCaptureImmediate
2277
- });
2278
- throw enrichedError;
2279
- }
2237
+ const availableTools = extractAvailableToolCalls('vercel', params);
2238
+ try {
2239
+ const result = await model.doGenerate(params);
2240
+ const modelId = mergedOptions.posthogModelOverride ?? (result.response?.modelId ? result.response.modelId : model.modelId);
2241
+ const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
2242
+ const baseURL = ''; // cannot currently get baseURL from vercel
2243
+ const content = mapVercelOutput(result.content);
2244
+ const latency = (Date.now() - startTime) / 1000;
2245
+ const providerMetadata = result.providerMetadata;
2246
+ const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
2247
+ const webSearchCount = extractWebSearchCount(providerMetadata, result.usage);
2248
+ // V2 usage has simple numbers, V3 has objects with .total - normalize both
2249
+ const usageObj = result.usage;
2250
+ const usage = {
2251
+ inputTokens: extractTokenCount(result.usage.inputTokens),
2252
+ outputTokens: extractTokenCount(result.usage.outputTokens),
2253
+ reasoningTokens: extractReasoningTokens(usageObj),
2254
+ cacheReadInputTokens: extractCacheReadTokens(usageObj),
2255
+ webSearchCount,
2256
+ ...additionalTokenValues
2257
+ };
2258
+ adjustAnthropicV3CacheTokens(model, provider, usage);
2259
+ await sendEventToPosthog({
2260
+ client: phClient,
2261
+ distinctId: mergedOptions.posthogDistinctId,
2262
+ traceId: mergedOptions.posthogTraceId ?? v4(),
2263
+ model: modelId,
2264
+ provider: provider,
2265
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2266
+ output: content,
2267
+ latency,
2268
+ baseURL,
2269
+ params: mergedParams,
2270
+ httpStatus: 200,
2271
+ usage,
2272
+ tools: availableTools,
2273
+ captureImmediate: mergedOptions.posthogCaptureImmediate
2274
+ });
2275
+ return result;
2276
+ } catch (error) {
2277
+ const modelId = model.modelId;
2278
+ const enrichedError = await sendEventWithErrorToPosthog({
2279
+ client: phClient,
2280
+ distinctId: mergedOptions.posthogDistinctId,
2281
+ traceId: mergedOptions.posthogTraceId ?? v4(),
2282
+ model: modelId,
2283
+ provider: model.provider,
2284
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2285
+ output: [],
2286
+ latency: 0,
2287
+ baseURL: '',
2288
+ params: mergedParams,
2289
+ usage: {
2290
+ inputTokens: 0,
2291
+ outputTokens: 0
2292
+ },
2293
+ error: error,
2294
+ tools: availableTools,
2295
+ captureImmediate: mergedOptions.posthogCaptureImmediate
2296
+ });
2297
+ throw enrichedError;
2298
+ }
2299
+ },
2300
+ writable: true,
2301
+ configurable: true,
2302
+ enumerable: false
2280
2303
  },
2281
- doStream: async params => {
2282
- const startTime = Date.now();
2283
- let generatedText = '';
2284
- let reasoningText = '';
2285
- let usage = {};
2286
- let providerMetadata = undefined;
2287
- const mergedParams = {
2288
- ...mergedOptions,
2289
- ...mapVercelParams(params)
2290
- };
2291
- const modelId = mergedOptions.posthogModelOverride ?? model.modelId;
2292
- const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
2293
- const availableTools = extractAvailableToolCalls('vercel', params);
2294
- const baseURL = ''; // cannot currently get baseURL from vercel
2295
- // Map to track in-progress tool calls
2296
- const toolCallsInProgress = new Map();
2297
- try {
2298
- const {
2299
- stream,
2300
- ...rest
2301
- } = await model.doStream(params);
2302
- const transformStream = new TransformStream({
2303
- transform(chunk, controller) {
2304
- // Handle streaming patterns - compatible with both V2 and V3
2305
- if (chunk.type === 'text-delta') {
2306
- generatedText += chunk.delta;
2307
- }
2308
- if (chunk.type === 'reasoning-delta') {
2309
- reasoningText += chunk.delta;
2310
- }
2311
- // Handle tool call chunks
2312
- if (chunk.type === 'tool-input-start') {
2313
- // Initialize a new tool call
2314
- toolCallsInProgress.set(chunk.id, {
2315
- toolCallId: chunk.id,
2316
- toolName: chunk.toolName,
2317
- input: ''
2318
- });
2319
- }
2320
- if (chunk.type === 'tool-input-delta') {
2321
- // Accumulate tool call arguments
2322
- const toolCall = toolCallsInProgress.get(chunk.id);
2323
- if (toolCall) {
2324
- toolCall.input += chunk.delta;
2304
+ doStream: {
2305
+ value: async params => {
2306
+ const startTime = Date.now();
2307
+ let generatedText = '';
2308
+ let reasoningText = '';
2309
+ let usage = {};
2310
+ let providerMetadata = undefined;
2311
+ const mergedParams = {
2312
+ ...mergedOptions,
2313
+ ...mapVercelParams(params)
2314
+ };
2315
+ const modelId = mergedOptions.posthogModelOverride ?? model.modelId;
2316
+ const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
2317
+ const availableTools = extractAvailableToolCalls('vercel', params);
2318
+ const baseURL = ''; // cannot currently get baseURL from vercel
2319
+ // Map to track in-progress tool calls
2320
+ const toolCallsInProgress = new Map();
2321
+ try {
2322
+ const {
2323
+ stream,
2324
+ ...rest
2325
+ } = await model.doStream(params);
2326
+ const transformStream = new TransformStream({
2327
+ transform(chunk, controller) {
2328
+ // Handle streaming patterns - compatible with both V2 and V3
2329
+ if (chunk.type === 'text-delta') {
2330
+ generatedText += chunk.delta;
2325
2331
  }
2326
- }
2327
- if (chunk.type === 'tool-input-end') {
2328
- // Tool call is complete, keep it in the map for final processing
2329
- }
2330
- if (chunk.type === 'tool-call') {
2331
- // Direct tool call chunk (complete tool call)
2332
- toolCallsInProgress.set(chunk.toolCallId, {
2333
- toolCallId: chunk.toolCallId,
2334
- toolName: chunk.toolName,
2335
- input: chunk.input
2336
- });
2337
- }
2338
- if (chunk.type === 'finish') {
2339
- providerMetadata = chunk.providerMetadata;
2340
- const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
2341
- const chunkUsage = chunk.usage || {};
2342
- usage = {
2343
- inputTokens: extractTokenCount(chunk.usage?.inputTokens),
2344
- outputTokens: extractTokenCount(chunk.usage?.outputTokens),
2345
- reasoningTokens: extractReasoningTokens(chunkUsage),
2346
- cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
2347
- ...additionalTokenValues
2348
- };
2349
- }
2350
- controller.enqueue(chunk);
2351
- },
2352
- flush: async () => {
2353
- const latency = (Date.now() - startTime) / 1000;
2354
- // Build content array similar to mapVercelOutput structure
2355
- const content = [];
2356
- if (reasoningText) {
2357
- content.push({
2358
- type: 'reasoning',
2359
- text: truncate(reasoningText)
2360
- });
2361
- }
2362
- if (generatedText) {
2363
- content.push({
2364
- type: 'text',
2365
- text: truncate(generatedText)
2366
- });
2367
- }
2368
- // Add completed tool calls to content
2369
- for (const toolCall of toolCallsInProgress.values()) {
2370
- if (toolCall.toolName) {
2332
+ if (chunk.type === 'reasoning-delta') {
2333
+ reasoningText += chunk.delta;
2334
+ }
2335
+ // Handle tool call chunks
2336
+ if (chunk.type === 'tool-input-start') {
2337
+ // Initialize a new tool call
2338
+ toolCallsInProgress.set(chunk.id, {
2339
+ toolCallId: chunk.id,
2340
+ toolName: chunk.toolName,
2341
+ input: ''
2342
+ });
2343
+ }
2344
+ if (chunk.type === 'tool-input-delta') {
2345
+ // Accumulate tool call arguments
2346
+ const toolCall = toolCallsInProgress.get(chunk.id);
2347
+ if (toolCall) {
2348
+ toolCall.input += chunk.delta;
2349
+ }
2350
+ }
2351
+ if (chunk.type === 'tool-input-end') {
2352
+ // Tool call is complete, keep it in the map for final processing
2353
+ }
2354
+ if (chunk.type === 'tool-call') {
2355
+ // Direct tool call chunk (complete tool call)
2356
+ toolCallsInProgress.set(chunk.toolCallId, {
2357
+ toolCallId: chunk.toolCallId,
2358
+ toolName: chunk.toolName,
2359
+ input: chunk.input
2360
+ });
2361
+ }
2362
+ if (chunk.type === 'finish') {
2363
+ providerMetadata = chunk.providerMetadata;
2364
+ const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
2365
+ const chunkUsage = chunk.usage || {};
2366
+ usage = {
2367
+ inputTokens: extractTokenCount(chunk.usage?.inputTokens),
2368
+ outputTokens: extractTokenCount(chunk.usage?.outputTokens),
2369
+ reasoningTokens: extractReasoningTokens(chunkUsage),
2370
+ cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
2371
+ ...additionalTokenValues
2372
+ };
2373
+ }
2374
+ controller.enqueue(chunk);
2375
+ },
2376
+ flush: async () => {
2377
+ const latency = (Date.now() - startTime) / 1000;
2378
+ // Build content array similar to mapVercelOutput structure
2379
+ const content = [];
2380
+ if (reasoningText) {
2371
2381
  content.push({
2372
- type: 'tool-call',
2373
- id: toolCall.toolCallId,
2374
- function: {
2375
- name: toolCall.toolName,
2376
- arguments: toolCall.input
2377
- }
2382
+ type: 'reasoning',
2383
+ text: truncate(reasoningText)
2378
2384
  });
2379
2385
  }
2386
+ if (generatedText) {
2387
+ content.push({
2388
+ type: 'text',
2389
+ text: truncate(generatedText)
2390
+ });
2391
+ }
2392
+ // Add completed tool calls to content
2393
+ for (const toolCall of toolCallsInProgress.values()) {
2394
+ if (toolCall.toolName) {
2395
+ content.push({
2396
+ type: 'tool-call',
2397
+ id: toolCall.toolCallId,
2398
+ function: {
2399
+ name: toolCall.toolName,
2400
+ arguments: toolCall.input
2401
+ }
2402
+ });
2403
+ }
2404
+ }
2405
+ // Structure output like mapVercelOutput does
2406
+ const output = content.length > 0 ? [{
2407
+ role: 'assistant',
2408
+ content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
2409
+ }] : [];
2410
+ const webSearchCount = extractWebSearchCount(providerMetadata, usage);
2411
+ // Update usage with web search count
2412
+ const finalUsage = {
2413
+ ...usage,
2414
+ webSearchCount
2415
+ };
2416
+ adjustAnthropicV3CacheTokens(model, provider, finalUsage);
2417
+ await sendEventToPosthog({
2418
+ client: phClient,
2419
+ distinctId: mergedOptions.posthogDistinctId,
2420
+ traceId: mergedOptions.posthogTraceId ?? v4(),
2421
+ model: modelId,
2422
+ provider: provider,
2423
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2424
+ output: output,
2425
+ latency,
2426
+ baseURL,
2427
+ params: mergedParams,
2428
+ httpStatus: 200,
2429
+ usage: finalUsage,
2430
+ tools: availableTools,
2431
+ captureImmediate: mergedOptions.posthogCaptureImmediate
2432
+ });
2380
2433
  }
2381
- // Structure output like mapVercelOutput does
2382
- const output = content.length > 0 ? [{
2383
- role: 'assistant',
2384
- content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
2385
- }] : [];
2386
- const webSearchCount = extractWebSearchCount(providerMetadata, usage);
2387
- // Update usage with web search count
2388
- const finalUsage = {
2389
- ...usage,
2390
- webSearchCount
2391
- };
2392
- await sendEventToPosthog({
2393
- client: phClient,
2394
- distinctId: mergedOptions.posthogDistinctId,
2395
- traceId: mergedOptions.posthogTraceId ?? v4(),
2396
- model: modelId,
2397
- provider: provider,
2398
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2399
- output: output,
2400
- latency,
2401
- baseURL,
2402
- params: mergedParams,
2403
- httpStatus: 200,
2404
- usage: finalUsage,
2405
- tools: availableTools,
2406
- captureImmediate: mergedOptions.posthogCaptureImmediate
2407
- });
2408
- }
2409
- });
2410
- return {
2411
- stream: stream.pipeThrough(transformStream),
2412
- ...rest
2413
- };
2414
- } catch (error) {
2415
- const enrichedError = await sendEventWithErrorToPosthog({
2416
- client: phClient,
2417
- distinctId: mergedOptions.posthogDistinctId,
2418
- traceId: mergedOptions.posthogTraceId ?? v4(),
2419
- model: modelId,
2420
- provider: provider,
2421
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2422
- output: [],
2423
- latency: 0,
2424
- baseURL: '',
2425
- params: mergedParams,
2426
- usage: {
2427
- inputTokens: 0,
2428
- outputTokens: 0
2429
- },
2430
- error: error,
2431
- tools: availableTools,
2432
- captureImmediate: mergedOptions.posthogCaptureImmediate
2433
- });
2434
- throw enrichedError;
2435
- }
2434
+ });
2435
+ return {
2436
+ stream: stream.pipeThrough(transformStream),
2437
+ ...rest
2438
+ };
2439
+ } catch (error) {
2440
+ const enrichedError = await sendEventWithErrorToPosthog({
2441
+ client: phClient,
2442
+ distinctId: mergedOptions.posthogDistinctId,
2443
+ traceId: mergedOptions.posthogTraceId ?? v4(),
2444
+ model: modelId,
2445
+ provider: provider,
2446
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2447
+ output: [],
2448
+ latency: 0,
2449
+ baseURL: '',
2450
+ params: mergedParams,
2451
+ usage: {
2452
+ inputTokens: 0,
2453
+ outputTokens: 0
2454
+ },
2455
+ error: error,
2456
+ tools: availableTools,
2457
+ captureImmediate: mergedOptions.posthogCaptureImmediate
2458
+ });
2459
+ throw enrichedError;
2460
+ }
2461
+ },
2462
+ writable: true,
2463
+ configurable: true,
2464
+ enumerable: false
2436
2465
  }
2437
- };
2466
+ });
2438
2467
  return wrappedModel;
2439
2468
  };
2440
2469