fix(cliproxy): preserve auth-file source fallback

This commit is contained in:
Tam Nhu Tran
2026-03-26 17:18:48 -04:00
parent 368a625d92
commit 63c8ec5d7c
2 changed files with 146 additions and 61 deletions
+10 -4
View File
@@ -13,7 +13,7 @@ interface BuildCliproxyStatsOptions {
}
interface ResolvedAuthFile {
provider: string;
provider?: string;
source?: string;
}
@@ -32,13 +32,19 @@ function buildAuthIndexLookup(
const lookup = new Map<string, ResolvedAuthFile>();
for (const authFile of authFiles ?? []) {
if (authFile.auth_index === undefined || authFile.auth_index === null || !authFile.provider) {
if (authFile.auth_index === undefined || authFile.auth_index === null) {
continue;
}
const provider = authFile.provider ? normalizeProvider(authFile.provider) : undefined;
const source = authFile.email?.trim() || authFile.name?.trim() || undefined;
if (!provider && !source) {
continue;
}
lookup.set(String(authFile.auth_index), {
provider: normalizeProvider(authFile.provider),
source: authFile.email?.trim() || authFile.name?.trim() || undefined,
provider,
source,
});
}
+136 -57
View File
@@ -1,10 +1,51 @@
import { describe, expect, it } from 'bun:test';
import type {
CliproxyManagementAuthFile,
CliproxyRequestDetail,
CliproxyUsageApiResponse,
} from '../../../src/cliproxy/stats-fetcher';
import { buildCliproxyStatsFromUsageResponse } from '../../../src/cliproxy/stats-transformer';
function createDetail(overrides: Partial<CliproxyRequestDetail> = {}): CliproxyRequestDetail {
return {
timestamp: '2025-03-26T10:00:00.000Z',
source: 'shared@example.com',
auth_index: 'shared-auth-index',
tokens: {
input_tokens: 10,
output_tokens: 5,
reasoning_tokens: 0,
cached_tokens: 0,
total_tokens: 15,
},
failed: false,
...overrides,
};
}
function createInternallyBucketedUsage(
details: CliproxyRequestDetail[]
): CliproxyUsageApiResponse {
return {
usage: {
total_requests: details.length,
success_count: details.filter((detail) => !detail.failed).length,
failure_count: details.filter((detail) => detail.failed).length,
apis: {
'ccs-internal-managed': {
total_requests: details.length,
models: {
'gpt-5': {
total_requests: details.length,
details,
},
},
},
},
},
};
}
describe('buildCliproxyStatsFromUsageResponse', () => {
it('keeps duplicate emails isolated by provider', () => {
const usage: CliproxyUsageApiResponse = {
@@ -126,64 +167,32 @@ describe('buildCliproxyStatsFromUsageResponse', () => {
});
it('resolves canonical providers from auth_index when usage is internally bucketed', () => {
const usage: CliproxyUsageApiResponse = {
usage: {
total_requests: 3,
success_count: 2,
failure_count: 1,
apis: {
'ccs-internal-managed': {
total_requests: 3,
models: {
'gpt-5': {
total_requests: 3,
details: [
{
timestamp: '2026-03-26T10:00:00.000Z',
source: 'shared@example.com',
auth_index: 'codex-1',
tokens: {
input_tokens: 10,
output_tokens: 5,
reasoning_tokens: 0,
cached_tokens: 0,
total_tokens: 15,
},
failed: false,
},
{
timestamp: '2026-03-26T10:01:00.000Z',
source: 'shared@example.com',
auth_index: 'gemini-1',
tokens: {
input_tokens: 12,
output_tokens: 7,
reasoning_tokens: 0,
cached_tokens: 0,
total_tokens: 19,
},
failed: false,
},
{
timestamp: '2026-03-26T10:02:00.000Z',
source: 'shared@example.com',
auth_index: 'agy-1',
tokens: {
input_tokens: 8,
output_tokens: 2,
reasoning_tokens: 0,
cached_tokens: 0,
total_tokens: 10,
},
failed: true,
},
],
},
},
},
const usage = createInternallyBucketedUsage([
createDetail({ auth_index: 'codex-1' }),
createDetail({
timestamp: '2025-03-26T10:01:00.000Z',
auth_index: 'gemini-1',
tokens: {
input_tokens: 12,
output_tokens: 7,
reasoning_tokens: 0,
cached_tokens: 0,
total_tokens: 19,
},
},
};
}),
createDetail({
timestamp: '2025-03-26T10:02:00.000Z',
auth_index: 'agy-1',
tokens: {
input_tokens: 8,
output_tokens: 2,
reasoning_tokens: 0,
cached_tokens: 0,
total_tokens: 10,
},
failed: true,
}),
]);
const authFiles: CliproxyManagementAuthFile[] = [
{ auth_index: 'codex-1', provider: 'codex', email: 'shared@example.com' },
{ auth_index: 'gemini-1', provider: 'gemini-cli', email: 'shared@example.com' },
@@ -209,4 +218,74 @@ describe('buildCliproxyStatsFromUsageResponse', () => {
});
expect(stats.requestsByProvider).toEqual({ codex: 1, gemini: 1, agy: 1 });
});
it('falls back to the usage provider when auth_index lookup cannot resolve a provider', () => {
const usage = createInternallyBucketedUsage([createDetail({ auth_index: 'codex-1' })]);
const scenarios: Array<{ label: string; authFiles: CliproxyManagementAuthFile[] }> = [
{ label: 'empty authFiles', authFiles: [] },
{
label: 'missing auth_index match',
authFiles: [{ auth_index: 'other-auth-index', provider: 'codex', email: 'shared@example.com' }],
},
{
label: 'matching auth_index without provider metadata',
authFiles: [{ auth_index: 'codex-1', email: 'shared@example.com' }],
},
];
for (const scenario of scenarios) {
const stats = buildCliproxyStatsFromUsageResponse(usage, { authFiles: scenario.authFiles });
expect(stats.accountStats['ccs-internal-managed:shared@example.com'], scenario.label).toMatchObject({
provider: 'ccs-internal-managed',
source: 'shared@example.com',
successCount: 1,
failureCount: 0,
});
expect(stats.requestsByProvider, scenario.label).toEqual({ 'ccs-internal-managed': 1 });
}
});
it('falls back to auth-file source metadata when detail source is blank and supports mixed auth_index outcomes', () => {
const usage = createInternallyBucketedUsage([
createDetail({ source: ' ', auth_index: 'codex-1' }),
createDetail({
timestamp: '2025-03-26T10:01:00.000Z',
source: 'unmatched@example.com',
auth_index: 'missing-auth-index',
}),
createDetail({
timestamp: '2025-03-26T10:02:00.000Z',
source: ' ',
auth_index: 'providerless-auth-index',
failed: true,
}),
]);
const authFiles: CliproxyManagementAuthFile[] = [
{ auth_index: 'codex-1', provider: 'codex', email: 'fallback@example.com' },
{ auth_index: 'providerless-auth-index', email: 'providerless-fallback@example.com' },
];
const stats = buildCliproxyStatsFromUsageResponse(usage, { authFiles });
expect(stats.accountStats['codex:fallback@example.com']).toMatchObject({
provider: 'codex',
source: 'fallback@example.com',
successCount: 1,
failureCount: 0,
});
expect(stats.accountStats['ccs-internal-managed:unmatched@example.com']).toMatchObject({
provider: 'ccs-internal-managed',
source: 'unmatched@example.com',
successCount: 1,
failureCount: 0,
});
expect(stats.accountStats['ccs-internal-managed:providerless-fallback@example.com']).toMatchObject({
provider: 'ccs-internal-managed',
source: 'providerless-fallback@example.com',
successCount: 0,
failureCount: 1,
});
expect(stats.requestsByProvider).toEqual({ codex: 1, 'ccs-internal-managed': 2 });
});
});