Fix token usage handling and developer proxy save UX (#704)
This commit is contained in:
committed by
GitHub
Unverified
parent
2668082809
commit
870abb99c4
@@ -49,6 +49,7 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
agentId: 'default',
|
||||
model: 'claude-sonnet',
|
||||
provider: 'anthropic',
|
||||
usageStatus: 'available',
|
||||
inputTokens: 200,
|
||||
outputTokens: 80,
|
||||
cacheReadTokens: 25,
|
||||
@@ -62,6 +63,7 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
agentId: 'default',
|
||||
model: 'gpt-5',
|
||||
provider: 'openai',
|
||||
usageStatus: 'available',
|
||||
inputTokens: 100,
|
||||
outputTokens: 50,
|
||||
cacheReadTokens: 0,
|
||||
@@ -81,7 +83,7 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([]);
|
||||
});
|
||||
|
||||
it('skips tool result entries without positive token usage', () => {
|
||||
it('still skips tool result entries without usage payload', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
type: 'message',
|
||||
@@ -100,6 +102,111 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps assistant usage entries with zero total tokens when usage is explicitly provided', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
type: 'message',
|
||||
timestamp: '2026-03-10T03:00:00.000Z',
|
||||
message: {
|
||||
role: 'assistant',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usage: {
|
||||
total: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
].join('\n');
|
||||
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([
|
||||
{
|
||||
timestamp: '2026-03-10T03:00:00.000Z',
|
||||
sessionId: 'abc',
|
||||
agentId: 'default',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usageStatus: 'available',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
totalTokens: 0,
|
||||
costUsd: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('extracts usage fields from snake_case provider payloads', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
type: 'message',
|
||||
timestamp: '2026-03-10T03:10:00.000Z',
|
||||
message: {
|
||||
role: 'assistant',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usage: {
|
||||
input_tokens: 12,
|
||||
output_tokens: 3,
|
||||
cache_read: 4,
|
||||
cache_write: 1,
|
||||
total_tokens: 20,
|
||||
},
|
||||
},
|
||||
}),
|
||||
].join('\n');
|
||||
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([
|
||||
{
|
||||
timestamp: '2026-03-10T03:10:00.000Z',
|
||||
sessionId: 'abc',
|
||||
agentId: 'default',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usageStatus: 'available',
|
||||
inputTokens: 12,
|
||||
outputTokens: 3,
|
||||
cacheReadTokens: 4,
|
||||
cacheWriteTokens: 1,
|
||||
totalTokens: 20,
|
||||
costUsd: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('supports tool result usage data without explicit provider/model keys', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
type: 'message',
|
||||
timestamp: '2026-03-10T03:20:00.000Z',
|
||||
message: {
|
||||
role: 'toolResult',
|
||||
details: {
|
||||
usage: {
|
||||
input_tokens: 10,
|
||||
output_tokens: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
].join('\n');
|
||||
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([
|
||||
{
|
||||
timestamp: '2026-03-10T03:20:00.000Z',
|
||||
sessionId: 'abc',
|
||||
agentId: 'default',
|
||||
usageStatus: 'available',
|
||||
inputTokens: 10,
|
||||
outputTokens: 20,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
totalTokens: 30,
|
||||
costUsd: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses tool result usage when provided', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
@@ -129,6 +236,7 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
agentId: 'default',
|
||||
model: 'moonshot-v1-128k',
|
||||
provider: 'kimi',
|
||||
usageStatus: 'available',
|
||||
inputTokens: 120,
|
||||
outputTokens: 30,
|
||||
cacheReadTokens: 10,
|
||||
@@ -163,6 +271,7 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
agentId: 'default',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usageStatus: 'available',
|
||||
content: '这是一条测试回复内容。',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
@@ -200,6 +309,7 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
agentId: 'default',
|
||||
model: 'moonshot-v1-128k',
|
||||
provider: 'kimi',
|
||||
usageStatus: 'available',
|
||||
content: '外部搜索原文内容',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
@@ -211,6 +321,70 @@ describe('parseUsageEntriesFromJsonl', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('maps usage object with no recognized fields to missing state', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
type: 'message',
|
||||
timestamp: '2026-03-10T03:30:00.000Z',
|
||||
message: {
|
||||
role: 'assistant',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usage: { notes: 'tool call' },
|
||||
},
|
||||
}),
|
||||
].join('\n');
|
||||
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([
|
||||
{
|
||||
timestamp: '2026-03-10T03:30:00.000Z',
|
||||
sessionId: 'abc',
|
||||
agentId: 'default',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usageStatus: 'missing',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
totalTokens: 0,
|
||||
costUsd: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('marks non-object usage payload as error', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
type: 'message',
|
||||
timestamp: '2026-03-10T03:40:00.000Z',
|
||||
message: {
|
||||
role: 'assistant',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usage: 'invalid',
|
||||
},
|
||||
}),
|
||||
].join('\n');
|
||||
|
||||
expect(parseUsageEntriesFromJsonl(jsonl, { sessionId: 'abc', agentId: 'default' })).toEqual([
|
||||
{
|
||||
timestamp: '2026-03-10T03:40:00.000Z',
|
||||
sessionId: 'abc',
|
||||
agentId: 'default',
|
||||
model: 'kimi-k2.5',
|
||||
provider: 'moonshot',
|
||||
usageStatus: 'error',
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
totalTokens: 0,
|
||||
costUsd: undefined,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns all matching entries when no limit is provided', () => {
|
||||
const jsonl = [
|
||||
JSON.stringify({
|
||||
|
||||
Reference in New Issue
Block a user