Skip to content

Commit 79b01ac

Browse files
Brooooooklynclaude
andauthored
Keep provider-native AI calls out of platform AI usage
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 6dd6761 commit 79b01ac

2 files changed

Lines changed: 10 additions & 19 deletions

File tree

‎platform/packages/proxy/src/ai.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,13 @@ function applyProviderHeaders({
210210
headers,
211211
provider,
212212
providerKey,
213-
usageTag,
214213
apiKeyHeader,
215214
apiKeyPrefix,
216215
contentType,
217216
}: {
218217
headers: Headers;
219218
provider: string;
220219
providerKey: string;
221-
usageTag: string;
222220
apiKeyHeader?: string | null;
223221
apiKeyPrefix?: string | null;
224222
contentType?: string;
@@ -228,15 +226,17 @@ function applyProviderHeaders({
228226
const keyPrefix = apiKeyPrefix ?? defaults.prefix;
229227

230228
headers.set(keyHeader, `${keyPrefix}${providerKey}`);
231-
// Runs after the key header is set, so a caller-chosen key header name
232-
// cannot set a gateway control either.
229+
// Provider-native calls bill to the app's own provider key, so they carry
230+
// no usage tag and do not count toward the platform AI allowance. Caller
231+
// gateway headers are dropped after the key header is set, so neither they
232+
// nor a caller-chosen key header name can set gateway metadata, logging,
233+
// cost or billing.
233234
const callerGatewayHeaders = [...headers.keys()].filter((name) =>
234235
GATEWAY_CONTROL_HEADER.test(name),
235236
);
236237
for (const name of callerGatewayHeaders) {
237238
headers.delete(name);
238239
}
239-
headers.set('cf-aig-metadata', JSON.stringify({ projectId: usageTag }));
240240
if (contentType && !headers.has('content-type')) {
241241
headers.set('content-type', contentType);
242242
}
@@ -421,7 +421,6 @@ ai.post('/provider', async (context) => {
421421
headers: headersFromRecord(body.headers),
422422
provider,
423423
providerKey,
424-
usageTag: await deriveAiUsageTag(context.env.JWT_SECRET, projectId),
425424
apiKeyHeader,
426425
apiKeyPrefix,
427426
contentType: requestBody.contentType,

‎platform/packages/proxy/test/unit/proxy.test.ts‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,10 +1834,7 @@ describe('proxy worker', () => {
18341834
expect(init.method).toBe('POST');
18351835
const headers = new Headers(init.headers);
18361836
expect(headers.get('authorization')).toBe('Bearer sk-test-key');
1837-
expect(headers.get('cf-aig-metadata')).toBe(
1838-
JSON.stringify({ projectId: usageTags['proj_000000000001'] }),
1839-
);
1840-
expect(headers.has('cf-aig-metadata-projectId')).toBe(false);
1837+
expect([...headers.keys()].filter((name) => name.startsWith('cf-aig-'))).toEqual([]);
18411838
expect(headers.get('content-type')).toBe('application/json');
18421839
expect(headers.get('openai-beta')).toBe('assistants=v2');
18431840
expect(JSON.parse(init.body as string)).toEqual({
@@ -1896,16 +1893,13 @@ describe('proxy worker', () => {
18961893
const headers = new Headers(fetchMock.mock.calls[0][1]!.headers);
18971894
expect(headers.get('x-goog-api-key')).toBe('google-key');
18981895
expect(headers.get('authorization')).toBeNull();
1899-
expect(headers.get('cf-aig-metadata')).toBe(
1900-
JSON.stringify({ projectId: usageTags['proj_000000000001'] }),
1901-
);
1896+
expect([...headers.keys()].filter((name) => name.startsWith('cf-aig-'))).toEqual([]);
19021897
});
19031898

1904-
it('keeps gateway control headers under platform control', async () => {
1899+
it('drops caller gateway control headers on provider-native calls', async () => {
19051900
function gatewayHeaders(headers: Headers): Array<[string, string]> {
19061901
return [...headers].filter(([name]) => name.startsWith('cf-aig-'));
19071902
}
1908-
const signedMetadata = JSON.stringify({ projectId: usageTags['proj_000000000001'] });
19091903
const fetchMock = mockProviderFetch('{}', {
19101904
headers: { 'content-type': 'application/json' },
19111905
});
@@ -1931,7 +1925,7 @@ describe('proxy worker', () => {
19311925
);
19321926
expect(callerHeaders.status).toBe(200);
19331927
const forwarded = new Headers(fetchMock.mock.calls[0][1]!.headers);
1934-
expect(gatewayHeaders(forwarded)).toStrictEqual([['cf-aig-metadata', signedMetadata]]);
1928+
expect(gatewayHeaders(forwarded)).toStrictEqual([]);
19351929
expect(forwarded.get('x-keep')).toBe('yes');
19361930
expect(forwarded.get('authorization')).toBe('Bearer sk-test');
19371931

@@ -1948,9 +1942,7 @@ describe('proxy worker', () => {
19481942
env,
19491943
);
19501944
expect(keyHeader.status).toBe(200);
1951-
expect(gatewayHeaders(new Headers(fetchMock.mock.calls[1][1]!.headers))).toStrictEqual([
1952-
['cf-aig-metadata', signedMetadata],
1953-
]);
1945+
expect(gatewayHeaders(new Headers(fetchMock.mock.calls[1][1]!.headers))).toStrictEqual([]);
19541946
});
19551947

19561948
it('reconstructs multipart form data bodies', async () => {

0 commit comments

Comments
 (0)