UNPKG

@posthog/ai

Version:
250 lines (243 loc) 8.69 kB
import { LanguageModelV2, LanguageModelV3 } from '@ai-sdk/provider'; import { PostHog } from 'posthog-node'; /** * Token usage information for AI model responses */ interface TokenUsage { inputTokens?: number; outputTokens?: number; reasoningTokens?: unknown; cacheReadInputTokens?: unknown; cacheCreationInputTokens?: unknown; webSearchCount?: number; rawUsage?: unknown; } /** * Options for fetching a prompt */ interface GetPromptOptions { cacheTtlSeconds?: number; fallback?: string; version?: number; } /** * Result from the Prompts API or local cache — carries real metadata. */ interface PromptRemoteResult { source: 'api' | 'cache' | 'stale_cache'; prompt: string; name: string; version: number; } /** * Result when the fetch failed and no cache was available — fell back to the * hardcoded fallback string. name and version are undefined so they remain * accessible on the PromptResult union without a type guard. */ interface PromptCodeFallbackResult { source: 'code_fallback'; prompt: string; name: undefined; version: undefined; } /** * Discriminated union returned by `Prompts.get()`. * * Narrow on `source` to guarantee metadata, or access `result.name` / * `result.version` directly as `string | undefined` / `number | undefined`. */ type PromptResult = PromptRemoteResult | PromptCodeFallbackResult; /** * Variables for prompt compilation */ type PromptVariables = Record<string, string | number | boolean>; /** * Direct options for initializing Prompts without a PostHog client */ interface PromptsDirectOptions { personalApiKey: string; projectApiKey: string; host?: string; defaultCacheTtlSeconds?: number; } interface CostOverride { inputCost: number; outputCost: number; } declare enum AIEvent { Generation = "$ai_generation", Embedding = "$ai_embedding" } type LanguageModel = LanguageModelV2 | LanguageModelV3; interface ClientOptions { posthogDistinctId?: string; posthogTraceId?: string; posthogProperties?: Record<string, any>; posthogPrivacyMode?: boolean; posthogGroups?: Record<string, any>; posthogModelOverride?: string; posthogProviderOverride?: string; posthogCostOverride?: CostOverride; posthogCaptureImmediate?: boolean; } /** * Wraps a Vercel AI SDK language model (V2 or V3) with PostHog tracing. * Automatically detects the model version and applies appropriate instrumentation. */ declare const wrapVercelLanguageModel: <T extends LanguageModel>(model: T, phClient: PostHog, options: ClientOptions) => T; interface PromptsWithPostHogOptions { posthog: PostHog; defaultCacheTtlSeconds?: number; } type PromptsOptions = PromptsWithPostHogOptions | PromptsDirectOptions; /** * Prompts class for fetching and compiling LLM prompts from PostHog * * @example * ```ts * // With PostHog client * const prompts = new Prompts({ posthog }) * * // Or with direct options (no PostHog client needed) * const prompts = new Prompts({ * personalApiKey: 'phx_xxx', * projectApiKey: 'phc_xxx', * host: 'https://us.posthog.com', * }) * * // Fetch with caching and fallback * const result = await prompts.get('support-system-prompt', { * cacheTtlSeconds: 300, * fallback: 'You are a helpful assistant.', * }) * * // Or fetch an exact published version * const v3 = await prompts.get('support-system-prompt', { * version: 3, * }) * * // Compile with variables * const systemPrompt = prompts.compile(result.prompt, { * company: 'Acme Corp', * tier: 'premium', * }) * ``` */ declare class Prompts { private personalApiKey; private projectApiKey; private host; private defaultCacheTtlSeconds; private cache; constructor(options: PromptsOptions); private getPromptCache; private getOrCreatePromptCache; private getPromptLabel; /** * Fetch a prompt by name from the PostHog API. * * Returns a `PromptResult` object carrying the prompt text alongside `source`, * `name`, and `version` metadata. Read `result.prompt` for the template string. */ get(name: string, options?: GetPromptOptions): Promise<PromptResult>; /** * Internal method that handles cache + fetch logic, returning full metadata. * Does NOT handle the string `fallback` option — callers handle that. */ private getInternal; /** * Compile a prompt template with variable substitution * * Variables in the format `{{variableName}}` will be replaced with values from the variables object. * Unmatched variables are left unchanged. * * @param prompt - The prompt template string * @param variables - Object containing variable values * @returns The compiled prompt string */ compile(prompt: string, variables: PromptVariables): string; /** * Clear the cache for a specific prompt or all prompts * * @param name - Optional prompt name to clear. If provided, clears all cached versions for that prompt unless a version is also provided. * @param version - Optional prompt version to clear. Requires a prompt name. */ clearCache(name?: string, version?: number): void; private fetchPromptFromApi; } /** * Options for `captureAiGeneration`. Mirrors the `$ai_generation` event shape * directly so that any caller — first-party SDK wrappers and external code * alike — produces an identical event. */ interface CaptureAiGenerationOptions { distinctId?: string; /** Auto-generated when omitted. */ traceId?: string; /** Defaults to `$ai_generation`. */ eventType?: AIEvent; /** Required for the event to be useful, but accepted as optional so SDK wrappers can pass through whatever they detect. */ model?: string; provider: string; input: unknown; output: unknown; /** Maps to `$ai_model_parameters` (temperature, max_tokens, top_p, …). */ modelParameters?: Record<string, unknown>; baseURL?: string; httpStatus?: number; /** Wall-clock latency in seconds. */ latency?: number; /** Time from request start to the first streamed token, in seconds. */ timeToFirstToken?: number; usage?: TokenUsage; /** Extra event properties merged into the captured event. */ properties?: Record<string, unknown>; /** Mapping of group type to group id, matching `EventMessage.groups`. */ groups?: Record<string, string | number>; privacyMode?: boolean; /** * For SDK wrappers: overrides the auto-detected model. External callers * should pass `model` directly instead. */ modelOverride?: string; /** * For SDK wrappers: overrides the auto-detected provider. External callers * should pass `provider` directly instead. */ providerOverride?: string; costOverride?: CostOverride; tools?: unknown[] | null; stopReason?: string; /** * Provider-assigned ID for the generation (e.g. OpenAI's `chatcmpl-…` / * `resp_…`). Maps to `$ai_completion_id`. Response IDs generalize across * providers, so this lives in the shared schema rather than under * `providerMetadata`. */ completionId?: string; /** * Provider-specific response metadata that has no place in the shared, * provider-agnostic `$ai_*` schema (e.g. OpenAI's `system_fingerprint` and * `request_id`). Maps to `$ai_provider_metadata`; omitted when empty. */ providerMetadata?: Record<string, unknown>; /** When set, the event is captured as an error. */ error?: unknown; /** Awaits delivery instead of batching. Useful in serverless environments. */ captureImmediate?: boolean; } /** * Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog. * * This is the canonical primitive that every `@posthog/ai` wrapper * (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so * external code can use it directly to instrument LLM calls made through * arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the * same events the SDK wrappers produce. * * When `error` is set, the event is captured as an error. If the error is an * object, it is mutated in place to set `__posthog_previously_captured_error` * so callers can re-throw the original error reference safely. */ declare const captureAiGeneration: (client: PostHog, options: CaptureAiGenerationOptions) => Promise<void>; export { AIEvent, type CaptureAiGenerationOptions, type PromptCodeFallbackResult, type PromptRemoteResult, type PromptResult, Prompts, captureAiGeneration, wrapVercelLanguageModel as withTracing };