@posthog/ai 8.14.5 → 8.15.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 (47) hide show
  1. package/README.md +14 -0
  2. package/dist/adk/index.cjs +12 -5
  3. package/dist/adk/index.cjs.map +1 -1
  4. package/dist/adk/index.mjs +12 -5
  5. package/dist/adk/index.mjs.map +1 -1
  6. package/dist/anthropic/index.cjs +12 -5
  7. package/dist/anthropic/index.cjs.map +1 -1
  8. package/dist/anthropic/index.mjs +12 -5
  9. package/dist/anthropic/index.mjs.map +1 -1
  10. package/dist/claude-agent-sdk/index.cjs +12 -5
  11. package/dist/claude-agent-sdk/index.cjs.map +1 -1
  12. package/dist/claude-agent-sdk/index.mjs +12 -5
  13. package/dist/claude-agent-sdk/index.mjs.map +1 -1
  14. package/dist/gemini/index.cjs +377 -5
  15. package/dist/gemini/index.cjs.map +1 -1
  16. package/dist/gemini/index.d.ts +28 -0
  17. package/dist/gemini/index.mjs +377 -5
  18. package/dist/gemini/index.mjs.map +1 -1
  19. package/dist/index.cjs +12 -5
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.mjs +12 -5
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/langchain/index.cjs +12 -5
  24. package/dist/langchain/index.cjs.map +1 -1
  25. package/dist/langchain/index.mjs +12 -5
  26. package/dist/langchain/index.mjs.map +1 -1
  27. package/dist/langchain/middleware/index.cjs +12 -5
  28. package/dist/langchain/middleware/index.cjs.map +1 -1
  29. package/dist/langchain/middleware/index.mjs +12 -5
  30. package/dist/langchain/middleware/index.mjs.map +1 -1
  31. package/dist/openai/index.cjs +12 -5
  32. package/dist/openai/index.cjs.map +1 -1
  33. package/dist/openai/index.mjs +12 -5
  34. package/dist/openai/index.mjs.map +1 -1
  35. package/dist/openai-agents/index.cjs +12 -5
  36. package/dist/openai-agents/index.cjs.map +1 -1
  37. package/dist/openai-agents/index.mjs +12 -5
  38. package/dist/openai-agents/index.mjs.map +1 -1
  39. package/dist/otel/index.cjs +11 -4
  40. package/dist/otel/index.cjs.map +1 -1
  41. package/dist/otel/index.mjs +11 -4
  42. package/dist/otel/index.mjs.map +1 -1
  43. package/dist/vercel/index.cjs +12 -5
  44. package/dist/vercel/index.cjs.map +1 -1
  45. package/dist/vercel/index.mjs +12 -5
  46. package/dist/vercel/index.mjs.map +1 -1
  47. package/package.json +4 -3
@@ -96,6 +96,12 @@ const FILE_FAMILY_TYPES = /* @__PURE__ */ new Set([
96
96
  "media",
97
97
  "file-data"
98
98
  ]);
99
+ const TYPED_BINARY_DATA_TYPES = /* @__PURE__ */ new Set([
100
+ "image",
101
+ "audio",
102
+ "video",
103
+ "document"
104
+ ]);
99
105
  const KNOWN_AUDIO_FORMATS = /* @__PURE__ */ new Set([
100
106
  "wav",
101
107
  "mp3",
@@ -147,7 +153,8 @@ var MediaTypeContext = class MediaTypeContext {
147
153
  if (key.includes("image")) return "image";
148
154
  if (key.includes("file") || key.includes("document")) return "application/octet-stream";
149
155
  }
150
- hasExplicitBinaryMediaType() {
156
+ hasDefiniteBinaryData() {
157
+ if (this.parent && this.key === "data" && typeof this.parent.type === "string" && TYPED_BINARY_DATA_TYPES.has(this.parent.type)) return true;
151
158
  if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
152
159
  const mediaType = this.inferFromSiblingMime();
153
160
  return mediaType !== void 0 && !mediaType.toLowerCase().startsWith("text/");
@@ -201,9 +208,9 @@ var BinaryContentRedactor = class {
201
208
  return out;
202
209
  }
203
210
  redactString(value, ctx) {
204
- const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
205
- const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, "") : value;
206
- const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
211
+ const hasDefiniteBinaryData = ctx.hasDefiniteBinaryData();
212
+ const recognitionValue = hasDefiniteBinaryData ? value.replace(/[\r\n]/g, "") : value;
213
+ const minLength = hasDefiniteBinaryData ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
207
214
  const recognition = this.recognizer.recognize(recognitionValue, minLength);
208
215
  switch (recognition.kind) {
209
216
  case "data-url": return this.placeholderFor(recognition.mediaType);
@@ -456,7 +463,7 @@ function addDefaults(params) {
456
463
  }
457
464
  //#endregion
458
465
  //#region package.json
459
- var version = "8.14.5";
466
+ var version = "8.15.0";
460
467
  //#endregion
461
468
  //#region src/serializeError.ts
462
469
  const DEFAULT_MAX_DEPTH = 3;
@@ -722,6 +729,370 @@ function mapGeminiUsage(metadata, additionalUsage = {}) {
722
729
  };
723
730
  }
724
731
  //#endregion
732
+ //#region src/gemini/interactions.ts
733
+ function record(value) {
734
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
735
+ }
736
+ function string(value) {
737
+ return typeof value === "string" ? value : void 0;
738
+ }
739
+ function number(value) {
740
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
741
+ }
742
+ function isStream(value) {
743
+ return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
744
+ }
745
+ function usageFromInteraction(value, model) {
746
+ const usage = record(value);
747
+ if (!usage) return {};
748
+ const cached = number(usage.total_cached_tokens);
749
+ const searches = Array.isArray(usage.grounding_tool_count) ? usage.grounding_tool_count.reduce((total, entry) => {
750
+ const grounding = record(entry);
751
+ return total + (grounding?.type === "google_search" ? number(grounding.count) ?? 0 : 0);
752
+ }, 0) : void 0;
753
+ const modelMajor = /^(?:models\/)?gemini-(\d+)/.exec(model ?? "")?.[1];
754
+ const webSearchCount = modelMajor === void 0 ? void 0 : Number(modelMajor) >= 3 ? searches : searches ? 1 : 0;
755
+ return {
756
+ inputTokens: number(usage.total_input_tokens),
757
+ outputTokens: number(usage.total_output_tokens),
758
+ reasoningTokens: number(usage.total_thought_tokens),
759
+ cacheReadInputTokens: cached,
760
+ ...cached !== void 0 ? { cacheReportingExclusive: false } : {},
761
+ webSearchCount,
762
+ rawUsage: usage
763
+ };
764
+ }
765
+ function functionArguments(value) {
766
+ if (typeof value !== "string") return record(value) ?? {};
767
+ try {
768
+ return record(JSON.parse(value)) ?? value;
769
+ } catch {
770
+ return value;
771
+ }
772
+ }
773
+ function outputFromSteps(steps, fallbackText) {
774
+ const content = [];
775
+ if (Array.isArray(steps)) for (const rawStep of steps) {
776
+ const step = record(rawStep);
777
+ if (step?.type === "model_output" && Array.isArray(step.content)) content.push(...step.content);
778
+ else if (step?.type === "function_call" && string(step.name)) content.push({
779
+ type: "function",
780
+ id: string(step.id),
781
+ function: {
782
+ name: step.name,
783
+ arguments: functionArguments(step.arguments)
784
+ }
785
+ });
786
+ }
787
+ if (content.length === 0 && fallbackText) content.push({
788
+ type: "text",
789
+ text: fallbackText
790
+ });
791
+ return content.length ? [{
792
+ role: "assistant",
793
+ content
794
+ }] : [];
795
+ }
796
+ function inputFromRequest(input, systemInstruction) {
797
+ const messages = [];
798
+ if (typeof systemInstruction === "string") messages.push({
799
+ role: "system",
800
+ content: systemInstruction
801
+ });
802
+ let userContent = [];
803
+ const flushUserContent = () => {
804
+ if (userContent.length) messages.push({
805
+ role: "user",
806
+ content: userContent
807
+ });
808
+ userContent = [];
809
+ };
810
+ for (const part of Array.isArray(input) ? input : [input]) {
811
+ const step = record(part);
812
+ if (step?.type === "text" || step?.type === "image" || step?.type === "audio" || step?.type === "video" || step?.type === "document") {
813
+ userContent.push(part);
814
+ continue;
815
+ }
816
+ flushUserContent();
817
+ if (step?.type === "function_result") messages.push({
818
+ role: "tool",
819
+ content: [{
820
+ type: "tool_result",
821
+ tool_use_id: step.call_id,
822
+ content: step.result
823
+ }]
824
+ });
825
+ else if (step?.type === "user_input") messages.push({
826
+ role: "user",
827
+ content: step.content ?? [part]
828
+ });
829
+ else if (step?.type === "model_output") messages.push({
830
+ role: "assistant",
831
+ content: step.content ?? [part]
832
+ });
833
+ else if (step?.type === "function_call") {
834
+ const output = outputFromSteps([part]);
835
+ messages.push(...output.length ? output : [{
836
+ role: "assistant",
837
+ content: [part]
838
+ }]);
839
+ } else messages.push({
840
+ role: "user",
841
+ content: part
842
+ });
843
+ }
844
+ flushUserContent();
845
+ return messages;
846
+ }
847
+ function safeTools(tools) {
848
+ if (!Array.isArray(tools)) return void 0;
849
+ return tools.map((tool) => ({
850
+ type: string(record(tool)?.type),
851
+ name: string(record(tool)?.name)
852
+ }));
853
+ }
854
+ function isFailure(status) {
855
+ return status === "failed" || status === "cancelled" || status === "incomplete" || status === "budget_exceeded";
856
+ }
857
+ var WrappedInteractions = class {
858
+ constructor(client, phClient) {
859
+ this.client = client;
860
+ this.phClient = phClient;
861
+ }
862
+ async create(params, options) {
863
+ const { providerParams, posthogParams } = extractPosthogParams(params);
864
+ const request = providerParams;
865
+ const requestData = record(request) ?? {};
866
+ if (requestData.background === true || typeof requestData.model !== "string" || requestData.agent !== void 0) throw new Error("Gemini Interactions tracing supports foreground model calls only");
867
+ const startTime = Date.now();
868
+ const capture = async (interaction, error, output, firstTokenTime) => {
869
+ try {
870
+ const privateCapture = withPrivacyMode(this.phClient, posthogParams.privacyMode, false) === null;
871
+ const model = string(interaction?.model) ?? string(requestData.model);
872
+ const generationConfig = record(requestData.generation_config);
873
+ const modelParameters = privateCapture ? {} : {
874
+ ...number(generationConfig?.max_output_tokens) !== void 0 ? { max_tokens: generationConfig?.max_output_tokens } : {},
875
+ ...string(generationConfig?.thinking_level) !== void 0 ? { thinking_level: generationConfig?.thinking_level } : {}
876
+ };
877
+ await captureAiGeneration(this.phClient, {
878
+ ...posthogParams,
879
+ model,
880
+ provider: "gemini",
881
+ input: privateCapture ? null : inputFromRequest(sanitizeGemini(requestData.input, this.phClient), sanitizeGemini(requestData.system_instruction, this.phClient)),
882
+ output: privateCapture ? null : sanitizeGemini(output ?? outputFromSteps(interaction?.steps, string(interaction?.output_text)), this.phClient),
883
+ latency: (Date.now() - startTime) / 1e3,
884
+ timeToFirstToken: firstTokenTime === void 0 ? void 0 : (firstTokenTime - startTime) / 1e3,
885
+ baseURL: "https://generativelanguage.googleapis.com",
886
+ modelParameters,
887
+ completionId: string(interaction?.id),
888
+ stopReason: string(interaction?.status),
889
+ servedServiceTier: string(interaction?.service_tier),
890
+ usage: usageFromInteraction(interaction?.usage, model),
891
+ tools: privateCapture ? void 0 : safeTools(requestData.tools),
892
+ error: privateCapture && error ? /* @__PURE__ */ new Error("Gemini interaction failed") : error
893
+ });
894
+ } catch {}
895
+ };
896
+ try {
897
+ if (!this.client.interactions?.create) throw new Error("Gemini Interactions requires @google/genai 2.18.0 or newer");
898
+ const result = await (options === void 0 ? this.client.interactions.create(request) : this.client.interactions.create(request, options));
899
+ if (requestData.stream === true) {
900
+ if (!isStream(result)) throw new Error("Gemini Interactions did not return a stream");
901
+ return this.wrapStream(result, capture);
902
+ }
903
+ if (isStream(result)) throw new Error("Gemini Interactions returned an unexpected stream");
904
+ const interaction = record(result);
905
+ let unsupportedSchema = false;
906
+ try {
907
+ unsupportedSchema = Array.isArray(interaction?.outputs) && !Array.isArray(interaction?.steps);
908
+ } catch {
909
+ return result;
910
+ }
911
+ if (unsupportedSchema) throw new Error("Gemini Interactions GA schema requires @google/genai 2.18.0 or newer");
912
+ try {
913
+ const status = interaction?.status;
914
+ if (status !== "queued" && status !== "in_progress") await capture(interaction, isFailure(status) ? /* @__PURE__ */ new Error(`Gemini interaction ${status}`) : void 0);
915
+ } catch {}
916
+ return result;
917
+ } catch (error) {
918
+ await capture(void 0, error);
919
+ throw error;
920
+ }
921
+ }
922
+ wrapStream(stream, capture) {
923
+ const reader = "getReader" in stream && typeof stream.getReader === "function" ? stream.getReader() : void 0;
924
+ const iterator = reader ? void 0 : stream[Symbol.asyncIterator]();
925
+ let readerReleased = false;
926
+ let errorIterator;
927
+ let readError;
928
+ const next = async () => {
929
+ if (!reader) return iterator.next();
930
+ try {
931
+ return await reader.read();
932
+ } catch (providerError) {
933
+ if (closed) throw providerError;
934
+ readError = providerError;
935
+ release();
936
+ try {
937
+ errorIterator = stream[Symbol.asyncIterator]();
938
+ } catch {
939
+ throw providerError;
940
+ }
941
+ try {
942
+ await errorIterator.next();
943
+ } finally {
944
+ try {
945
+ await errorIterator.return?.();
946
+ } catch {}
947
+ }
948
+ throw providerError;
949
+ }
950
+ };
951
+ const cancelSource = async (reason) => {
952
+ if (reader && !readerReleased) await reader.cancel(reason);
953
+ else if (reader) {
954
+ try {
955
+ await errorIterator?.return?.();
956
+ } catch {}
957
+ throw readError;
958
+ } else await iterator?.return?.();
959
+ };
960
+ const release = () => {
961
+ if (readerReleased) return;
962
+ try {
963
+ reader?.releaseLock();
964
+ readerReleased = true;
965
+ } catch {}
966
+ };
967
+ let interaction;
968
+ let latestUsage;
969
+ let finalUsage;
970
+ let error;
971
+ let firstTokenTime;
972
+ const steps = /* @__PURE__ */ new Map();
973
+ let completed = false;
974
+ let closed = false;
975
+ let cancelled = false;
976
+ let captured = false;
977
+ const observe = (event) => {
978
+ let schemaError;
979
+ try {
980
+ const item = record(event);
981
+ const eventType = item?.event_type;
982
+ if (eventType === "interaction.start" || eventType === "interaction.complete" || eventType === "interaction.delta" || typeof eventType === "string" && eventType.startsWith("content.")) schemaError = /* @__PURE__ */ new Error("Gemini Interactions GA schema requires @google/genai 2.18.0 or newer");
983
+ else {
984
+ const usage = record(record(item?.metadata)?.total_usage) ?? (eventType === "step.stop" ? record(item?.usage) : void 0);
985
+ if (usage) latestUsage = { ...usage };
986
+ if (eventType === "interaction.created") {
987
+ interaction = { ...record(item?.interaction) };
988
+ const initialUsage = record(interaction.usage);
989
+ if (initialUsage) latestUsage = { ...initialUsage };
990
+ } else if (eventType === "step.start") {
991
+ const index = number(item?.index);
992
+ const step = record(item?.step);
993
+ if (index !== void 0) steps.set(index, {
994
+ ...step,
995
+ content: Array.isArray(step?.content) ? step.content.map((item) => ({ ...record(item) })) : []
996
+ });
997
+ } else if (eventType === "step.delta") {
998
+ const index = number(item?.index);
999
+ const delta = record(item?.delta);
1000
+ const step = index === void 0 ? void 0 : steps.get(index);
1001
+ if (step && delta?.type === "text" && typeof delta.text === "string") {
1002
+ const last = step.content?.at(-1);
1003
+ const text = record(last);
1004
+ if (text?.type === "text") text.text = `${text.text ?? ""}${delta.text}`;
1005
+ else step.content?.push({
1006
+ type: "text",
1007
+ text: delta.text
1008
+ });
1009
+ firstTokenTime ??= Date.now();
1010
+ } else if (step && (delta?.type === "image" || delta?.type === "audio" || delta?.type === "video" || delta?.type === "document")) {
1011
+ step.content?.push({ ...delta });
1012
+ firstTokenTime ??= Date.now();
1013
+ } else if (step && delta?.type === "arguments_delta" && typeof delta.arguments === "string") {
1014
+ step.argumentChunks = `${step.argumentChunks ?? ""}${delta.arguments}`;
1015
+ firstTokenTime ??= Date.now();
1016
+ }
1017
+ } else if (eventType === "interaction.completed") {
1018
+ const result = record(item?.interaction);
1019
+ interaction = {
1020
+ ...interaction,
1021
+ ...result
1022
+ };
1023
+ completed = interaction.status !== "queued" && interaction.status !== "in_progress";
1024
+ const reportedUsage = record(result?.usage);
1025
+ if (reportedUsage) {
1026
+ latestUsage = { ...reportedUsage };
1027
+ if (completed) finalUsage = latestUsage;
1028
+ }
1029
+ if (isFailure(interaction.status)) error = /* @__PURE__ */ new Error(`Gemini interaction ${interaction.status}`);
1030
+ if (!completed) error = /* @__PURE__ */ new Error("Gemini interaction stream ended without a terminal result");
1031
+ } else if (eventType === "error") error = /* @__PURE__ */ new Error("Gemini interaction stream failed");
1032
+ }
1033
+ } catch {}
1034
+ if (schemaError) throw schemaError;
1035
+ };
1036
+ const finish = async (status, providerError) => {
1037
+ if (captured) return;
1038
+ captured = true;
1039
+ try {
1040
+ const streamedSteps = [...steps.entries()].sort(([left], [right]) => left - right).map(([, step]) => ({
1041
+ ...step,
1042
+ arguments: step.argumentChunks === void 0 ? step.arguments : functionArguments(step.argumentChunks)
1043
+ }));
1044
+ const output = outputFromSteps(Array.isArray(interaction?.steps) && interaction.steps.length > 0 ? interaction.steps : streamedSteps);
1045
+ await capture({
1046
+ ...interaction,
1047
+ usage: finalUsage ?? latestUsage,
1048
+ ...completed ? {} : { status }
1049
+ }, providerError ?? error ?? (!completed && status === "incomplete" ? /* @__PURE__ */ new Error("Gemini interaction stream ended without completion") : void 0), output, firstTokenTime);
1050
+ } catch {}
1051
+ };
1052
+ return new ReadableStream({
1053
+ async pull(controller) {
1054
+ try {
1055
+ const result = await next();
1056
+ if (closed) return;
1057
+ if (result.done) {
1058
+ closed = true;
1059
+ await finish("incomplete");
1060
+ release();
1061
+ if (!cancelled) controller.close();
1062
+ } else {
1063
+ observe(result.value);
1064
+ controller.enqueue(result.value);
1065
+ }
1066
+ } catch (providerError) {
1067
+ if (closed) return;
1068
+ closed = true;
1069
+ try {
1070
+ await cancelSource(providerError);
1071
+ } catch {}
1072
+ await finish("failed", providerError);
1073
+ release();
1074
+ if (!cancelled) controller.error(providerError);
1075
+ }
1076
+ },
1077
+ async cancel(reason) {
1078
+ cancelled = true;
1079
+ if (closed) return;
1080
+ closed = true;
1081
+ let cancellationError;
1082
+ try {
1083
+ await cancelSource(reason);
1084
+ } catch (providerError) {
1085
+ cancellationError = providerError;
1086
+ throw providerError;
1087
+ } finally {
1088
+ await finish("cancelled", cancellationError);
1089
+ release();
1090
+ }
1091
+ }
1092
+ });
1093
+ }
1094
+ };
1095
+ //#endregion
725
1096
  //#region src/gemini/index.ts
726
1097
  /** Append streamed text to the last block of the same type, or start a new one. */
727
1098
  function appendStreamedText(content, type, text) {
@@ -750,6 +1121,7 @@ var PostHogGoogleGenAI = class {
750
1121
  this.phClient = posthog;
751
1122
  this.client = new _google_genai.GoogleGenAI(geminiConfig);
752
1123
  this.models = new WrappedModels(this.client, this.phClient);
1124
+ this.interactions = new WrappedInteractions(this.client, this.phClient);
753
1125
  }
754
1126
  };
755
1127
  var WrappedModels = class {