Skip to content

Commit 4ca2e68

Browse files
committed
Move trace writes off provider path
1 parent 35a73d2 commit 4ca2e68

5 files changed

Lines changed: 409 additions & 332 deletions

File tree

web/src/app/api/v1/chat/completions/_post.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@ import type { NextRequest } from 'next/server'
4646

4747
import type { ChatCompletionRequestBody } from '@/llm-api/types'
4848

49-
import {
50-
createRequestAuditRecord,
51-
recordChatCompletionTrace,
52-
} from '@/llm-api/helpers'
49+
import { recordChatCompletionTrace } from '@/llm-api/chat-completion-trace'
50+
import { createRequestAuditRecord } from '@/llm-api/helpers'
5351
import {
5452
CanopyWaveError,
5553
handleCanopyWaveNonStream,
@@ -709,7 +707,7 @@ export async function postChatCompletions(params: {
709707
const openrouterApiKey = req.headers.get(BYOK_OPENROUTER_HEADER)
710708
const providerLogger = sampleSuccessLogger(logger, sampleFreebuffSuccess)
711709

712-
await recordChatCompletionTrace({
710+
recordChatCompletionTrace({
713711
body: typedBody,
714712
userId,
715713
agentId,

web/src/llm-api/__tests__/chat-completion-trace.test.ts

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
import { beforeAll, beforeEach, describe, expect, it, jest } from '@jest/globals'
1+
import {
2+
beforeAll,
3+
beforeEach,
4+
describe,
5+
expect,
6+
it,
7+
jest,
8+
} from '@jest/globals'
29

310
jest.mock('@codebuff/bigquery', () => ({
411
setupBigQuery: jest.fn(),
512
}))
613

7-
jest.mock('@codebuff/billing', () => ({
8-
consumeCreditsAndAddAgentStep: jest.fn(),
9-
recordMessageWithoutBilling: jest.fn(),
10-
}))
11-
1214
import type { ChatCompletionTraceRow } from '@codebuff/common/types/contracts/bigquery'
1315
import type { ChatCompletionRequestBody } from '../types'
1416
import type {
1517
recordChatCompletionTrace as recordChatCompletionTraceType,
1618
resetChatCompletionTraceCacheForTests as resetChatCompletionTraceCacheForTestsType,
17-
} from '../helpers'
19+
} from '../chat-completion-trace'
1820

1921
const testLogger = {
2022
debug: () => {},
@@ -26,48 +28,60 @@ const testLogger = {
2628
const baseBody = (
2729
messages: ChatCompletionRequestBody['messages'],
2830
): ChatCompletionRequestBody => ({
29-
model: 'deepseek/deepseek-v4-pro',
30-
stream: true,
31-
messages,
32-
tools: [
33-
{
34-
type: 'function',
35-
function: { name: 'read_files', parameters: {} },
36-
},
37-
],
38-
codebuff_metadata: {
39-
client_id: 'client-1',
40-
run_id: 'run-1',
41-
trace_session_id: 'session-1',
42-
trace_request_id: 'trace-1',
43-
cost_mode: 'free',
31+
model: 'deepseek/deepseek-v4-pro',
32+
stream: true,
33+
messages,
34+
tools: [
35+
{
36+
type: 'function',
37+
function: { name: 'read_files', parameters: {} },
4438
},
39+
],
40+
codebuff_metadata: {
41+
client_id: 'client-1',
42+
run_id: 'run-1',
43+
trace_session_id: 'session-1',
44+
trace_request_id: 'trace-1',
45+
cost_mode: 'free',
46+
},
4547
})
4648

4749
describe('buildChatCompletionTraceRow', () => {
4850
let recordChatCompletionTrace: typeof recordChatCompletionTraceType
4951
let resetChatCompletionTraceCacheForTests: typeof resetChatCompletionTraceCacheForTestsType
5052
let rows: ChatCompletionTraceRow[]
53+
let traceWriteTasks: Promise<void>[]
5154

5255
beforeAll(async () => {
53-
const helpers = await import('../helpers')
54-
recordChatCompletionTrace = helpers.recordChatCompletionTrace
56+
const traceModule = await import('../chat-completion-trace')
57+
recordChatCompletionTrace = traceModule.recordChatCompletionTrace
5558
resetChatCompletionTraceCacheForTests =
56-
helpers.resetChatCompletionTraceCacheForTests
59+
traceModule.resetChatCompletionTraceCacheForTests
5760
})
5861

5962
beforeEach(() => {
6063
resetChatCompletionTraceCacheForTests()
6164
rows = []
65+
traceWriteTasks = []
6266
})
6367

68+
const scheduleTraceWrite = (task: () => Promise<void>) => {
69+
traceWriteTasks.push(task())
70+
}
71+
72+
const flushTraceWrites = async () => {
73+
const tasks = traceWriteTasks
74+
traceWriteTasks = []
75+
await Promise.all(tasks)
76+
}
77+
6478
const record = async (params: {
6579
body: ChatCompletionRequestBody
6680
userId?: string
6781
agentId?: string
6882
ancestorRunIds?: string[]
6983
}) => {
70-
await recordChatCompletionTrace({
84+
recordChatCompletionTrace({
7185
body: params.body,
7286
userId: params.userId ?? 'user-1',
7387
agentId: params.agentId ?? 'base2-free-deepseek',
@@ -77,7 +91,9 @@ describe('buildChatCompletionTraceRow', () => {
7791
rows.push(row)
7892
return true
7993
},
94+
scheduleTraceWrite,
8095
})
96+
await flushTraceWrites()
8197
return rows.at(-1)!
8298
}
8399

@@ -178,14 +194,16 @@ describe('buildChatCompletionTraceRow', () => {
178194
})
179195

180196
it('does not advance the prefix cache when BigQuery insert fails', async () => {
181-
await recordChatCompletionTrace({
197+
recordChatCompletionTrace({
182198
body: baseBody([{ role: 'user', content: 'hello' }]),
183199
userId: 'user-1',
184200
agentId: 'base2-free-deepseek',
185201
ancestorRunIds: [],
186202
logger: testLogger,
187203
insertChatCompletionTraceBigquery: async () => false,
204+
scheduleTraceWrite,
188205
})
206+
await flushTraceWrites()
189207

190208
const row = await record({
191209
body: baseBody([
@@ -210,7 +228,7 @@ describe('buildChatCompletionTraceRow', () => {
210228
cost_mode: 'free',
211229
}
212230

213-
const traceRequestId = await recordChatCompletionTrace({
231+
const traceRequestId = recordChatCompletionTrace({
214232
body,
215233
userId: 'user-1',
216234
agentId: 'base2-free-deepseek',
@@ -220,10 +238,40 @@ describe('buildChatCompletionTraceRow', () => {
220238
rows.push(row)
221239
return true
222240
},
241+
scheduleTraceWrite,
223242
})
224243

225244
expect(traceRequestId).toBeNull()
226245
expect(rows).toHaveLength(0)
227246
expect(body.codebuff_metadata?.trace_request_id).toBeUndefined()
228247
})
248+
249+
it('schedules BigQuery work off the caller stack', async () => {
250+
let scheduledTask: (() => Promise<void>) | undefined
251+
const body = baseBody([{ role: 'user', content: 'hello' }])
252+
253+
const traceRequestId = recordChatCompletionTrace({
254+
body,
255+
userId: 'user-1',
256+
agentId: 'base2-free-deepseek',
257+
ancestorRunIds: [],
258+
logger: testLogger,
259+
insertChatCompletionTraceBigquery: async ({ row }) => {
260+
rows.push(row)
261+
return true
262+
},
263+
scheduleTraceWrite: (task) => {
264+
scheduledTask = task
265+
},
266+
})
267+
268+
expect(typeof traceRequestId).toBe('string')
269+
expect(body.codebuff_metadata?.trace_request_id).toBe(traceRequestId)
270+
expect(rows).toHaveLength(0)
271+
272+
await scheduledTask?.()
273+
274+
expect(rows).toHaveLength(1)
275+
expect(rows[0]?.id).toBe(traceRequestId)
276+
})
229277
})

0 commit comments

Comments
 (0)