Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 1x 1x 4x 4x 3x 3x 3x 3x 2x 2x 1x 1x 4x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 9x 5x 4x 1x | import type { Batcher, LLMCallRecord, OpenAIClientLike } from '../types.js';
const OPENAI_COST_PER_1K: Record<string, { input: number; output: number }> = {
'gpt-4o': { input: 0.005, output: 0.015 },
'gpt-4o-mini': { input: 0.00015, output: 0.0006 },
'gpt-4-turbo': { input: 0.01, output: 0.03 },
'gpt-3.5-turbo': { input: 0.0005, output: 0.0015 },
};
export function wrapOpenAI<TClient extends OpenAIClientLike>(
client: TClient,
batcher: Batcher,
projectId: string | null,
): TClient {
const originalCreate = client.chat.completions.create.bind(client.chat.completions);
client.chat.completions.create = async (...args: unknown[]): Promise<unknown> => {
const start = Date.now();
const timestamp = new Date(start).toISOString();
try {
const response = await originalCreate(...args);
pushOpenAISuccess(response, batcher, projectId, timestamp, Date.now() - start);
return response;
} catch (error) {
pushOpenAIError(args[0], error, batcher, projectId, timestamp, Date.now() - start);
throw error;
}
};
return client;
}
function pushOpenAISuccess(
response: unknown,
batcher: Batcher,
projectId: string | null,
timestamp: string,
latencyMs: number,
): void {
try {
const responseRecord = asRecord(response);
const usage = asRecord(responseRecord?.usage);
const model = getString(responseRecord?.model) ?? 'unknown';
const promptTokens = getNumber(usage?.prompt_tokens);
const completionTokens = getNumber(usage?.completion_tokens);
const preview = getOpenAIPreview(responseRecord?.choices);
pushSafely(batcher, {
project_id: projectId,
timestamp,
provider: 'openai',
model,
prompt_tokens: promptTokens,
completion_tokens: completionTokens,
latency_ms: latencyMs,
cost_usd: computeCost(model, promptTokens, completionTokens),
response_preview: preview,
error: null,
metadata: {},
});
} catch {
// Logging must never affect the provider call.
}
}
function pushOpenAIError(
request: unknown,
error: unknown,
batcher: Batcher,
projectId: string | null,
timestamp: string,
latencyMs: number,
): void {
try {
const requestRecord = asRecord(request);
pushSafely(batcher, {
project_id: projectId,
timestamp,
provider: 'openai',
model: getString(requestRecord?.model) ?? 'unknown',
prompt_tokens: 0,
completion_tokens: 0,
latency_ms: latencyMs,
cost_usd: 0,
response_preview: '',
error: stringifyError(error),
metadata: {},
});
} catch {
// Preserve the original provider error exactly.
}
}
function computeCost(model: string, promptTokens: number, completionTokens: number): number {
const pricing = OPENAI_COST_PER_1K[model];
Iif (!pricing) return 0;
return (promptTokens / 1000) * pricing.input + (completionTokens / 1000) * pricing.output;
}
function getOpenAIPreview(choices: unknown): string {
Iif (!Array.isArray(choices)) return '';
const firstChoice = asRecord(choices[0]);
const message = asRecord(firstChoice?.message);
const content = getString(message?.content);
return (content ?? '').slice(0, 200);
}
function pushSafely(batcher: Batcher, record: LLMCallRecord): void {
try {
batcher.push(record);
} catch {
// Explicitly isolate provider behavior from logging failures.
}
}
function asRecord(value: unknown): Record<string, unknown> | undefined {
return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined;
}
function getString(value: unknown): string | undefined {
return typeof value === 'string' ? value : undefined;
}
function getNumber(value: unknown): number {
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
}
function stringifyError(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
|