@antseed/cli 0.1.189 → 0.1.190

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.
Files changed (59) hide show
  1. package/README.md +0 -9
  2. package/dist/config/service-metadata.test.js +0 -4
  3. package/dist/config/service-metadata.test.js.map +1 -1
  4. package/dist/gateway/gateway-policy.test.js +0 -19
  5. package/dist/gateway/gateway-policy.test.js.map +1 -1
  6. package/dist/plugins/registry.d.ts.map +1 -1
  7. package/dist/plugins/registry.js +0 -6
  8. package/dist/plugins/registry.js.map +1 -1
  9. package/dist/proxy/buyer-proxy.d.ts +4 -56
  10. package/dist/proxy/buyer-proxy.d.ts.map +1 -1
  11. package/dist/proxy/buyer-proxy.js +120 -480
  12. package/dist/proxy/buyer-proxy.js.map +1 -1
  13. package/dist/proxy/buyer-proxy.test.js +5 -1331
  14. package/dist/proxy/buyer-proxy.test.js.map +1 -1
  15. package/dist/proxy/conversation-store.d.ts +0 -8
  16. package/dist/proxy/conversation-store.d.ts.map +1 -1
  17. package/dist/proxy/conversation-store.js +0 -20
  18. package/dist/proxy/conversation-store.js.map +1 -1
  19. package/dist/proxy/conversation-store.test.js +1 -54
  20. package/dist/proxy/conversation-store.test.js.map +1 -1
  21. package/dist/proxy/gateway-policy.test.js +0 -110
  22. package/dist/proxy/gateway-policy.test.js.map +1 -1
  23. package/dist/proxy/network-models.d.ts +1 -21
  24. package/dist/proxy/network-models.d.ts.map +1 -1
  25. package/dist/proxy/network-models.js +0 -2
  26. package/dist/proxy/network-models.js.map +1 -1
  27. package/dist/proxy/request-utils.d.ts +0 -20
  28. package/dist/proxy/request-utils.d.ts.map +1 -1
  29. package/dist/proxy/request-utils.js +1 -46
  30. package/dist/proxy/request-utils.js.map +1 -1
  31. package/dist/proxy/route-policy.d.ts +0 -7
  32. package/dist/proxy/route-policy.d.ts.map +1 -1
  33. package/dist/proxy/route-policy.js +0 -10
  34. package/dist/proxy/route-policy.js.map +1 -1
  35. package/package.json +8 -10
  36. package/dist/proxy/router-execution.d.ts +0 -63
  37. package/dist/proxy/router-execution.d.ts.map +0 -1
  38. package/dist/proxy/router-execution.js +0 -220
  39. package/dist/proxy/router-execution.js.map +0 -1
  40. package/dist/proxy/router-execution.test.d.ts +0 -2
  41. package/dist/proxy/router-execution.test.d.ts.map +0 -1
  42. package/dist/proxy/router-execution.test.js +0 -269
  43. package/dist/proxy/router-execution.test.js.map +0 -1
  44. package/dist/proxy/routing-services.d.ts +0 -37
  45. package/dist/proxy/routing-services.d.ts.map +0 -1
  46. package/dist/proxy/routing-services.js +0 -58
  47. package/dist/proxy/routing-services.js.map +0 -1
  48. package/dist/proxy/routing-services.test.d.ts +0 -2
  49. package/dist/proxy/routing-services.test.d.ts.map +0 -1
  50. package/dist/proxy/routing-services.test.js +0 -95
  51. package/dist/proxy/routing-services.test.js.map +0 -1
  52. package/dist/proxy/routing-usage.d.ts +0 -4
  53. package/dist/proxy/routing-usage.d.ts.map +0 -1
  54. package/dist/proxy/routing-usage.js +0 -19
  55. package/dist/proxy/routing-usage.js.map +0 -1
  56. package/dist/proxy/routing-usage.test.d.ts +0 -2
  57. package/dist/proxy/routing-usage.test.d.ts.map +0 -1
  58. package/dist/proxy/routing-usage.test.js +0 -32
  59. package/dist/proxy/routing-usage.test.js.map +0 -1
@@ -5,12 +5,10 @@ import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import { Readable } from 'node:stream';
7
7
  import test from 'node:test';
8
- import localPlugin from '@antseed/router-local';
9
8
  import { ResourceRoutes } from './resource-routes.js';
10
9
  import { ANTSEED_BUYER_FAULT_ERROR_CODE, ANTSEED_FAULT_ATTRIBUTION_HEADER, CONNECTION_CAPABILITY_COOPERATIVE_CLOSE_V1, CONNECTION_CAPABILITY_RELAYS_SWEEPS_V1, adaptPeerFaultErrorResponse, buyerFault, computeTrustScore, } from '@antseed/node';
11
10
  import { DEFAULT_BUYER_PEER_REFRESH_INTERVAL_MS } from '../config/defaults.js';
12
11
  import { TeeVerification } from './tee-verification.js';
13
- import { ConversationStore } from './conversation-store.js';
14
12
  import { BuyerProxy, isModelNotFoundResponse, makeVerifierReach, mergeJsonStateFile, parsePeerPinnedService, parsePersistedPeers, rewritePeerPinnedServiceInBody, sanitizePeerBuyerFaultMarker, selectCandidatePeersForRouting, substituteRoutedModelAlias, sweepStaleStateTmpFiles, } from './buyer-proxy.js';
15
13
  import { extractRequestedService, overrideRoutedModelInBody, SYSTEM_ROUTED_MODEL_HEADER, } from './request-utils.js';
16
14
  function makePeer(seed, providers) {
@@ -21,1314 +19,6 @@ function makePeer(seed, providers) {
21
19
  providers,
22
20
  };
23
21
  }
24
- test('the buyer switches between a model and a model-routing service without replacing the plugin', async () => {
25
- const inferencePeer = makeRankedRoutingFixture().peers[0];
26
- const routingPeer = makePeer('d', ['alpha']);
27
- routingPeer.metadata = {
28
- peerId: routingPeer.peerId, version: 12, timestamp: Date.now(), signature: '', region: 'test',
29
- providers: [{ provider: 'alpha', services: ['alpha-route'], maxConcurrency: 1, currentLoad: 0,
30
- defaultPricing: { inputUsdPerMillion: 0, outputUsdPerMillion: 0 },
31
- serviceApiProtocols: { 'alpha-route': ['model-routing'] },
32
- serviceUnitBillingModels: { 'alpha-route': { 'model-routing': {
33
- version: 1, components: [{ unit: 'completed_requests', priceUsd: 0.001 }],
34
- } } },
35
- }],
36
- };
37
- const router = await localPlugin.createRouter({});
38
- const peers = [inferencePeer, routingPeer];
39
- const proxy = makeBuyerProxyWithPeers(peers, peers, router);
40
- proxy._mergeStateFile = async () => { };
41
- const initial = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/_antseed/route' }));
42
- assert.deepEqual(JSON.parse(initial.body), { ok: true, model: null, router: null });
43
- const models = { object: 'list', data: [{ id: 'model-a', object: 'model' }] };
44
- const requests = [];
45
- proxy._node.sendRequest = async (peer, request, options) => {
46
- requests.push(request);
47
- if (request.path === '/v1/routing/models') {
48
- assert.equal(peer.peerId, routingPeer.peerId);
49
- assert.equal(options.controlPlane, true);
50
- assert.equal(request.headers['x-antseed-service'], undefined);
51
- return { requestId: request.requestId, statusCode: 200, headers: {}, body: Buffer.from(JSON.stringify(models)) };
52
- }
53
- if (request.path === '/v1/routing/rank') {
54
- assert.equal(peer.peerId, routingPeer.peerId);
55
- const payload = JSON.parse(Buffer.from(request.body).toString());
56
- assert.equal(request.headers['x-antseed-service'], undefined);
57
- assert.deepEqual(payload, {
58
- request: { messages: [{ role: 'user', content: 'Hello' }] },
59
- routing: { cost_quality_tradeoff: 1, candidates: [{ id: `openai:model-a@${inferencePeer.peerId}`, model: 'model-a', pricing: { input: 1, cache_read: 1, output: 2 } }] },
60
- });
61
- const response = { requestId: request.requestId, statusCode: 200, headers: {}, body: Buffer.from(JSON.stringify({
62
- id: 'rank-1', object: 'routing.ranking', created: 1, router: { id: 'alpha', version: '1' },
63
- ranked: [{ candidate_id: `openai:model-a@${inferencePeer.peerId}` }],
64
- })) };
65
- return response;
66
- }
67
- assert.equal(peer.peerId, inferencePeer.peerId);
68
- assert.equal(JSON.parse(Buffer.from(request.body).toString()).model, 'model-a');
69
- return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from('{"choices":[]}') };
70
- };
71
- const setModel = () => invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'model', model: 'model-a' }) }));
72
- const infer = (model = 'antseed') => invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
73
- assert.equal((await setModel()).statusCode, 200);
74
- assert.equal((await infer()).statusCode, 200);
75
- assert.equal(requests.length, 1);
76
- const selection = { kind: 'router', service: { peerId: routingPeer.peerId, provider: 'alpha', serviceId: 'alpha-route' }, costQualityTradeoff: 1 };
77
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody(selection) }))).statusCode, 200);
78
- const routed = await infer();
79
- assert.equal(routed.statusCode, 200, routed.body);
80
- assert.deepEqual(requests.map(request => request.path.split('?')[0]), ['/v1/chat/completions', '/v1/routing/models', '/v1/routing/rank', '/v1/chat/completions']);
81
- assert.notEqual(requests[2].requestId, requests[3].requestId);
82
- assert.equal((await infer('model-a')).statusCode, 200);
83
- assert.equal((await infer(`${inferencePeer.peerId}@model-a`)).statusCode, 200);
84
- assert.equal((await setModel()).statusCode, 200);
85
- assert.equal((await infer()).statusCode, 200);
86
- assert.equal(requests.filter(request => request.path === '/v1/routing/rank').length, 1);
87
- assert.equal(requests.filter(request => request.path === '/v1/routing/models').length, 1);
88
- const discovery = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/_antseed/routing-services' }));
89
- assert.equal(discovery.statusCode, 200);
90
- assert.deepEqual(JSON.parse(discovery.body).services[0].catalog, { models: [{ provider: 'openai', serviceId: 'model-a' }] });
91
- assert.equal(requests.filter(request => request.path === '/v1/routing/models').length, 1);
92
- assert.equal(proxy._node.router, router);
93
- });
94
- test('a failed routing-service model list fails the request closed without buying inference', async () => {
95
- const { proxy, calls, client } = makeRankedRoutingFixture([200]);
96
- client.listModels = async () => { throw new Error('routing service unavailable'); };
97
- proxy._mergeStateFile = async () => { };
98
- const selected = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody(chatRouterSelection('a', 5)) }));
99
- assert.equal(selected.statusCode, 200, selected.body);
100
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
101
- assert.equal(response.statusCode, 502);
102
- assert.match(response.body, /routing service unavailable/);
103
- assert.equal(calls.routing, 0);
104
- assert.equal(calls.requests.length, 0);
105
- });
106
- test('local routing stays in model mode by default and rejects unadvertised routing targets', async () => {
107
- const router = await localPlugin.createRouter({});
108
- const proxy = makeBuyerProxyWithPeers([], [], router);
109
- proxy._mergeStateFile = async () => { };
110
- proxy._node.sendRequest = () => { throw new Error('No purchase should be dispatched'); };
111
- const initial = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/_antseed/route' }));
112
- assert.deepEqual(JSON.parse(initial.body), { ok: true, model: null, router: null });
113
- const selected = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody(chatRouterSelection('d', 5)) }));
114
- assert.equal(selected.statusCode, 200, selected.body);
115
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
116
- assert.equal(response.statusCode, 502);
117
- assert.match(response.body, /not advertised/);
118
- });
119
- test('restored router peers without signed announcements return a discovery error, not a filter crash', async () => {
120
- const selection = chatRouterSelection('d', 5);
121
- const peers = parsePersistedPeers({ discoveredPeers: [{ peerId: 'd'.repeat(40), providers: ['alpha'],
122
- capabilities: ['transport.webrtc.v1'], lastSeen: Date.now() }] });
123
- assert.deepEqual(Object.keys(peers[0].metadata), ['capabilities']);
124
- const router = await localPlugin.createRouter({});
125
- const proxy = makeBuyerProxyWithPeers(peers, peers, router);
126
- proxy._mergeStateFile = async () => { };
127
- proxy._node.sendRequest = () => { throw new Error('No purchase should be dispatched'); };
128
- const selected = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody(selection) }));
129
- assert.equal(selected.statusCode, 200, selected.body);
130
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
131
- assert.equal(response.statusCode, 502);
132
- assert.match(response.body, /Selected router metadata is not available yet/);
133
- assert.doesNotMatch(response.body, /filter|TypeError/);
134
- });
135
- test('the buyer and chat router selections pass their cost/quality tradeoff to the routing client', async (testContext) => {
136
- const inferencePeer = makeRankedRoutingFixture().peers[0];
137
- const routingPeer = makePeer('d', ['alternative']);
138
- routingPeer.metadata = { peerId: routingPeer.peerId, version: 12, region: 'test', timestamp: Date.now(), signature: '',
139
- providers: [{ provider: 'alternative', services: ['route'], maxConcurrency: 1, currentLoad: 0,
140
- defaultPricing: { inputUsdPerMillion: 0, outputUsdPerMillion: 0 },
141
- serviceApiProtocols: { route: ['model-routing'] },
142
- }] };
143
- const contexts = [];
144
- const client = {
145
- listModels: listModelsWith(),
146
- async selectRoute(_request, _peers, context) {
147
- contexts.push(context);
148
- const routes = [{ serviceId: 'model-a', peerId: inferencePeer.peerId }];
149
- assert.equal(context.acceptRecommendations(routes), true);
150
- return routes;
151
- },
152
- };
153
- const router = localPlugin.createRouter({});
154
- const peers = [inferencePeer, routingPeer];
155
- const proxy = makeBuyerProxyWithPeers(peers, peers, router, undefined, undefined, client);
156
- const directory = await mkdtemp(join(tmpdir(), 'antseed-client-selection-'));
157
- const store = new ConversationStore(directory);
158
- testContext.after(async () => { await store.flush(); await rm(directory, { recursive: true, force: true }); });
159
- proxy._conversations = store;
160
- proxy._mergeStateFile = async () => { };
161
- let inferenceCalls = 0;
162
- proxy._node.sendRequest = async (peer, request) => {
163
- inferenceCalls++;
164
- assert.equal(peer.peerId, inferencePeer.peerId);
165
- return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from('{"choices":[]}') };
166
- };
167
- const target = { peerId: routingPeer.peerId, provider: 'alternative', serviceId: 'route' };
168
- const select = (costQualityTradeoff) => invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route',
169
- body: routeBody({ kind: 'router', service: target, ...(costQualityTradeoff !== undefined ? { costQualityTradeoff } : {}) }) }));
170
- const chosen = await select(0);
171
- assert.equal(chosen.statusCode, 200, chosen.body);
172
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
173
- assert.equal(response.statusCode, 200, response.body);
174
- assert.equal(contexts.length, 1);
175
- assert.equal(inferenceCalls, 1);
176
- assert.equal(contexts[0].costQualityTradeoff, 0);
177
- assert.deepEqual(contexts[0].routingService, target);
178
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'router', service: target }) }))).statusCode, 200);
179
- store.touch({ tool: 'vpr', sessionKey: 'client-chat' });
180
- const selectChat = (costQualityTradeoff) => invokeProxy(proxy, makeProxyRequest({
181
- path: '/_antseed/conversations/update', body: { id: 'vpr:client-chat', routingSelection: { kind: 'router', service: target, costQualityTradeoff } },
182
- }));
183
- assert.equal((await selectChat(10)).statusCode, 200);
184
- const chatResponse = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': 'client-chat' },
185
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] },
186
- }));
187
- assert.equal(chatResponse.statusCode, 200, chatResponse.body);
188
- assert.equal(contexts[1].costQualityTradeoff, 10);
189
- assert.deepEqual(contexts[1].routingService, target);
190
- assert.equal((await select()).statusCode, 200);
191
- const defaultResponse = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
192
- assert.equal(defaultResponse.statusCode, 200, defaultResponse.body);
193
- assert.equal(contexts[2].costQualityTradeoff, undefined);
194
- assert.equal(contexts.length, 3);
195
- assert.equal(inferenceCalls, 3);
196
- });
197
- test('selected routing service hands auto requests to normal inference and bypasses concrete models and pins', async () => {
198
- const peer = makePeer('a', ['openai']);
199
- peer.reputationScore = 90;
200
- peer.providerPricing = { openai: { defaults: { inputUsdPerMillion: 1, outputUsdPerMillion: 2 }, services: { 'gpt-4o': { inputUsdPerMillion: 1, outputUsdPerMillion: 2 } } } };
201
- peer.providerServiceApiProtocols = { openai: { services: { 'gpt-4o': ['openai-chat-completions'] } } };
202
- let routingCalls = 0;
203
- const client = {
204
- listModels: listModelsWith(),
205
- async selectRoute(_request, _peers, context) {
206
- routingCalls += 1;
207
- const routes = [{ serviceId: 'gpt-4o', peerId: peer.peerId }];
208
- assert.equal(context.acceptRecommendations(routes), true);
209
- return routes;
210
- },
211
- };
212
- const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter(), undefined, undefined, client);
213
- setRouter(proxy, { kind: 'router', service: chatRouterSelection('a', 5).service });
214
- const dispatched = [];
215
- proxy._node.sendRequest = async (_peer, request) => {
216
- dispatched.push(request);
217
- return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from('{"choices":[]}') };
218
- };
219
- for (const model of ['antseed', 'gpt-4o', `${peer.peerId}@gpt-4o`]) {
220
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
221
- assert.equal(response.statusCode, 200, response.body);
222
- assert.equal(JSON.parse(Buffer.from(dispatched.at(-1).body).toString()).model, 'gpt-4o');
223
- }
224
- assert.equal(routingCalls, 1);
225
- assert.equal(dispatched.length, 3);
226
- });
227
- test('selected routing service failure never silently uses an inference seller', async () => {
228
- const peer = makeRankedRoutingFixture().peers[0];
229
- const client = {
230
- listModels: listModelsWith(),
231
- async selectRoute() { throw new Error('Routing service unavailable'); },
232
- };
233
- const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter(), undefined, undefined, client);
234
- setRouter(proxy, { kind: 'router', service: chatRouterSelection('a', 5).service });
235
- proxy._node.sendRequest = () => { throw new Error('inference must not execute'); };
236
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
237
- assert.equal(response.statusCode, 502);
238
- assert.match(response.body, /Routing service unavailable/);
239
- });
240
- function makeRankedRoutingFixture(statuses = [503, 200], recommendations) {
241
- const peers = ['a', 'b', 'c'].map(seed => {
242
- const peer = makePeer(seed, ['openai']);
243
- peer.reputationScore = 90;
244
- const services = Object.fromEntries(['model-a', 'model-b'].map(service => [service, { inputUsdPerMillion: 1, outputUsdPerMillion: 2 }]));
245
- peer.providerPricing = { openai: { defaults: { inputUsdPerMillion: 1, outputUsdPerMillion: 2 }, services } };
246
- peer.providerServiceApiProtocols = { openai: { services: { 'model-a': ['openai-chat-completions'], 'model-b': ['openai-chat-completions'] } } };
247
- return peer;
248
- });
249
- const calls = { routing: 0, requests: [] };
250
- const client = {
251
- listModels: listModelsWith(),
252
- async selectRoute(_request, _peers, context) {
253
- calls.routing += 1;
254
- const routes = recommendations ?? [
255
- { serviceId: 'model-a', peerId: peers[0].peerId },
256
- { serviceId: 'model-b', peerId: peers[1].peerId },
257
- ];
258
- assert.equal(context.acceptRecommendations(routes), true);
259
- return routes;
260
- },
261
- };
262
- const proxy = makeBuyerProxyWithPeers(peers, peers, permissiveRouter(), undefined, undefined, client);
263
- setRouter(proxy, { kind: 'router', service: chatRouterSelection('a', 5).service });
264
- proxy._node.sendRequest = async (peer, request) => {
265
- const statusCode = statuses[calls.requests.length] ?? 200;
266
- calls.requests.push({ peer, request });
267
- return { requestId: request.requestId, statusCode, headers: { 'content-type': 'application/json' },
268
- body: Buffer.from(statusCode >= 400 ? '{"error":{"message":"upstream unavailable"}}' : '{"choices":[]}') };
269
- };
270
- return { proxy, peers, calls, client };
271
- }
272
- function setDefaultModel(proxy, model) {
273
- ;
274
- proxy._defaultRoutedModel = model;
275
- proxy._defaultRouter = null;
276
- }
277
- function setRouter(proxy, selection) {
278
- ;
279
- proxy._defaultRoutedModel = null;
280
- proxy._defaultRouter = selection;
281
- }
282
- /** `POST /_antseed/route` body: `{ model }` for a model, `{ router: { service, costQualityTradeoff } }` for a router. */
283
- function routeBody(selection) {
284
- if (selection.kind === 'model')
285
- return { model: selection.model ?? '' };
286
- const { kind: _kind, ...router } = selection;
287
- return { router };
288
- }
289
- function chatRouterSelection(peer, costQualityTradeoff) {
290
- return { kind: 'router', service: { peerId: peer.repeat(40), provider: 'alpha', serviceId: 'alpha-route' }, costQualityTradeoff };
291
- }
292
- async function makeConversationRoutingFixture(context) {
293
- const fixture = makeRankedRoutingFixture([]);
294
- const directory = await mkdtemp(join(tmpdir(), 'antseed-chat-routing-'));
295
- const store = new ConversationStore(directory);
296
- context.after(async () => { await store.flush(); await rm(directory, { recursive: true, force: true }); });
297
- fixture.proxy._conversations = store;
298
- fixture.proxy._mergeStateFile = async () => { };
299
- const client = fixture.proxy._modelRoutingClient;
300
- setRouter(fixture.proxy, chatRouterSelection('c', 5));
301
- const captured = [];
302
- const select = client.selectRoute;
303
- client.selectRoute = (request, peers, routing) => { captured.push(routing); return select(request, peers, routing); };
304
- for (const sessionKey of ['chat-a', 'chat-b', 'chat-c'])
305
- store.touch({ tool: 'vpr', sessionKey });
306
- const update = (session, routingSelection) => invokeProxy(fixture.proxy, makeProxyRequest({
307
- path: '/_antseed/conversations/update', body: { id: `vpr:${session}`, routingSelection },
308
- }));
309
- const request = (session, parent) => invokeProxy(fixture.proxy, makeProxyRequest({
310
- headers: { 'x-vpr-session-id': session, ...(parent ? { 'x-parent-session-id': parent } : {}) },
311
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] },
312
- }));
313
- return { ...fixture, directory, store, client, captured, update, request };
314
- }
315
- test('chats keep independent routing-service targets and tradeoffs while unconfigured chats inherit defaults', async (context) => {
316
- const { proxy, store, captured, update, request } = await makeConversationRoutingFixture(context);
317
- const first = chatRouterSelection('d', 9);
318
- const second = chatRouterSelection('e', 1);
319
- assert.equal((await update('chat-a', first)).statusCode, 200);
320
- assert.equal((await update('chat-b', second)).statusCode, 200);
321
- for (const session of ['chat-a', 'chat-b', 'chat-c'])
322
- assert.equal((await request(session)).statusCode, 200);
323
- assert.deepEqual(captured.map(entry => [entry.routingService, entry.costQualityTradeoff]), [first, second, chatRouterSelection('c', 5)].map(selection => [selection.service, selection.costQualityTradeoff]));
324
- setRouter(proxy, chatRouterSelection('f', 3));
325
- for (const session of ['chat-a', 'chat-b', 'chat-c'])
326
- assert.equal((await request(session)).statusCode, 200);
327
- assert.deepEqual(captured.slice(3).map(entry => [entry.routingService, entry.costQualityTradeoff]), [first, second, chatRouterSelection('f', 3)].map(selection => [selection.service, selection.costQualityTradeoff]));
328
- assert.equal(store.get('vpr:chat-c')?.routingSelection, null);
329
- const get = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/conversations/vpr%3Achat-a', method: 'GET' }));
330
- assert.deepEqual(JSON.parse(get.body).conversation.routingSelection, first);
331
- assert.equal((await update('chat-a', null)).statusCode, 200);
332
- assert.equal((await request('chat-a')).statusCode, 200);
333
- assert.deepEqual(captured.at(-1).routingService, chatRouterSelection('f', 3).service);
334
- assert.equal(captured.at(-1).costQualityTradeoff, 3);
335
- });
336
- test('chat routing overrides beat the buyer model default, inherit into children, and yield to explicit model pins', async (context) => {
337
- const { proxy, peers, calls, captured, update, request, store } = await makeConversationRoutingFixture(context);
338
- setDefaultModel(proxy, 'model-b');
339
- store.setPinnedModel('vpr:chat-a', 'model-b', 'user');
340
- const selection = chatRouterSelection('d', 9);
341
- assert.equal((await update('chat-a', selection)).statusCode, 200);
342
- assert.equal((await request('child-a', 'chat-a')).statusCode, 200);
343
- assert.equal(captured[0].conversationKey, 'vpr:child-a');
344
- assert.deepEqual(captured[0].routingService, selection.service);
345
- const intercepted = await invokeProxy(proxy, makeProxyRequest({
346
- headers: { 'x-vpr-session-id': 'chat-a', [SYSTEM_ROUTED_MODEL_HEADER]: '1' },
347
- body: { model: 'old-connect-model', messages: [{ role: 'user', content: 'Hello' }] },
348
- }));
349
- assert.equal(intercepted.statusCode, 200, intercepted.body);
350
- assert.equal(calls.routing, 2);
351
- const pin = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/conversations/update',
352
- body: { id: 'vpr:chat-a', pinnedModel: `${peers[1].peerId}@model-b`, peerSource: 'user' } }));
353
- assert.equal(pin.statusCode, 200);
354
- assert.equal(store.get('vpr:chat-a')?.routingSelection, null);
355
- assert.equal((await request('chat-a')).statusCode, 200);
356
- assert.equal(calls.routing, 2);
357
- assert.equal(JSON.parse(Buffer.from(calls.requests.at(-1).request.body).toString()).model, 'model-b');
358
- });
359
- test('chat routing updates reject malformed selections without partially changing the chat', async (context) => {
360
- const { proxy, store, update } = await makeConversationRoutingFixture(context);
361
- const selection = chatRouterSelection('d', 9);
362
- assert.equal((await update('chat-a', selection)).statusCode, 200);
363
- for (const invalid of [[], { kind: 'model', model: 'model-a' }, { kind: 'router' },
364
- { ...selection, service: { ...selection.service, peerId: 'invalid' } },
365
- { ...selection, costQualityTradeoff: 11 }, { ...selection, costQualityTradeoff: 2.5 }, { ...selection, preferences: { tradeoff: '9' } }]) {
366
- assert.equal((await update('chat-a', invalid)).statusCode, 400);
367
- assert.deepEqual(store.get('vpr:chat-a')?.routingSelection, selection);
368
- }
369
- const conflict = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/conversations/update',
370
- body: { id: 'vpr:chat-a', routingSelection: null, pinnedModel: 'model-a', label: 'not applied' } }));
371
- assert.equal(conflict.statusCode, 400);
372
- assert.deepEqual(store.get('vpr:chat-a')?.routingSelection, selection);
373
- assert.equal(store.get('vpr:chat-a')?.label, null);
374
- assert.equal((await update('missing', selection)).statusCode, 404);
375
- const body = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/conversations/update', body: null }));
376
- assert.equal(body.statusCode, 400);
377
- });
378
- test('persisted chat overrides route after reload and fail closed when the router stops listing the models', async (context) => {
379
- const { proxy, directory, store, client, captured, update, request } = await makeConversationRoutingFixture(context);
380
- const selection = chatRouterSelection('d', 9);
381
- await update('chat-a', selection);
382
- await store.flush();
383
- proxy._conversations = new ConversationStore(directory);
384
- assert.equal((await request('chat-a')).statusCode, 200);
385
- assert.equal(captured[0].costQualityTradeoff, selection.costQualityTradeoff);
386
- client.listModels = listModelsWith(['model-z']);
387
- proxy._routingModels.invalidate(selection.service);
388
- assert.equal((await request('chat-a')).statusCode, 422);
389
- assert.equal(captured.length, 1);
390
- await proxy._conversations.flush();
391
- });
392
- test('chat and buyer selection changes leave in-flight requests using their original selection', { timeout: 5_000 }, async (context) => {
393
- const { proxy, peers, client, update, request } = await makeConversationRoutingFixture(context);
394
- await update('chat-a', chatRouterSelection('d', 9));
395
- await update('chat-b', chatRouterSelection('e', 1));
396
- const waiting = new Map();
397
- let started;
398
- const ready = new Promise(resolve => { started = resolve; });
399
- client.selectRoute = (_request, _peers, routing) => new Promise(resolve => {
400
- waiting.set(routing.conversationKey, { signal: routing.signal, finish: () => {
401
- const routes = [{ serviceId: 'model-a', peerId: peers[0].peerId }];
402
- assert.equal(routing.acceptRecommendations(routes), true);
403
- resolve(routes);
404
- } });
405
- if (waiting.size === 3)
406
- started();
407
- });
408
- const first = request('child-a', 'chat-a');
409
- const second = request('chat-b');
410
- const inherited = request('chat-c');
411
- await ready;
412
- assert.equal((await update('chat-a', chatRouterSelection('d', 7))).statusCode, 200);
413
- assert.equal(waiting.get('vpr:child-a').signal.aborted, false);
414
- assert.equal(waiting.get('vpr:chat-b').signal.aborted, false);
415
- assert.equal(waiting.get('vpr:chat-c').signal.aborted, false);
416
- setRouter(proxy, chatRouterSelection('f', 3));
417
- for (const pending of waiting.values()) {
418
- assert.equal(pending.signal.aborted, false);
419
- pending.finish();
420
- }
421
- for (const response of await Promise.all([first, second, inherited]))
422
- assert.equal(response.statusCode, 200);
423
- });
424
- /** A peer advertising one `model-routing` service, `alpha-route`. */
425
- function makeRoutingPeer(seed) {
426
- const peer = makePeer(seed, ['alpha']);
427
- peer.metadata = {
428
- peerId: peer.peerId, version: 12, timestamp: Date.now(), signature: '', region: 'test',
429
- providers: [{ provider: 'alpha', services: ['alpha-route'], maxConcurrency: 1, currentLoad: 0,
430
- defaultPricing: { inputUsdPerMillion: 0, outputUsdPerMillion: 0 },
431
- serviceApiProtocols: { 'alpha-route': ['model-routing'] },
432
- serviceUnitBillingModels: { 'alpha-route': { 'model-routing': {
433
- version: 1, components: [{ unit: 'completed_requests', priceUsd: 0.001 }],
434
- } } },
435
- }],
436
- };
437
- return peer;
438
- }
439
- function makePinnedRouterFixture() {
440
- const fixture = makeRankedRoutingFixture([200, 200, 200]);
441
- const routingPeer = makeRoutingPeer('d');
442
- const peers = [...fixture.peers, routingPeer];
443
- fixture.proxy._getPeers = async () => peers;
444
- setDefaultModel(fixture.proxy, null);
445
- const captured = [];
446
- const select = fixture.client.selectRoute;
447
- fixture.client.selectRoute = (request, routingPeers, context) => {
448
- captured.push(context);
449
- return select(request, routingPeers, context);
450
- };
451
- const target = { peerId: routingPeer.peerId, provider: 'alpha', serviceId: 'alpha-route' };
452
- return { ...fixture, routingPeer, captured, target, model: `${routingPeer.peerId}@alpha-route` };
453
- }
454
- test('a routing service pinned as the model ranks and serves the request without any buyer router selection', async () => {
455
- const { proxy, peers, calls, captured, target, model } = makePinnedRouterFixture();
456
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
457
- assert.equal(response.statusCode, 200, response.body);
458
- assert.equal(calls.routing, 1);
459
- assert.deepEqual(captured[0].routingService, target);
460
- assert.equal(captured[0].costQualityTradeoff, undefined);
461
- assert.equal(calls.requests.length, 1);
462
- assert.equal(calls.requests[0].peer.peerId, peers[0].peerId);
463
- assert.equal(JSON.parse(Buffer.from(calls.requests[0].request.body).toString()).model, 'model-a');
464
- });
465
- test('the body routing object sets the tradeoff for a pinned router and never reaches a seller', async () => {
466
- const { proxy, calls, captured, model } = makePinnedRouterFixture();
467
- const response = await invokeProxy(proxy, makeProxyRequest({ body: {
468
- model, messages: [{ role: 'user', content: 'Hello' }], routing: { cost_quality_tradeoff: 7 },
469
- } }));
470
- assert.equal(response.statusCode, 200, response.body);
471
- assert.equal(captured[0].costQualityTradeoff, 7);
472
- const sent = JSON.parse(Buffer.from(calls.requests[0].request.body).toString());
473
- assert.equal('routing' in sent, false);
474
- const direct = await invokeProxy(proxy, makeProxyRequest({ body: {
475
- model: `${calls.requests[0].peer.peerId}@model-b`, messages: [{ role: 'user', content: 'Hi' }], routing: { cost_quality_tradeoff: 2 },
476
- } }));
477
- assert.equal(direct.statusCode, 200, direct.body);
478
- assert.equal(calls.routing, 1);
479
- const pinned = JSON.parse(Buffer.from(calls.requests.at(-1).request.body).toString());
480
- assert.equal(pinned.model, 'model-b');
481
- assert.equal('routing' in pinned, false);
482
- });
483
- test('the body routing tradeoff overrides the buyer default router tradeoff', async () => {
484
- const { proxy, captured } = makePinnedRouterFixture();
485
- setRouter(proxy, { kind: 'router', service: { peerId: 'd'.repeat(40), provider: 'alpha', serviceId: 'alpha-route' }, costQualityTradeoff: 1 });
486
- const response = await invokeProxy(proxy, makeProxyRequest({ body: {
487
- model: 'antseed', messages: [{ role: 'user', content: 'Hello' }], routing: { cost_quality_tradeoff: 9 },
488
- } }));
489
- assert.equal(response.statusCode, 200, response.body);
490
- assert.equal(captured[0].costQualityTradeoff, 9);
491
- });
492
- test('a pinned router without a routing object uses the saved settings for that router only', async () => {
493
- const { proxy, peers, captured, target, model } = makePinnedRouterFixture();
494
- setRouter(proxy, { kind: 'router', service: target, costQualityTradeoff: 3, allowedModels: [{ provider: 'openai', serviceId: 'model-b' }] });
495
- const saved = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
496
- assert.equal(saved.statusCode, 200, saved.body);
497
- assert.equal(captured[0].costQualityTradeoff, 3);
498
- assert.deepEqual([...new Set(captured[0].candidates.map((candidate) => candidate.serviceId))], ['model-b']);
499
- const overridden = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }], routing: { cost_quality_tradeoff: 9 } } }));
500
- assert.equal(overridden.statusCode, 200, overridden.body);
501
- assert.equal(captured[1].costQualityTradeoff, 9);
502
- assert.deepEqual([...new Set(captured[1].candidates.map((candidate) => candidate.serviceId))], ['model-b']);
503
- setRouter(proxy, { kind: 'router', service: { ...target, peerId: 'e'.repeat(40) }, costQualityTradeoff: 3, allowedModels: [{ provider: 'openai', serviceId: 'model-b' }] });
504
- const other = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
505
- assert.equal(other.statusCode, 200, other.body);
506
- assert.equal(captured[2].costQualityTradeoff, undefined);
507
- assert.ok(captured[2].candidates.some((candidate) => candidate.serviceId === 'model-a'));
508
- assert.ok(peers.length > 0);
509
- });
510
- test('a router pinned as the default route serves the antseed alias through that router', async () => {
511
- const { proxy, peers, calls, captured, target, model } = makePinnedRouterFixture();
512
- setDefaultModel(proxy, model);
513
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }], routing: { cost_quality_tradeoff: 6 } } }));
514
- assert.equal(response.statusCode, 200, response.body);
515
- assert.equal(calls.routing, 1);
516
- assert.deepEqual(captured[0].routingService, target);
517
- assert.equal(captured[0].costQualityTradeoff, 6);
518
- assert.equal(calls.requests[0].peer.peerId, peers[0].peerId);
519
- assert.equal(JSON.parse(Buffer.from(calls.requests[0].request.body).toString()).model, 'model-a');
520
- });
521
- test('a chat pinned to a router is ranked on every turn, through the alias and the system proxy', async (context) => {
522
- const fixture = makePinnedRouterFixture();
523
- const { proxy, calls, captured, target, model } = fixture;
524
- const directory = await mkdtemp(join(tmpdir(), 'antseed-router-pin-'));
525
- const store = new ConversationStore(directory);
526
- context.after(async () => { await store.flush(); await rm(directory, { recursive: true, force: true }); });
527
- proxy._conversations = store;
528
- proxy._mergeStateFile = async () => { };
529
- setDefaultModel(proxy, 'model-b');
530
- store.touch({ tool: 'vpr', sessionKey: 'chat-r' });
531
- const pin = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/conversations/update',
532
- body: { id: 'vpr:chat-r', pinnedModel: model, peerSource: 'user' } }));
533
- assert.equal(pin.statusCode, 200, pin.body);
534
- const turn = (headers, requestModel) => invokeProxy(proxy, makeProxyRequest({
535
- headers: { 'x-vpr-session-id': 'chat-r', ...headers },
536
- body: { model: requestModel, messages: [{ role: 'user', content: 'Hello' }] },
537
- }));
538
- assert.equal((await turn({}, 'antseed')).statusCode, 200);
539
- assert.equal((await turn({}, 'antseed')).statusCode, 200);
540
- assert.equal((await turn({ [SYSTEM_ROUTED_MODEL_HEADER]: '1' }, 'old-connect-model')).statusCode, 200);
541
- assert.equal(calls.routing, 3);
542
- for (const context of captured)
543
- assert.deepEqual(context.routingService, target);
544
- for (const sent of calls.requests)
545
- assert.equal(JSON.parse(Buffer.from(sent.request.body).toString()).model, 'model-a');
546
- assert.equal(store.get('vpr:chat-r')?.pinnedModel, model);
547
- });
548
- test('a chat\'s own router wins over a router pinned as the default route', async (context) => {
549
- const { proxy, peers, routingPeer, calls, captured, model } = makePinnedRouterFixture();
550
- const chatRouter = makeRoutingPeer('e');
551
- const allPeers = [...peers, routingPeer, chatRouter];
552
- proxy._getPeers = async () => allPeers;
553
- const directory = await mkdtemp(join(tmpdir(), 'antseed-router-chat-'));
554
- const store = new ConversationStore(directory);
555
- context.after(async () => { await store.flush(); await rm(directory, { recursive: true, force: true }); });
556
- proxy._conversations = store;
557
- proxy._mergeStateFile = async () => { };
558
- setDefaultModel(proxy, model);
559
- store.touch({ tool: 'vpr', sessionKey: 'chat-b' });
560
- const chatSelection = chatRouterSelection('e', 9);
561
- const update = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/conversations/update',
562
- body: { id: 'vpr:chat-b', routingSelection: chatSelection } }));
563
- assert.equal(update.statusCode, 200, update.body);
564
- const turn = (session) => invokeProxy(proxy, makeProxyRequest({
565
- headers: session ? { 'x-vpr-session-id': session } : {},
566
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] },
567
- }));
568
- const chatTurn = await turn('chat-b');
569
- assert.equal(chatTurn.statusCode, 200, chatTurn.body);
570
- const plain = await turn();
571
- assert.equal(plain.statusCode, 200, plain.body);
572
- assert.deepEqual(captured.map((entry) => [entry.routingService.peerId, entry.costQualityTradeoff]), [[chatRouter.peerId, 9], [routingPeer.peerId, undefined]]);
573
- assert.equal(calls.routing, 2);
574
- });
575
- test('an automatic chat route never turns a seller into a router pin', async (context) => {
576
- const { proxy, calls } = makePinnedRouterFixture();
577
- const directory = await mkdtemp(join(tmpdir(), 'antseed-router-auto-'));
578
- const store = new ConversationStore(directory);
579
- context.after(async () => { await store.flush(); await rm(directory, { recursive: true, force: true }); });
580
- proxy._conversations = store;
581
- setDefaultModel(proxy, 'model-b');
582
- const response = await invokeProxy(proxy, makeProxyRequest({
583
- headers: { 'x-vpr-session-id': 'chat-plain' },
584
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] },
585
- }));
586
- assert.equal(response.statusCode, 200, response.body);
587
- assert.equal(calls.routing, 0);
588
- });
589
- test('routers are listed under /v1/models?type=router only, with the model that pins them', async () => {
590
- const { proxy, routingPeer } = makePinnedRouterFixture();
591
- const all = JSON.parse((await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/v1/models' }))).body);
592
- assert.equal(all.data.some((entry) => entry.type === 'router'), false);
593
- const routers = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/v1/models?type=router' }));
594
- assert.equal(routers.statusCode, 200, routers.body);
595
- const data = JSON.parse(routers.body).data;
596
- assert.equal(data.length, 1);
597
- assert.equal(data[0].id, 'alpha-route');
598
- assert.equal(data[0].type, 'router');
599
- assert.deepEqual(data[0].peers.map((peer) => [peer.model, peer.priceUsdPerRequest, peer.models]), [[`${routingPeer.peerId}@alpha-route`, 0.001, ['model-a', 'model-b']]]);
600
- });
601
- test('a pinned router that is offline is reported unreachable, and one with nothing to rank answers 422, before any purchase', async () => {
602
- const { proxy, calls, target } = makePinnedRouterFixture();
603
- const offline = await invokeProxy(proxy, makeProxyRequest({ body: { model: `${'e'.repeat(40)}@alpha-route`, messages: [{ role: 'user', content: 'Hello' }] } }));
604
- assert.equal(offline.statusCode, 502, offline.body);
605
- assert.match(offline.body, /\(seller or routing service\) is not reachable right now/);
606
- setRouter(proxy, { kind: 'router', service: target, allowedModels: [{ provider: 'openai', serviceId: 'model-z' }] });
607
- const empty = await invokeProxy(proxy, makeProxyRequest({ body: { model: `${target.peerId}@alpha-route`, messages: [{ role: 'user', content: 'Hello' }] } }));
608
- assert.equal(empty.statusCode, 422, empty.body);
609
- assert.equal(JSON.parse(empty.body).error.code, 'no_eligible_candidates');
610
- assert.equal(calls.routing, 0);
611
- assert.equal(calls.requests.length, 0);
612
- });
613
- test('a pinned router missing from the peer cache is found after one refresh, not taken for a seller', async () => {
614
- const { proxy, peers, calls, routingPeer, model } = makePinnedRouterFixture();
615
- const refreshes = [];
616
- let cached = peers;
617
- proxy._getPeers = async (options) => {
618
- refreshes.push(options?.forceRefresh === true);
619
- if (options?.forceRefresh)
620
- cached = [...peers, routingPeer];
621
- return cached;
622
- };
623
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
624
- assert.equal(response.statusCode, 200, response.body);
625
- assert.equal(calls.routing, 1);
626
- assert.equal(refreshes.includes(true), true);
627
- });
628
- test('a router below the buyer minimum reputation is refused before any ranking is bought, and not listed', async () => {
629
- const { proxy, calls, routingPeer, model } = makePinnedRouterFixture();
630
- routingPeer.reputationScore = 10;
631
- routingPeer.onChainReputationScore = 10;
632
- proxy._minPeerReputation = 50;
633
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
634
- assert.equal(response.statusCode, 403, response.body);
635
- const error = JSON.parse(response.body).error;
636
- assert.equal(error.code, 'peer_not_allowed');
637
- assert.match(error.message, /buyer config: reputation 10 below 50/);
638
- assert.equal(calls.routing, 0);
639
- assert.equal(calls.requests.length, 0);
640
- const routers = JSON.parse((await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/v1/models?type=router' }))).body);
641
- assert.deepEqual(routers.data, []);
642
- });
643
- test('a router is not held to required verification, which attests inference, not rankings', async () => {
644
- const { proxy, calls, model } = makePinnedRouterFixture();
645
- proxy._verifier = { require: true, prefer: ['antseed-verifier'] };
646
- proxy._verifyPeer = async () => ({ ok: true, verified: true });
647
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }] } }));
648
- assert.equal(response.statusCode, 200, response.body);
649
- assert.equal(calls.routing, 1);
650
- });
651
- test('an invalid body routing object is rejected before routing', async () => {
652
- const { proxy, calls, model } = makePinnedRouterFixture();
653
- for (const routing of [{ cost_quality_tradeoff: 11 }, { cost_quality_tradeoff: 2.5 }, { cost_quality_tradeoff: '5' }, { candidates: [] }, [], 'cheap']) {
654
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model, messages: [{ role: 'user', content: 'Hello' }], routing } }));
655
- assert.equal(response.statusCode, 400, response.body);
656
- assert.equal(JSON.parse(response.body).error.code, 'invalid_routing');
657
- }
658
- assert.equal(calls.routing, 0);
659
- assert.equal(calls.requests.length, 0);
660
- });
661
- test('an invalid body routing object is dropped, not rejected, when no router serves the request', async () => {
662
- const { proxy, peers, calls } = makePinnedRouterFixture();
663
- const response = await invokeProxy(proxy, makeProxyRequest({ body: {
664
- model: `${peers[0].peerId}@model-a`, messages: [{ role: 'user', content: 'Hello' }], routing: { cost_quality_tradeoff: 11 },
665
- } }));
666
- assert.equal(response.statusCode, 200, response.body);
667
- assert.equal(calls.routing, 0);
668
- assert.equal(calls.requests.length, 1);
669
- assert.equal('routing' in JSON.parse(Buffer.from(calls.requests[0].request.body).toString()), false);
670
- });
671
- test('a pinned service that is not the peer\'s routing service is a normal seller pin', async () => {
672
- const { proxy, peers, calls } = makePinnedRouterFixture();
673
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: `${peers[1].peerId}@model-a`, messages: [{ role: 'user', content: 'Hello' }] } }));
674
- assert.equal(response.statusCode, 200, response.body);
675
- assert.equal(calls.routing, 0);
676
- assert.equal(calls.requests[0].peer.peerId, peers[1].peerId);
677
- });
678
- test('routing selection carries the tradeoff, rejects invalid values and preserves model mode', async () => {
679
- const { proxy, calls } = makeRankedRoutingFixture([200, 200, 200, 200]);
680
- const client = proxy._modelRoutingClient;
681
- const captured = [];
682
- const select = client.selectRoute;
683
- client.selectRoute = (request, peers, context) => {
684
- captured.push({ tradeoff: context.costQualityTradeoff, target: context.routingService });
685
- return select(request, peers, context);
686
- };
687
- proxy._mergeStateFile = async () => { };
688
- setDefaultModel(proxy, 'stale-model');
689
- const service = { peerId: 'd'.repeat(40), provider: 'alpha', serviceId: 'alpha-route' };
690
- const selected = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'router', service, costQualityTradeoff: 9 }) }));
691
- assert.equal(selected.statusCode, 200, selected.body);
692
- const first = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
693
- assert.equal(first.statusCode, 200, first.body);
694
- assert.deepEqual(captured[0], { tradeoff: 9, target: service });
695
- const updated = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'router', service, costQualityTradeoff: 1 }) }));
696
- assert.equal(updated.statusCode, 200, updated.body);
697
- await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
698
- assert.equal(captured[1].tradeoff, 1);
699
- for (const selection of [{ kind: 'router', service, costQualityTradeoff: -1 }, { kind: 'router', service, costQualityTradeoff: '5' }, { kind: 'router', service, preferences: { tradeoff: '9' } }]) {
700
- const invalid = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody(selection) }));
701
- assert.equal(invalid.statusCode, 400, invalid.body);
702
- }
703
- const modelMode = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'model', model: 'model-b' }) }));
704
- assert.equal(modelMode.statusCode, 200, modelMode.body);
705
- const fixed = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
706
- assert.equal(fixed.statusCode, 200, fixed.body);
707
- assert.equal(calls.routing, 2);
708
- assert.equal(JSON.parse(Buffer.from(calls.requests.at(-1).request.body).toString()).model, 'model-b');
709
- });
710
- test('intercepted connected-app defaults use the selected routing service rather than the connect-time model', async () => {
711
- const { proxy, calls } = makeRankedRoutingFixture([200]);
712
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { [SYSTEM_ROUTED_MODEL_HEADER]: '1' },
713
- body: { model: 'stale-connect-time-model', messages: [{ role: 'user', content: 'Hello' }] } }));
714
- assert.equal(response.statusCode, 200, response.body);
715
- assert.equal(calls.routing, 1);
716
- assert.equal(JSON.parse(Buffer.from(calls.requests[0].request.body).toString()).model, 'model-a');
717
- });
718
- test('a session peer pin under a default router keeps a system-routed connect-time model instead of answering 400', async () => {
719
- const { proxy, peers, calls } = makeRankedRoutingFixture([200]);
720
- proxy._pinnedPeer = peers[1].peerId;
721
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { [SYSTEM_ROUTED_MODEL_HEADER]: '1' },
722
- body: { model: 'model-b', messages: [{ role: 'user', content: 'Hello' }] } }));
723
- assert.equal(response.statusCode, 200, response.body);
724
- assert.equal(calls.routing, 0);
725
- assert.equal(calls.requests.length, 1);
726
- assert.equal(calls.requests[0].peer.peerId, peers[1].peerId);
727
- assert.equal(JSON.parse(Buffer.from(calls.requests[0].request.body).toString()).model, 'model-b');
728
- });
729
- test('switching a routed conversation back to model mode keeps the chat model, like main', async () => {
730
- const { proxy, calls } = makeRankedRoutingFixture([200, 200, 200, 200]);
731
- proxy._mergeStateFile = async () => { };
732
- const session = randomUUID();
733
- const headers = { 'x-vpr-session-id': session };
734
- const body = { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] };
735
- const sentModel = () => JSON.parse(Buffer.from(calls.requests.at(-1).request.body).toString()).model;
736
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ headers, body }))).statusCode, 200);
737
- assert.equal(sentModel(), 'model-a');
738
- await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'model', model: 'model-b' }) }));
739
- // An existing chat keeps the model it last used; only chats without one use the default.
740
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ headers, body }))).statusCode, 200);
741
- assert.equal(sentModel(), 'model-a');
742
- assert.equal(calls.routing, 1);
743
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': randomUUID() }, body }))).statusCode, 200);
744
- assert.equal(sentModel(), 'model-b');
745
- // System-routed apps are only overridden by the chat's own model.
746
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ headers: { ...headers, [SYSTEM_ROUTED_MODEL_HEADER]: '1' }, body: { ...body, model: 'connect-time-model' } }))).statusCode, 200);
747
- assert.equal(sentModel(), 'model-a');
748
- });
749
- test('gateway routing policy limits which sellers and models the router may recommend', async () => {
750
- const { proxy, peers, client } = makeRankedRoutingFixture([200], []);
751
- const offered = [];
752
- client.selectRoute = async (_request, _peers, context) => {
753
- offered.push(context.candidates.map(candidate => `${candidate.peerId}:${candidate.serviceId}`));
754
- return [];
755
- };
756
- const request = { requestId: randomUUID(), method: 'POST', path: '/v1/chat/completions',
757
- headers: { 'content-type': 'application/json' },
758
- body: Buffer.from(JSON.stringify({ model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] })) };
759
- const dispatch = (requestPolicy) => proxy._dispatchViaModelRouter(makeProxyResponse(), {
760
- request, requiredParameters: [], requestPolicy, signal: new AbortController().signal,
761
- conversationIdentity: null, storedConversation: null, routingConversationKey: null,
762
- }, chatRouterSelection('a', 5));
763
- await dispatch(null);
764
- await dispatch({ allowedPeerIds: [peers[1].peerId], allowedModels: ['model-b'] });
765
- assert.equal(offered[0].length, 6);
766
- assert.deepEqual(offered[1], [`${peers[1].peerId}:model-b`]);
767
- });
768
- test('explicit routing-service target and tradeoff survive the local state-file round trip', async (context) => {
769
- const directory = await mkdtemp(join(tmpdir(), 'antseed-client-selection-'));
770
- context.after(() => rm(directory, { recursive: true, force: true }));
771
- const { proxy } = makeRankedRoutingFixture([200]);
772
- const client = proxy._modelRoutingClient;
773
- const stateFile = join(directory, 'buyer.state.json');
774
- proxy._stateFile = stateFile;
775
- const selection = { kind: 'router', service: { peerId: 'c'.repeat(40), provider: 'alpha', serviceId: 'alpha-route' }, costQualityTradeoff: 9 };
776
- const response = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody(selection) }));
777
- assert.equal(response.statusCode, 200, response.body);
778
- assert.deepEqual(JSON.parse(await readFile(stateFile, 'utf8')).defaultRouter, routeBody(selection).router);
779
- const restarted = new BuyerProxy({ port: 0, dataDir: directory, node: { router: permissiveRouter() }, modelRoutingClient: client });
780
- await restarted._reloadSessionOverrides();
781
- const restored = await invokeProxy(restarted, makeProxyRequest({ path: '/_antseed/route', method: 'GET' }));
782
- assert.deepEqual(JSON.parse(restored.body).router, routeBody(selection).router);
783
- });
784
- test('saved default routes restore a router and ignore an invalid or conflicting one', async (context) => {
785
- const directory = await mkdtemp(join(tmpdir(), 'antseed-router-state-'));
786
- context.after(() => rm(directory, { recursive: true, force: true }));
787
- const { proxy } = makeRankedRoutingFixture([200]);
788
- const stateFile = join(directory, 'buyer.state.json');
789
- proxy._stateFile = stateFile;
790
- const selection = chatRouterSelection('a', 5);
791
- const { router } = routeBody(selection);
792
- await writeFile(stateFile, JSON.stringify({ defaultRoutedModel: null, defaultRouter: router }));
793
- await proxy._reloadSessionOverrides();
794
- assert.deepEqual(proxy._defaultRouter, selection);
795
- await writeFile(stateFile, JSON.stringify({ defaultRoutedModel: 'saved-model', defaultRouter: router }));
796
- await proxy._reloadSessionOverrides();
797
- assert.deepEqual([proxy._defaultRoutedModel, proxy._defaultRouter], ['saved-model', null]);
798
- for (const invalid of ['model-a', [], { service: null }]) {
799
- await writeFile(stateFile, JSON.stringify({ defaultRouter: invalid }));
800
- await proxy._reloadSessionOverrides();
801
- assert.deepEqual([proxy._defaultRoutedModel, proxy._defaultRouter], [null, null]);
802
- }
803
- });
804
- test('config reload updates eligibility policy without replacing the active routing selection', async (context) => {
805
- const directory = await mkdtemp(join(tmpdir(), 'antseed-client-config-'));
806
- context.after(() => rm(directory, { recursive: true, force: true }));
807
- const { proxy } = makeRankedRoutingFixture([200]);
808
- const path = join(directory, 'config.json');
809
- proxy._configPath = path;
810
- proxy._mergeStateFile = async () => { assert.fail('Config reload must not persist selection'); };
811
- const service = { peerId: 'c'.repeat(40), provider: 'alpha', serviceId: 'alpha-route' };
812
- const activeSelection = { kind: 'router', service, costQualityTradeoff: 5 };
813
- setRouter(proxy, activeSelection);
814
- for (const maxInputUsdPerMillion of [15, 25]) {
815
- const routingPreferences = { ...priceAndTrustPreferences, maxInputUsdPerMillion };
816
- await writeFile(path, JSON.stringify({ buyer: { routingPreferences } }));
817
- await proxy._reloadRoutingPreferences();
818
- assert.deepEqual(proxy._defaultRouter, activeSelection);
819
- assert.deepEqual(proxy._routingPreferences, routingPreferences);
820
- }
821
- });
822
- test('routing observations use successful native usage and isolate child sessions from their parent', async () => {
823
- const { proxy, peers } = makeRankedRoutingFixture([200]);
824
- const client = proxy._modelRoutingClient;
825
- const usage = [];
826
- const conversationKeys = [];
827
- const select = client.selectRoute;
828
- client.selectRoute = (request, available, context) => {
829
- conversationKeys.push(context.conversationKey);
830
- return select(request, available, context);
831
- };
832
- client.recordUsage = observation => { usage.push(observation); };
833
- proxy._node.sendRequest = async (_peer, request) => ({
834
- requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' },
835
- body: Buffer.from('{"usage":{"prompt_tokens":100,"completion_tokens":10,"prompt_tokens_details":{"cached_tokens":80}}}'),
836
- });
837
- const parent = randomUUID();
838
- const child = randomUUID();
839
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-session-id': child, 'x-parent-session-id': parent },
840
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
841
- assert.equal(response.statusCode, 200, response.body);
842
- assert.equal(usage.length, 1);
843
- const observation = usage[0];
844
- assert.equal(observation.conversationKey, conversationKeys[0]);
845
- assert.ok(observation.conversationKey.endsWith(`:${child}`));
846
- assert.equal(observation.inputTokens, 100);
847
- assert.equal(observation.cachedInputTokens, 80);
848
- assert.equal(observation.peerId, peers[0].peerId);
849
- assert.equal(observation.serviceId, 'model-a');
850
- });
851
- test('direct-model inference reports cache observations only while a router is configured', async () => {
852
- const { proxy, client, calls } = makeRankedRoutingFixture([200]);
853
- const observations = [];
854
- client.recordUsage = observation => { observations.push(observation); };
855
- proxy._node.sendRequest = async (_peer, request) => ({
856
- requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' },
857
- body: Buffer.from('{"usage":{"prompt_tokens":100,"completion_tokens":10,"prompt_tokens_details":{"cached_tokens":80}}}'),
858
- });
859
- const send = (content) => invokeProxy(proxy, makeProxyRequest({ headers: { 'x-session-id': randomUUID() },
860
- body: { model: 'model-a', messages: [{ role: 'user', content }] },
861
- }));
862
- // A router is the default, but this request names a model: no recommendation is bought.
863
- const response = await send('Hello');
864
- assert.equal(response.statusCode, 200, response.body);
865
- assert.equal(calls.routing, 0);
866
- assert.equal(observations.length, 1);
867
- assert.equal(observations[0].cachedInputTokens, 80);
868
- client.recordUsage = () => { throw new Error('Observation failure must not fail inference'); };
869
- assert.equal((await send('Hello again')).statusCode, 200);
870
- // Without any router, responses are not parsed for router cache observations.
871
- client.recordUsage = observation => { observations.push(observation); };
872
- setDefaultModel(proxy, 'model-a');
873
- assert.equal((await send('No router')).statusCode, 200);
874
- assert.equal(observations.length, 1);
875
- });
876
- test('changing model selection affects the next request without cancelling the current decision', async () => {
877
- const { proxy, calls, peers } = makeRankedRoutingFixture([200, 200]);
878
- const client = proxy._modelRoutingClient;
879
- let started;
880
- const ready = new Promise(resolve => { started = resolve; });
881
- let receivedSignal;
882
- let finish;
883
- client.selectRoute = async (_request, _peers, context) => {
884
- receivedSignal = context.signal;
885
- started();
886
- return new Promise(resolve => {
887
- finish = () => {
888
- const routes = [{ serviceId: 'model-a', peerId: peers[0].peerId }];
889
- assert.equal(context.acceptRecommendations(routes), true);
890
- resolve(routes);
891
- };
892
- });
893
- };
894
- proxy._mergeStateFile = async () => { };
895
- const pending = invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
896
- await ready;
897
- const change = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: routeBody({ kind: 'model', model: 'model-b' }) }));
898
- assert.equal(change.statusCode, 200);
899
- assert.equal(receivedSignal.aborted, false);
900
- finish();
901
- const response = await pending;
902
- assert.equal(response.statusCode, 200);
903
- assert.equal(JSON.parse(Buffer.from(calls.requests[0].request.body).toString()).model, 'model-a');
904
- const next = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [{ role: 'user', content: 'Next' }] } }));
905
- assert.equal(next.statusCode, 200);
906
- assert.equal(JSON.parse(Buffer.from(calls.requests[1].request.body).toString()).model, 'model-b');
907
- });
908
- test('client disconnect still cancels a pending model-routing decision', { timeout: 5_000 }, async () => {
909
- const { proxy, calls } = makeRankedRoutingFixture([200]);
910
- const client = proxy._modelRoutingClient;
911
- let started;
912
- const ready = new Promise(resolve => { started = resolve; });
913
- let receivedSignal;
914
- client.selectRoute = async (_request, _peers, context) => {
915
- receivedSignal = context.signal;
916
- started();
917
- return new Promise(() => { });
918
- };
919
- const response = makeProxyResponse();
920
- let disconnect;
921
- response.once = (...args) => {
922
- if (args[0] === 'close')
923
- disconnect = args[1];
924
- return response;
925
- };
926
- const pending = proxy._handleRequest(makeProxyRequest({ body: {
927
- model: 'antseed', messages: [{ role: 'user', content: 'Hello' }],
928
- } }), response);
929
- await ready;
930
- disconnect();
931
- await pending;
932
- assert.equal(receivedSignal.aborted, true);
933
- assert.equal(calls.requests.length, 0);
934
- });
935
- for (const model of ['model-a', 'antseed']) {
936
- test(`client disconnect cancels inference for ${model === 'antseed' ? 'client' : 'direct model'} dispatch`, { timeout: 5_000 }, async () => {
937
- const { proxy, calls } = makeRankedRoutingFixture([200]);
938
- let started;
939
- const ready = new Promise(resolve => { started = resolve; });
940
- let receivedSignal;
941
- let inferenceCalls = 0;
942
- proxy._node.sendRequest = async (_peer, _request, options) => {
943
- inferenceCalls += 1;
944
- receivedSignal = options.signal;
945
- return new Promise((_resolve, reject) => {
946
- options.signal.addEventListener('abort', () => reject(new Error('Client disconnected')), { once: true });
947
- started();
948
- });
949
- };
950
- const response = makeProxyResponse();
951
- let disconnect;
952
- response.once = (...args) => {
953
- if (args[0] === 'close')
954
- disconnect = args[1];
955
- return response;
956
- };
957
- const pending = proxy._handleRequest(makeProxyRequest({ body: {
958
- model, messages: [{ role: 'user', content: 'Hello' }],
959
- } }), response);
960
- await ready;
961
- disconnect();
962
- await pending;
963
- assert.equal(receivedSignal.aborted, true);
964
- assert.equal(inferenceCalls, 1);
965
- assert.equal(calls.routing, model === 'antseed' ? 1 : 0);
966
- });
967
- }
968
- for (const status of [401, 403, 429, 500, 502, 503]) {
969
- test(`ranked routing falls back after ${status} with one routing call and separate inference IDs`, async () => {
970
- const { proxy, peers, calls } = makeRankedRoutingFixture([status, 200]);
971
- const body = { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }], temperature: 0.2 };
972
- const response = await invokeProxy(proxy, makeProxyRequest({ body }));
973
- assert.equal(response.statusCode, 200, response.body);
974
- assert.equal(calls.routing, 1);
975
- assert.deepEqual(calls.requests.map(entry => entry.peer.peerId), [peers[0].peerId, peers[1].peerId]);
976
- assert.equal(new Set(calls.requests.map(entry => entry.request.requestId)).size, 2);
977
- assert.deepEqual(calls.requests.map(entry => JSON.parse(Buffer.from(entry.request.body).toString())), [
978
- { ...body, model: 'model-a' }, { ...body, model: 'model-b' },
979
- ]);
980
- });
981
- }
982
- for (const status of [400, 402, 404, 408, 422, 504]) {
983
- test(`ranked routing stops on ${status} without another purchase or inference attempt`, async () => {
984
- const { proxy, calls } = makeRankedRoutingFixture([status, 200]);
985
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
986
- assert.equal(response.statusCode, status, response.body);
987
- assert.equal(calls.routing, 1);
988
- assert.equal(calls.requests.length, 1);
989
- });
990
- }
991
- test('ranked routing exhausts only listed destinations and never purchases another decision', async () => {
992
- const { proxy, peers, calls } = makeRankedRoutingFixture([503, 503]);
993
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
994
- assert.equal(response.statusCode, 503);
995
- assert.equal(calls.routing, 1);
996
- assert.deepEqual(calls.requests.map(entry => entry.peer.peerId), [peers[0].peerId, peers[1].peerId]);
997
- });
998
- test('ranked routing skips a recommendation that fails required verification', async () => {
999
- const { proxy, peers, calls } = makeRankedRoutingFixture([200]);
1000
- proxy._verifier = { require: true, prefer: ['antseed-verifier'] };
1001
- const verifiedPeers = [];
1002
- proxy._verifyPeer = async (peer) => {
1003
- verifiedPeers.push(peer.peerId);
1004
- return { ok: peer.peerId === peers[1].peerId, verified: peer.peerId === peers[1].peerId };
1005
- };
1006
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1007
- assert.equal(response.statusCode, 200);
1008
- assert.deepEqual(verifiedPeers, [peers[0].peerId, peers[1].peerId]);
1009
- assert.deepEqual(calls.requests.map(entry => entry.peer.peerId), [peers[1].peerId]);
1010
- });
1011
- test('ranked routing can try another recommended model on the same peer with a new billing ID', async () => {
1012
- const { proxy, calls } = makeRankedRoutingFixture([503, 200], [
1013
- { serviceId: 'model-a', peerId: 'a'.repeat(40) },
1014
- { serviceId: 'model-b', peerId: 'a'.repeat(40) },
1015
- { serviceId: 'model-b', peerId: 'a'.repeat(40) },
1016
- ]);
1017
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1018
- assert.equal(response.statusCode, 200);
1019
- assert.equal(calls.requests.length, 2);
1020
- assert.equal(calls.requests[0].peer.peerId, calls.requests[1].peer.peerId);
1021
- assert.notEqual(calls.requests[0].request.requestId, calls.requests[1].request.requestId);
1022
- });
1023
- test('ranked routing expands a model-only entry without widening an exact entry', async () => {
1024
- const { proxy, calls } = makeRankedRoutingFixture([503, 503, 200], [
1025
- { serviceId: 'model-a', peerId: 'b'.repeat(40) },
1026
- { serviceId: 'model-b' },
1027
- ]);
1028
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1029
- assert.equal(response.statusCode, 200);
1030
- assert.deepEqual(calls.requests.map(entry => [entry.peer.peerId, JSON.parse(Buffer.from(entry.request.body).toString()).model]), [
1031
- ['b'.repeat(40), 'model-a'], ['a'.repeat(40), 'model-b'], ['b'.repeat(40), 'model-b'],
1032
- ]);
1033
- });
1034
- test('model-only routing exhausts its allowed sellers before trying the next recommendation', async () => {
1035
- const { proxy, calls } = makeRankedRoutingFixture([503, 503, 503, 200], [
1036
- { serviceId: 'model-a' },
1037
- { serviceId: 'model-b', peerId: 'b'.repeat(40) },
1038
- ]);
1039
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1040
- assert.equal(response.statusCode, 200);
1041
- assert.equal(calls.routing, 1);
1042
- assert.deepEqual(calls.requests.map(entry => [entry.peer.peerId, JSON.parse(Buffer.from(entry.request.body).toString()).model]), [
1043
- ['a'.repeat(40), 'model-a'], ['b'.repeat(40), 'model-a'], ['c'.repeat(40), 'model-a'], ['b'.repeat(40), 'model-b'],
1044
- ]);
1045
- // Sellers of one model share the recommendation's billing ID, as in normal model routing; each recommendation gets its own.
1046
- assert.equal(new Set(calls.requests.map(entry => entry.request.requestId)).size, 2);
1047
- });
1048
- test('model-only routing uses the normal seller selection, restricted to allowed providers', async () => {
1049
- const { proxy, peers, calls } = makeRankedRoutingFixture([200], [{ serviceId: 'model-a' }]);
1050
- peers[0].providers = ['blocked'];
1051
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1052
- assert.equal(response.statusCode, 200, response.body);
1053
- assert.equal(calls.requests[0].request.headers['x-antseed-provider'], 'openai');
1054
- });
1055
- for (const failure of ['buyer', 'payment']) {
1056
- test(`model-only routing stops on ${failure} failures without trying another seller or recommendation`, async () => {
1057
- const { proxy, calls } = makeRankedRoutingFixture([failure === 'payment' ? 402 : 503, 200], [
1058
- { serviceId: 'model-a' },
1059
- { serviceId: 'model-b', peerId: 'b'.repeat(40) },
1060
- ]);
1061
- const sendRequest = proxy._node.sendRequest;
1062
- proxy._node.sendRequest = async (peer, request) => {
1063
- const response = await sendRequest(peer, request);
1064
- if (failure === 'buyer')
1065
- response.headers[ANTSEED_FAULT_ATTRIBUTION_HEADER] = 'buyer';
1066
- return response;
1067
- };
1068
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1069
- assert.equal(response.statusCode, failure === 'payment' ? 402 : 503);
1070
- assert.equal(calls.requests.length, 1);
1071
- assert.equal(calls.routing, 1);
1072
- });
1073
- }
1074
- for (const failure of ['transport', 'timeout']) {
1075
- test(`model-only routing retries other sellers like normal routing but never moves to the next recommendation after a ${failure}`, async () => {
1076
- const { proxy, peers, calls } = makeRankedRoutingFixture([504, 504, 504, 200], [
1077
- { serviceId: 'model-a' },
1078
- { serviceId: 'model-b', peerId: 'b'.repeat(40) },
1079
- ]);
1080
- const sendRequest = proxy._node.sendRequest;
1081
- proxy._node.sendRequest = async (peer, request) => {
1082
- const response = await sendRequest(peer, request);
1083
- if (failure === 'transport')
1084
- throw new Error('Connection closed after dispatch');
1085
- return response;
1086
- };
1087
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1088
- assert.equal(response.statusCode, failure === 'transport' ? 502 : 504);
1089
- assert.deepEqual(calls.requests.map(entry => entry.peer.peerId), peers.map(peer => peer.peerId));
1090
- assert.equal(calls.routing, 1);
1091
- });
1092
- }
1093
- test('ranked routing stops on buyer-attributed failures', async () => {
1094
- const { proxy, calls } = makeRankedRoutingFixture();
1095
- const sendRequest = proxy._node.sendRequest;
1096
- proxy._node.sendRequest = async (peer, request) => {
1097
- const response = await sendRequest(peer, request);
1098
- response.headers[ANTSEED_FAULT_ATTRIBUTION_HEADER] = 'buyer';
1099
- return response;
1100
- };
1101
- await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1102
- assert.equal(calls.requests.length, 1);
1103
- });
1104
- test('ranked routing never retries an ambiguous transport failure', async () => {
1105
- const { proxy, calls } = makeRankedRoutingFixture();
1106
- proxy._node.sendRequest = async (peer, request) => {
1107
- calls.requests.push({ peer, request });
1108
- throw new Error('Connection closed after dispatch');
1109
- };
1110
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [] } }));
1111
- assert.equal(response.statusCode, 502);
1112
- assert.equal(calls.requests.length, 1);
1113
- });
1114
- test('ranked routing never falls back after streaming has started', async () => {
1115
- const { proxy, calls } = makeRankedRoutingFixture();
1116
- proxy._node.sendRequestStream = async (peer, request, callbacks) => {
1117
- calls.requests.push({ peer, request });
1118
- callbacks.onResponseStart({ requestId: request.requestId, statusCode: 200,
1119
- headers: { 'content-type': 'text/event-stream' }, body: Buffer.from('data: partial\n\n') }, { streaming: true });
1120
- throw new Error('Stream disconnected');
1121
- };
1122
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [], stream: true } }));
1123
- assert.equal(response.statusCode, 200);
1124
- assert.match(response.body, /partial/);
1125
- assert.equal(calls.requests.length, 1);
1126
- });
1127
- test('ranked routing can fall back before a requested stream starts', async () => {
1128
- const { proxy, calls } = makeRankedRoutingFixture();
1129
- proxy._node.sendRequestStream = async (peer, request, callbacks) => {
1130
- calls.requests.push({ peer, request });
1131
- const response = { requestId: request.requestId, statusCode: calls.requests.length === 1 ? 503 : 200,
1132
- headers: { 'content-type': 'text/event-stream' }, body: Buffer.from('data: response\n\n') };
1133
- callbacks.onResponseStart(response, { streaming: response.statusCode === 200 });
1134
- return response;
1135
- };
1136
- const response = await invokeProxy(proxy, makeProxyRequest({ body: { model: 'antseed', messages: [], stream: true } }));
1137
- assert.equal(response.statusCode, 200);
1138
- assert.equal(calls.requests.length, 2);
1139
- assert.equal(calls.routing, 1);
1140
- });
1141
- test('ranked routing stops on cancellation between attempts', async () => {
1142
- const { proxy, calls } = makeRankedRoutingFixture();
1143
- const response = makeProxyResponse();
1144
- const close = [];
1145
- response.once = (...args) => {
1146
- if (args[0] === 'close')
1147
- close.push(args[1]);
1148
- return response;
1149
- };
1150
- const sendRequest = proxy._node.sendRequest;
1151
- proxy._node.sendRequest = async (peer, request) => {
1152
- const result = await sendRequest(peer, request);
1153
- for (const listener of close)
1154
- listener();
1155
- return result;
1156
- };
1157
- await proxy._handleRequest(makeProxyRequest({ body: { model: 'antseed', messages: [] } }), response);
1158
- assert.equal(calls.requests.length, 1);
1159
- });
1160
- test('ranked inference attempts share conversation spend attribution without counting extra turns', async () => {
1161
- const { proxy, calls, peers } = makeRankedRoutingFixture();
1162
- const session = randomUUID();
1163
- const sendRequest = proxy._node.sendRequest;
1164
- proxy._node.sendRequest = async (peer, request) => {
1165
- const result = await sendRequest(peer, request);
1166
- proxy._attributeSpend({ sellerPeerId: peer.peerId, requestId: request.requestId, amountUsdc: '1000',
1167
- inputTokens: '1', cachedInputTokens: '0', outputTokens: '1', outputImages: '0' });
1168
- return result;
1169
- };
1170
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1171
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1172
- assert.equal(response.statusCode, 200);
1173
- const conversation = proxy._conversations.get(`vpr:${session}`);
1174
- assert.equal(conversation.spentUsdc, '2000');
1175
- assert.equal(conversation.requestCount, 1);
1176
- assert.equal(conversation.lastModel, `${peers[1].peerId}@model-b`);
1177
- assert.equal(calls.requests.length, 2);
1178
- });
1179
- function makeRoutingSpendFixture(options = {}) {
1180
- const fixture = makeRankedRoutingFixture(options.inferenceStatuses ?? [200]);
1181
- const node = fixture.proxy._node;
1182
- const client = fixture.client;
1183
- const selectRoute = client.selectRoute.bind(client);
1184
- const sendRequest = node.sendRequest;
1185
- const spend = (requestId, amountUsdc, inputTokens, cachedInputTokens = '0') => {
1186
- ;
1187
- fixture.proxy._attributeSpend({ sellerPeerId: fixture.peers[0].peerId, requestId, amountUsdc,
1188
- inputTokens, cachedInputTokens, outputTokens: inputTokens, outputImages: '0' });
1189
- };
1190
- const pendingFees = [];
1191
- const snapshots = [];
1192
- client.selectRoute = async (request, peers, context) => {
1193
- await context.sendRequest(peers[0], { ...request, requestId: options.routingRequestId ?? randomUUID(), path: '/_antseed/route' }, {});
1194
- if (options.rejected)
1195
- throw new Error('Recommendation rejected');
1196
- return selectRoute(request, peers, context);
1197
- };
1198
- node.sendRequest = async (peer, request) => {
1199
- if (request.path === '/_antseed/route') {
1200
- pendingFees.push(request.requestId);
1201
- if (!options.late && !options.rejected)
1202
- spend(request.requestId, '1000', '9', '3');
1203
- const tracked = fixture.proxy._requestConversations.get(request.requestId);
1204
- if (tracked) {
1205
- const conversation = fixture.proxy._conversations.get(tracked.convId);
1206
- snapshots.push({ spentUsdc: conversation.spentUsdc, requestCount: conversation.requestCount });
1207
- }
1208
- return { requestId: request.requestId, statusCode: 200, headers: {}, body: new Uint8Array() };
1209
- }
1210
- const response = await sendRequest(peer, request);
1211
- if (response.statusCode === 200)
1212
- spend(request.requestId, '20000', '4');
1213
- return response;
1214
- };
1215
- return { ...fixture, pendingFees, snapshots, spend };
1216
- }
1217
- test('routing fees and reported tokens are attributed without counting an extra turn', async () => {
1218
- const { proxy, snapshots } = makeRoutingSpendFixture();
1219
- const session = randomUUID();
1220
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1221
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1222
- assert.equal(response.statusCode, 200);
1223
- assert.deepEqual(snapshots, [{ spentUsdc: '1000', requestCount: 0 }]);
1224
- const conversation = proxy._conversations.get(`vpr:${session}`);
1225
- assert.equal(conversation.spentUsdc, '21000');
1226
- assert.equal(conversation.requestCount, 1);
1227
- assert.equal(conversation.inputTokens, '13');
1228
- assert.equal(conversation.cachedInputTokens, '3');
1229
- assert.equal(conversation.outputTokens, '13');
1230
- });
1231
- test('late routing authorization is still attributed after inference finishes', async () => {
1232
- const { proxy, pendingFees, spend } = makeRoutingSpendFixture({ late: true });
1233
- const session = randomUUID();
1234
- await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1235
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1236
- assert.equal(proxy._conversations.get(`vpr:${session}`).spentUsdc, '20000');
1237
- spend(pendingFees[0], '1000', '0');
1238
- const conversation = proxy._conversations.get(`vpr:${session}`);
1239
- assert.equal(conversation.spentUsdc, '21000');
1240
- assert.equal(conversation.requestCount, 1);
1241
- assert.equal(conversation.inputTokens, '4');
1242
- assert.equal(conversation.cachedInputTokens, '0');
1243
- assert.equal(conversation.outputTokens, '4');
1244
- });
1245
- test('routing spend stays on the conversation when all inference destinations fail', async () => {
1246
- const { proxy } = makeRoutingSpendFixture({ inferenceStatuses: [503, 503] });
1247
- const session = randomUUID();
1248
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1249
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1250
- assert.equal(response.statusCode, 503);
1251
- const conversation = proxy._conversations.get(`vpr:${session}`);
1252
- assert.equal(conversation.spentUsdc, '1000');
1253
- assert.equal(conversation.requestCount, 0);
1254
- assert.equal(conversation.inputTokens, '9');
1255
- });
1256
- test('rejected routing responses do not invent a conversation charge', async () => {
1257
- const { proxy, calls } = makeRoutingSpendFixture({ rejected: true });
1258
- const session = randomUUID();
1259
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1260
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1261
- assert.equal(response.statusCode, 502);
1262
- assert.equal(calls.requests.length, 0);
1263
- const conversation = proxy._conversations.get(`vpr:${session}`);
1264
- assert.equal(conversation.spentUsdc, '0');
1265
- assert.equal(conversation.requestCount, 0);
1266
- });
1267
- test('simultaneous routing purchases on one seller remain attributed to separate chats', async () => {
1268
- const { proxy } = makeRoutingSpendFixture();
1269
- const sessions = [randomUUID(), randomUUID()];
1270
- await Promise.all(sessions.map(session => invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1271
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }))));
1272
- for (const session of sessions) {
1273
- const conversation = proxy._conversations.get(`vpr:${session}`);
1274
- assert.equal(conversation.spentUsdc, '21000');
1275
- assert.equal(conversation.requestCount, 1);
1276
- }
1277
- });
1278
- test('subagent routing fees roll up to the parent conversation', async () => {
1279
- const { proxy } = makeRoutingSpendFixture();
1280
- const parent = randomUUID();
1281
- const child = randomUUID();
1282
- await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-session-id': child, 'x-parent-session-id': parent },
1283
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1284
- const conversations = proxy._conversations.list().filter((conversation) => conversation.sessionKey === parent);
1285
- assert.equal(conversations.length, 1);
1286
- assert.equal(conversations[0].spentUsdc, '21000');
1287
- assert.equal(conversations[0].requestCount, 1);
1288
- });
1289
- test('ordinary requests keep attributing spend after the conversation already exists', async () => {
1290
- const { proxy, calls } = makeRoutingSpendFixture();
1291
- setDefaultModel(proxy, null);
1292
- const session = randomUUID();
1293
- for (const message of ['Hello', 'Follow up']) {
1294
- const response = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': session },
1295
- body: { model: 'model-a', messages: [{ role: 'user', content: message }] } }));
1296
- assert.equal(response.statusCode, 200, response.body);
1297
- }
1298
- const conversation = proxy._conversations.get(`vpr:${session}`);
1299
- assert.equal(calls.routing, 0);
1300
- assert.equal(conversation.spentUsdc, '40000');
1301
- assert.equal(conversation.requestCount, 2);
1302
- assert.equal(conversation.inputTokens, '8');
1303
- });
1304
- test('model and model-routing requests use the same duplicate tracking guard', () => {
1305
- const proxy = makeBuyerProxyWithPeers([]);
1306
- for (const purpose of [undefined, 'routing']) {
1307
- const requestId = randomUUID();
1308
- proxy._trackRequestConversation(requestId, 'first-chat', undefined, purpose);
1309
- const original = proxy._requestConversations.get(requestId);
1310
- assert.throws(() => proxy._trackRequestConversation(requestId, 'other-chat', undefined, purpose), /Request ID already tracked/);
1311
- assert.equal(proxy._requestConversations.get(requestId), original);
1312
- assert.equal(original.convId, 'first-chat');
1313
- }
1314
- });
1315
- test('reused routing IDs cannot move a late payment into another conversation', async () => {
1316
- const { proxy, spend } = makeRoutingSpendFixture({ late: true, routingRequestId: randomUUID() });
1317
- const first = randomUUID();
1318
- const second = randomUUID();
1319
- const firstResponse = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': first },
1320
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1321
- assert.equal(firstResponse.statusCode, 200);
1322
- const routingId = [...proxy._requestConversations.entries()]
1323
- .find(([, entry]) => entry.convId === `vpr:${first}` && entry.purpose === 'routing')[0];
1324
- const secondResponse = await invokeProxy(proxy, makeProxyRequest({ headers: { 'x-vpr-session-id': second },
1325
- body: { model: 'antseed', messages: [{ role: 'user', content: 'Hello' }] } }));
1326
- assert.equal(secondResponse.statusCode, 502);
1327
- assert.match(secondResponse.body, /already tracked/);
1328
- spend(routingId, '1000', '0');
1329
- assert.equal(proxy._conversations.get(`vpr:${first}`).spentUsdc, '21000');
1330
- assert.equal(proxy._conversations.get(`vpr:${second}`).spentUsdc, '0');
1331
- });
1332
22
  for (const provider of ['venice']) {
1333
23
  test(`native ${provider} routes concurrent jobs independently, persist before returning, and survive pin changes`, async () => {
1334
24
  const protocol = 'venice-video';
@@ -1573,15 +263,10 @@ function makeProxyResponse() {
1573
263
  },
1574
264
  };
1575
265
  }
1576
- const TEST_ROUTER_MODELS = ['model-a', 'model-b', 'gpt-4o'];
1577
- function listModelsWith(models = TEST_ROUTER_MODELS) {
1578
- return async () => models.map(id => ({ id }));
1579
- }
1580
- function makeBuyerProxyWithPeers(initialPeers, refreshedPeers = initialPeers, router = null, now, routingPreferences, modelRoutingClient) {
266
+ function makeBuyerProxyWithPeers(initialPeers, refreshedPeers = initialPeers, router = null, now, routingPreferences) {
1581
267
  const proxy = new BuyerProxy({
1582
268
  port: 0,
1583
269
  dataDir: '/tmp/antseed-test',
1584
- modelRoutingClient,
1585
270
  node: {
1586
271
  router,
1587
272
  },
@@ -1719,9 +404,6 @@ test('BuyerProxy starts incremental discovery on startup', async (t) => {
1719
404
  await proxy.start();
1720
405
  await proxy.stop();
1721
406
  assert.equal(sweepCalls, 1);
1722
- await proxy.start();
1723
- await proxy.stop();
1724
- assert.equal(sweepCalls, 2);
1725
407
  });
1726
408
  test('selectCandidatePeersForRouting enforces explicit provider overrides even without request protocol', () => {
1727
409
  const peers = [
@@ -4124,28 +2806,20 @@ test('route control endpoint sets, persists, and returns the default routed mode
4124
2806
  });
4125
2807
  const set = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: { model: `${validPeerId}@gpt-4o` } }));
4126
2808
  assert.equal(set.statusCode, 200);
4127
- assert.deepEqual(JSON.parse(set.body), { ok: true, model: `${validPeerId}@gpt-4o`, router: null });
2809
+ assert.deepEqual(JSON.parse(set.body), { ok: true, model: `${validPeerId}@gpt-4o` });
4128
2810
  const get = await invokeProxy(proxy, makeProxyRequest({ method: 'GET', path: '/_antseed/route' }));
4129
- assert.deepEqual(JSON.parse(get.body), { ok: true, model: `${validPeerId}@gpt-4o`, router: null });
2811
+ assert.deepEqual(JSON.parse(get.body), { ok: true, model: `${validPeerId}@gpt-4o` });
4130
2812
  const persisted = JSON.parse(await readFile(join(dir, 'buyer.state.json'), 'utf-8'));
4131
2813
  assert.equal(persisted['defaultRoutedModel'], `${validPeerId}@gpt-4o`);
4132
2814
  const automatic = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: { model: 'gpt-4o' } }));
4133
2815
  assert.equal(automatic.statusCode, 200);
4134
- assert.deepEqual(JSON.parse(automatic.body), { ok: true, model: 'gpt-4o', router: null });
2816
+ assert.deepEqual(JSON.parse(automatic.body), { ok: true, model: 'gpt-4o' });
4135
2817
  const automaticPersisted = JSON.parse(await readFile(join(dir, 'buyer.state.json'), 'utf-8'));
4136
2818
  assert.equal(automaticPersisted['defaultRoutedModel'], 'gpt-4o');
4137
2819
  const invalid = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: { model: 'not-a-peer@gpt-4o' } }));
4138
2820
  assert.equal(invalid.statusCode, 400);
4139
- const router = { service: { peerId: validPeerId, provider: 'alpha', serviceId: 'alpha-route' }, costQualityTradeoff: 5 };
4140
- const routed = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: { router } }));
4141
- assert.deepEqual(JSON.parse(routed.body), { ok: true, model: null, router });
4142
- const routedPersisted = JSON.parse(await readFile(join(dir, 'buyer.state.json'), 'utf-8'));
4143
- assert.deepEqual([routedPersisted['defaultRoutedModel'], routedPersisted['defaultRouter']], [null, router]);
4144
- for (const body of [{ model: 'gpt-4o', router }, { router: { service: { ...router.service, peerId: 'invalid' } } }, { router: [] }]) {
4145
- assert.equal((await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body }))).statusCode, 400);
4146
- }
4147
2821
  const cleared = await invokeProxy(proxy, makeProxyRequest({ path: '/_antseed/route', body: { model: '' } }));
4148
- assert.deepEqual(JSON.parse(cleared.body), { ok: true, model: null, router: null });
2822
+ assert.deepEqual(JSON.parse(cleared.body), { ok: true, model: null });
4149
2823
  });
4150
2824
  test('buyer-usage endpoint reports lastActivityAt, null until a request is dispatched', async () => {
4151
2825
  const proxy = new BuyerProxy({