@antseed/cli 0.1.141 → 0.1.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/buyer/sweep.d.ts.map +1 -1
- package/dist/cli/commands/buyer/sweep.js +11 -5
- package/dist/cli/commands/buyer/sweep.js.map +1 -1
- package/dist/cli/commands/payments.d.ts.map +1 -1
- package/dist/cli/commands/payments.js +37 -20
- package/dist/cli/commands/payments.js.map +1 -1
- package/dist/cli/commands/system-proxy/ca-trust-state.d.ts +8 -0
- package/dist/cli/commands/system-proxy/ca-trust-state.d.ts.map +1 -0
- package/dist/cli/commands/system-proxy/ca-trust-state.js +20 -0
- package/dist/cli/commands/system-proxy/ca-trust-state.js.map +1 -0
- package/dist/cli/commands/system-proxy/index.d.ts.map +1 -1
- package/dist/cli/commands/system-proxy/index.js +2 -0
- package/dist/cli/commands/system-proxy/index.js.map +1 -1
- package/dist/cli/commands/system-proxy/install-ca.d.ts.map +1 -1
- package/dist/cli/commands/system-proxy/install-ca.js +9 -0
- package/dist/cli/commands/system-proxy/install-ca.js.map +1 -1
- package/dist/cli/commands/system-proxy/start.d.ts.map +1 -1
- package/dist/cli/commands/system-proxy/start.js +3 -1
- package/dist/cli/commands/system-proxy/start.js.map +1 -1
- package/dist/cli/commands/wrapped-tools.d.ts.map +1 -1
- package/dist/cli/commands/wrapped-tools.js +7 -0
- package/dist/cli/commands/wrapped-tools.js.map +1 -1
- package/dist/cli/commands/wrapped-tools.test.js +6 -1
- package/dist/cli/commands/wrapped-tools.test.js.map +1 -1
- package/dist/proxy/buyer-proxy.d.ts +38 -1
- package/dist/proxy/buyer-proxy.d.ts.map +1 -1
- package/dist/proxy/buyer-proxy.js +340 -23
- package/dist/proxy/buyer-proxy.js.map +1 -1
- package/dist/proxy/buyer-proxy.test.js +382 -11
- package/dist/proxy/buyer-proxy.test.js.map +1 -1
- package/dist/proxy/conversation-identity.d.ts +79 -0
- package/dist/proxy/conversation-identity.d.ts.map +1 -0
- package/dist/proxy/conversation-identity.js +302 -0
- package/dist/proxy/conversation-identity.js.map +1 -0
- package/dist/proxy/conversation-identity.test.d.ts +2 -0
- package/dist/proxy/conversation-identity.test.d.ts.map +1 -0
- package/dist/proxy/conversation-identity.test.js +236 -0
- package/dist/proxy/conversation-identity.test.js.map +1 -0
- package/dist/proxy/conversation-store.d.ts +96 -0
- package/dist/proxy/conversation-store.d.ts.map +1 -0
- package/dist/proxy/conversation-store.js +242 -0
- package/dist/proxy/conversation-store.js.map +1 -0
- package/dist/proxy/conversation-store.test.d.ts +2 -0
- package/dist/proxy/conversation-store.test.d.ts.map +1 -0
- package/dist/proxy/conversation-store.test.js +277 -0
- package/dist/proxy/conversation-store.test.js.map +1 -0
- package/dist/proxy/count-tokens.d.ts +20 -0
- package/dist/proxy/count-tokens.d.ts.map +1 -0
- package/dist/proxy/count-tokens.js +75 -0
- package/dist/proxy/count-tokens.js.map +1 -0
- package/dist/proxy/count-tokens.test.d.ts +2 -0
- package/dist/proxy/count-tokens.test.d.ts.map +1 -0
- package/dist/proxy/count-tokens.test.js +56 -0
- package/dist/proxy/count-tokens.test.js.map +1 -0
- package/dist/proxy/request-utils.d.ts +38 -0
- package/dist/proxy/request-utils.d.ts.map +1 -1
- package/dist/proxy/request-utils.js +84 -8
- package/dist/proxy/request-utils.js.map +1 -1
- package/dist/proxy/telemetry.d.ts +6 -0
- package/dist/proxy/telemetry.d.ts.map +1 -1
- package/dist/proxy/telemetry.js +53 -13
- package/dist/proxy/telemetry.js.map +1 -1
- package/dist/system-proxy/ca-manager.d.ts +67 -0
- package/dist/system-proxy/ca-manager.d.ts.map +1 -1
- package/dist/system-proxy/ca-manager.js +258 -39
- package/dist/system-proxy/ca-manager.js.map +1 -1
- package/dist/system-proxy/ca-manager.test.js +50 -1
- package/dist/system-proxy/ca-manager.test.js.map +1 -1
- package/dist/system-proxy/profiles.d.ts +2 -1
- package/dist/system-proxy/profiles.d.ts.map +1 -1
- package/dist/system-proxy/profiles.js +55 -4
- package/dist/system-proxy/profiles.js.map +1 -1
- package/dist/system-proxy/proxy-server.d.ts +18 -1
- package/dist/system-proxy/proxy-server.d.ts.map +1 -1
- package/dist/system-proxy/proxy-server.js +91 -38
- package/dist/system-proxy/proxy-server.js.map +1 -1
- package/dist/system-proxy/proxy-server.test.js +142 -13
- package/dist/system-proxy/proxy-server.test.js.map +1 -1
- package/dist/system-proxy/system-proxy.d.ts +1 -0
- package/dist/system-proxy/system-proxy.d.ts.map +1 -1
- package/dist/system-proxy/system-proxy.js +57 -15
- package/dist/system-proxy/system-proxy.js.map +1 -1
- package/dist/system-proxy/types.d.ts +17 -4
- package/dist/system-proxy/types.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { ConversationStore, CONVERSATIONS_FILE, conversationId } from './conversation-store.js';
|
|
7
|
+
async function makeDir() {
|
|
8
|
+
return mkdtemp(join(tmpdir(), 'antseed-conv-'));
|
|
9
|
+
}
|
|
10
|
+
test('touch creates a conversation and keeps the original snippet', async () => {
|
|
11
|
+
const dir = await makeDir();
|
|
12
|
+
try {
|
|
13
|
+
const store = new ConversationStore(dir);
|
|
14
|
+
const created = store.touch({ tool: 'codex', sessionKey: 's1', snippet: 'first prompt' });
|
|
15
|
+
assert.equal(created.id, 'codex:s1');
|
|
16
|
+
assert.equal(created.snippet, 'first prompt');
|
|
17
|
+
assert.equal(created.pinnedModel, null);
|
|
18
|
+
const touched = store.touch({ tool: 'codex', sessionKey: 's1', snippet: 'second prompt', lastModel: 'a'.repeat(40) + '@gpt-5.4' });
|
|
19
|
+
assert.equal(touched.snippet, 'first prompt');
|
|
20
|
+
assert.equal(touched.lastModel, 'a'.repeat(40) + '@gpt-5.4');
|
|
21
|
+
assert.ok(touched.lastActiveAt >= created.lastActiveAt);
|
|
22
|
+
await store.flush();
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await rm(dir, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
test('first resolved model becomes the pin and later touches never replace it', async () => {
|
|
29
|
+
const dir = await makeDir();
|
|
30
|
+
try {
|
|
31
|
+
const store = new ConversationStore(dir);
|
|
32
|
+
const firstModel = 'a'.repeat(40) + '@gpt-5.4';
|
|
33
|
+
const laterModel = 'b'.repeat(40) + '@glm-5';
|
|
34
|
+
// Created without a resolved model (e.g. a title request): no pin yet.
|
|
35
|
+
const created = store.touch({ tool: 'codex', sessionKey: 's1' });
|
|
36
|
+
assert.equal(created.pinnedModel, null);
|
|
37
|
+
// The first request that resolves a model pins the chat to it.
|
|
38
|
+
const pinned = store.touch({ tool: 'codex', sessionKey: 's1', lastModel: firstModel });
|
|
39
|
+
assert.equal(pinned.pinnedModel, firstModel);
|
|
40
|
+
// A later request served by a different route (default changed) keeps
|
|
41
|
+
// the original pin — the default only steers chats that haven't started.
|
|
42
|
+
const touched = store.touch({ tool: 'codex', sessionKey: 's1', lastModel: laterModel });
|
|
43
|
+
assert.equal(touched.pinnedModel, firstModel);
|
|
44
|
+
assert.equal(touched.lastModel, laterModel);
|
|
45
|
+
// A brand-new chat pins to its first model immediately.
|
|
46
|
+
const fresh = store.touch({ tool: 'codex', sessionKey: 's2', lastModel: laterModel });
|
|
47
|
+
assert.equal(fresh.pinnedModel, laterModel);
|
|
48
|
+
await store.flush();
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await rm(dir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
test('persists to conversations.json and reloads across instances', async () => {
|
|
55
|
+
const dir = await makeDir();
|
|
56
|
+
try {
|
|
57
|
+
const store = new ConversationStore(dir);
|
|
58
|
+
store.touch({ tool: 'opencode', sessionKey: 'ses_a', snippet: 'hello there' });
|
|
59
|
+
store.setLabel(conversationId('opencode', 'ses_a'), ' My renamed chat ');
|
|
60
|
+
store.setPinnedModel(conversationId('opencode', 'ses_a'), 'b'.repeat(40) + '@glm-5');
|
|
61
|
+
await store.flush();
|
|
62
|
+
const raw = JSON.parse(await readFile(join(dir, CONVERSATIONS_FILE), 'utf8'));
|
|
63
|
+
assert.equal(raw.conversations.length, 1);
|
|
64
|
+
const reloaded = new ConversationStore(dir);
|
|
65
|
+
const record = reloaded.get('opencode:ses_a');
|
|
66
|
+
assert.ok(record);
|
|
67
|
+
assert.equal(record.label, 'My renamed chat');
|
|
68
|
+
assert.equal(record.snippet, 'hello there');
|
|
69
|
+
assert.equal(reloaded.getPinnedModel('opencode', 'ses_a'), 'b'.repeat(40) + '@glm-5');
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await rm(dir, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
test('label can be cleared and pin can be cleared', async () => {
|
|
76
|
+
const dir = await makeDir();
|
|
77
|
+
try {
|
|
78
|
+
const store = new ConversationStore(dir);
|
|
79
|
+
store.touch({ tool: 'claude-code', sessionKey: 'k1' });
|
|
80
|
+
const id = conversationId('claude-code', 'k1');
|
|
81
|
+
store.setLabel(id, 'named');
|
|
82
|
+
store.setPinnedModel(id, 'c'.repeat(40) + '@claude-opus-4-8');
|
|
83
|
+
assert.equal(store.setLabel(id, null)?.label, null);
|
|
84
|
+
assert.equal(store.setPinnedModel(id, null)?.pinnedModel, null);
|
|
85
|
+
assert.equal(store.setLabel('unknown:id', 'x'), null);
|
|
86
|
+
await store.flush();
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
await rm(dir, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
test('remove deletes the record; a later touch recreates it fresh', async () => {
|
|
93
|
+
const dir = await makeDir();
|
|
94
|
+
try {
|
|
95
|
+
const store = new ConversationStore(dir);
|
|
96
|
+
store.touch({ tool: 'codex', sessionKey: 's1', snippet: 'old snippet' });
|
|
97
|
+
assert.equal(store.remove('codex:s1'), true);
|
|
98
|
+
assert.equal(store.remove('codex:s1'), false);
|
|
99
|
+
assert.equal(store.get('codex:s1'), null);
|
|
100
|
+
const recreated = store.touch({ tool: 'codex', sessionKey: 's1', snippet: 'new snippet' });
|
|
101
|
+
assert.equal(recreated.snippet, 'new snippet');
|
|
102
|
+
await store.flush();
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await rm(dir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
test('prunes beyond the LRU cap, keeping the most recently active', async () => {
|
|
109
|
+
const dir = await makeDir();
|
|
110
|
+
try {
|
|
111
|
+
const store = new ConversationStore(dir);
|
|
112
|
+
for (let i = 0; i < 60; i += 1) {
|
|
113
|
+
store.touch({ tool: 'codex', sessionKey: `s${i}` });
|
|
114
|
+
}
|
|
115
|
+
const listed = store.list();
|
|
116
|
+
assert.equal(listed.length, 50);
|
|
117
|
+
// The earliest-touched sessions are the ones evicted.
|
|
118
|
+
assert.equal(store.get('codex:s59') !== null, true);
|
|
119
|
+
await store.flush();
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await rm(dir, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
test('list returns newest activity first', async () => {
|
|
126
|
+
const dir = await makeDir();
|
|
127
|
+
try {
|
|
128
|
+
const store = new ConversationStore(dir);
|
|
129
|
+
store.touch({ tool: 'codex', sessionKey: 'older' });
|
|
130
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
131
|
+
store.touch({ tool: 'codex', sessionKey: 'newer' });
|
|
132
|
+
const [first] = store.list();
|
|
133
|
+
assert.equal(first?.sessionKey, 'newer');
|
|
134
|
+
await store.flush();
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await rm(dir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
test('survives a corrupted file by starting clean', async () => {
|
|
141
|
+
const dir = await makeDir();
|
|
142
|
+
try {
|
|
143
|
+
const { writeFile } = await import('node:fs/promises');
|
|
144
|
+
await writeFile(join(dir, CONVERSATIONS_FILE), 'not json at all', 'utf8');
|
|
145
|
+
const store = new ConversationStore(dir);
|
|
146
|
+
assert.deepEqual(store.list(), []);
|
|
147
|
+
store.touch({ tool: 'codex', sessionKey: 's1' });
|
|
148
|
+
await store.flush();
|
|
149
|
+
const reloaded = new ConversationStore(dir);
|
|
150
|
+
assert.equal(reloaded.list().length, 1);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await rm(dir, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
test('reload re-cleans snippets persisted by older extraction rules', async () => {
|
|
157
|
+
const dir = await makeDir();
|
|
158
|
+
try {
|
|
159
|
+
const { writeFile } = await import('node:fs/promises');
|
|
160
|
+
await writeFile(join(dir, CONVERSATIONS_FILE), JSON.stringify({
|
|
161
|
+
conversations: [
|
|
162
|
+
{
|
|
163
|
+
tool: 'claude-code', sessionKey: 'old1',
|
|
164
|
+
snippet: '<session> try to understand who reviews are from </session> and more text',
|
|
165
|
+
createdAt: Date.now(), lastActiveAt: Date.now(),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
tool: 'opencode', sessionKey: 'old2',
|
|
169
|
+
snippet: 'Generate a title for this conversation:',
|
|
170
|
+
createdAt: Date.now(), lastActiveAt: Date.now(),
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}), 'utf8');
|
|
174
|
+
const store = new ConversationStore(dir);
|
|
175
|
+
assert.equal(store.get('claude-code:old1')?.snippet, 'try to understand who reviews are from and more text');
|
|
176
|
+
// Old title-request snippets are dropped so the next real turn names the chat.
|
|
177
|
+
assert.equal(store.get('opencode:old2')?.snippet, '');
|
|
178
|
+
await store.flush();
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await rm(dir, { recursive: true, force: true });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
test('addSpend accumulates cost and counts requests only when told to', async () => {
|
|
185
|
+
const dir = await makeDir();
|
|
186
|
+
try {
|
|
187
|
+
const store = new ConversationStore(dir);
|
|
188
|
+
const created = store.touch({ tool: 'codex', sessionKey: 's1', snippet: 'hello' });
|
|
189
|
+
assert.equal(created.spentUsdc, '0');
|
|
190
|
+
assert.equal(created.requestCount, 0);
|
|
191
|
+
store.addSpend(created.id, { amountUsdc: '12000', inputTokens: '900', cachedInputTokens: '600', outputTokens: '120' });
|
|
192
|
+
// A second delta for the same request: cost adds up, the request does not.
|
|
193
|
+
store.addSpend(created.id, { amountUsdc: '3000', inputTokens: '0', cachedInputTokens: '0', outputTokens: '0' }, false);
|
|
194
|
+
store.addSpend(created.id, { amountUsdc: '5000', inputTokens: '100', cachedInputTokens: '25', outputTokens: '40' });
|
|
195
|
+
const row = store.get(created.id);
|
|
196
|
+
assert.equal(row?.spentUsdc, '20000');
|
|
197
|
+
assert.equal(row?.inputTokens, '1000');
|
|
198
|
+
assert.equal(row?.cachedInputTokens, '625');
|
|
199
|
+
assert.equal(row?.outputTokens, '160');
|
|
200
|
+
assert.equal(row?.requestCount, 2);
|
|
201
|
+
await store.flush();
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
await rm(dir, { recursive: true, force: true });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
test('addSpend ignores unknown ids, empty deltas and malformed amounts', async () => {
|
|
208
|
+
const dir = await makeDir();
|
|
209
|
+
try {
|
|
210
|
+
const store = new ConversationStore(dir);
|
|
211
|
+
const created = store.touch({ tool: 'codex', sessionKey: 's1' });
|
|
212
|
+
// A chat pruned mid-request must not come back as a spend-only row.
|
|
213
|
+
store.addSpend('codex:gone', { amountUsdc: '5000', inputTokens: '0', cachedInputTokens: '0', outputTokens: '0' });
|
|
214
|
+
assert.equal(store.get('codex:gone'), null);
|
|
215
|
+
store.addSpend(created.id, { amountUsdc: '0', inputTokens: '0', cachedInputTokens: '0', outputTokens: '0' });
|
|
216
|
+
store.addSpend(created.id, { amountUsdc: 'not-a-number', inputTokens: '0', cachedInputTokens: '0', outputTokens: '0' });
|
|
217
|
+
const row = store.get(created.id);
|
|
218
|
+
assert.equal(row?.spentUsdc, '0');
|
|
219
|
+
assert.equal(row?.requestCount, 0);
|
|
220
|
+
await store.flush();
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
await rm(dir, { recursive: true, force: true });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
test('spend counters survive a reload and heal from corrupt values', async () => {
|
|
227
|
+
const dir = await makeDir();
|
|
228
|
+
try {
|
|
229
|
+
const store = new ConversationStore(dir);
|
|
230
|
+
const created = store.touch({ tool: 'codex', sessionKey: 's1' });
|
|
231
|
+
store.addSpend(created.id, { amountUsdc: '7500', inputTokens: '10', cachedInputTokens: '4', outputTokens: '5' });
|
|
232
|
+
// addSpend defers persistence to the next touch, mirroring live traffic.
|
|
233
|
+
store.touch({ tool: 'codex', sessionKey: 's1' });
|
|
234
|
+
await store.flush();
|
|
235
|
+
const reloaded = new ConversationStore(dir);
|
|
236
|
+
assert.equal(reloaded.get(created.id)?.spentUsdc, '7500');
|
|
237
|
+
assert.equal(reloaded.get(created.id)?.cachedInputTokens, '4');
|
|
238
|
+
assert.equal(reloaded.get(created.id)?.requestCount, 1);
|
|
239
|
+
const { writeFile } = await import('node:fs/promises');
|
|
240
|
+
await writeFile(join(dir, CONVERSATIONS_FILE), JSON.stringify({
|
|
241
|
+
conversations: [{
|
|
242
|
+
tool: 'codex', sessionKey: 's2', spentUsdc: 'garbage', cachedInputTokens: null, requestCount: -3,
|
|
243
|
+
createdAt: Date.now(), lastActiveAt: Date.now(),
|
|
244
|
+
}],
|
|
245
|
+
}), 'utf8');
|
|
246
|
+
const healed = new ConversationStore(dir);
|
|
247
|
+
assert.equal(healed.get(conversationId('codex', 's2'))?.spentUsdc, '0');
|
|
248
|
+
assert.equal(healed.get(conversationId('codex', 's2'))?.cachedInputTokens, '0');
|
|
249
|
+
assert.equal(healed.get(conversationId('codex', 's2'))?.requestCount, 0);
|
|
250
|
+
await healed.flush();
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
await rm(dir, { recursive: true, force: true });
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
test('cached input is clamped to the input it is a subset of', async () => {
|
|
257
|
+
const dir = await makeDir();
|
|
258
|
+
try {
|
|
259
|
+
const store = new ConversationStore(dir);
|
|
260
|
+
const created = store.touch({ tool: 'codex', sessionKey: 's1' });
|
|
261
|
+
// A seller reporting more cached than total input is malformed — the
|
|
262
|
+
// subset must never exceed the whole it is measured against.
|
|
263
|
+
store.addSpend(created.id, { amountUsdc: '1000', inputTokens: '50', cachedInputTokens: '900', outputTokens: '10' });
|
|
264
|
+
const row = store.get(created.id);
|
|
265
|
+
assert.equal(row?.inputTokens, '50');
|
|
266
|
+
assert.equal(row?.cachedInputTokens, '50');
|
|
267
|
+
// Fresh input stays derivable as inputTokens - cachedInputTokens.
|
|
268
|
+
store.addSpend(created.id, { amountUsdc: '1000', inputTokens: '200', cachedInputTokens: '150', outputTokens: '10' });
|
|
269
|
+
const after = store.get(created.id);
|
|
270
|
+
assert.equal(BigInt(after.inputTokens) - BigInt(after.cachedInputTokens), 50n);
|
|
271
|
+
await store.flush();
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
await rm(dir, { recursive: true, force: true });
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
//# sourceMappingURL=conversation-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-store.test.js","sourceRoot":"","sources":["../../src/proxy/conversation-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic clients size a conversation against the context window by calling
|
|
3
|
+
* `POST /v1/messages/count_tokens` before a turn. It looks like a completion
|
|
4
|
+
* path but is not one: it must not reach a seller. Routed as a completion it
|
|
5
|
+
* bills a full inference, spends seconds of model time, and answers with a
|
|
6
|
+
* message body where the client expects `{ "input_tokens": n }` — so the tool
|
|
7
|
+
* stalls on every turn that has context to measure.
|
|
8
|
+
*
|
|
9
|
+
* The buyer answers it locally instead, from the same tokenx estimate the
|
|
10
|
+
* pricing layer uses (~95-98% of the real count).
|
|
11
|
+
*/
|
|
12
|
+
/** Anthropic's token-count endpoint, on any API version prefix. */
|
|
13
|
+
export declare function isCountTokensPath(path: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Estimate the prompt tokens an Anthropic `count_tokens` request would bill:
|
|
16
|
+
* system prompt, tool definitions and the full message history. Structural
|
|
17
|
+
* JSON is skipped — only the text a model actually reads is counted.
|
|
18
|
+
*/
|
|
19
|
+
export declare function estimateAnthropicPromptTokens(body: Uint8Array): number;
|
|
20
|
+
//# sourceMappingURL=count-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-tokens.d.ts","sourceRoot":"","sources":["../../src/proxy/count-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAkBtE"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic clients size a conversation against the context window by calling
|
|
3
|
+
* `POST /v1/messages/count_tokens` before a turn. It looks like a completion
|
|
4
|
+
* path but is not one: it must not reach a seller. Routed as a completion it
|
|
5
|
+
* bills a full inference, spends seconds of model time, and answers with a
|
|
6
|
+
* message body where the client expects `{ "input_tokens": n }` — so the tool
|
|
7
|
+
* stalls on every turn that has context to measure.
|
|
8
|
+
*
|
|
9
|
+
* The buyer answers it locally instead, from the same tokenx estimate the
|
|
10
|
+
* pricing layer uses (~95-98% of the real count).
|
|
11
|
+
*/
|
|
12
|
+
import { estimateTokensFromText } from '@antseed/node';
|
|
13
|
+
import { parseJsonObject } from '@antseed/api-adapter';
|
|
14
|
+
/** Anthropic's token-count endpoint, on any API version prefix. */
|
|
15
|
+
export function isCountTokensPath(path) {
|
|
16
|
+
const clean = (path.split('?')[0] ?? '').replace(/\/+$/, '');
|
|
17
|
+
return clean.endsWith('/messages/count_tokens');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Estimate the prompt tokens an Anthropic `count_tokens` request would bill:
|
|
21
|
+
* system prompt, tool definitions and the full message history. Structural
|
|
22
|
+
* JSON is skipped — only the text a model actually reads is counted.
|
|
23
|
+
*/
|
|
24
|
+
export function estimateAnthropicPromptTokens(body) {
|
|
25
|
+
const parsed = parseJsonObject(body);
|
|
26
|
+
if (!parsed)
|
|
27
|
+
return 0;
|
|
28
|
+
const parts = [];
|
|
29
|
+
collectText(parsed['system'], parts);
|
|
30
|
+
collectText(parsed['messages'], parts);
|
|
31
|
+
for (const tool of asArray(parsed['tools'])) {
|
|
32
|
+
const record = asRecord(tool);
|
|
33
|
+
if (!record)
|
|
34
|
+
continue;
|
|
35
|
+
collectText(record['name'], parts);
|
|
36
|
+
collectText(record['description'], parts);
|
|
37
|
+
// Schemas are read by the model verbatim, punctuation included.
|
|
38
|
+
if (record['input_schema'] !== undefined)
|
|
39
|
+
parts.push(JSON.stringify(record['input_schema']));
|
|
40
|
+
}
|
|
41
|
+
const text = parts.join('\n');
|
|
42
|
+
return text.length > 0 ? estimateTokensFromText(text) : 0;
|
|
43
|
+
}
|
|
44
|
+
/** Walk any Anthropic content shape (string, block, array of blocks) for text. */
|
|
45
|
+
function collectText(value, out) {
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
if (value.length > 0)
|
|
48
|
+
out.push(value);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const item of asArray(value)) {
|
|
52
|
+
collectText(item, out);
|
|
53
|
+
}
|
|
54
|
+
const record = asRecord(value);
|
|
55
|
+
if (!record)
|
|
56
|
+
return;
|
|
57
|
+
for (const key of ['text', 'content', 'thinking']) {
|
|
58
|
+
if (record[key] !== undefined)
|
|
59
|
+
collectText(record[key], out);
|
|
60
|
+
}
|
|
61
|
+
// tool_use arguments and tool_result payloads are prompt text too.
|
|
62
|
+
if (record['input'] !== undefined)
|
|
63
|
+
out.push(JSON.stringify(record['input']));
|
|
64
|
+
if (typeof record['name'] === 'string' && record['type'] === 'tool_use')
|
|
65
|
+
out.push(record['name']);
|
|
66
|
+
}
|
|
67
|
+
function asArray(value) {
|
|
68
|
+
return Array.isArray(value) ? value : [];
|
|
69
|
+
}
|
|
70
|
+
function asRecord(value) {
|
|
71
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
72
|
+
? value
|
|
73
|
+
: null;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=count-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-tokens.js","sourceRoot":"","sources":["../../src/proxy/count-tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-tokens.test.d.ts","sourceRoot":"","sources":["../../src/proxy/count-tokens.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { estimateAnthropicPromptTokens, isCountTokensPath } from './count-tokens.js';
|
|
4
|
+
const encode = (value) => new TextEncoder().encode(JSON.stringify(value));
|
|
5
|
+
test('isCountTokensPath matches only the token-count endpoint', () => {
|
|
6
|
+
assert.equal(isCountTokensPath('/v1/messages/count_tokens'), true);
|
|
7
|
+
assert.equal(isCountTokensPath('/v1/messages/count_tokens?beta=true'), true);
|
|
8
|
+
assert.equal(isCountTokensPath('/v1/messages/count_tokens/'), true);
|
|
9
|
+
assert.equal(isCountTokensPath('/v1/messages'), false);
|
|
10
|
+
assert.equal(isCountTokensPath('/v1/messages/batches'), false);
|
|
11
|
+
assert.equal(isCountTokensPath('/v1/chat/completions'), false);
|
|
12
|
+
});
|
|
13
|
+
test('estimate counts system, tools and the whole message history', () => {
|
|
14
|
+
const words = 'sentinel '.repeat(200).trim();
|
|
15
|
+
const systemOnly = estimateAnthropicPromptTokens(encode({
|
|
16
|
+
model: 'claude-sonnet-4-5',
|
|
17
|
+
system: words,
|
|
18
|
+
messages: [{ role: 'user', content: 'hi' }],
|
|
19
|
+
}));
|
|
20
|
+
const withHistory = estimateAnthropicPromptTokens(encode({
|
|
21
|
+
model: 'claude-sonnet-4-5',
|
|
22
|
+
system: words,
|
|
23
|
+
messages: [
|
|
24
|
+
{ role: 'user', content: 'hi' },
|
|
25
|
+
{ role: 'assistant', content: [{ type: 'text', text: words }] },
|
|
26
|
+
{ role: 'user', content: 'and now?' },
|
|
27
|
+
],
|
|
28
|
+
}));
|
|
29
|
+
const withTools = estimateAnthropicPromptTokens(encode({
|
|
30
|
+
model: 'claude-sonnet-4-5',
|
|
31
|
+
system: words,
|
|
32
|
+
tools: [{ name: 'Read', description: words, input_schema: { type: 'object' } }],
|
|
33
|
+
messages: [{ role: 'user', content: 'hi' }],
|
|
34
|
+
}));
|
|
35
|
+
assert.ok(systemOnly > 100, `expected a real count, got ${systemOnly}`);
|
|
36
|
+
assert.ok(withHistory > systemOnly * 1.8, `history must be counted: ${withHistory} vs ${systemOnly}`);
|
|
37
|
+
assert.ok(withTools > systemOnly * 1.8, `tools must be counted: ${withTools} vs ${systemOnly}`);
|
|
38
|
+
});
|
|
39
|
+
test('estimate reads array system blocks and tool traffic', () => {
|
|
40
|
+
const tokens = estimateAnthropicPromptTokens(encode({
|
|
41
|
+
system: [
|
|
42
|
+
{ type: 'text', text: 'You are a CLI assistant.' },
|
|
43
|
+
{ type: 'text', text: 'Policy text. '.repeat(50), cache_control: { type: 'ephemeral' } },
|
|
44
|
+
],
|
|
45
|
+
messages: [
|
|
46
|
+
{ role: 'assistant', content: [{ type: 'tool_use', id: 't1', name: 'Read', input: { path: 'package.json' } }] },
|
|
47
|
+
{ role: 'user', content: [{ type: 'tool_result', tool_use_id: 't1', content: 'file contents here' }] },
|
|
48
|
+
],
|
|
49
|
+
}));
|
|
50
|
+
assert.ok(tokens > 50, `expected blocks to be counted, got ${tokens}`);
|
|
51
|
+
});
|
|
52
|
+
test('estimate is zero for a body with no prompt text', () => {
|
|
53
|
+
assert.equal(estimateAnthropicPromptTokens(encode({ model: 'claude-sonnet-4-5' })), 0);
|
|
54
|
+
assert.equal(estimateAnthropicPromptTokens(new TextEncoder().encode('not json')), 0);
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=count-tokens.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-tokens.test.js","sourceRoot":"","sources":["../../src/proxy/count-tokens.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,44 @@ export declare function summarizeRequestShape(request: SerializedHttpRequest): s
|
|
|
11
11
|
export declare function summarizeErrorResponse(response: SerializedHttpResponse): string;
|
|
12
12
|
export declare function requestWantsStreaming(headers: Record<string, string>, body: Uint8Array): boolean;
|
|
13
13
|
export declare function isLoopbackPeer(peer: PeerInfo): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Model alias that tool configs can carry instead of a concrete
|
|
16
|
+
* `<peerId>@<service>` key. The buyer substitutes the session's default
|
|
17
|
+
* routed model at request time, so the route selected in the desktop
|
|
18
|
+
* (floating pill / VPR) applies to already-running tool sessions without
|
|
19
|
+
* rewriting their configs.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ROUTED_MODEL_ALIAS = "antseed";
|
|
22
|
+
export declare function substituteRoutedModelAlias(body: Uint8Array, headers: Record<string, string>, defaultRoutedModel: string | null): {
|
|
23
|
+
body: Uint8Array;
|
|
24
|
+
headers: Record<string, string>;
|
|
25
|
+
aliasRequested: boolean;
|
|
26
|
+
substituted: boolean;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Marker header the system proxy stamps on requests whose `model` field IT
|
|
30
|
+
* rewrote from the connect-time route — intercepted tools (Claude Code
|
|
31
|
+
* talking to api.anthropic.com through the MITM proxy) send upstream model
|
|
32
|
+
* names, not an AntSeed route, so the model on such requests is AntSeed
|
|
33
|
+
* plumbing rather than a client choice. The buyer treats those models like
|
|
34
|
+
* the routed-model alias for per-chat routing: a chat's pinned model
|
|
35
|
+
* overrides the proxy-resolved route. Internal — stripped before dispatch.
|
|
36
|
+
*/
|
|
37
|
+
export declare const SYSTEM_ROUTED_MODEL_HEADER = "x-antseed-system-routed";
|
|
38
|
+
/** Internal profile marker stamped by the system proxy so intercepted app
|
|
39
|
+
traffic is attributed to the Connected App profile that captured it,
|
|
40
|
+
rather than to a generic SDK User-Agent. Stripped before dispatch. */
|
|
41
|
+
export declare const SYSTEM_PROXY_SOURCE_HEADER = "x-antseed-system-proxy-source";
|
|
42
|
+
/**
|
|
43
|
+
* Replace a proxy-assigned model with the chat's pinned route. Only used for
|
|
44
|
+
* requests carrying SYSTEM_ROUTED_MODEL_HEADER — client-chosen models are
|
|
45
|
+
* never overridden.
|
|
46
|
+
*/
|
|
47
|
+
export declare function overrideRoutedModelInBody(body: Uint8Array, headers: Record<string, string>, pinnedModel: string): {
|
|
48
|
+
body: Uint8Array;
|
|
49
|
+
headers: Record<string, string>;
|
|
50
|
+
overridden: boolean;
|
|
51
|
+
};
|
|
14
52
|
export declare function rewritePeerPinnedServiceInBody(body: Uint8Array, headers: Record<string, string>): {
|
|
15
53
|
body: Uint8Array;
|
|
16
54
|
headers: Record<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-utils.d.ts","sourceRoot":"","sources":["../../src/proxy/request-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,QAAQ,EAAE,KAAK,qBAAqB,EAAE,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAW3H,wBAAgB,KAAK,IAAI,OAAO,CAE/B;AAED,wBAAgB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAE5C;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYhG;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAarF;AAiFD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CA2C5E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,CAqC/E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAWhG;AAsBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAMtD;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"request-utils.d.ts","sourceRoot":"","sources":["../../src/proxy/request-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,QAAQ,EAAE,KAAK,qBAAqB,EAAE,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAW3H,wBAAgB,KAAK,IAAI,OAAO,CAE/B;AAED,wBAAgB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAE5C;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYhG;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAarF;AAiFD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CA2C5E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,CAqC/E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAWhG;AAsBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAMtD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,YAAY,CAAA;AAE3C,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAyBtG;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,4BAA4B,CAAA;AAEnE;;yEAEyE;AACzE,eAAO,MAAM,0BAA0B,kCAAkC,CAAA;AAEzE;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,WAAW,EAAE,MAAM,GAClB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAkB5E;AAgBD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAgCpF"}
|
|
@@ -221,6 +221,89 @@ export function isLoopbackPeer(peer) {
|
|
|
221
221
|
const host = extractHostFromAddress(peer.publicAddress);
|
|
222
222
|
return isLoopbackHost(host);
|
|
223
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Model alias that tool configs can carry instead of a concrete
|
|
226
|
+
* `<peerId>@<service>` key. The buyer substitutes the session's default
|
|
227
|
+
* routed model at request time, so the route selected in the desktop
|
|
228
|
+
* (floating pill / VPR) applies to already-running tool sessions without
|
|
229
|
+
* rewriting their configs.
|
|
230
|
+
*/
|
|
231
|
+
export const ROUTED_MODEL_ALIAS = 'antseed';
|
|
232
|
+
export function substituteRoutedModelAlias(body, headers, defaultRoutedModel) {
|
|
233
|
+
if (!getHeader(headers, 'content-type').toLowerCase().includes('application/json') || body.length === 0) {
|
|
234
|
+
return { body, headers, aliasRequested: false, substituted: false };
|
|
235
|
+
}
|
|
236
|
+
const obj = parseJsonObject(body);
|
|
237
|
+
if (!obj) {
|
|
238
|
+
return { body, headers, aliasRequested: false, substituted: false };
|
|
239
|
+
}
|
|
240
|
+
const rawModel = typeof obj['model'] === 'string' ? obj['model'].trim() : '';
|
|
241
|
+
const rawService = typeof obj['service'] === 'string' ? obj['service'].trim() : '';
|
|
242
|
+
const modelIsAlias = rawModel.toLowerCase() === ROUTED_MODEL_ALIAS;
|
|
243
|
+
const serviceIsAlias = rawService.toLowerCase() === ROUTED_MODEL_ALIAS;
|
|
244
|
+
if (!modelIsAlias && !serviceIsAlias) {
|
|
245
|
+
return { body, headers, aliasRequested: false, substituted: false };
|
|
246
|
+
}
|
|
247
|
+
const target = defaultRoutedModel?.trim();
|
|
248
|
+
if (!target) {
|
|
249
|
+
return { body, headers, aliasRequested: true, substituted: false };
|
|
250
|
+
}
|
|
251
|
+
if (modelIsAlias)
|
|
252
|
+
obj['model'] = target;
|
|
253
|
+
if (serviceIsAlias)
|
|
254
|
+
obj['service'] = target;
|
|
255
|
+
const { body: rewritten, headers: updatedHeaders } = encodeRewrittenJsonBody(obj, headers);
|
|
256
|
+
return { body: rewritten, headers: updatedHeaders, aliasRequested: true, substituted: true };
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Marker header the system proxy stamps on requests whose `model` field IT
|
|
260
|
+
* rewrote from the connect-time route — intercepted tools (Claude Code
|
|
261
|
+
* talking to api.anthropic.com through the MITM proxy) send upstream model
|
|
262
|
+
* names, not an AntSeed route, so the model on such requests is AntSeed
|
|
263
|
+
* plumbing rather than a client choice. The buyer treats those models like
|
|
264
|
+
* the routed-model alias for per-chat routing: a chat's pinned model
|
|
265
|
+
* overrides the proxy-resolved route. Internal — stripped before dispatch.
|
|
266
|
+
*/
|
|
267
|
+
export const SYSTEM_ROUTED_MODEL_HEADER = 'x-antseed-system-routed';
|
|
268
|
+
/** Internal profile marker stamped by the system proxy so intercepted app
|
|
269
|
+
traffic is attributed to the Connected App profile that captured it,
|
|
270
|
+
rather than to a generic SDK User-Agent. Stripped before dispatch. */
|
|
271
|
+
export const SYSTEM_PROXY_SOURCE_HEADER = 'x-antseed-system-proxy-source';
|
|
272
|
+
/**
|
|
273
|
+
* Replace a proxy-assigned model with the chat's pinned route. Only used for
|
|
274
|
+
* requests carrying SYSTEM_ROUTED_MODEL_HEADER — client-chosen models are
|
|
275
|
+
* never overridden.
|
|
276
|
+
*/
|
|
277
|
+
export function overrideRoutedModelInBody(body, headers, pinnedModel) {
|
|
278
|
+
if (!getHeader(headers, 'content-type').toLowerCase().includes('application/json') || body.length === 0) {
|
|
279
|
+
return { body, headers, overridden: false };
|
|
280
|
+
}
|
|
281
|
+
const obj = parseJsonObject(body);
|
|
282
|
+
if (!obj) {
|
|
283
|
+
return { body, headers, overridden: false };
|
|
284
|
+
}
|
|
285
|
+
const rawModel = typeof obj['model'] === 'string' ? obj['model'].trim() : '';
|
|
286
|
+
if (!rawModel || rawModel === pinnedModel) {
|
|
287
|
+
return { body, headers, overridden: false };
|
|
288
|
+
}
|
|
289
|
+
obj['model'] = pinnedModel;
|
|
290
|
+
if (typeof obj['service'] === 'string' && obj['service'].trim() === rawModel) {
|
|
291
|
+
obj['service'] = pinnedModel;
|
|
292
|
+
}
|
|
293
|
+
const { body: rewritten, headers: updatedHeaders } = encodeRewrittenJsonBody(obj, headers);
|
|
294
|
+
return { body: rewritten, headers: updatedHeaders, overridden: true };
|
|
295
|
+
}
|
|
296
|
+
function encodeRewrittenJsonBody(obj, headers) {
|
|
297
|
+
const rewritten = new TextEncoder().encode(JSON.stringify(obj));
|
|
298
|
+
const updatedHeaders = { ...headers };
|
|
299
|
+
if ('content-length' in updatedHeaders) {
|
|
300
|
+
updatedHeaders['content-length'] = String(rewritten.length);
|
|
301
|
+
}
|
|
302
|
+
else if ('Content-Length' in updatedHeaders) {
|
|
303
|
+
updatedHeaders['Content-Length'] = String(rewritten.length);
|
|
304
|
+
}
|
|
305
|
+
return { body: rewritten, headers: updatedHeaders };
|
|
306
|
+
}
|
|
224
307
|
export function rewritePeerPinnedServiceInBody(body, headers) {
|
|
225
308
|
if (!getHeader(headers, 'content-type').toLowerCase().includes('application/json') || body.length === 0) {
|
|
226
309
|
return { body, headers, pinnedPeerId: null };
|
|
@@ -249,14 +332,7 @@ export function rewritePeerPinnedServiceInBody(body, headers) {
|
|
|
249
332
|
obj['model'] = parsedService.service;
|
|
250
333
|
}
|
|
251
334
|
}
|
|
252
|
-
const rewritten =
|
|
253
|
-
const updatedHeaders = { ...headers };
|
|
254
|
-
if ('content-length' in updatedHeaders) {
|
|
255
|
-
updatedHeaders['content-length'] = String(rewritten.length);
|
|
256
|
-
}
|
|
257
|
-
else if ('Content-Length' in updatedHeaders) {
|
|
258
|
-
updatedHeaders['Content-Length'] = String(rewritten.length);
|
|
259
|
-
}
|
|
335
|
+
const { body: rewritten, headers: updatedHeaders } = encodeRewrittenJsonBody(obj, headers);
|
|
260
336
|
return { body: rewritten, headers: updatedHeaders, pinnedPeerId: parsed.peerId };
|
|
261
337
|
}
|
|
262
338
|
//# sourceMappingURL=request-utils.js.map
|