Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
05859af
feat: align eval traces with Braintrust's span spec
mattrossman Sep 30, 2026
c79e321
Merge remote-tracking branch 'origin/main' into mattrossman/ai-1264-a…
mattrossman Sep 30, 2026
f20d5ce
chore: refresh eval results [skip ci]
github-actions[bot] Sep 30, 2026
e760f67
Revert "chore: refresh eval results [skip ci]"
mattrossman Sep 30, 2026
1821742
chore: refresh eval results [skip ci]
github-actions[bot] Sep 30, 2026
e127ebb
Revert "chore: refresh eval results [skip ci]"
mattrossman Sep 30, 2026
fc0eaab
fix: match Codex tool calls around redacted secrets
mattrossman Oct 1, 2026
6ae97d5
fix: match Codex tool calls to the rollout argv exactly
mattrossman Oct 1, 2026
5a45e2a
chore: refresh eval results [skip ci]
github-actions[bot] Oct 1, 2026
4c43dbe
Revert "chore: refresh eval results [skip ci]"
mattrossman Oct 1, 2026
0f7ef57
docs: trim enrichEvents comment
mattrossman Oct 1, 2026
4e5513a
feat: tag skill-load tool spans with skill metadata
mattrossman Oct 1, 2026
4f15146
docs: cite Braintrust's Codex translator for LLM span start
mattrossman Oct 1, 2026
f13ef1d
chore: refresh eval results [skip ci]
github-actions[bot] Oct 1, 2026
f3bedac
style: format upload-braintrust
mattrossman Oct 1, 2026
b93f9d5
Revert "style: format upload-braintrust"
mattrossman Oct 1, 2026
2abe04b
Revert "feat: tag skill-load tool spans with skill metadata"
mattrossman Oct 1, 2026
6be1f96
Revert "chore: refresh eval results [skip ci]"
mattrossman Oct 1, 2026
3e48e95
fix: tag grok tool calls with the request that made them
mattrossman Oct 2, 2026
6361395
fix: pair only tool items from the codex rollout
mattrossman Oct 2, 2026
1f5ceec
style: format grok parser
mattrossman Oct 2, 2026
0a50211
fix: give model calls with no text or tool calls their own span
mattrossman Oct 2, 2026
d6c3972
docs: correct stale opencode stream comments
mattrossman Oct 2, 2026
541aed1
fix: keep compaction spans and skip false empty spans in codex traces
mattrossman Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions apps/framework/scripts/upload-braintrust.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { describe, expect, it } from 'vitest';
import {
experimentName,
logTranscript,
runViewUrl,
unwrapShell,
type SpanSink,
tokenMetrics,
utcStamp,
} from './upload-braintrust.js';
Expand Down Expand Up @@ -89,3 +92,157 @@ describe('runViewUrl', () => {
);
});
});

interface RecordedSpan {
name?: string;
start?: number;
end?: number;
metrics?: unknown;
input?: unknown;
children: RecordedSpan[];
}

function recorder(node: RecordedSpan): SpanSink {
return {
startSpan(args) {
const child: RecordedSpan = {
name: args?.name,
start: args?.startTime,
children: [],
};
node.children.push(child);
return recorder(child);
},
log(event) {
if (event.metrics) {
node.metrics = event.metrics;
}
if (event.input !== undefined && node.name === 'm') {
node.input = event.input;
}
},
end(args) {
node.end = args?.endTime;
},
};
}

describe('logTranscript', () => {
it('logs back-to-back LLM spans per request, with tool calls as siblings under task', () => {
const root: RecordedSpan = { children: [] };
const ms = (s: number) => (100 + s) * 1000;
logTranscript(recorder(root), {
prompt: 'go',
agentReport: 'done',
checks: [],
passed: true,
modelId: 'm',
startTime: 100,
endTime: 170,
toolLabels: [],
transcript: [
{
type: 'message',
role: 'assistant',
content: 'a',
ts: ms(4),
requestId: 'r1',
usage: {
inputTokens: 10,
cacheReadInputTokens: 0,
cacheWriteInputTokens: 0,
outputTokens: 2,
},
},
{
type: 'tool_call',
name: 'Skill',
input: {},
output: 'loaded',
id: 't1',
ts: ms(4),
resultTs: ms(5),
requestId: 'r1',
},
{
type: 'tool_call',
name: 'Bash',
input: {},
output: 'ok',
id: 't2',
ts: ms(8),
resultTs: ms(31),
requestId: 'r2',
},
{
type: 'message',
role: 'assistant',
content: 'b',
ts: ms(38),
requestId: 'r3',
},
],
});
const span = (
name: string,
start: number,
end: number,
...children: RecordedSpan[]
) => ({
name,
start: 100 + start,
end: 100 + end,
children,
});
const call = (id: string, name: string) => ({
id,
type: 'function',
function: { name, arguments: '{}' },
});
const prompt = { role: 'user', content: 'go' };
const first = {
role: 'assistant',
content: 'a',
tool_calls: [call('t1', 'Skill')],
};
const skillResult = { role: 'tool', tool_call_id: 't1', content: 'loaded' };
const second = {
role: 'assistant',
content: '',
tool_calls: [call('t2', 'Bash')],
};
const bashResult = { role: 'tool', tool_call_id: 't2', content: 'ok' };
expect(root.children).toEqual([
span(
'task',
0,
38,
{
...span('m', 0, 4),
input: [prompt],
metrics: { prompt_tokens: 10, completion_tokens: 2, tokens: 12 },
},
span('Skill', 4, 5),
{ ...span('m', 5, 8), input: [prompt, first, skillResult] },
span('Bash', 8, 31),
{
...span('m', 31, 38),
input: [prompt, first, skillResult, second, bashResult],
}
),
span('passed', 70, 70),
]);
});
});

describe('unwrapShell', () => {
it("drops Codex's bash -lc wrapper and leaves other commands alone", () => {
expect(unwrapShell(`/bin/bash -lc "ls -la && cat 'a b'"`)).toBe(
"ls -la && cat 'a b'"
);
expect(unwrapShell(`/bin/bash -lc "apply_patch <<'PATCH' x PATCH'`)).toBe(
"apply_patch <<'PATCH' x PATCH"
);
expect(unwrapShell('supabase status')).toBe('supabase status');
});
});
Loading