@posthog/ai 8.6.1 → 8.6.3
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/anthropic/index.cjs +334 -108
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +335 -109
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +121 -110
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +122 -111
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +237 -183
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +238 -184
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +19 -32
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.d.ts +1 -0
- package/dist/langchain/index.mjs +19 -32
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +351 -125
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +352 -126
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +24 -4
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.d.ts +1 -1
- package/dist/openai-agents/index.mjs +24 -4
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +5 -3
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.mjs +5 -3
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +238 -186
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +239 -187
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/anthropic/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AnthropicOriginal from '@anthropic-ai/sdk';
|
|
2
2
|
import { v4 } from 'uuid';
|
|
3
|
-
import { uuidv7 } from '@posthog/core';
|
|
3
|
+
import { toJsonSafeValue, uuidv7 } from '@posthog/core';
|
|
4
|
+
import { Stream } from '@anthropic-ai/sdk/streaming';
|
|
4
5
|
|
|
5
6
|
const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
|
|
6
7
|
const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
|
|
@@ -291,7 +292,7 @@ function addDefaults(params) {
|
|
|
291
292
|
};
|
|
292
293
|
}
|
|
293
294
|
|
|
294
|
-
var version = "8.6.
|
|
295
|
+
var version = "8.6.3";
|
|
295
296
|
|
|
296
297
|
const DEFAULT_MAX_DEPTH = 3;
|
|
297
298
|
const MAX_STACK_LINES = 20;
|
|
@@ -402,122 +403,347 @@ const warnIfPostHogAiGateway = baseURL => {
|
|
|
402
403
|
* so callers can re-throw the original error reference safely.
|
|
403
404
|
*/
|
|
404
405
|
const captureAiGeneration = async (client, options) => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
warnIfPostHogAiGateway(options.baseURL);
|
|
409
|
-
const traceId = options.traceId ?? v4();
|
|
410
|
-
const eventType = options.eventType ?? AIEvent.Generation;
|
|
411
|
-
const privacyMode = options.privacyMode ?? false;
|
|
412
|
-
const usage = options.usage ?? {};
|
|
413
|
-
const safeInput = sanitizeValues(options.input);
|
|
414
|
-
const safeOutput = sanitizeValues(options.output);
|
|
415
|
-
let httpStatus = options.httpStatus;
|
|
416
|
-
let errorData = {};
|
|
417
|
-
if (options.error) {
|
|
418
|
-
if (httpStatus === undefined) {
|
|
419
|
-
if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
|
|
420
|
-
httpStatus = options.error.status;
|
|
421
|
-
} else {
|
|
422
|
-
httpStatus = 500;
|
|
423
|
-
}
|
|
406
|
+
try {
|
|
407
|
+
if (!client.capture) {
|
|
408
|
+
return;
|
|
424
409
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
410
|
+
warnIfPostHogAiGateway(options.baseURL);
|
|
411
|
+
const traceId = options.traceId ?? v4();
|
|
412
|
+
const eventType = options.eventType ?? AIEvent.Generation;
|
|
413
|
+
const privacyMode = options.privacyMode ?? false;
|
|
414
|
+
const usage = options.usage ?? {};
|
|
415
|
+
|
|
416
|
+
// Check privacy before reading or traversing input/output. Besides avoiding
|
|
417
|
+
// needless work, this ensures hostile getters/proxies cannot observe a value
|
|
418
|
+
// that the caller explicitly requested us to redact.
|
|
419
|
+
const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
|
|
420
|
+
const safeInput = shouldRedact ? null : toJsonSafeValue(options.input);
|
|
421
|
+
const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output);
|
|
422
|
+
let httpStatus = options.httpStatus;
|
|
423
|
+
let errorData = {};
|
|
424
|
+
if (options.error) {
|
|
425
|
+
if (httpStatus === undefined) {
|
|
426
|
+
if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
|
|
427
|
+
httpStatus = options.error.status;
|
|
428
|
+
} else {
|
|
429
|
+
httpStatus = 500;
|
|
430
|
+
}
|
|
433
431
|
}
|
|
432
|
+
let exceptionId;
|
|
433
|
+
if (client.options?.enableExceptionAutocapture) {
|
|
434
|
+
exceptionId = uuidv7();
|
|
435
|
+
client.captureException(options.error, undefined, {
|
|
436
|
+
$ai_trace_id: traceId
|
|
437
|
+
}, exceptionId);
|
|
438
|
+
if (typeof options.error === 'object') {
|
|
439
|
+
;
|
|
440
|
+
options.error.__posthog_previously_captured_error = true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
errorData = {
|
|
444
|
+
$ai_is_error: true,
|
|
445
|
+
$ai_error: stringifyError(options.error),
|
|
446
|
+
$exception_event_id: exceptionId
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
httpStatus = httpStatus ?? 200;
|
|
450
|
+
let costOverrideData = {};
|
|
451
|
+
if (options.costOverride) {
|
|
452
|
+
const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
|
|
453
|
+
const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
|
|
454
|
+
costOverrideData = {
|
|
455
|
+
$ai_input_cost_usd: inputCostUSD,
|
|
456
|
+
$ai_output_cost_usd: outputCostUSD,
|
|
457
|
+
$ai_total_cost_usd: inputCostUSD + outputCostUSD
|
|
458
|
+
};
|
|
434
459
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
460
|
+
const additionalTokenValues = {
|
|
461
|
+
...(usage.reasoningTokens ? {
|
|
462
|
+
$ai_reasoning_tokens: usage.reasoningTokens
|
|
463
|
+
} : {}),
|
|
464
|
+
...(usage.cacheReadInputTokens ? {
|
|
465
|
+
$ai_cache_read_input_tokens: usage.cacheReadInputTokens
|
|
466
|
+
} : {}),
|
|
467
|
+
...(usage.cacheCreationInputTokens ? {
|
|
468
|
+
$ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
|
|
469
|
+
} : {}),
|
|
470
|
+
...(usage.webSearchCount ? {
|
|
471
|
+
$ai_web_search_count: usage.webSearchCount
|
|
472
|
+
} : {}),
|
|
473
|
+
...(usage.rawUsage ? {
|
|
474
|
+
$ai_usage: usage.rawUsage
|
|
475
|
+
} : {})
|
|
439
476
|
};
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
$
|
|
448
|
-
$
|
|
449
|
-
$
|
|
477
|
+
const properties = {
|
|
478
|
+
$ai_lib: 'posthog-ai',
|
|
479
|
+
$ai_lib_version: version,
|
|
480
|
+
$ai_provider: options.providerOverride ?? options.provider,
|
|
481
|
+
$ai_model: options.modelOverride ?? options.model,
|
|
482
|
+
$ai_model_parameters: options.modelParameters ?? {},
|
|
483
|
+
$ai_input: safeInput,
|
|
484
|
+
$ai_output_choices: safeOutput,
|
|
485
|
+
$ai_http_status: httpStatus,
|
|
486
|
+
$ai_input_tokens: usage.inputTokens ?? 0,
|
|
487
|
+
...(usage.outputTokens !== undefined ? {
|
|
488
|
+
$ai_output_tokens: usage.outputTokens
|
|
489
|
+
} : {}),
|
|
490
|
+
...additionalTokenValues,
|
|
491
|
+
$ai_latency: options.latency ?? 0,
|
|
492
|
+
...(options.timeToFirstToken !== undefined ? {
|
|
493
|
+
$ai_time_to_first_token: options.timeToFirstToken
|
|
494
|
+
} : {}),
|
|
495
|
+
$ai_trace_id: traceId,
|
|
496
|
+
$ai_base_url: options.baseURL ?? '',
|
|
497
|
+
...options.properties,
|
|
498
|
+
$ai_tokens_source: getTokensSource(options.properties),
|
|
499
|
+
...(options.distinctId ? {} : {
|
|
500
|
+
$process_person_profile: false
|
|
501
|
+
}),
|
|
502
|
+
...(options.stopReason ? {
|
|
503
|
+
$ai_stop_reason: options.stopReason
|
|
504
|
+
} : {}),
|
|
505
|
+
...(options.tools ? {
|
|
506
|
+
$ai_tools: options.tools
|
|
507
|
+
} : {}),
|
|
508
|
+
...(options.completionId ? {
|
|
509
|
+
$ai_completion_id: options.completionId
|
|
510
|
+
} : {}),
|
|
511
|
+
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
512
|
+
$ai_provider_metadata: options.providerMetadata
|
|
513
|
+
} : {}),
|
|
514
|
+
...errorData,
|
|
515
|
+
...costOverrideData
|
|
516
|
+
};
|
|
517
|
+
const event = {
|
|
518
|
+
distinctId: options.distinctId ?? traceId,
|
|
519
|
+
event: eventType,
|
|
520
|
+
properties,
|
|
521
|
+
groups: options.groups
|
|
450
522
|
};
|
|
523
|
+
if (options.captureImmediate) {
|
|
524
|
+
await client.captureImmediate(event);
|
|
525
|
+
} else {
|
|
526
|
+
client.capture(event);
|
|
527
|
+
}
|
|
528
|
+
} catch (error) {
|
|
529
|
+
// Telemetry failures must never affect the instrumented provider call.
|
|
530
|
+
console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
|
|
451
531
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Splits an SDK stream into a monitoring branch and a caller branch without
|
|
536
|
+
* allowing either branch to read ahead of the other. Unlike the SDKs' `tee()`
|
|
537
|
+
* implementations, this keeps at most one result in flight and makes caller
|
|
538
|
+
* cancellation terminate the monitoring branch and the source iterator.
|
|
539
|
+
*/
|
|
540
|
+
function monitoredStreamTee(source, createStream) {
|
|
541
|
+
const controller = source.controller ?? new AbortController();
|
|
542
|
+
const sourceIterator = source[Symbol.asyncIterator]();
|
|
543
|
+
const callerQueue = [];
|
|
544
|
+
let monitorPending;
|
|
545
|
+
let monitorActive = true;
|
|
546
|
+
let operationInFlight = false;
|
|
547
|
+
let terminalResult;
|
|
548
|
+
let bufferedMonitorResult;
|
|
549
|
+
let terminalError;
|
|
550
|
+
let hasTerminalError = false;
|
|
551
|
+
let cancellationPromise;
|
|
552
|
+
let abortListener;
|
|
553
|
+
const removeAbortListener = () => {
|
|
554
|
+
if (abortListener) {
|
|
555
|
+
controller.signal.removeEventListener('abort', abortListener);
|
|
556
|
+
abortListener = undefined;
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
const settleMonitorTerminal = () => {
|
|
560
|
+
if (!monitorPending) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const pending = monitorPending;
|
|
564
|
+
monitorPending = undefined;
|
|
565
|
+
if (hasTerminalError) {
|
|
566
|
+
pending.reject(terminalError);
|
|
567
|
+
} else if (terminalResult) {
|
|
568
|
+
pending.resolve(terminalResult);
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
const settleCallersTerminal = () => {
|
|
572
|
+
while (callerQueue.length > 0) {
|
|
573
|
+
const pending = callerQueue.shift();
|
|
574
|
+
if (hasTerminalError) {
|
|
575
|
+
pending.reject(terminalError);
|
|
576
|
+
} else if (terminalResult) {
|
|
577
|
+
pending.resolve(terminalResult);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
const pump = () => {
|
|
582
|
+
if (operationInFlight || callerQueue.length === 0 || monitorActive && !monitorPending) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const pendingCaller = callerQueue.shift();
|
|
586
|
+
const pendingMonitor = monitorPending;
|
|
587
|
+
monitorPending = undefined;
|
|
588
|
+
operationInFlight = true;
|
|
589
|
+
void sourceIterator.next().then(result => {
|
|
590
|
+
operationInFlight = false;
|
|
591
|
+
if (result.done) {
|
|
592
|
+
terminalResult = result;
|
|
593
|
+
removeAbortListener();
|
|
594
|
+
}
|
|
595
|
+
pendingCaller.resolve(result);
|
|
596
|
+
pendingMonitor?.resolve(result);
|
|
597
|
+
if (result.done) {
|
|
598
|
+
settleCallersTerminal();
|
|
599
|
+
} else {
|
|
600
|
+
pump();
|
|
601
|
+
}
|
|
602
|
+
}, error => {
|
|
603
|
+
operationInFlight = false;
|
|
604
|
+
terminalError = error;
|
|
605
|
+
hasTerminalError = true;
|
|
606
|
+
removeAbortListener();
|
|
607
|
+
pendingCaller.reject(error);
|
|
608
|
+
pendingMonitor?.reject(error);
|
|
609
|
+
settleCallersTerminal();
|
|
610
|
+
});
|
|
468
611
|
};
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
612
|
+
const monitoringStream = {
|
|
613
|
+
[Symbol.asyncIterator]() {
|
|
614
|
+
return {
|
|
615
|
+
next: () => {
|
|
616
|
+
if (hasTerminalError) {
|
|
617
|
+
return Promise.reject(terminalError);
|
|
618
|
+
}
|
|
619
|
+
if (terminalResult) {
|
|
620
|
+
return Promise.resolve(terminalResult);
|
|
621
|
+
}
|
|
622
|
+
if (bufferedMonitorResult) {
|
|
623
|
+
const result = bufferedMonitorResult;
|
|
624
|
+
bufferedMonitorResult = undefined;
|
|
625
|
+
return Promise.resolve(result);
|
|
626
|
+
}
|
|
627
|
+
return new Promise((resolve, reject) => {
|
|
628
|
+
monitorPending = {
|
|
629
|
+
resolve,
|
|
630
|
+
reject
|
|
631
|
+
};
|
|
632
|
+
pump();
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
return: async value => {
|
|
636
|
+
monitorActive = false;
|
|
637
|
+
monitorPending = undefined;
|
|
638
|
+
pump();
|
|
639
|
+
return {
|
|
640
|
+
done: true,
|
|
641
|
+
value: value
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const cancelSource = value => {
|
|
648
|
+
if (cancellationPromise) {
|
|
649
|
+
return cancellationPromise;
|
|
650
|
+
}
|
|
651
|
+
removeAbortListener();
|
|
652
|
+
if (!controller.signal.aborted) {
|
|
653
|
+
controller.abort();
|
|
654
|
+
}
|
|
655
|
+
cancellationPromise = (async () => {
|
|
656
|
+
try {
|
|
657
|
+
const defaultResult = {
|
|
658
|
+
done: true,
|
|
659
|
+
value
|
|
660
|
+
};
|
|
661
|
+
const result = sourceIterator.return ? await sourceIterator.return(value) : defaultResult;
|
|
662
|
+
if (result.done) {
|
|
663
|
+
terminalResult = result;
|
|
664
|
+
removeAbortListener();
|
|
665
|
+
settleMonitorTerminal();
|
|
666
|
+
settleCallersTerminal();
|
|
667
|
+
} else if (monitorPending) {
|
|
668
|
+
monitorPending.resolve(result);
|
|
669
|
+
monitorPending = undefined;
|
|
670
|
+
cancellationPromise = undefined;
|
|
671
|
+
} else {
|
|
672
|
+
bufferedMonitorResult = result;
|
|
673
|
+
cancellationPromise = undefined;
|
|
674
|
+
}
|
|
675
|
+
return result;
|
|
676
|
+
} catch (error) {
|
|
677
|
+
terminalError = error;
|
|
678
|
+
hasTerminalError = true;
|
|
679
|
+
removeAbortListener();
|
|
680
|
+
settleMonitorTerminal();
|
|
681
|
+
settleCallersTerminal();
|
|
682
|
+
throw error;
|
|
683
|
+
}
|
|
684
|
+
})();
|
|
685
|
+
// An AbortController cancellation has no caller awaiting this promise.
|
|
686
|
+
void cancellationPromise.catch(() => undefined);
|
|
687
|
+
return cancellationPromise;
|
|
508
688
|
};
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
event: eventType,
|
|
512
|
-
properties,
|
|
513
|
-
groups: options.groups
|
|
689
|
+
abortListener = () => {
|
|
690
|
+
void cancelSource();
|
|
514
691
|
};
|
|
515
|
-
if (
|
|
516
|
-
|
|
692
|
+
if (controller.signal.aborted) {
|
|
693
|
+
abortListener();
|
|
517
694
|
} else {
|
|
518
|
-
|
|
695
|
+
controller.signal.addEventListener('abort', abortListener, {
|
|
696
|
+
once: true
|
|
697
|
+
});
|
|
519
698
|
}
|
|
520
|
-
|
|
699
|
+
const callerStream = createStream(() => ({
|
|
700
|
+
next: () => {
|
|
701
|
+
if (hasTerminalError) {
|
|
702
|
+
return Promise.reject(terminalError);
|
|
703
|
+
}
|
|
704
|
+
if (terminalResult) {
|
|
705
|
+
return Promise.resolve(terminalResult);
|
|
706
|
+
}
|
|
707
|
+
return new Promise((resolve, reject) => {
|
|
708
|
+
callerQueue.push({
|
|
709
|
+
resolve,
|
|
710
|
+
reject
|
|
711
|
+
});
|
|
712
|
+
pump();
|
|
713
|
+
});
|
|
714
|
+
},
|
|
715
|
+
return: value => cancelSource(value),
|
|
716
|
+
throw: async error => {
|
|
717
|
+
if (!sourceIterator.throw) {
|
|
718
|
+
await cancelSource();
|
|
719
|
+
throw error;
|
|
720
|
+
}
|
|
721
|
+
try {
|
|
722
|
+
const result = await sourceIterator.throw(error);
|
|
723
|
+
if (result.done) {
|
|
724
|
+
terminalResult = result;
|
|
725
|
+
removeAbortListener();
|
|
726
|
+
settleCallersTerminal();
|
|
727
|
+
}
|
|
728
|
+
if (monitorPending) {
|
|
729
|
+
monitorPending.resolve(result);
|
|
730
|
+
monitorPending = undefined;
|
|
731
|
+
} else {
|
|
732
|
+
bufferedMonitorResult = result;
|
|
733
|
+
}
|
|
734
|
+
return result;
|
|
735
|
+
} catch (sourceError) {
|
|
736
|
+
terminalError = sourceError;
|
|
737
|
+
hasTerminalError = true;
|
|
738
|
+
removeAbortListener();
|
|
739
|
+
settleMonitorTerminal();
|
|
740
|
+
settleCallersTerminal();
|
|
741
|
+
throw sourceError;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}), controller);
|
|
745
|
+
return [monitoringStream, callerStream];
|
|
746
|
+
}
|
|
521
747
|
|
|
522
748
|
class PostHogAnthropic extends AnthropicOriginal {
|
|
523
749
|
constructor(config) {
|
|
@@ -559,8 +785,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
|
|
|
559
785
|
webSearchCount: 0
|
|
560
786
|
};
|
|
561
787
|
let lastRawUsage;
|
|
562
|
-
if (
|
|
563
|
-
const [stream1, stream2] = value
|
|
788
|
+
if (Symbol.asyncIterator in value) {
|
|
789
|
+
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
564
790
|
(async () => {
|
|
565
791
|
try {
|
|
566
792
|
for await (const chunk of stream1) {
|