@posthog/ai 3.1.1 → 3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/ai",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "description": "PostHog Node.js AI integrations",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,7 +1,6 @@
1
1
  import AnthropicOriginal from '@anthropic-ai/sdk'
2
2
  import { PostHog } from 'posthog-node'
3
3
  import { v4 as uuidv4 } from 'uuid'
4
- import { PassThrough } from 'stream'
5
4
  import { formatResponseAnthropic, mergeSystemPrompt, MonitoringParams, sendEventToPosthog } from '../utils'
6
5
 
7
6
  type MessageCreateParamsNonStreaming = AnthropicOriginal.Messages.MessageCreateParamsNonStreaming
@@ -70,17 +69,16 @@ export class WrappedMessages extends AnthropicOriginal.Messages {
70
69
 
71
70
  if (anthropicParams.stream) {
72
71
  return parentPromise.then((value) => {
73
- const passThroughStream = new PassThrough({ objectMode: true })
74
72
  let accumulatedContent = ''
75
73
  const usage: { inputTokens: number; outputTokens: number } = {
76
74
  inputTokens: 0,
77
75
  outputTokens: 0,
78
76
  }
79
77
  if ('tee' in value) {
80
- const anthropicStream = value
78
+ const [stream1, stream2] = value.tee()
81
79
  ;(async () => {
82
80
  try {
83
- for await (const chunk of anthropicStream) {
81
+ for await (const chunk of stream1) {
84
82
  if ('delta' in chunk) {
85
83
  if ('text' in chunk.delta) {
86
84
  const delta = chunk?.delta?.text ?? ''
@@ -93,7 +91,6 @@ export class WrappedMessages extends AnthropicOriginal.Messages {
93
91
  if ('usage' in chunk) {
94
92
  usage.outputTokens = chunk.usage.output_tokens ?? 0
95
93
  }
96
- passThroughStream.write(chunk)
97
94
  }
98
95
  const latency = (Date.now() - startTime) / 1000
99
96
  sendEventToPosthog({
@@ -110,7 +107,6 @@ export class WrappedMessages extends AnthropicOriginal.Messages {
110
107
  httpStatus: 200,
111
108
  usage,
112
109
  })
113
- passThroughStream.end()
114
110
  } catch (error: any) {
115
111
  // error handling
116
112
  sendEventToPosthog({
@@ -132,11 +128,13 @@ export class WrappedMessages extends AnthropicOriginal.Messages {
132
128
  isError: true,
133
129
  error: JSON.stringify(error),
134
130
  })
135
- passThroughStream.emit('error', error)
136
131
  }
137
132
  })()
133
+
134
+ // Return the other stream to the user
135
+ return stream2
138
136
  }
139
- return passThroughStream as unknown as Stream<RawMessageStreamEvent>
137
+ return value
140
138
  }) as APIPromise<Stream<RawMessageStreamEvent>>
141
139
  } else {
142
140
  const wrappedPromise = parentPromise.then(
@@ -1,7 +1,6 @@
1
1
  import OpenAIOrignal, { AzureOpenAI } from 'openai'
2
2
  import { PostHog } from 'posthog-node'
3
3
  import { v4 as uuidv4 } from 'uuid'
4
- import { PassThrough } from 'stream'
5
4
  import { formatResponseOpenAI, MonitoringParams, sendEventToPosthog } from '../utils'
6
5
 
7
6
  type ChatCompletion = OpenAIOrignal.ChatCompletion
@@ -86,7 +85,6 @@ export class WrappedCompletions extends AzureOpenAI.Chat.Completions {
86
85
 
87
86
  if (openAIParams.stream) {
88
87
  return parentPromise.then((value) => {
89
- const passThroughStream = new PassThrough({ objectMode: true })
90
88
  let accumulatedContent = ''
91
89
  let usage: { inputTokens: number; outputTokens: number } = {
92
90
  inputTokens: 0,
@@ -94,10 +92,10 @@ export class WrappedCompletions extends AzureOpenAI.Chat.Completions {
94
92
  }
95
93
  let model = openAIParams.model
96
94
  if ('tee' in value) {
97
- const openAIStream = value
95
+ const [stream1, stream2] = value.tee()
98
96
  ;(async () => {
99
97
  try {
100
- for await (const chunk of openAIStream) {
98
+ for await (const chunk of stream1) {
101
99
  const delta = chunk?.choices?.[0]?.delta?.content ?? ''
102
100
  accumulatedContent += delta
103
101
  if (chunk.usage) {
@@ -109,7 +107,6 @@ export class WrappedCompletions extends AzureOpenAI.Chat.Completions {
109
107
  outputTokens: chunk.usage.completion_tokens ?? 0,
110
108
  }
111
109
  }
112
- passThroughStream.write(chunk)
113
110
  }
114
111
  const latency = (Date.now() - startTime) / 1000
115
112
  sendEventToPosthog({
@@ -126,7 +123,6 @@ export class WrappedCompletions extends AzureOpenAI.Chat.Completions {
126
123
  httpStatus: 200,
127
124
  usage,
128
125
  })
129
- passThroughStream.end()
130
126
  } catch (error: any) {
131
127
  // error handling
132
128
  sendEventToPosthog({
@@ -148,11 +144,13 @@ export class WrappedCompletions extends AzureOpenAI.Chat.Completions {
148
144
  isError: true,
149
145
  error: JSON.stringify(error),
150
146
  })
151
- passThroughStream.emit('error', error)
152
147
  }
153
148
  })()
149
+
150
+ // Return the other stream to the user
151
+ return stream2
154
152
  }
155
- return passThroughStream as unknown as Stream<ChatCompletionChunk>
153
+ return value
156
154
  }) as APIPromise<Stream<ChatCompletionChunk>>
157
155
  } else {
158
156
  const wrappedPromise = parentPromise.then(
@@ -1,7 +1,6 @@
1
1
  import OpenAIOrignal from 'openai'
2
2
  import { PostHog } from 'posthog-node'
3
3
  import { v4 as uuidv4 } from 'uuid'
4
- import { PassThrough } from 'stream'
5
4
  import { formatResponseOpenAI, MonitoringParams, sendEventToPosthog } from '../utils'
6
5
 
7
6
  type ChatCompletion = OpenAIOrignal.ChatCompletion
@@ -87,17 +86,15 @@ export class WrappedCompletions extends OpenAIOrignal.Chat.Completions {
87
86
 
88
87
  if (openAIParams.stream) {
89
88
  return parentPromise.then((value) => {
90
- const passThroughStream = new PassThrough({ objectMode: true })
91
- let accumulatedContent = ''
92
- let usage: { inputTokens: number; outputTokens: number } = {
93
- inputTokens: 0,
94
- outputTokens: 0,
95
- }
96
89
  if ('tee' in value) {
97
- const openAIStream = value
90
+ const [stream1, stream2] = value.tee()
91
+ // Use one stream for tracking
98
92
  ;(async () => {
99
93
  try {
100
- for await (const chunk of openAIStream) {
94
+ let accumulatedContent = ''
95
+ let usage = { inputTokens: 0, outputTokens: 0 }
96
+
97
+ for await (const chunk of stream1) {
101
98
  const delta = chunk?.choices?.[0]?.delta?.content ?? ''
102
99
  accumulatedContent += delta
103
100
  if (chunk.usage) {
@@ -106,8 +103,8 @@ export class WrappedCompletions extends OpenAIOrignal.Chat.Completions {
106
103
  outputTokens: chunk.usage.completion_tokens ?? 0,
107
104
  }
108
105
  }
109
- passThroughStream.write(chunk)
110
106
  }
107
+
111
108
  const latency = (Date.now() - startTime) / 1000
112
109
  sendEventToPosthog({
113
110
  client: this.phClient,
@@ -123,9 +120,7 @@ export class WrappedCompletions extends OpenAIOrignal.Chat.Completions {
123
120
  httpStatus: 200,
124
121
  usage,
125
122
  })
126
- passThroughStream.end()
127
123
  } catch (error: any) {
128
- // error handling
129
124
  sendEventToPosthog({
130
125
  client: this.phClient,
131
126
  distinctId: posthogDistinctId ?? traceId,
@@ -138,18 +133,17 @@ export class WrappedCompletions extends OpenAIOrignal.Chat.Completions {
138
133
  baseURL: (this as any).baseURL ?? '',
139
134
  params: body,
140
135
  httpStatus: error?.status ? error.status : 500,
141
- usage: {
142
- inputTokens: 0,
143
- outputTokens: 0,
144
- },
136
+ usage: { inputTokens: 0, outputTokens: 0 },
145
137
  isError: true,
146
138
  error: JSON.stringify(error),
147
139
  })
148
- passThroughStream.emit('error', error)
149
140
  }
150
141
  })()
142
+
143
+ // Return the other stream to the user
144
+ return stream2
151
145
  }
152
- return passThroughStream as unknown as Stream<ChatCompletionChunk>
146
+ return value
153
147
  }) as APIPromise<Stream<ChatCompletionChunk>>
154
148
  } else {
155
149
  const wrappedPromise = parentPromise.then(