@antseed/cli 0.1.4 → 0.1.5
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 +20 -4
- package/dist/cli/commands/plugin.test.js +6 -7
- package/dist/cli/commands/plugin.test.js.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/plugins/loader.js +2 -2
- package/dist/plugins/loader.js.map +1 -1
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +6 -12
- package/dist/plugins/registry.js.map +1 -1
- package/dist/proxy/buyer-proxy.d.ts +12 -1
- package/dist/proxy/buyer-proxy.d.ts.map +1 -1
- package/dist/proxy/buyer-proxy.js +185 -64
- package/dist/proxy/buyer-proxy.js.map +1 -1
- package/dist/proxy/buyer-proxy.test.d.ts +2 -0
- package/dist/proxy/buyer-proxy.test.d.ts.map +1 -0
- package/dist/proxy/buyer-proxy.test.js +41 -0
- package/dist/proxy/buyer-proxy.test.js.map +1 -0
- package/dist/proxy/model-api-adapter.d.ts +20 -0
- package/dist/proxy/model-api-adapter.d.ts.map +1 -0
- package/dist/proxy/model-api-adapter.js +514 -0
- package/dist/proxy/model-api-adapter.js.map +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -35,8 +35,8 @@ antseed seed --provider anthropic
|
|
|
35
35
|
**Routers** select peers and proxy requests (consumer mode):
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
antseed plugin add @antseed/router-local
|
|
39
|
-
antseed connect --router local
|
|
38
|
+
antseed plugin add @antseed/router-local
|
|
39
|
+
antseed connect --router local
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Run `antseed init` to install all trusted plugins interactively.
|
|
@@ -72,10 +72,13 @@ Initialize a new config:
|
|
|
72
72
|
antseed config init
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Pricing is configured in USD per 1M tokens with role-specific defaults and optional provider/model overrides:
|
|
75
|
+
Pricing is configured in USD per 1M tokens with role-specific defaults and optional provider/model overrides. You can also set node `displayName` and optional per-model category tags announced in discovery metadata:
|
|
76
76
|
|
|
77
77
|
```json
|
|
78
78
|
{
|
|
79
|
+
"identity": {
|
|
80
|
+
"displayName": "Acme Inference - us-east-1"
|
|
81
|
+
},
|
|
79
82
|
"seller": {
|
|
80
83
|
"pricing": {
|
|
81
84
|
"defaults": {
|
|
@@ -92,6 +95,11 @@ Pricing is configured in USD per 1M tokens with role-specific defaults and optio
|
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
}
|
|
98
|
+
},
|
|
99
|
+
"modelCategories": {
|
|
100
|
+
"anthropic": {
|
|
101
|
+
"claude-sonnet-4-5-20250929": ["coding", "privacy"]
|
|
102
|
+
}
|
|
95
103
|
}
|
|
96
104
|
},
|
|
97
105
|
"buyer": {
|
|
@@ -106,9 +114,14 @@ Pricing is configured in USD per 1M tokens with role-specific defaults and optio
|
|
|
106
114
|
}
|
|
107
115
|
```
|
|
108
116
|
|
|
117
|
+
Model categories are normalized to lowercase tags. Recommended tags include: `privacy`, `legal`, `uncensored`, `coding`, `finance`, `tee` (custom tags are also allowed).
|
|
118
|
+
|
|
109
119
|
Role-first config examples:
|
|
110
120
|
|
|
111
121
|
```bash
|
|
122
|
+
# Identity / metadata display name
|
|
123
|
+
antseed config set identity.displayName "Acme Inference - us-east-1"
|
|
124
|
+
|
|
112
125
|
# Seller defaults
|
|
113
126
|
antseed config seller set pricing.defaults.inputUsdPerMillion 12
|
|
114
127
|
antseed config seller set pricing.defaults.outputUsdPerMillion 36
|
|
@@ -116,6 +129,9 @@ antseed config seller set pricing.defaults.outputUsdPerMillion 36
|
|
|
116
129
|
# Seller per-model override for a provider
|
|
117
130
|
antseed config seller set pricing.providers.anthropic.models '{"claude-sonnet-4-5-20250929":{"inputUsdPerMillion":14,"outputUsdPerMillion":42}}'
|
|
118
131
|
|
|
132
|
+
# Seller per-model category tags announced in metadata
|
|
133
|
+
antseed config seller set modelCategories.anthropic.claude-sonnet-4-5-20250929 '["coding","legal"]'
|
|
134
|
+
|
|
119
135
|
# Buyer preferences and max pricing
|
|
120
136
|
antseed config buyer set preferredProviders '["anthropic","openai"]'
|
|
121
137
|
antseed config buyer set maxPricing.defaults.inputUsdPerMillion 25
|
|
@@ -126,7 +142,7 @@ Runtime-only overrides (do not write your config file):
|
|
|
126
142
|
|
|
127
143
|
```bash
|
|
128
144
|
antseed seed --provider anthropic --input-usd-per-million 10 --output-usd-per-million 30
|
|
129
|
-
antseed connect --router local
|
|
145
|
+
antseed connect --router local --max-input-usd-per-million 20 --max-output-usd-per-million 60
|
|
130
146
|
```
|
|
131
147
|
|
|
132
148
|
## Settlement Runtime (Seeder)
|
|
@@ -2,15 +2,14 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import test from 'node:test';
|
|
3
3
|
import { TRUSTED_PLUGINS } from '../../plugins/registry.js';
|
|
4
4
|
import { buildPluginConfig, LEGACY_PACKAGE_MAP } from '../../plugins/loader.js';
|
|
5
|
-
test('TRUSTED_PLUGINS contains
|
|
6
|
-
assert.equal(TRUSTED_PLUGINS.length,
|
|
5
|
+
test('TRUSTED_PLUGINS contains 5 official plugins', () => {
|
|
6
|
+
assert.equal(TRUSTED_PLUGINS.length, 5);
|
|
7
7
|
const names = TRUSTED_PLUGINS.map(p => p.name);
|
|
8
8
|
assert.ok(names.includes('anthropic'));
|
|
9
9
|
assert.ok(names.includes('claude-code'));
|
|
10
|
-
assert.ok(names.includes('
|
|
10
|
+
assert.ok(names.includes('openai'));
|
|
11
11
|
assert.ok(names.includes('local-llm'));
|
|
12
|
-
assert.ok(names.includes('local
|
|
13
|
-
assert.ok(names.includes('local-chat'));
|
|
12
|
+
assert.ok(names.includes('local'));
|
|
14
13
|
});
|
|
15
14
|
test('TRUSTED_PLUGINS all have scoped package names', () => {
|
|
16
15
|
for (const plugin of TRUSTED_PLUGINS) {
|
|
@@ -47,7 +46,7 @@ test('buildPluginConfig works without instanceConfig (backwards compatible)', ()
|
|
|
47
46
|
assert.equal(result['TEST_KEY_COMPAT'], 'override');
|
|
48
47
|
});
|
|
49
48
|
test('LEGACY_PACKAGE_MAP maps old names to scoped names', () => {
|
|
50
|
-
assert.equal(LEGACY_PACKAGE_MAP['antseed-provider-anthropic'], '@antseed/provider-
|
|
51
|
-
assert.equal(LEGACY_PACKAGE_MAP['antseed-router-claude-code'], '@antseed/router-local
|
|
49
|
+
assert.equal(LEGACY_PACKAGE_MAP['antseed-provider-anthropic'], '@antseed/provider-anthropic');
|
|
50
|
+
assert.equal(LEGACY_PACKAGE_MAP['antseed-router-claude-code'], '@antseed/router-local');
|
|
52
51
|
});
|
|
53
52
|
//# sourceMappingURL=plugin.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.test.js","sourceRoot":"","sources":["../../../src/cli/commands/plugin.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE/E,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACvD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACvC,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAA;IACtC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAA;IACxC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"plugin.test.js","sourceRoot":"","sources":["../../../src/cli/commands/plugin.test.ts"],"names":[],"mappings":"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"}
|
package/dist/config/defaults.js
CHANGED
|
@@ -19,7 +19,7 @@ export function createDefaultConfig() {
|
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
buyer: {
|
|
22
|
-
preferredProviders: ['anthropic', 'openai', 'claude-code', 'claude-oauth', '
|
|
22
|
+
preferredProviders: ['anthropic', 'openai', 'claude-code', 'claude-oauth', 'local-llm'],
|
|
23
23
|
maxPricing: {
|
|
24
24
|
defaults: {
|
|
25
25
|
inputUsdPerMillion: 100,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,QAAQ,EAAE;YACR,WAAW,EAAE,cAAc;SAC5B;QACD,SAAS,EAAE,EAAE;QACb,MAAM,EAAE;YACN,YAAY,EAAE,EAAE;YAChB,mBAAmB,EAAE,CAAC;YACtB,gBAAgB,EAAE,EAAE;YACpB,OAAO,EAAE;gBACP,QAAQ,EAAE;oBACR,kBAAkB,EAAE,EAAE;oBACtB,mBAAmB,EAAE,EAAE;iBACxB;aACF;SACF;QACD,KAAK,EAAE;YACL,kBAAkB,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,QAAQ,EAAE;YACR,WAAW,EAAE,cAAc;SAC5B;QACD,SAAS,EAAE,EAAE;QACb,MAAM,EAAE;YACN,YAAY,EAAE,EAAE;YAChB,mBAAmB,EAAE,CAAC;YACtB,gBAAgB,EAAE,EAAE;YACpB,OAAO,EAAE;gBACP,QAAQ,EAAE;oBACR,kBAAkB,EAAE,EAAE;oBACtB,mBAAmB,EAAE,EAAE;iBACxB;aACF;SACF;QACD,KAAK,EAAE;YACL,kBAAkB,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,CAAC;YACvF,UAAU,EAAE;gBACV,QAAQ,EAAE;oBACR,kBAAkB,EAAE,GAAG;oBACvB,mBAAmB,EAAE,GAAG;iBACzB;aACF;YACD,iBAAiB,EAAE,EAAE;YACrB,SAAS,EAAE,IAAI;SAChB;QACD,QAAQ,EAAE;YACR,eAAe,EAAE,QAAQ;YACzB,eAAe,EAAE,IAAI;YACrB,MAAM,EAAE;gBACN,OAAO,EAAE,YAAY;gBACrB,MAAM,EAAE,uBAAuB;gBAC/B,qBAAqB,EAAE,4CAA4C;gBACnE,mBAAmB,EAAE,4CAA4C;gBACjE,qBAAqB,EAAE,GAAG;aAC3B;SACF;QACD,OAAO,EAAE;YACP,cAAc,EAAE,EAAE;SACnB;KACF,CAAC;AACJ,CAAC"}
|
package/dist/plugins/loader.js
CHANGED
|
@@ -64,7 +64,7 @@ export function buildPluginConfig(configKeys, runtimeOverrides, instanceConfig)
|
|
|
64
64
|
}
|
|
65
65
|
/** Map legacy package names to current names */
|
|
66
66
|
export const LEGACY_PACKAGE_MAP = {
|
|
67
|
-
'antseed-provider-anthropic': '@antseed/provider-
|
|
68
|
-
'antseed-router-claude-code': '@antseed/router-local
|
|
67
|
+
'antseed-provider-anthropic': '@antseed/provider-anthropic',
|
|
68
|
+
'antseed-router-claude-code': '@antseed/router-local',
|
|
69
69
|
};
|
|
70
70
|
//# sourceMappingURL=loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,eAAe,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,eAAe,EAAE,aAAa,EA+B1C,CAAA"}
|
package/dist/plugins/registry.js
CHANGED
|
@@ -12,10 +12,10 @@ export const TRUSTED_PLUGINS = [
|
|
|
12
12
|
package: '@antseed/provider-claude-code',
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
|
-
name: '
|
|
15
|
+
name: 'openai',
|
|
16
16
|
type: 'provider',
|
|
17
|
-
description: '
|
|
18
|
-
package: '@antseed/provider-
|
|
17
|
+
description: 'OpenAI-compatible provider (OpenAI, Together, OpenRouter, API key)',
|
|
18
|
+
package: '@antseed/provider-openai',
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
name: 'local-llm',
|
|
@@ -24,16 +24,10 @@ export const TRUSTED_PLUGINS = [
|
|
|
24
24
|
package: '@antseed/provider-local-llm',
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
name: 'local
|
|
27
|
+
name: 'local',
|
|
28
28
|
type: 'router',
|
|
29
|
-
description: 'Local
|
|
30
|
-
package: '@antseed/router-local
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'local-chat',
|
|
34
|
-
type: 'router',
|
|
35
|
-
description: 'Local desktop chat router',
|
|
36
|
-
package: '@antseed/router-local-chat',
|
|
29
|
+
description: 'Local router for Claude Code, Aider, Codex',
|
|
30
|
+
package: '@antseed/router-local',
|
|
37
31
|
},
|
|
38
32
|
];
|
|
39
33
|
//# sourceMappingURL=registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,kCAAkC;QAC/C,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,8CAA8C;QAC3D,OAAO,EAAE,+BAA+B;KACzC;IACD;QACE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,kCAAkC;QAC/C,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,8CAA8C;QAC3D,OAAO,EAAE,+BAA+B;KACzC;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,oEAAoE;QACjF,OAAO,EAAE,0BAA0B;KACpC;IACD;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,wCAAwC;QACrD,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,4CAA4C;QACzD,OAAO,EAAE,uBAAuB;KACjC;CACF,CAAA"}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import type { AntseedNode } from '@antseed/node';
|
|
1
|
+
import type { AntseedNode, PeerInfo } from '@antseed/node';
|
|
2
|
+
import { type ModelApiProtocol, type TargetProtocolSelection } from './model-api-adapter.js';
|
|
2
3
|
export interface BuyerProxyConfig {
|
|
3
4
|
port: number;
|
|
4
5
|
node: AntseedNode;
|
|
5
6
|
/** How long to cache discovered peers before re-querying DHT (ms). Default: 30000 */
|
|
6
7
|
peerCacheTtlMs?: number;
|
|
7
8
|
}
|
|
9
|
+
type PeerProtocolRoutePlan = {
|
|
10
|
+
provider: string;
|
|
11
|
+
selection: TargetProtocolSelection | null;
|
|
12
|
+
};
|
|
13
|
+
export type CandidatePeerRouteSelection = {
|
|
14
|
+
candidatePeers: PeerInfo[];
|
|
15
|
+
routePlanByPeerId: Map<string, PeerProtocolRoutePlan>;
|
|
16
|
+
};
|
|
17
|
+
export declare function selectCandidatePeersForRouting(peers: PeerInfo[], requestProtocol: ModelApiProtocol | null, requestedModel: string | null, explicitProvider: string | null): CandidatePeerRouteSelection;
|
|
8
18
|
/**
|
|
9
19
|
* Local HTTP proxy that forwards requests to P2P sellers.
|
|
10
20
|
*
|
|
@@ -27,4 +37,5 @@ export declare class BuyerProxy {
|
|
|
27
37
|
private _formatPeerSelectionDiagnostics;
|
|
28
38
|
private _handleRequest;
|
|
29
39
|
}
|
|
40
|
+
export {};
|
|
30
41
|
//# sourceMappingURL=buyer-proxy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buyer-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/buyer-proxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"buyer-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/buyer-proxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAKT,MAAM,eAAe,CAAA;AACtB,OAAO,EAGL,KAAK,gBAAgB,EAErB,KAAK,uBAAuB,EAG7B,MAAM,wBAAwB,CAAA;AAE/B,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AA+BD,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,uBAAuB,GAAG,IAAI,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,cAAc,EAAE,QAAQ,EAAE,CAAA;IAC1B,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;CACtD,CAAA;AA0ED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,QAAQ,EAAE,EACjB,eAAe,EAAE,gBAAgB,GAAG,IAAI,EACxC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAC9B,2BAA2B,CAoB7B;AAwWD;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAExC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,eAAe,CAAI;gBAEf,MAAM,EAAE,gBAAgB;IAe9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAUtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAMb,wBAAwB;YAsDxB,SAAS;IAkCvB,OAAO,CAAC,+BAA+B;YAuBzB,cAAc;CAiQ7B"}
|
|
@@ -3,12 +3,81 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
|
+
import { detectRequestModelApiProtocol, inferProviderDefaultModelApiProtocols, selectTargetProtocolForRequest, transformAnthropicMessagesRequestToOpenAIChat, transformOpenAIChatResponseToAnthropicMessage, } from './model-api-adapter.js';
|
|
6
7
|
const DAEMON_STATE_FILE = join(homedir(), '.antseed', 'daemon.state.json');
|
|
7
8
|
const DEBUG = () => ['1', 'true', 'yes', 'on'].includes((process.env['ANTSEED_DEBUG'] ?? '').trim().toLowerCase());
|
|
8
9
|
function log(...args) {
|
|
9
10
|
if (DEBUG())
|
|
10
11
|
console.log('[proxy]', ...args);
|
|
11
12
|
}
|
|
13
|
+
function getExplicitProviderOverride(request) {
|
|
14
|
+
const provider = request.headers['x-antseed-provider']?.trim().toLowerCase();
|
|
15
|
+
return provider && provider.length > 0 ? provider : null;
|
|
16
|
+
}
|
|
17
|
+
function getPeerProviderProtocols(peer, provider, requestedModel) {
|
|
18
|
+
const fromMetadata = peer.providerModelApiProtocols?.[provider]?.models;
|
|
19
|
+
if (fromMetadata) {
|
|
20
|
+
if (requestedModel && fromMetadata[requestedModel]?.length) {
|
|
21
|
+
return Array.from(new Set(fromMetadata[requestedModel]));
|
|
22
|
+
}
|
|
23
|
+
const merged = Object.values(fromMetadata).flat();
|
|
24
|
+
if (merged.length > 0) {
|
|
25
|
+
return Array.from(new Set(merged));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return inferProviderDefaultModelApiProtocols(provider);
|
|
29
|
+
}
|
|
30
|
+
function resolvePeerRoutePlan(peer, requestProtocol, requestedModel, explicitProvider) {
|
|
31
|
+
const providers = peer.providers
|
|
32
|
+
.map((provider) => provider.trim().toLowerCase())
|
|
33
|
+
.filter((provider) => provider.length > 0);
|
|
34
|
+
if (providers.length === 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (explicitProvider && !providers.includes(explicitProvider)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const candidates = explicitProvider ? [explicitProvider] : providers;
|
|
41
|
+
if (!requestProtocol) {
|
|
42
|
+
const provider = candidates[0];
|
|
43
|
+
return provider ? { provider, selection: null } : null;
|
|
44
|
+
}
|
|
45
|
+
let transformedFallback = null;
|
|
46
|
+
for (const provider of candidates) {
|
|
47
|
+
const supportedProtocols = getPeerProviderProtocols(peer, provider, requestedModel);
|
|
48
|
+
const selection = selectTargetProtocolForRequest(requestProtocol, supportedProtocols);
|
|
49
|
+
if (!selection) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!selection.requiresTransform) {
|
|
53
|
+
return { provider, selection };
|
|
54
|
+
}
|
|
55
|
+
if (!transformedFallback) {
|
|
56
|
+
transformedFallback = { provider, selection };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return transformedFallback;
|
|
60
|
+
}
|
|
61
|
+
export function selectCandidatePeersForRouting(peers, requestProtocol, requestedModel, explicitProvider) {
|
|
62
|
+
const routePlanByPeerId = new Map();
|
|
63
|
+
if (!requestProtocol && !explicitProvider) {
|
|
64
|
+
return {
|
|
65
|
+
candidatePeers: peers,
|
|
66
|
+
routePlanByPeerId,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const candidatePeers = peers.filter((peer) => {
|
|
70
|
+
const plan = resolvePeerRoutePlan(peer, requestProtocol, requestedModel, explicitProvider);
|
|
71
|
+
if (!plan)
|
|
72
|
+
return false;
|
|
73
|
+
routePlanByPeerId.set(peer.peerId, plan);
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
candidatePeers,
|
|
78
|
+
routePlanByPeerId,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
12
81
|
function parseTokenCount(value) {
|
|
13
82
|
const parsed = Number(value);
|
|
14
83
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
@@ -130,9 +199,9 @@ function parseJsonUsage(body) {
|
|
|
130
199
|
}
|
|
131
200
|
}
|
|
132
201
|
function pickProviderForPeer(peer, request) {
|
|
133
|
-
const explicit = request
|
|
134
|
-
if (explicit
|
|
135
|
-
return explicit
|
|
202
|
+
const explicit = getExplicitProviderOverride(request);
|
|
203
|
+
if (explicit) {
|
|
204
|
+
return explicit;
|
|
136
205
|
}
|
|
137
206
|
if (request.path.startsWith('/v1/messages') && peer.providers.includes('anthropic')) {
|
|
138
207
|
return 'anthropic';
|
|
@@ -160,33 +229,42 @@ function extractRequestedModel(request) {
|
|
|
160
229
|
return null;
|
|
161
230
|
}
|
|
162
231
|
}
|
|
232
|
+
function toFiniteNumberOrNull(value) {
|
|
233
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
234
|
+
}
|
|
235
|
+
function setFiniteNumberHeader(headers, name, value) {
|
|
236
|
+
const finite = toFiniteNumberOrNull(value);
|
|
237
|
+
if (finite !== null) {
|
|
238
|
+
headers[name] = String(finite);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function setPeerIdentityHeaders(headers, selectedPeer) {
|
|
242
|
+
headers['x-antseed-peer-id'] = selectedPeer.peerId;
|
|
243
|
+
if (selectedPeer.publicAddress) {
|
|
244
|
+
headers['x-antseed-peer-address'] = selectedPeer.publicAddress;
|
|
245
|
+
}
|
|
246
|
+
if (selectedPeer.providers.length > 0) {
|
|
247
|
+
headers['x-antseed-peer-providers'] = selectedPeer.providers.join(',');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
163
250
|
function resolvePeerPricing(peer, provider, model) {
|
|
164
251
|
const providerPricing = peer.providerPricing?.[provider];
|
|
165
252
|
if (providerPricing) {
|
|
166
|
-
|
|
253
|
+
const modelPricing = model ? providerPricing.models?.[model] : undefined;
|
|
254
|
+
if (modelPricing) {
|
|
167
255
|
return {
|
|
168
|
-
inputUsdPerMillion:
|
|
169
|
-
|
|
170
|
-
: null,
|
|
171
|
-
outputUsdPerMillion: Number.isFinite(providerPricing.models[model].outputUsdPerMillion)
|
|
172
|
-
? providerPricing.models[model].outputUsdPerMillion
|
|
173
|
-
: null,
|
|
256
|
+
inputUsdPerMillion: toFiniteNumberOrNull(modelPricing.inputUsdPerMillion),
|
|
257
|
+
outputUsdPerMillion: toFiniteNumberOrNull(modelPricing.outputUsdPerMillion),
|
|
174
258
|
};
|
|
175
259
|
}
|
|
176
260
|
return {
|
|
177
|
-
inputUsdPerMillion:
|
|
178
|
-
|
|
179
|
-
: null,
|
|
180
|
-
outputUsdPerMillion: Number.isFinite(providerPricing.defaults.outputUsdPerMillion)
|
|
181
|
-
? providerPricing.defaults.outputUsdPerMillion
|
|
182
|
-
: null,
|
|
261
|
+
inputUsdPerMillion: toFiniteNumberOrNull(providerPricing.defaults.inputUsdPerMillion),
|
|
262
|
+
outputUsdPerMillion: toFiniteNumberOrNull(providerPricing.defaults.outputUsdPerMillion),
|
|
183
263
|
};
|
|
184
264
|
}
|
|
185
|
-
const input = peer.defaultInputUsdPerMillion;
|
|
186
|
-
const output = peer.defaultOutputUsdPerMillion;
|
|
187
265
|
return {
|
|
188
|
-
inputUsdPerMillion:
|
|
189
|
-
outputUsdPerMillion:
|
|
266
|
+
inputUsdPerMillion: toFiniteNumberOrNull(peer.defaultInputUsdPerMillion),
|
|
267
|
+
outputUsdPerMillion: toFiniteNumberOrNull(peer.defaultOutputUsdPerMillion),
|
|
190
268
|
};
|
|
191
269
|
}
|
|
192
270
|
function computeResponseTelemetry(request, responseHeaders, responseBody, selectedPeer) {
|
|
@@ -232,35 +310,17 @@ function attachAntseedTelemetryHeaders(upstreamHeaders, selectedPeer, telemetry,
|
|
|
232
310
|
const headers = { ...upstreamHeaders };
|
|
233
311
|
headers['x-antseed-request-id'] = requestId;
|
|
234
312
|
headers['x-antseed-latency-ms'] = String(Math.max(0, Math.floor(latencyMs)));
|
|
235
|
-
headers
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
headers['x-antseed-peer-providers'] = selectedPeer.providers.join(',');
|
|
241
|
-
}
|
|
242
|
-
if (typeof selectedPeer.reputationScore === 'number' && Number.isFinite(selectedPeer.reputationScore)) {
|
|
243
|
-
headers['x-antseed-peer-reputation'] = String(selectedPeer.reputationScore);
|
|
244
|
-
}
|
|
245
|
-
if (typeof selectedPeer.trustScore === 'number' && Number.isFinite(selectedPeer.trustScore)) {
|
|
246
|
-
headers['x-antseed-peer-trust-score'] = String(selectedPeer.trustScore);
|
|
247
|
-
}
|
|
248
|
-
if (typeof selectedPeer.currentLoad === 'number' && Number.isFinite(selectedPeer.currentLoad)) {
|
|
249
|
-
headers['x-antseed-peer-current-load'] = String(selectedPeer.currentLoad);
|
|
250
|
-
}
|
|
251
|
-
if (typeof selectedPeer.maxConcurrency === 'number' && Number.isFinite(selectedPeer.maxConcurrency)) {
|
|
252
|
-
headers['x-antseed-peer-max-concurrency'] = String(selectedPeer.maxConcurrency);
|
|
253
|
-
}
|
|
313
|
+
setPeerIdentityHeaders(headers, selectedPeer);
|
|
314
|
+
setFiniteNumberHeader(headers, 'x-antseed-peer-reputation', selectedPeer.reputationScore);
|
|
315
|
+
setFiniteNumberHeader(headers, 'x-antseed-peer-trust-score', selectedPeer.trustScore);
|
|
316
|
+
setFiniteNumberHeader(headers, 'x-antseed-peer-current-load', selectedPeer.currentLoad);
|
|
317
|
+
setFiniteNumberHeader(headers, 'x-antseed-peer-max-concurrency', selectedPeer.maxConcurrency);
|
|
254
318
|
headers['x-antseed-provider'] = telemetry.pricing.provider;
|
|
255
319
|
if (telemetry.pricing.model) {
|
|
256
320
|
headers['x-antseed-model'] = telemetry.pricing.model;
|
|
257
321
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
if (telemetry.pricing.outputUsdPerMillion !== null) {
|
|
262
|
-
headers['x-antseed-output-usd-per-million'] = String(telemetry.pricing.outputUsdPerMillion);
|
|
263
|
-
}
|
|
322
|
+
setFiniteNumberHeader(headers, 'x-antseed-input-usd-per-million', telemetry.pricing.inputUsdPerMillion);
|
|
323
|
+
setFiniteNumberHeader(headers, 'x-antseed-output-usd-per-million', telemetry.pricing.outputUsdPerMillion);
|
|
264
324
|
headers['x-antseed-token-source'] = telemetry.usage.source;
|
|
265
325
|
headers['x-antseed-input-tokens'] = String(telemetry.usage.inputTokens);
|
|
266
326
|
headers['x-antseed-output-tokens'] = String(telemetry.usage.outputTokens);
|
|
@@ -273,13 +333,7 @@ function attachAntseedTelemetryHeaders(upstreamHeaders, selectedPeer, telemetry,
|
|
|
273
333
|
function attachStreamingAntseedHeaders(upstreamHeaders, selectedPeer, requestId) {
|
|
274
334
|
const headers = { ...upstreamHeaders };
|
|
275
335
|
headers['x-antseed-request-id'] = requestId;
|
|
276
|
-
headers
|
|
277
|
-
if (selectedPeer.publicAddress) {
|
|
278
|
-
headers['x-antseed-peer-address'] = selectedPeer.publicAddress;
|
|
279
|
-
}
|
|
280
|
-
if (selectedPeer.providers.length > 0) {
|
|
281
|
-
headers['x-antseed-peer-providers'] = selectedPeer.providers.join(',');
|
|
282
|
-
}
|
|
336
|
+
setPeerIdentityHeaders(headers, selectedPeer);
|
|
283
337
|
return headers;
|
|
284
338
|
}
|
|
285
339
|
function requestWantsStreaming(headers, body) {
|
|
@@ -492,9 +546,26 @@ export class BuyerProxy {
|
|
|
492
546
|
res.end('No sellers available on the network. Is a seeder running?');
|
|
493
547
|
return;
|
|
494
548
|
}
|
|
549
|
+
const requestProtocol = detectRequestModelApiProtocol(serializedReq);
|
|
550
|
+
const requestedModel = extractRequestedModel(serializedReq);
|
|
551
|
+
const explicitProvider = getExplicitProviderOverride(serializedReq);
|
|
552
|
+
const { candidatePeers, routePlanByPeerId, } = selectCandidatePeersForRouting(peers, requestProtocol, requestedModel, explicitProvider);
|
|
553
|
+
if (candidatePeers.length === 0) {
|
|
554
|
+
const diagnostics = this._formatPeerSelectionDiagnostics(peers);
|
|
555
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
556
|
+
if (requestProtocol) {
|
|
557
|
+
const protocolLabel = requestProtocol;
|
|
558
|
+
const providerLabel = explicitProvider ? ` for provider "${explicitProvider}"` : '';
|
|
559
|
+
res.end(`No peers support ${protocolLabel}${providerLabel}. ${diagnostics}`);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
res.end(`No peers advertise provider "${explicitProvider}". ${diagnostics}`);
|
|
563
|
+
}
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
495
566
|
// Use router to select peer
|
|
496
567
|
const router = this._node.router;
|
|
497
|
-
const localPeers =
|
|
568
|
+
const localPeers = candidatePeers.filter((peer) => isLoopbackPeer(peer));
|
|
498
569
|
let selectedPeer = null;
|
|
499
570
|
if (localPeers.length > 0) {
|
|
500
571
|
selectedPeer = router
|
|
@@ -506,29 +577,76 @@ export class BuyerProxy {
|
|
|
506
577
|
}
|
|
507
578
|
if (!selectedPeer) {
|
|
508
579
|
selectedPeer = router
|
|
509
|
-
? router.selectPeer(serializedReq,
|
|
510
|
-
:
|
|
580
|
+
? router.selectPeer(serializedReq, candidatePeers)
|
|
581
|
+
: candidatePeers[0] ?? null;
|
|
511
582
|
}
|
|
512
583
|
if (!selectedPeer) {
|
|
513
|
-
const diagnostics = this._formatPeerSelectionDiagnostics(
|
|
584
|
+
const diagnostics = this._formatPeerSelectionDiagnostics(candidatePeers);
|
|
514
585
|
log('Router could not select a peer.', diagnostics);
|
|
515
586
|
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
516
587
|
res.end(`Router could not select a suitable peer. ${diagnostics}`);
|
|
517
588
|
return;
|
|
518
589
|
}
|
|
590
|
+
const selectedRoutePlan = routePlanByPeerId.get(selectedPeer.peerId)
|
|
591
|
+
?? resolvePeerRoutePlan(selectedPeer, requestProtocol, requestedModel, explicitProvider);
|
|
592
|
+
if (!selectedRoutePlan) {
|
|
593
|
+
const diagnostics = this._formatPeerSelectionDiagnostics(candidatePeers);
|
|
594
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
595
|
+
res.end(`No compatible provider route found for selected peer. ${diagnostics}`);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
let requestForPeer = {
|
|
599
|
+
...serializedReq,
|
|
600
|
+
headers: {
|
|
601
|
+
...serializedReq.headers,
|
|
602
|
+
'x-antseed-provider': selectedRoutePlan.provider,
|
|
603
|
+
},
|
|
604
|
+
};
|
|
605
|
+
let adaptResponse = null;
|
|
606
|
+
let forceDisableUpstreamStreaming = false;
|
|
607
|
+
if (selectedRoutePlan.selection?.requiresTransform) {
|
|
608
|
+
if (requestProtocol === 'anthropic-messages'
|
|
609
|
+
&& selectedRoutePlan.selection.targetProtocol === 'openai-chat-completions') {
|
|
610
|
+
log(`Applying protocol adapter anthropic-messages -> openai-chat-completions via provider "${selectedRoutePlan.provider}"`);
|
|
611
|
+
const transformed = transformAnthropicMessagesRequestToOpenAIChat(requestForPeer);
|
|
612
|
+
if (!transformed) {
|
|
613
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
614
|
+
res.end('Failed to transform Anthropic request for selected provider protocol');
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
requestForPeer = {
|
|
618
|
+
...transformed.request,
|
|
619
|
+
headers: {
|
|
620
|
+
...transformed.request.headers,
|
|
621
|
+
'x-antseed-provider': selectedRoutePlan.provider,
|
|
622
|
+
},
|
|
623
|
+
};
|
|
624
|
+
adaptResponse = (response) => transformOpenAIChatResponseToAnthropicMessage(response, {
|
|
625
|
+
streamRequested: transformed.streamRequested,
|
|
626
|
+
fallbackModel: transformed.requestedModel,
|
|
627
|
+
});
|
|
628
|
+
forceDisableUpstreamStreaming = true;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
632
|
+
res.end('Unsupported protocol transformation path');
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
519
636
|
log(`Routing to peer ${selectedPeer.peerId.slice(0, 12)}...`);
|
|
520
637
|
// Forward through P2P
|
|
521
|
-
const wantsStreaming =
|
|
638
|
+
const wantsStreaming = !forceDisableUpstreamStreaming
|
|
639
|
+
&& requestWantsStreaming(requestForPeer.headers, requestForPeer.body);
|
|
522
640
|
const startTime = Date.now();
|
|
523
641
|
try {
|
|
524
642
|
if (wantsStreaming) {
|
|
525
643
|
let streamed = false;
|
|
526
|
-
const response = await this._node.sendRequestStream(selectedPeer,
|
|
644
|
+
const response = await this._node.sendRequestStream(selectedPeer, requestForPeer, {
|
|
527
645
|
onResponseStart: (startResponse, metadata) => {
|
|
528
646
|
if (!metadata.streaming)
|
|
529
647
|
return;
|
|
530
648
|
streamed = true;
|
|
531
|
-
const streamingHeaders = attachStreamingAntseedHeaders(startResponse.headers, selectedPeer,
|
|
649
|
+
const streamingHeaders = attachStreamingAntseedHeaders(startResponse.headers, selectedPeer, requestForPeer.requestId);
|
|
532
650
|
res.writeHead(startResponse.statusCode, streamingHeaders);
|
|
533
651
|
if (startResponse.body.length > 0) {
|
|
534
652
|
res.write(Buffer.from(startResponse.body));
|
|
@@ -544,7 +662,7 @@ export class BuyerProxy {
|
|
|
544
662
|
});
|
|
545
663
|
const latencyMs = Date.now() - startTime;
|
|
546
664
|
log(`Response: ${response.statusCode} (${latencyMs}ms, ${response.body.length} bytes)`);
|
|
547
|
-
const telemetry = computeResponseTelemetry(
|
|
665
|
+
const telemetry = computeResponseTelemetry(requestForPeer, response.headers, response.body, selectedPeer);
|
|
548
666
|
if (router) {
|
|
549
667
|
router.onResult(selectedPeer, {
|
|
550
668
|
success: response.statusCode < 500,
|
|
@@ -558,17 +676,20 @@ export class BuyerProxy {
|
|
|
558
676
|
}
|
|
559
677
|
}
|
|
560
678
|
else {
|
|
561
|
-
const responseHeaders = attachAntseedTelemetryHeaders(response.headers, selectedPeer, telemetry,
|
|
679
|
+
const responseHeaders = attachAntseedTelemetryHeaders(response.headers, selectedPeer, telemetry, requestForPeer.requestId, latencyMs);
|
|
562
680
|
res.writeHead(response.statusCode, responseHeaders);
|
|
563
681
|
res.end(Buffer.from(response.body));
|
|
564
682
|
}
|
|
565
683
|
}
|
|
566
684
|
else {
|
|
567
|
-
|
|
685
|
+
let response = await this._node.sendRequest(selectedPeer, requestForPeer);
|
|
686
|
+
if (adaptResponse) {
|
|
687
|
+
response = adaptResponse(response);
|
|
688
|
+
}
|
|
568
689
|
const latencyMs = Date.now() - startTime;
|
|
569
690
|
log(`Response: ${response.statusCode} (${latencyMs}ms, ${response.body.length} bytes)`);
|
|
570
|
-
const telemetry = computeResponseTelemetry(
|
|
571
|
-
const responseHeaders = attachAntseedTelemetryHeaders(response.headers, selectedPeer, telemetry,
|
|
691
|
+
const telemetry = computeResponseTelemetry(requestForPeer, response.headers, response.body, selectedPeer);
|
|
692
|
+
const responseHeaders = attachAntseedTelemetryHeaders(response.headers, selectedPeer, telemetry, requestForPeer.requestId, latencyMs);
|
|
572
693
|
// Report result to router for learning
|
|
573
694
|
if (router) {
|
|
574
695
|
router.onResult(selectedPeer, {
|