197 lines
5.4 KiB
TypeScript
197 lines
5.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
extractSelfLogItems,
|
|
findBestSelfLogMatch,
|
|
shouldLookupSelfLog,
|
|
type SelfLogItem,
|
|
} from './proxyUsageFallbackService.js';
|
|
|
|
describe('proxyUsageFallbackService', () => {
|
|
it('extracts logs from nested /api/log/self payload', () => {
|
|
const payload = {
|
|
success: true,
|
|
data: {
|
|
data: [
|
|
{
|
|
model_name: 'gpt-4o',
|
|
prompt_tokens: 120,
|
|
completion_tokens: 80,
|
|
quota: 3300,
|
|
created_at: 1_700_000_123,
|
|
request_time: 1450,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const logs = extractSelfLogItems(payload);
|
|
expect(logs).toEqual([
|
|
{
|
|
modelName: 'gpt-4o',
|
|
tokenName: '',
|
|
promptTokens: 120,
|
|
completionTokens: 80,
|
|
totalTokens: 200,
|
|
quota: 3300,
|
|
createdAtMs: 1_700_000_123_000,
|
|
requestTimeMs: 1450,
|
|
billingMeta: null,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('extracts cache billing metadata from self-log other payload', () => {
|
|
const payload = {
|
|
success: true,
|
|
data: {
|
|
items: [
|
|
{
|
|
model_name: 'claude-haiku-4-5-20251001',
|
|
token_name: 'anyrouter',
|
|
prompt_tokens: 146638,
|
|
completion_tokens: 172,
|
|
quota: 41528,
|
|
created_at: 1_772_790_705,
|
|
request_time: 1700,
|
|
other: JSON.stringify({
|
|
cache_creation_ratio: 1.25,
|
|
cache_creation_tokens: 945,
|
|
cache_ratio: 0.1,
|
|
cache_tokens: 145692,
|
|
completion_ratio: 5,
|
|
group_ratio: 1,
|
|
model_ratio: 2.5,
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
const logs = extractSelfLogItems(payload);
|
|
expect(logs[0]).toMatchObject({
|
|
modelName: 'claude-haiku-4-5-20251001',
|
|
tokenName: 'anyrouter',
|
|
promptTokens: 146638,
|
|
completionTokens: 172,
|
|
quota: 41528,
|
|
billingMeta: {
|
|
cacheReadTokens: 145692,
|
|
cacheCreationTokens: 945,
|
|
cacheRatio: 0.1,
|
|
cacheCreationRatio: 1.25,
|
|
completionRatio: 5,
|
|
groupRatio: 1,
|
|
modelRatio: 2.5,
|
|
},
|
|
});
|
|
});
|
|
|
|
it('matches best log by model + time window + request time', () => {
|
|
const logs: SelfLogItem[] = [
|
|
{
|
|
modelName: 'gpt-4o',
|
|
tokenName: 'default',
|
|
promptTokens: 10,
|
|
completionTokens: 5,
|
|
totalTokens: 15,
|
|
quota: 1000,
|
|
createdAtMs: 1_700_000_000_000,
|
|
requestTimeMs: 800,
|
|
},
|
|
{
|
|
modelName: 'gpt-4o',
|
|
tokenName: 'default',
|
|
promptTokens: 200,
|
|
completionTokens: 100,
|
|
totalTokens: 300,
|
|
quota: 4500,
|
|
createdAtMs: 1_700_000_007_000,
|
|
requestTimeMs: 3100,
|
|
},
|
|
{
|
|
modelName: 'gpt-4o-mini',
|
|
tokenName: 'default',
|
|
promptTokens: 999,
|
|
completionTokens: 1,
|
|
totalTokens: 1000,
|
|
quota: 9999,
|
|
createdAtMs: 1_700_000_007_000,
|
|
requestTimeMs: 3100,
|
|
},
|
|
];
|
|
|
|
const matched = findBestSelfLogMatch(logs, {
|
|
modelName: 'gpt-4o',
|
|
requestStartedAtMs: 1_700_000_004_000,
|
|
requestEndedAtMs: 1_700_000_008_200,
|
|
localLatencyMs: 3200,
|
|
});
|
|
|
|
expect(matched).toEqual(logs[1]);
|
|
});
|
|
|
|
it('returns null when no safe match exists', () => {
|
|
const logs: SelfLogItem[] = [
|
|
{
|
|
modelName: 'gpt-4o',
|
|
tokenName: 'default',
|
|
promptTokens: 120,
|
|
completionTokens: 80,
|
|
totalTokens: 200,
|
|
quota: 3300,
|
|
createdAtMs: 1_700_000_123_000,
|
|
requestTimeMs: 1450,
|
|
},
|
|
];
|
|
|
|
const matched = findBestSelfLogMatch(logs, {
|
|
modelName: 'gpt-4o',
|
|
requestStartedAtMs: 1_700_010_000_000,
|
|
requestEndedAtMs: 1_700_010_004_000,
|
|
localLatencyMs: 4000,
|
|
});
|
|
|
|
expect(matched).toBeNull();
|
|
});
|
|
|
|
it('matches provider-prefixed model names', () => {
|
|
const logs: SelfLogItem[] = [
|
|
{
|
|
modelName: 'z-ai/glm4.7',
|
|
tokenName: 'sys_playground',
|
|
promptTokens: 1095,
|
|
completionTokens: 2179,
|
|
totalTokens: 3274,
|
|
quota: 1890000,
|
|
createdAtMs: 1_772_024_884_000,
|
|
requestTimeMs: 59_000,
|
|
},
|
|
];
|
|
|
|
const matched = findBestSelfLogMatch(logs, {
|
|
modelName: 'glm4.7',
|
|
tokenName: 'windhub',
|
|
requestStartedAtMs: 1_772_024_825_000,
|
|
requestEndedAtMs: 1_772_024_884_500,
|
|
localLatencyMs: 59_000,
|
|
});
|
|
|
|
expect(matched).toEqual(logs[0]);
|
|
});
|
|
|
|
it('always enables self-log lookup for done-hub/one-hub', () => {
|
|
expect(shouldLookupSelfLog('done-hub', { promptTokens: 12, completionTokens: 8, totalTokens: 20 })).toBe(true);
|
|
expect(shouldLookupSelfLog('one-hub', { promptTokens: 1, completionTokens: 1, totalTokens: 2 })).toBe(true);
|
|
});
|
|
|
|
it('only enables self-log lookup for new-api when usage is missing', () => {
|
|
expect(shouldLookupSelfLog('new-api', { promptTokens: 0, completionTokens: 0, totalTokens: 0 })).toBe(true);
|
|
expect(shouldLookupSelfLog('new-api', { promptTokens: 12, completionTokens: 3, totalTokens: 15 })).toBe(false);
|
|
});
|
|
|
|
it('always enables self-log lookup for anyrouter to recover exact billing metadata', () => {
|
|
expect(shouldLookupSelfLog('anyrouter', { promptTokens: 0, completionTokens: 0, totalTokens: 0 })).toBe(true);
|
|
expect(shouldLookupSelfLog('anyrouter', { promptTokens: 12, completionTokens: 3, totalTokens: 15 })).toBe(true);
|
|
});
|
|
});
|