@antseed/cli 0.1.168 → 0.1.170
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/README.md +4 -0
- package/dist/buyer-identities/loader.d.ts +28 -0
- package/dist/buyer-identities/loader.d.ts.map +1 -0
- package/dist/buyer-identities/loader.js +58 -0
- package/dist/buyer-identities/loader.js.map +1 -0
- package/dist/buyer-identities/store.d.ts +41 -0
- package/dist/buyer-identities/store.d.ts.map +1 -0
- package/dist/buyer-identities/store.js +171 -0
- package/dist/buyer-identities/store.js.map +1 -0
- package/dist/buyer-identities/store.test.d.ts +2 -0
- package/dist/buyer-identities/store.test.d.ts.map +1 -0
- package/dist/buyer-identities/store.test.js +122 -0
- package/dist/buyer-identities/store.test.js.map +1 -0
- package/dist/cli/commands/buyer/channel-withdraw.d.ts.map +1 -1
- package/dist/cli/commands/buyer/channel-withdraw.js +9 -4
- package/dist/cli/commands/buyer/channel-withdraw.js.map +1 -1
- package/dist/cli/commands/buyer/channel-withdraw.test.js +69 -0
- package/dist/cli/commands/buyer/channel-withdraw.test.js.map +1 -1
- package/dist/cli/commands/buyer/channels.js +1 -1
- package/dist/cli/commands/buyer/channels.js.map +1 -1
- package/dist/cli/commands/buyer/identity.d.ts +3 -0
- package/dist/cli/commands/buyer/identity.d.ts.map +1 -0
- package/dist/cli/commands/buyer/identity.js +115 -0
- package/dist/cli/commands/buyer/identity.js.map +1 -0
- package/dist/cli/commands/buyer/index.d.ts.map +1 -1
- package/dist/cli/commands/buyer/index.js +2 -0
- package/dist/cli/commands/buyer/index.js.map +1 -1
- package/dist/cli/commands/buyer/start.d.ts.map +1 -1
- package/dist/cli/commands/buyer/start.js +30 -2
- package/dist/cli/commands/buyer/start.js.map +1 -1
- package/dist/cli/commands/gateway/index.d.ts +3 -0
- package/dist/cli/commands/gateway/index.d.ts.map +1 -0
- package/dist/cli/commands/gateway/index.js +43 -0
- package/dist/cli/commands/gateway/index.js.map +1 -0
- package/dist/cli/commands/gateway/key.d.ts +3 -0
- package/dist/cli/commands/gateway/key.d.ts.map +1 -0
- package/dist/cli/commands/gateway/key.js +240 -0
- package/dist/cli/commands/gateway/key.js.map +1 -0
- package/dist/cli/commands/gateway/shared.d.ts +29 -0
- package/dist/cli/commands/gateway/shared.d.ts.map +1 -0
- package/dist/cli/commands/gateway/shared.js +90 -0
- package/dist/cli/commands/gateway/shared.js.map +1 -0
- package/dist/cli/commands/seller/setup.d.ts.map +1 -1
- package/dist/cli/commands/seller/setup.js +4 -0
- package/dist/cli/commands/seller/setup.js.map +1 -1
- package/dist/cli/commands/seller/start.d.ts.map +1 -1
- package/dist/cli/commands/seller/start.js +8 -2
- package/dist/cli/commands/seller/start.js.map +1 -1
- package/dist/cli/commands/seller/start.test.js +20 -0
- package/dist/cli/commands/seller/start.test.js.map +1 -1
- package/dist/cli/commands/tunnel/start.d.ts.map +1 -1
- package/dist/cli/commands/tunnel/start.js +25 -14
- package/dist/cli/commands/tunnel/start.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/accounting.d.ts +45 -0
- package/dist/gateway/accounting.d.ts.map +1 -0
- package/dist/gateway/accounting.js +94 -0
- package/dist/gateway/accounting.js.map +1 -0
- package/dist/gateway/cdp-auth.d.ts +12 -0
- package/dist/gateway/cdp-auth.d.ts.map +1 -0
- package/dist/gateway/cdp-auth.js +53 -0
- package/dist/gateway/cdp-auth.js.map +1 -0
- package/dist/gateway/cdp-auth.test.d.ts +2 -0
- package/dist/gateway/cdp-auth.test.d.ts.map +1 -0
- package/dist/gateway/cdp-auth.test.js +96 -0
- package/dist/gateway/cdp-auth.test.js.map +1 -0
- package/dist/gateway/gateway.test.d.ts.map +1 -0
- package/dist/gateway/gateway.test.js +709 -0
- package/dist/gateway/gateway.test.js.map +1 -0
- package/dist/gateway/keys.d.ts +13 -0
- package/dist/gateway/keys.d.ts.map +1 -0
- package/dist/gateway/keys.js +24 -0
- package/dist/gateway/keys.js.map +1 -0
- package/dist/gateway/limits.d.ts +25 -0
- package/dist/gateway/limits.d.ts.map +1 -0
- package/dist/gateway/limits.js +45 -0
- package/dist/gateway/limits.js.map +1 -0
- package/dist/gateway/money.d.ts +9 -0
- package/dist/gateway/money.d.ts.map +1 -0
- package/dist/gateway/money.js +37 -0
- package/dist/gateway/money.js.map +1 -0
- package/dist/gateway/runtime.d.ts +38 -0
- package/dist/gateway/runtime.d.ts.map +1 -0
- package/dist/gateway/runtime.js +144 -0
- package/dist/gateway/runtime.js.map +1 -0
- package/dist/gateway/server.d.ts +49 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +460 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/spend-feed.d.ts +32 -0
- package/dist/gateway/spend-feed.d.ts.map +1 -0
- package/dist/gateway/spend-feed.js +86 -0
- package/dist/gateway/spend-feed.js.map +1 -0
- package/dist/gateway/store.d.ts +114 -0
- package/dist/gateway/store.d.ts.map +1 -0
- package/dist/gateway/store.js +253 -0
- package/dist/gateway/store.js.map +1 -0
- package/dist/gateway/x402.d.ts +95 -0
- package/dist/gateway/x402.d.ts.map +1 -0
- package/dist/gateway/x402.js +120 -0
- package/dist/gateway/x402.js.map +1 -0
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/registry.js.map +1 -1
- package/dist/proxy/buyer-proxy.d.ts +15 -0
- package/dist/proxy/buyer-proxy.d.ts.map +1 -1
- package/dist/proxy/buyer-proxy.js +157 -15
- package/dist/proxy/buyer-proxy.js.map +1 -1
- package/dist/proxy/buyer-proxy.test.js +157 -1
- package/dist/proxy/buyer-proxy.test.js.map +1 -1
- package/dist/proxy/native-video-proxy.d.ts +34 -0
- package/dist/proxy/native-video-proxy.d.ts.map +1 -0
- package/dist/proxy/native-video-proxy.js +41 -0
- package/dist/proxy/native-video-proxy.js.map +1 -0
- package/dist/proxy/native-video-proxy.test.d.ts +2 -0
- package/dist/proxy/native-video-proxy.test.d.ts.map +1 -0
- package/dist/proxy/native-video-proxy.test.js +23 -0
- package/dist/proxy/native-video-proxy.test.js.map +1 -0
- package/dist/proxy/network-models.d.ts +3 -2
- package/dist/proxy/network-models.d.ts.map +1 -1
- package/dist/proxy/network-models.js +3 -0
- package/dist/proxy/network-models.js.map +1 -1
- package/dist/proxy/network-models.test.js +13 -0
- package/dist/proxy/network-models.test.js.map +1 -1
- package/dist/proxy/request-utils.d.ts +3 -0
- package/dist/proxy/request-utils.d.ts.map +1 -1
- package/dist/proxy/request-utils.js +6 -1
- package/dist/proxy/request-utils.js.map +1 -1
- package/dist/proxy/resource-routes.d.ts +20 -0
- package/dist/proxy/resource-routes.d.ts.map +1 -0
- package/dist/proxy/resource-routes.js +48 -0
- package/dist/proxy/resource-routes.js.map +1 -0
- package/dist/proxy/resource-routes.test.d.ts +2 -0
- package/dist/proxy/resource-routes.test.d.ts.map +1 -0
- package/dist/proxy/resource-routes.test.js +21 -0
- package/dist/proxy/resource-routes.test.js.map +1 -0
- package/dist/proxy/routing.d.ts.map +1 -1
- package/dist/proxy/routing.js +5 -2
- package/dist/proxy/routing.js.map +1 -1
- package/dist/proxy/service-api-adapter.d.ts +1 -1
- package/dist/proxy/service-api-adapter.d.ts.map +1 -1
- package/dist/proxy/service-api-adapter.js +1 -1
- package/dist/proxy/service-api-adapter.js.map +1 -1
- package/dist/proxy/spend-attribution.d.ts +63 -0
- package/dist/proxy/spend-attribution.d.ts.map +1 -0
- package/dist/proxy/spend-attribution.js +97 -0
- package/dist/proxy/spend-attribution.js.map +1 -0
- package/dist/proxy/video-download.d.ts +7 -0
- package/dist/proxy/video-download.d.ts.map +1 -0
- package/dist/proxy/video-download.js +66 -0
- package/dist/proxy/video-download.js.map +1 -0
- package/dist/proxy/video-download.test.d.ts +2 -0
- package/dist/proxy/video-download.test.d.ts.map +1 -0
- package/dist/proxy/video-download.test.js +108 -0
- package/dist/proxy/video-download.test.js.map +1 -0
- package/package.json +9 -7
- package/dist/tunnel/gateway.d.ts +0 -16
- package/dist/tunnel/gateway.d.ts.map +0 -1
- package/dist/tunnel/gateway.js +0 -146
- package/dist/tunnel/gateway.js.map +0 -1
- package/dist/tunnel/gateway.test.d.ts.map +0 -1
- package/dist/tunnel/gateway.test.js +0 -89
- package/dist/tunnel/gateway.test.js.map +0 -1
- /package/dist/{tunnel → gateway}/gateway.test.d.ts +0 -0
|
@@ -5,6 +5,7 @@ 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 { ResourceRoutes } from './resource-routes.js';
|
|
8
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';
|
|
9
10
|
import { DEFAULT_BUYER_PEER_REFRESH_INTERVAL_MS } from '../config/defaults.js';
|
|
10
11
|
import { TeeVerification } from './tee-verification.js';
|
|
@@ -18,6 +19,149 @@ function makePeer(seed, providers) {
|
|
|
18
19
|
providers,
|
|
19
20
|
};
|
|
20
21
|
}
|
|
22
|
+
for (const provider of ['venice']) {
|
|
23
|
+
test(`native ${provider} routes concurrent jobs independently, persist before returning, and survive pin changes`, async () => {
|
|
24
|
+
const protocol = 'venice-video';
|
|
25
|
+
const resourceId = (suffix) => `task-${suffix}`;
|
|
26
|
+
const statusPath = '/api/v1/video/retrieve';
|
|
27
|
+
const directory = await mkdtemp(join(tmpdir(), 'native-video-routes-'));
|
|
28
|
+
try {
|
|
29
|
+
const peers = [makePeer('a', [provider]), makePeer('b', [provider])];
|
|
30
|
+
for (const [index, peer] of peers.entries()) {
|
|
31
|
+
peer.providerServiceApiProtocols = { [provider]: { services: { [`model-${index}`]: [protocol] } } };
|
|
32
|
+
}
|
|
33
|
+
const proxy = makeBuyerProxyWithPeers(peers, peers, permissiveRouter());
|
|
34
|
+
proxy._stateDir = directory;
|
|
35
|
+
proxy._stateFile = join(directory, 'buyer.state.json');
|
|
36
|
+
await writeFile(join(directory, 'buyer.state.json'), JSON.stringify({ unrelated: 'preserved' }));
|
|
37
|
+
const calls = [];
|
|
38
|
+
const send = async (peer, request) => {
|
|
39
|
+
calls.push({ peer: peer.peerId, path: request.path, service: request.headers['x-antseed-service'], provider: request.headers['x-antseed-provider'] });
|
|
40
|
+
if (request.method === 'POST' && peer === peers[0])
|
|
41
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
42
|
+
return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify({ queue_id: resourceId(peer.peerId[0]) })) };
|
|
43
|
+
};
|
|
44
|
+
proxy._node.sendRequest = send;
|
|
45
|
+
proxy._node.sendRequestStream = send;
|
|
46
|
+
const submissions = await Promise.all([0, 1].map(index => invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: `model-${index}`, duration: '8s', prompt: 'cat' } }))));
|
|
47
|
+
assert.deepEqual(submissions.map(result => result.statusCode), [200, 200]);
|
|
48
|
+
const state = JSON.parse(await readFile(join(directory, 'buyer.state.json'), 'utf8'));
|
|
49
|
+
assert.equal(state.unrelated, 'preserved');
|
|
50
|
+
assert.equal(state.resourceRoutes.length, 2);
|
|
51
|
+
const restored = new ResourceRoutes();
|
|
52
|
+
restored.hydrate(state.resourceRoutes);
|
|
53
|
+
const restarted = makeBuyerProxyWithPeers(peers, peers, permissiveRouter());
|
|
54
|
+
restarted._stateDir = directory;
|
|
55
|
+
restarted._stateFile = join(directory, 'buyer.state.json');
|
|
56
|
+
restarted._resourceRoutes = restored;
|
|
57
|
+
restarted._node.sendRequest = proxy._node.sendRequest;
|
|
58
|
+
restarted._node.sendRequestStream = proxy._node.sendRequestStream;
|
|
59
|
+
restarted._pinnedPeer = peers[1].peerId;
|
|
60
|
+
restarted._defaultRoutedModel = `${peers[1].peerId}@unrelated-chat-model`;
|
|
61
|
+
for (const [index, suffix] of ['a', 'b'].entries()) {
|
|
62
|
+
const result = await invokeProxy(restarted, makeProxyRequest({ method: 'POST', path: statusPath, body: { model: `model-${index}`, queue_id: resourceId(suffix) } }));
|
|
63
|
+
assert.equal(result.statusCode, 200);
|
|
64
|
+
assert.equal(calls.at(-1)?.peer, peers[index].peerId);
|
|
65
|
+
assert.equal(calls.at(-1)?.service, `model-${index}`);
|
|
66
|
+
assert.equal(calls.at(-1)?.provider, provider);
|
|
67
|
+
}
|
|
68
|
+
const recorded = await invokeProxy(restarted, makeProxyRequest({ method: 'POST', path: statusPath, body: { model: 'model-0', queue_id: resourceId('a') }, headers: { 'x-antseed-pin-peer': peers[1].peerId } }));
|
|
69
|
+
assert.equal(recorded.statusCode, 200);
|
|
70
|
+
assert.equal(calls.at(-1)?.peer, peers[0].peerId);
|
|
71
|
+
await proxy._stateWriteChain;
|
|
72
|
+
await restarted._stateWriteChain;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await rm(directory, { recursive: true, force: true, maxRetries: 3 });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
test('native video resolves antseed and peer-pinned models like images', async () => {
|
|
80
|
+
const cases = [
|
|
81
|
+
{ provider: 'venice', protocol: 'venice-video', path: '/api/v1/video/queue', acceptance: { queue_id: 'queue' } },
|
|
82
|
+
];
|
|
83
|
+
for (const { provider, protocol, path, acceptance } of cases) {
|
|
84
|
+
const peers = [makePeer('a', [provider]), makePeer('b', [provider])];
|
|
85
|
+
for (const peer of peers)
|
|
86
|
+
peer.providerServiceApiProtocols = { [provider]: { services: { 'video-model': [protocol] } } };
|
|
87
|
+
for (const model of ['antseed', `${peers[1].peerId}@video-model`]) {
|
|
88
|
+
const proxy = makeBuyerProxyWithPeers(peers, peers, permissiveRouter());
|
|
89
|
+
proxy._defaultRoutedModel = `${peers[1].peerId}@video-model`;
|
|
90
|
+
proxy._persistResourceRoutes = async () => { };
|
|
91
|
+
const sent = [];
|
|
92
|
+
proxy._node.sendRequest = async (selected, request) => {
|
|
93
|
+
const body = JSON.parse(Buffer.from(request.body).toString());
|
|
94
|
+
sent.push({ peer: selected.peerId, model: body.model, custom: body.custom });
|
|
95
|
+
return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify(acceptance)) };
|
|
96
|
+
};
|
|
97
|
+
const response = await invokeProxy(proxy, makeProxyRequest({ path, body: { model, prompt: 'cat', duration: 5, custom: { prompt: '猫' } } }));
|
|
98
|
+
assert.equal(response.statusCode, 200, response.body);
|
|
99
|
+
assert.deepEqual(sent, [{ peer: peers[1].peerId, model: 'video-model', custom: { prompt: '猫' } }]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
test('native resource follow-ups fail when the recorded provider loses support instead of switching providers', async () => {
|
|
104
|
+
for (const provider of ['venice']) {
|
|
105
|
+
const protocol = 'venice-video';
|
|
106
|
+
const peer = makePeer('a', [provider, 'replacement']);
|
|
107
|
+
peer.providerServiceApiProtocols = { [provider]: { services: { other: [protocol] } }, replacement: { services: { 'video-model': [protocol] } } };
|
|
108
|
+
const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
|
|
109
|
+
proxy._persistResourceRoutes = async () => { };
|
|
110
|
+
const resourceId = 'task';
|
|
111
|
+
proxy._resourceRoutes.record({ protocol, resourceId, sellerPeerId: peer.peerId, provider, service: 'video-model' });
|
|
112
|
+
let calls = 0;
|
|
113
|
+
proxy._node.sendRequest = async () => { calls += 1; throw new Error('must not dispatch'); };
|
|
114
|
+
const response = await invokeProxy(proxy, makeProxyRequest({ method: 'POST', path: '/api/v1/video/retrieve', body: { model: 'video-model', queue_id: resourceId } }));
|
|
115
|
+
assert.ok(response.statusCode >= 400, response.body);
|
|
116
|
+
assert.equal(calls, 0);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
test('native video creates are not retried automatically', async () => {
|
|
120
|
+
const peers = [makePeer('a', ['venice']), makePeer('b', ['venice'])];
|
|
121
|
+
for (const peer of peers)
|
|
122
|
+
peer.providerServiceApiProtocols = { venice: { services: { model: ['venice-video'] } } };
|
|
123
|
+
const proxy = makeBuyerProxyWithPeers(peers, peers, permissiveRouter());
|
|
124
|
+
const sent = [];
|
|
125
|
+
proxy._node.sendRequest = async (_peer, request) => {
|
|
126
|
+
sent.push(request);
|
|
127
|
+
throw new Error('uncertain connection loss');
|
|
128
|
+
};
|
|
129
|
+
const result = await invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'model', duration: '8s' } }));
|
|
130
|
+
assert.ok(result.statusCode >= 400);
|
|
131
|
+
assert.equal(sent.length, 1);
|
|
132
|
+
assert.equal(sent[0].headers['x-antseed-pin-peer'], undefined);
|
|
133
|
+
});
|
|
134
|
+
test('native video options are validated by the seller without buyer filtering or retries', async () => {
|
|
135
|
+
const peers = [makePeer('a', ['venice']), makePeer('b', ['venice'])];
|
|
136
|
+
for (const peer of peers)
|
|
137
|
+
peer.providerServiceApiProtocols = { venice: { services: { model: ['venice-video'] } } };
|
|
138
|
+
peers[0].reputationScore = 99;
|
|
139
|
+
peers[1].reputationScore = 70;
|
|
140
|
+
peers[0].providerServiceCapabilities = { venice: { services: { model: { video: { durationsSeconds: [5] } } } } };
|
|
141
|
+
peers[1].providerServiceCapabilities = { venice: { services: { model: { video: { durationsSeconds: [5, 10, 15] } } } } };
|
|
142
|
+
const proxy = makeBuyerProxyWithPeers(peers, peers, permissiveRouter());
|
|
143
|
+
proxy._persistResourceRoutes = async () => { };
|
|
144
|
+
const selected = [];
|
|
145
|
+
const forwarded = [];
|
|
146
|
+
const sellerError = { error: { code: 'unsupported_video_options', message: 'Seller does not support this duration' } };
|
|
147
|
+
proxy._node.sendRequest = async (peer, request) => {
|
|
148
|
+
selected.push(peer.peerId);
|
|
149
|
+
const body = JSON.parse(Buffer.from(request.body).toString());
|
|
150
|
+
forwarded.push(body);
|
|
151
|
+
if (body.duration === '15s') {
|
|
152
|
+
return { requestId: request.requestId, statusCode: 422, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify(sellerError)) };
|
|
153
|
+
}
|
|
154
|
+
return { requestId: request.requestId, statusCode: 200, headers: { 'content-type': 'application/json' }, body: Buffer.from(JSON.stringify({ id: 'task' })) };
|
|
155
|
+
};
|
|
156
|
+
const create = (duration) => invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'model', duration: `${duration}s` } }));
|
|
157
|
+
assert.equal((await create(10)).statusCode, 200);
|
|
158
|
+
assert.deepEqual(selected, [peers[0].peerId]);
|
|
159
|
+
const rejected = await create(15);
|
|
160
|
+
assert.equal(rejected.statusCode, 422, rejected.body);
|
|
161
|
+
assert.deepEqual(JSON.parse(rejected.body), sellerError);
|
|
162
|
+
assert.deepEqual(selected, [peers[0].peerId, peers[0].peerId]);
|
|
163
|
+
assert.deepEqual(forwarded, [{ model: 'model', duration: '10s' }, { model: 'model', duration: '15s' }]);
|
|
164
|
+
});
|
|
21
165
|
test('existing required CLI verification rejects a failed pin without payment/inference and auto falls back to a verified seller', async () => {
|
|
22
166
|
const rejected = makePeer('a', ['openai']);
|
|
23
167
|
const accepted = makePeer('b', ['openai']);
|
|
@@ -53,7 +197,7 @@ test('existing required CLI verification rejects a failed pin without payment/in
|
|
|
53
197
|
assert.deepEqual(dispatches, [accepted.peerId, accepted.peerId]);
|
|
54
198
|
});
|
|
55
199
|
function makeProxyRequest(options) {
|
|
56
|
-
const body = JSON.stringify(options.body ?? { model: 'gpt-4o', messages: [] });
|
|
200
|
+
const body = options.rawBody ?? JSON.stringify(options.body ?? { model: 'gpt-4o', messages: [] });
|
|
57
201
|
const req = Readable.from([Buffer.from(body)]);
|
|
58
202
|
req.method = options.method ?? 'POST';
|
|
59
203
|
req.url = options.path ?? '/v1/chat/completions';
|
|
@@ -1147,6 +1291,18 @@ test('model-only routing does not fail over after a buyer-attributed failure', a
|
|
|
1147
1291
|
assert.deepEqual(attempts, [first.peerId]);
|
|
1148
1292
|
assert.equal(JSON.parse(res.body).error.code, ANTSEED_BUYER_FAULT_ERROR_CODE);
|
|
1149
1293
|
});
|
|
1294
|
+
test('video validation and buyer budget failures return client errors without penalizing sellers', async () => {
|
|
1295
|
+
for (const [code, status] of [['invalid-request', 400], ['buyer-budget-too-low', 422]]) {
|
|
1296
|
+
const peer = makePeer('a', ['venice']);
|
|
1297
|
+
peer.providerServiceApiProtocols = { venice: { services: { video: ['venice-video'] } } };
|
|
1298
|
+
const proxy = makeBuyerProxyWithPeers([peer], [peer], permissiveRouter());
|
|
1299
|
+
proxy._node.sendRequest = async () => { throw buyerFault('Invalid video request', code); };
|
|
1300
|
+
const result = await invokeProxy(proxy, makeProxyRequest({ path: '/api/v1/video/queue', body: { model: 'video', prompt: 'boat' } }));
|
|
1301
|
+
assert.equal(result.statusCode, status);
|
|
1302
|
+
assert.equal(JSON.parse(result.body).error.code, ANTSEED_BUYER_FAULT_ERROR_CODE);
|
|
1303
|
+
assert.equal(proxy._peerHealth.get(peer.peerId)?.failureStreak ?? 0, 0);
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1150
1306
|
test('pinned proxy request reports when the pinned peer is not discoverable', async () => {
|
|
1151
1307
|
const pinnedPeerId = 'a'.repeat(40);
|
|
1152
1308
|
const otherPeer = makePeer('b', ['openai']);
|