@posthog/ai 4.1.0 → 4.2.1

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/src/utils.ts CHANGED
@@ -7,7 +7,7 @@ type ChatCompletionCreateParamsBase = OpenAIOrignal.Chat.Completions.ChatComplet
7
7
  type MessageCreateParams = AnthropicOriginal.Messages.MessageCreateParams
8
8
 
9
9
  // limit large outputs by truncating to 200kb (approx 200k bytes)
10
- const MAX_OUTPUT_SIZE = 200000
10
+ export const MAX_OUTPUT_SIZE = 200000
11
11
  const STRING_FORMAT = 'utf8'
12
12
 
13
13
  export interface MonitoringParams {
@@ -19,6 +19,7 @@ export interface MonitoringParams {
19
19
  posthogModelOverride?: string
20
20
  posthogProviderOverride?: string
21
21
  posthogCostOverride?: CostOverride
22
+ fullDebug?: boolean
22
23
  }
23
24
 
24
25
  export interface CostOverride {
@@ -148,6 +149,7 @@ export type SendEventToPosthogParams = {
148
149
  isError?: boolean
149
150
  error?: string
150
151
  tools?: any
152
+ fullDebug?: boolean
151
153
  }
152
154
 
153
155
  function sanitizeValues(obj: any): any {
@@ -181,6 +183,7 @@ export const sendEventToPosthog = ({
181
183
  isError = false,
182
184
  error,
183
185
  tools,
186
+ fullDebug = false,
184
187
  }: SendEventToPosthogParams): void => {
185
188
  if (client.capture) {
186
189
  // sanitize input and output for UTF-8 validity
@@ -212,28 +215,49 @@ export const sendEventToPosthog = ({
212
215
  ...(usage.cacheCreationInputTokens ? { $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens } : {}),
213
216
  }
214
217
 
218
+ const properties = {
219
+ $ai_provider: params.posthogProviderOverride ?? provider,
220
+ $ai_model: params.posthogModelOverride ?? model,
221
+ $ai_model_parameters: getModelParams(params),
222
+ $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
223
+ $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
224
+ $ai_http_status: httpStatus,
225
+ $ai_input_tokens: usage.inputTokens ?? 0,
226
+ $ai_output_tokens: usage.outputTokens ?? 0,
227
+ ...additionalTokenValues,
228
+ $ai_latency: latency,
229
+ $ai_trace_id: traceId,
230
+ $ai_base_url: baseURL,
231
+ ...params.posthogProperties,
232
+ ...(distinctId ? {} : { $process_person_profile: false }),
233
+ ...(tools ? { $ai_tools: tools } : {}),
234
+ ...errorData,
235
+ ...costOverrideData,
236
+ }
237
+
238
+ if (fullDebug) {
239
+ // @ts-ignore
240
+ console.log('Sending event to PostHog', JSON.stringify(properties))
241
+ try {
242
+ // @ts-ignore
243
+ console.log(
244
+ 'Size of properties (kb)',
245
+ Math.round((Buffer.byteLength(JSON.stringify(properties), STRING_FORMAT) / 1024) * 10000) / 10000
246
+ )
247
+ // @ts-ignore
248
+ console.log(
249
+ 'Size of properties (mb)',
250
+ Math.round((Buffer.byteLength(JSON.stringify(properties), STRING_FORMAT) / 1024 / 1024) * 10000) / 10000
251
+ )
252
+ } catch (error) {
253
+ console.error('Error printing size of properties', error)
254
+ }
255
+ }
256
+
215
257
  client.capture({
216
258
  distinctId: distinctId ?? traceId,
217
259
  event: '$ai_generation',
218
- properties: {
219
- $ai_provider: params.posthogProviderOverride ?? provider,
220
- $ai_model: params.posthogModelOverride ?? model,
221
- $ai_model_parameters: getModelParams(params),
222
- $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
223
- $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
224
- $ai_http_status: httpStatus,
225
- $ai_input_tokens: usage.inputTokens ?? 0,
226
- $ai_output_tokens: usage.outputTokens ?? 0,
227
- ...additionalTokenValues,
228
- $ai_latency: latency,
229
- $ai_trace_id: traceId,
230
- $ai_base_url: baseURL,
231
- ...params.posthogProperties,
232
- ...(distinctId ? {} : { $process_person_profile: false }),
233
- ...(tools ? { $ai_tools: tools } : {}),
234
- ...errorData,
235
- ...costOverrideData,
236
- },
260
+ properties,
237
261
  groups: params.posthogGroups,
238
262
  })
239
263
  }
@@ -2,7 +2,8 @@ import { experimental_wrapLanguageModel as wrapLanguageModel } from 'ai'
2
2
  import type { LanguageModelV1, LanguageModelV1Middleware, LanguageModelV1Prompt, LanguageModelV1StreamPart } from 'ai'
3
3
  import { v4 as uuidv4 } from 'uuid'
4
4
  import { PostHog } from 'posthog-node'
5
- import { CostOverride, sendEventToPosthog, truncate } from '../utils'
5
+ import { CostOverride, sendEventToPosthog, truncate, MAX_OUTPUT_SIZE } from '../utils'
6
+ import { Buffer } from 'buffer'
6
7
 
7
8
  interface ClientOptions {
8
9
  posthogDistinctId?: string
@@ -13,6 +14,7 @@ interface ClientOptions {
13
14
  posthogModelOverride?: string
14
15
  posthogProviderOverride?: string
15
16
  posthogCostOverride?: CostOverride
17
+ fullDebug?: boolean
16
18
  }
17
19
 
18
20
  interface CreateInstrumentationMiddlewareOptions {
@@ -24,6 +26,7 @@ interface CreateInstrumentationMiddlewareOptions {
24
26
  posthogModelOverride?: string
25
27
  posthogProviderOverride?: string
26
28
  posthogCostOverride?: CostOverride
29
+ fullDebug?: boolean
27
30
  }
28
31
 
29
32
  interface PostHogInput {
@@ -58,7 +61,9 @@ const mapVercelPrompt = (prompt: LanguageModelV1Prompt): PostHogInput[] => {
58
61
  } else {
59
62
  promptsArray = prompt
60
63
  }
61
- return promptsArray.map((p) => {
64
+
65
+ // Map and truncate individual content
66
+ const inputs: PostHogInput[] = promptsArray.map((p) => {
62
67
  let content = {}
63
68
  if (Array.isArray(p.content)) {
64
69
  content = p.content.map((c: any) => {
@@ -119,6 +124,27 @@ const mapVercelPrompt = (prompt: LanguageModelV1Prompt): PostHogInput[] => {
119
124
  content,
120
125
  }
121
126
  })
127
+ try {
128
+ // Trim the inputs array until its JSON size fits within MAX_OUTPUT_SIZE
129
+ let serialized = JSON.stringify(inputs)
130
+ let removedCount = 0
131
+ while (Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE && inputs.length > 0) {
132
+ inputs.shift()
133
+ removedCount++
134
+ serialized = JSON.stringify(inputs)
135
+ }
136
+ if (removedCount > 0) {
137
+ // Add one placeholder to indicate how many were removed
138
+ inputs.unshift({
139
+ role: 'assistant',
140
+ content: `[${removedCount} message${removedCount === 1 ? '' : 's'} removed due to size limit]`,
141
+ })
142
+ }
143
+ } catch (error) {
144
+ console.error('Error stringifying inputs', error)
145
+ return [{ role: 'posthog', content: 'An error occurred while processing your request. Please try again.' }]
146
+ }
147
+ return inputs
122
148
  }
123
149
 
124
150
  const mapVercelOutput = (result: any): PostHogInput[] => {
@@ -215,6 +241,7 @@ export const createInstrumentationMiddleware = (
215
241
  outputTokens: result.usage.completionTokens,
216
242
  ...additionalTokenValues,
217
243
  },
244
+ fullDebug: options.fullDebug,
218
245
  })
219
246
 
220
247
  return result
@@ -238,6 +265,7 @@ export const createInstrumentationMiddleware = (
238
265
  },
239
266
  isError: true,
240
267
  error: truncate(JSON.stringify(error)),
268
+ fullDebug: options.fullDebug,
241
269
  })
242
270
  throw error
243
271
  }
@@ -304,6 +332,7 @@ export const createInstrumentationMiddleware = (
304
332
  params: mergedParams as any,
305
333
  httpStatus: 200,
306
334
  usage,
335
+ fullDebug: options.fullDebug,
307
336
  })
308
337
  },
309
338
  })
@@ -331,6 +360,7 @@ export const createInstrumentationMiddleware = (
331
360
  },
332
361
  isError: true,
333
362
  error: truncate(JSON.stringify(error)),
363
+ fullDebug: options.fullDebug,
334
364
  })
335
365
  throw error
336
366
  }