@antseed/cli 0.1.24 → 0.1.26

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 (42) hide show
  1. package/README.md +19 -19
  2. package/dist/cli/commands/browse.d.ts +1 -1
  3. package/dist/cli/commands/browse.js +4 -4
  4. package/dist/cli/commands/browse.js.map +1 -1
  5. package/dist/cli/commands/connection.js +21 -21
  6. package/dist/cli/commands/connection.js.map +1 -1
  7. package/dist/cli/commands/plugin-create.js +1 -1
  8. package/dist/cli/commands/seed.d.ts.map +1 -1
  9. package/dist/cli/commands/seed.js +40 -24
  10. package/dist/cli/commands/seed.js.map +1 -1
  11. package/dist/cli/commands/seed.test.js +6 -6
  12. package/dist/cli/commands/seed.test.js.map +1 -1
  13. package/dist/config/loader.js +21 -21
  14. package/dist/config/loader.js.map +1 -1
  15. package/dist/config/loader.test.js +12 -12
  16. package/dist/config/loader.test.js.map +1 -1
  17. package/dist/config/types.d.ts +17 -9
  18. package/dist/config/types.d.ts.map +1 -1
  19. package/dist/config/validation.js +14 -14
  20. package/dist/config/validation.js.map +1 -1
  21. package/dist/proxy/buyer-proxy.d.ts +7 -7
  22. package/dist/proxy/buyer-proxy.d.ts.map +1 -1
  23. package/dist/proxy/buyer-proxy.js +99 -75
  24. package/dist/proxy/buyer-proxy.js.map +1 -1
  25. package/dist/proxy/buyer-proxy.test.js +6 -6
  26. package/dist/proxy/buyer-proxy.test.js.map +1 -1
  27. package/dist/proxy/service-api-adapter.d.ts +2 -0
  28. package/dist/proxy/service-api-adapter.d.ts.map +1 -0
  29. package/dist/proxy/service-api-adapter.js +5 -0
  30. package/dist/proxy/service-api-adapter.js.map +1 -0
  31. package/dist/proxy/service-api-adapter.test.d.ts +2 -0
  32. package/dist/proxy/service-api-adapter.test.d.ts.map +1 -0
  33. package/dist/proxy/{model-api-adapter.test.js → service-api-adapter.test.js} +2 -2
  34. package/dist/proxy/service-api-adapter.test.js.map +1 -0
  35. package/package.json +4 -4
  36. package/dist/proxy/model-api-adapter.d.ts +0 -20
  37. package/dist/proxy/model-api-adapter.d.ts.map +0 -1
  38. package/dist/proxy/model-api-adapter.js +0 -535
  39. package/dist/proxy/model-api-adapter.js.map +0 -1
  40. package/dist/proxy/model-api-adapter.test.d.ts +0 -2
  41. package/dist/proxy/model-api-adapter.test.d.ts.map +0 -1
  42. package/dist/proxy/model-api-adapter.test.js.map +0 -1
@@ -4,7 +4,7 @@ import { watch } from 'node:fs';
4
4
  import { readFile, writeFile, rename, mkdir } from 'node:fs/promises';
5
5
  import { join } from 'node:path';
6
6
  import { homedir } from 'node:os';
7
- import { detectRequestModelApiProtocol, inferProviderDefaultModelApiProtocols, selectTargetProtocolForRequest, transformAnthropicMessagesRequestToOpenAIChat, transformOpenAIChatResponseToAnthropicMessage, } from './model-api-adapter.js';
7
+ import { detectRequestServiceApiProtocol, inferProviderDefaultServiceApiProtocols, selectTargetProtocolForRequest, transformAnthropicMessagesRequestToOpenAIChat, transformOpenAIChatResponseToAnthropicMessage, transformOpenAIResponsesRequestToOpenAIChat, transformOpenAIChatResponseToOpenAIResponses, } from './service-api-adapter.js';
8
8
  const DAEMON_STATE_FILE = join(homedir(), '.antseed', 'daemon.state.json');
9
9
  const BUYER_STATE_FILE = join(homedir(), '.antseed', 'buyer.state.json');
10
10
  const DEBUG = () => ['1', 'true', 'yes', 'on'].includes((process.env['ANTSEED_DEBUG'] ?? '').trim().toLowerCase());
@@ -13,9 +13,9 @@ function log(...args) {
13
13
  console.log('[proxy]', ...args);
14
14
  }
15
15
  const CLAUDE_PROVIDER_PREFERENCE = ['claude-oauth', 'anthropic', 'claude-code'];
16
- function inferPreferredProvidersForRequest(requestProtocol, requestedModel) {
17
- const model = requestedModel?.trim().toLowerCase() ?? '';
18
- if (model.length === 0) {
16
+ function inferPreferredProvidersForRequest(requestProtocol, requestedService) {
17
+ const service = requestedService?.trim().toLowerCase() ?? '';
18
+ if (service.length === 0) {
19
19
  return [];
20
20
  }
21
21
  const providers = [];
@@ -26,11 +26,11 @@ function inferPreferredProvidersForRequest(requestProtocol, requestedModel) {
26
26
  }
27
27
  providers.push(provider);
28
28
  };
29
- const slashIndex = model.indexOf('/');
29
+ const slashIndex = service.indexOf('/');
30
30
  if (slashIndex > 0) {
31
- pushProvider(model.slice(0, slashIndex));
31
+ pushProvider(service.slice(0, slashIndex));
32
32
  }
33
- if (requestProtocol === 'anthropic-messages' || model.startsWith('claude-') || model.includes('claude')) {
33
+ if (requestProtocol === 'anthropic-messages' || service.startsWith('claude-') || service.includes('claude')) {
34
34
  for (const provider of CLAUDE_PROVIDER_PREFERENCE) {
35
35
  pushProvider(provider);
36
36
  }
@@ -55,36 +55,36 @@ function getPreferredPeerIdHint(request) {
55
55
  }
56
56
  return header;
57
57
  }
58
- function getPeerProviderProtocols(peer, provider, requestedModel) {
59
- const normalizedRequestedModel = requestedModel?.trim();
60
- const fromMetadata = peer.providerModelApiProtocols?.[provider]?.models;
58
+ function getPeerProviderProtocols(peer, provider, requestedService) {
59
+ const normalizedRequestedService = requestedService?.trim();
60
+ const fromMetadata = peer.providerServiceApiProtocols?.[provider]?.services;
61
61
  if (fromMetadata) {
62
- if (normalizedRequestedModel) {
63
- const directMatchKey = Object.keys(fromMetadata).find((model) => model.toLowerCase() === normalizedRequestedModel.toLowerCase());
62
+ if (normalizedRequestedService) {
63
+ const directMatchKey = Object.keys(fromMetadata).find((key) => key.toLowerCase() === normalizedRequestedService.toLowerCase());
64
64
  if (directMatchKey && fromMetadata[directMatchKey]?.length) {
65
- log(`Model match: peer ${peer.peerId.slice(0, 8)} provider=${provider} model="${normalizedRequestedModel}" `
65
+ log(`Service match: peer ${peer.peerId.slice(0, 8)} provider=${provider} service="${normalizedRequestedService}" `
66
66
  + `→ [${fromMetadata[directMatchKey].join(',')}]`);
67
67
  return Array.from(new Set(fromMetadata[directMatchKey]));
68
68
  }
69
69
  if (Object.keys(fromMetadata).length > 0) {
70
- log(`Model strict-miss: peer ${peer.peerId.slice(0, 8)} provider=${provider} model="${normalizedRequestedModel}" `
70
+ log(`Service strict-miss: peer ${peer.peerId.slice(0, 8)} provider=${provider} service="${normalizedRequestedService}" `
71
71
  + 'not in metadata; excluding from route candidates.');
72
72
  return [];
73
73
  }
74
74
  }
75
75
  const merged = Object.values(fromMetadata).flat();
76
76
  if (merged.length > 0) {
77
- if (requestedModel) {
78
- log(`Model hint miss: peer ${peer.peerId.slice(0, 8)} provider=${provider} model="${requestedModel}" not in metadata; falling back to provider protocol set [${Array.from(new Set(merged)).join(',')}]`);
77
+ if (requestedService) {
78
+ log(`Service hint miss: peer ${peer.peerId.slice(0, 8)} provider=${provider} service="${requestedService}" not in metadata; falling back to provider protocol set [${Array.from(new Set(merged)).join(',')}]`);
79
79
  }
80
80
  return Array.from(new Set(merged));
81
81
  }
82
82
  }
83
- const inferred = inferProviderDefaultModelApiProtocols(provider);
83
+ const inferred = inferProviderDefaultServiceApiProtocols(provider);
84
84
  log(`No metadata: peer ${peer.peerId.slice(0, 8)} provider=${provider} → inferred [${inferred.join(',')}]`);
85
85
  return inferred;
86
86
  }
87
- function resolvePeerRoutePlan(peer, requestProtocol, requestedModel, explicitProvider) {
87
+ function resolvePeerRoutePlan(peer, requestProtocol, requestedService, explicitProvider) {
88
88
  const providers = peer.providers
89
89
  .map((provider) => provider.trim().toLowerCase())
90
90
  .filter((provider) => provider.length > 0);
@@ -101,7 +101,7 @@ function resolvePeerRoutePlan(peer, requestProtocol, requestedModel, explicitPro
101
101
  }
102
102
  let transformedFallback = null;
103
103
  for (const provider of candidates) {
104
- const supportedProtocols = getPeerProviderProtocols(peer, provider, requestedModel);
104
+ const supportedProtocols = getPeerProviderProtocols(peer, provider, requestedService);
105
105
  const selection = selectTargetProtocolForRequest(requestProtocol, supportedProtocols);
106
106
  if (!selection) {
107
107
  continue;
@@ -115,7 +115,7 @@ function resolvePeerRoutePlan(peer, requestProtocol, requestedModel, explicitPro
115
115
  }
116
116
  return transformedFallback;
117
117
  }
118
- export function selectCandidatePeersForRouting(peers, requestProtocol, requestedModel, explicitProvider) {
118
+ export function selectCandidatePeersForRouting(peers, requestProtocol, requestedService, explicitProvider) {
119
119
  const routePlanByPeerId = new Map();
120
120
  if (!requestProtocol && !explicitProvider) {
121
121
  return {
@@ -124,7 +124,7 @@ export function selectCandidatePeersForRouting(peers, requestProtocol, requested
124
124
  };
125
125
  }
126
126
  const candidatePeers = peers.filter((peer) => {
127
- const plan = resolvePeerRoutePlan(peer, requestProtocol, requestedModel, explicitProvider);
127
+ const plan = resolvePeerRoutePlan(peer, requestProtocol, requestedService, explicitProvider);
128
128
  if (!plan)
129
129
  return false;
130
130
  routePlanByPeerId.set(peer.peerId, plan);
@@ -269,16 +269,16 @@ function pickProviderForPeer(peer, request) {
269
269
  }
270
270
  return 'unknown';
271
271
  }
272
- function extractRequestedModel(request) {
272
+ function extractRequestedService(request) {
273
273
  const contentType = (request.headers['content-type'] ?? request.headers['Content-Type'] ?? '').toLowerCase();
274
274
  if (!contentType.includes('application/json')) {
275
275
  return null;
276
276
  }
277
277
  try {
278
278
  const parsed = JSON.parse(new TextDecoder().decode(request.body));
279
- const model = parsed.model;
280
- if (typeof model === 'string' && model.trim().length > 0) {
281
- return model.trim();
279
+ const service = parsed.service ?? parsed.model;
280
+ if (typeof service === 'string' && service.trim().length > 0) {
281
+ return service.trim();
282
282
  }
283
283
  return null;
284
284
  }
@@ -374,7 +374,7 @@ function summarizeRequestShape(request) {
374
374
  const accept = (request.headers['accept'] ?? request.headers['Accept'] ?? '').toLowerCase();
375
375
  const providerHeader = request.headers['x-antseed-provider'] ?? 'none';
376
376
  const preferPeerHeader = request.headers['x-antseed-prefer-peer'] ?? 'none';
377
- const model = extractRequestedModel(request) ?? 'none';
377
+ const service = extractRequestedService(request) ?? 'none';
378
378
  const wantsStreaming = requestWantsStreaming(request.headers, request.body);
379
379
  const baseParts = [
380
380
  `method=${request.method}`,
@@ -384,7 +384,7 @@ function summarizeRequestShape(request) {
384
384
  `contentType=${contentType || 'none'}`,
385
385
  `accept=${accept || 'none'}`,
386
386
  `stream=${String(wantsStreaming)}`,
387
- `model=${model}`,
387
+ `service=${service}`,
388
388
  `bodyBytes=${String(request.body.length)}`,
389
389
  ];
390
390
  const jsonBody = decodeJsonBody(request.body);
@@ -460,14 +460,14 @@ function setPeerIdentityHeaders(headers, selectedPeer) {
460
460
  headers['x-antseed-peer-providers'] = selectedPeer.providers.join(',');
461
461
  }
462
462
  }
463
- function resolvePeerPricing(peer, provider, model) {
463
+ function resolvePeerPricing(peer, provider, service) {
464
464
  const providerPricing = peer.providerPricing?.[provider];
465
465
  if (providerPricing) {
466
- const modelPricing = model ? providerPricing.models?.[model] : undefined;
467
- if (modelPricing) {
466
+ const servicePricing = service ? providerPricing.services?.[service] : undefined;
467
+ if (servicePricing) {
468
468
  return {
469
- inputUsdPerMillion: toFiniteNumberOrNull(modelPricing.inputUsdPerMillion),
470
- outputUsdPerMillion: toFiniteNumberOrNull(modelPricing.outputUsdPerMillion),
469
+ inputUsdPerMillion: toFiniteNumberOrNull(servicePricing.inputUsdPerMillion),
470
+ outputUsdPerMillion: toFiniteNumberOrNull(servicePricing.outputUsdPerMillion),
471
471
  };
472
472
  }
473
473
  return {
@@ -482,8 +482,8 @@ function resolvePeerPricing(peer, provider, model) {
482
482
  }
483
483
  function computeResponseTelemetry(request, responseHeaders, responseBody, selectedPeer) {
484
484
  const provider = pickProviderForPeer(selectedPeer, request);
485
- const model = extractRequestedModel(request);
486
- const pricing = resolvePeerPricing(selectedPeer, provider, model);
485
+ const service = extractRequestedService(request);
486
+ const pricing = resolvePeerPricing(selectedPeer, provider, service);
487
487
  const contentType = (responseHeaders['content-type'] ?? '').toLowerCase();
488
488
  const usageFromBody = contentType.includes('text/event-stream')
489
489
  ? parseSseUsage(responseBody)
@@ -512,7 +512,7 @@ function computeResponseTelemetry(request, responseHeaders, responseBody, select
512
512
  usage,
513
513
  pricing: {
514
514
  provider,
515
- model,
515
+ service,
516
516
  inputUsdPerMillion: pricing.inputUsdPerMillion,
517
517
  outputUsdPerMillion: pricing.outputUsdPerMillion,
518
518
  },
@@ -529,8 +529,8 @@ function attachAntseedTelemetryHeaders(upstreamHeaders, selectedPeer, telemetry,
529
529
  setFiniteNumberHeader(headers, 'x-antseed-peer-current-load', selectedPeer.currentLoad);
530
530
  setFiniteNumberHeader(headers, 'x-antseed-peer-max-concurrency', selectedPeer.maxConcurrency);
531
531
  headers['x-antseed-provider'] = telemetry.pricing.provider;
532
- if (telemetry.pricing.model) {
533
- headers['x-antseed-model'] = telemetry.pricing.model;
532
+ if (telemetry.pricing.service) {
533
+ headers['x-antseed-service'] = telemetry.pricing.service;
534
534
  }
535
535
  setFiniteNumberHeader(headers, 'x-antseed-input-usd-per-million', telemetry.pricing.inputUsdPerMillion);
536
536
  setFiniteNumberHeader(headers, 'x-antseed-output-usd-per-million', telemetry.pricing.outputUsdPerMillion);
@@ -646,7 +646,7 @@ export class BuyerProxy {
646
646
  _bgRefreshIntervalMs;
647
647
  _peerCacheTtlMs;
648
648
  _pinnedPeer;
649
- _pinnedModel;
649
+ _pinnedService;
650
650
  _stateFileWatcher = null;
651
651
  _stateWatchDebounce = null;
652
652
  _cachedPeers = [];
@@ -663,7 +663,7 @@ export class BuyerProxy {
663
663
  this._bgRefreshIntervalMs = config.backgroundRefreshIntervalMs ?? 5 * 60_000;
664
664
  this._peerCacheTtlMs = Math.max(0, config.peerCacheTtlMs ?? 30_000);
665
665
  this._pinnedPeer = config.pinnedPeerId?.toLowerCase() ?? null;
666
- this._pinnedModel = config.pinnedModel?.trim() ?? null;
666
+ this._pinnedService = config.pinnedService?.trim() ?? null;
667
667
  this._server = createServer((req, res) => {
668
668
  this._handleRequest(req, res).catch((err) => {
669
669
  log('Unhandled error:', err);
@@ -726,15 +726,15 @@ export class BuyerProxy {
726
726
  try {
727
727
  const raw = await readFile(BUYER_STATE_FILE, 'utf-8');
728
728
  const parsed = JSON.parse(raw);
729
- const pinnedModel = typeof parsed.pinnedModel === 'string' && parsed.pinnedModel.trim().length > 0
730
- ? parsed.pinnedModel.trim()
729
+ const pinnedService = typeof parsed.pinnedService === 'string' && parsed.pinnedService.trim().length > 0
730
+ ? parsed.pinnedService.trim()
731
731
  : null;
732
732
  const pinnedPeer = typeof parsed.pinnedPeerId === 'string' && parsed.pinnedPeerId.trim().length > 0
733
733
  ? parsed.pinnedPeerId.trim().toLowerCase()
734
734
  : null;
735
- this._pinnedModel = pinnedModel;
735
+ this._pinnedService = pinnedService;
736
736
  this._pinnedPeer = pinnedPeer;
737
- log(`Session overrides reloaded: model=${pinnedModel ?? 'none'} peer=${pinnedPeer ?? 'none'}`);
737
+ log(`Session overrides reloaded: service=${pinnedService ?? 'none'} peer=${pinnedPeer ?? 'none'}`);
738
738
  }
739
739
  catch {
740
740
  // state file unreadable; keep current values
@@ -752,11 +752,11 @@ export class BuyerProxy {
752
752
  catch {
753
753
  // file doesn't exist yet
754
754
  }
755
- // When stopping, preserve whatever pinnedModel/pinnedPeerId is already
755
+ // When stopping, preserve whatever pinnedService/pinnedPeerId is already
756
756
  // in the file — the debounce may have been cancelled before
757
757
  // _reloadSessionOverrides could commit the latest CLI-written values.
758
758
  const sessionOverrides = state === 'connected'
759
- ? { pinnedModel: this._pinnedModel, pinnedPeerId: this._pinnedPeer }
759
+ ? { pinnedService: this._pinnedService, pinnedPeerId: this._pinnedPeer }
760
760
  : {};
761
761
  const data = {
762
762
  ...existing,
@@ -819,19 +819,21 @@ export class BuyerProxy {
819
819
  }
820
820
  }
821
821
  }
822
- _buildRouteKey(path, requestProtocol, requestedModel, explicitProvider) {
822
+ _buildRouteKey(path, requestProtocol, requestedService, explicitProvider) {
823
823
  const normalizedPath = path.split('?')[0]?.trim().toLowerCase() ?? '/';
824
824
  const pathGroup = (normalizedPath.startsWith('/v1/messages')
825
825
  ? '/v1/messages'
826
826
  : normalizedPath.startsWith('/v1/chat/completions')
827
827
  ? '/v1/chat/completions'
828
- : normalizedPath.startsWith('/v1/models')
829
- ? '/v1/models'
830
- : normalizedPath);
828
+ : normalizedPath.startsWith('/v1/responses')
829
+ ? '/v1/responses'
830
+ : normalizedPath.startsWith('/v1/models')
831
+ ? '/v1/models'
832
+ : normalizedPath);
831
833
  return [
832
834
  pathGroup,
833
835
  requestProtocol ?? 'unknown-protocol',
834
- requestedModel ?? 'unknown-model',
836
+ requestedService ?? 'unknown-service',
835
837
  explicitProvider ?? 'auto-provider',
836
838
  ].join('|');
837
839
  }
@@ -989,14 +991,14 @@ export class BuyerProxy {
989
991
  body: new Uint8Array(body),
990
992
  };
991
993
  // Snapshot both session overrides together before any await so a concurrent
992
- // _reloadSessionOverrides() cannot produce a model/peer mismatch mid-request.
993
- const effectivePinnedModel = this._pinnedModel;
994
+ // _reloadSessionOverrides() cannot produce a service/peer mismatch mid-request.
995
+ const effectivePinnedService = this._pinnedService;
994
996
  const effectivePinnedPeer = this._pinnedPeer;
995
- if (effectivePinnedModel) {
996
- const { body: rewrittenBody, headers: rewrittenHeaders } = rewriteModelInBody(serializedReq.body, serializedReq.headers, effectivePinnedModel);
997
+ if (effectivePinnedService) {
998
+ const { body: rewrittenBody, headers: rewrittenHeaders } = rewriteModelInBody(serializedReq.body, serializedReq.headers, effectivePinnedService);
997
999
  if (rewrittenBody !== serializedReq.body) {
998
1000
  serializedReq = { ...serializedReq, body: rewrittenBody, headers: rewrittenHeaders };
999
- log(`Model override applied: ${effectivePinnedModel}`);
1001
+ log(`Service override applied: ${effectivePinnedService}`);
1000
1002
  }
1001
1003
  }
1002
1004
  const clientAbortController = new AbortController();
@@ -1025,15 +1027,15 @@ export class BuyerProxy {
1025
1027
  res.end('No sellers available on the network. Is a seeder running?');
1026
1028
  return;
1027
1029
  }
1028
- const requestProtocol = detectRequestModelApiProtocol(serializedReq);
1029
- const requestedModel = extractRequestedModel(serializedReq);
1030
- log(`Routing: protocol=${requestProtocol ?? 'null'} model=${requestedModel ?? 'null'}`);
1030
+ const requestProtocol = detectRequestServiceApiProtocol(serializedReq);
1031
+ const requestedService = extractRequestedService(serializedReq);
1032
+ log(`Routing: protocol=${requestProtocol ?? 'null'} service=${requestedService ?? 'null'}`);
1031
1033
  const explicitProvider = getExplicitProviderOverride(serializedReq);
1032
1034
  const explicitPeerId = getExplicitPeerIdOverride(serializedReq, effectivePinnedPeer ?? undefined);
1033
1035
  const preferredPeerId = getPreferredPeerIdHint(serializedReq);
1034
1036
  log(`Routing hints: provider=${explicitProvider ?? 'auto'} pin-peer=${explicitPeerId ?? 'none'} prefer-peer=${preferredPeerId ?? 'none'}`);
1035
- const routeKey = this._buildRouteKey(serializedReq.path, requestProtocol, requestedModel, explicitProvider);
1036
- const selectPeers = (candidateSources) => selectCandidatePeersForRouting(candidateSources, requestProtocol, requestedModel, explicitProvider);
1037
+ const routeKey = this._buildRouteKey(serializedReq.path, requestProtocol, requestedService, explicitProvider);
1038
+ const selectPeers = (candidateSources) => selectCandidatePeersForRouting(candidateSources, requestProtocol, requestedService, explicitProvider);
1037
1039
  let hasForcedRefresh = false;
1038
1040
  const refreshPeerSelection = async (reason) => {
1039
1041
  if (hasForcedRefresh) {
@@ -1069,7 +1071,7 @@ export class BuyerProxy {
1069
1071
  }
1070
1072
  const preferredProviders = explicitProvider
1071
1073
  ? []
1072
- : inferPreferredProvidersForRequest(requestProtocol, requestedModel);
1074
+ : inferPreferredProvidersForRequest(requestProtocol, requestedService);
1073
1075
  let hasPreferredProviderCandidate = preferredProviders.length > 0
1074
1076
  && routingPeers.some((peer) => {
1075
1077
  const provider = routingPlans.get(peer.peerId)?.provider?.trim().toLowerCase();
@@ -1108,9 +1110,9 @@ export class BuyerProxy {
1108
1110
  const peerDiscovered = discoveredPeers.some((peer) => peer.peerId.toLowerCase() === explicitPeerId);
1109
1111
  const protocolLabel = requestProtocol ? `protocol=${requestProtocol}` : 'protocol=unknown';
1110
1112
  const providerLabel = explicitProvider ? `provider=${explicitProvider}` : 'provider=auto';
1111
- const modelLabel = requestedModel ? `model=${requestedModel}` : 'model=none';
1113
+ const serviceLabel = requestedService ? `service=${requestedService}` : 'service=none';
1112
1114
  const mismatchHint = peerDiscovered
1113
- ? `Peer is discoverable but filtered as incompatible (${protocolLabel}, ${providerLabel}, ${modelLabel}).`
1115
+ ? `Peer is discoverable but filtered as incompatible (${protocolLabel}, ${providerLabel}, ${serviceLabel}).`
1114
1116
  : 'Peer is not discoverable right now.';
1115
1117
  log(`Pinned peer ${explicitPeerId.slice(0, 12)}... not found in candidate list (${source})`);
1116
1118
  res.writeHead(502, { 'content-type': 'text/plain' });
@@ -1118,7 +1120,7 @@ export class BuyerProxy {
1118
1120
  return;
1119
1121
  }
1120
1122
  log(`Using pinned peer ${selectedPeer.peerId.slice(0, 12)}...`);
1121
- const result = await this._dispatchToPeer(res, serializedReq, selectedPeer, routeKey, pinnedRoutePlans, requestProtocol, requestedModel, explicitProvider, router, RETRYABLE_STATUS_CODES, clientAbortController.signal);
1123
+ const result = await this._dispatchToPeer(res, serializedReq, selectedPeer, routeKey, pinnedRoutePlans, requestProtocol, requestedService, explicitProvider, router, RETRYABLE_STATUS_CODES, clientAbortController.signal);
1122
1124
  if (!result.done) {
1123
1125
  this._forgetSuccessfulPeer(routeKey, selectedPeer.peerId);
1124
1126
  // Pinned peer returned a retryable error, but we don't retry — send error to client
@@ -1167,7 +1169,7 @@ export class BuyerProxy {
1167
1169
  }
1168
1170
  }
1169
1171
  // Fallback to the latest globally successful peer.
1170
- if (!selectedPeer && attempt === 0 && this._lastSuccessfulPeerId && !requestedModel) {
1172
+ if (!selectedPeer && attempt === 0 && this._lastSuccessfulPeerId && !requestedService) {
1171
1173
  const remembered = availableCandidates.find((peer) => peer.peerId === this._lastSuccessfulPeerId) ?? null;
1172
1174
  if (remembered) {
1173
1175
  selectedPeer = remembered;
@@ -1182,7 +1184,7 @@ export class BuyerProxy {
1182
1184
  log(`Preferring requested peer ${selectedPeer.peerId.slice(0, 12)}...`);
1183
1185
  }
1184
1186
  }
1185
- // Strongly prefer providers that match the requested model family (e.g. claude-* -> claude/anthropic providers).
1187
+ // Strongly prefer providers that match the requested service family (e.g. claude-* -> claude/anthropic providers).
1186
1188
  if (!selectedPeer && attempt === 0 && preferredProviders.length > 0) {
1187
1189
  const providerMatchedPeers = availableCandidates.filter((peer) => {
1188
1190
  const plannedProvider = routingPlans.get(peer.peerId)?.provider?.trim().toLowerCase();
@@ -1194,7 +1196,7 @@ export class BuyerProxy {
1194
1196
  : providerMatchedPeers[0] ?? null;
1195
1197
  if (selectedPeer) {
1196
1198
  const plannedProvider = routingPlans.get(selectedPeer.peerId)?.provider ?? 'unknown';
1197
- log(`Preferring model-matched provider "${plannedProvider}" for model "${requestedModel ?? 'unknown'}"`);
1199
+ log(`Preferring service-matched provider "${plannedProvider}" for service "${requestedService ?? 'unknown'}"`);
1198
1200
  }
1199
1201
  }
1200
1202
  }
@@ -1212,7 +1214,7 @@ export class BuyerProxy {
1212
1214
  }
1213
1215
  // Prefer peers that can serve the request protocol directly without adapter transform.
1214
1216
  if (!selectedPeer && requestProtocol === 'anthropic-messages') {
1215
- const shouldPreferDirect = !requestedModel || /claude|anthropic/i.test(requestedModel);
1217
+ const shouldPreferDirect = !requestedService || /claude|anthropic/i.test(requestedService);
1216
1218
  if (shouldPreferDirect) {
1217
1219
  const directPeers = availableCandidates.filter((peer) => {
1218
1220
  const plan = routingPlans.get(peer.peerId);
@@ -1238,7 +1240,7 @@ export class BuyerProxy {
1238
1240
  if (!selectedPeer)
1239
1241
  break;
1240
1242
  triedPeerIds.add(selectedPeer.peerId);
1241
- const result = await this._dispatchToPeer(res, serializedReq, selectedPeer, routeKey, routingPlans, requestProtocol, requestedModel, explicitProvider, router, RETRYABLE_STATUS_CODES, clientAbortController.signal);
1243
+ const result = await this._dispatchToPeer(res, serializedReq, selectedPeer, routeKey, routingPlans, requestProtocol, requestedService, explicitProvider, router, RETRYABLE_STATUS_CODES, clientAbortController.signal);
1242
1244
  if (result.done)
1243
1245
  return;
1244
1246
  this._forgetSuccessfulPeer(routeKey, selectedPeer.peerId);
@@ -1270,9 +1272,9 @@ export class BuyerProxy {
1270
1272
  * was sent to the client (success or non-retryable error), or retry info if the
1271
1273
  * caller should try another peer.
1272
1274
  */
1273
- async _dispatchToPeer(res, serializedReq, selectedPeer, routeKey, routePlanByPeerId, requestProtocol, requestedModel, explicitProvider, router, retryableStatusCodes, requestSignal) {
1275
+ async _dispatchToPeer(res, serializedReq, selectedPeer, routeKey, routePlanByPeerId, requestProtocol, requestedService, explicitProvider, router, retryableStatusCodes, requestSignal) {
1274
1276
  const selectedRoutePlan = routePlanByPeerId.get(selectedPeer.peerId)
1275
- ?? resolvePeerRoutePlan(selectedPeer, requestProtocol, requestedModel, explicitProvider);
1277
+ ?? resolvePeerRoutePlan(selectedPeer, requestProtocol, requestedService, explicitProvider);
1276
1278
  if (!selectedRoutePlan) {
1277
1279
  return { done: false, statusCode: 502, responseBody: Buffer.from('No compatible provider route'), responseHeaders: { 'content-type': 'text/plain' }, errorMessage: null };
1278
1280
  }
@@ -1309,6 +1311,28 @@ export class BuyerProxy {
1309
1311
  });
1310
1312
  forceDisableUpstreamStreaming = true;
1311
1313
  }
1314
+ else if (requestProtocol === 'openai-responses'
1315
+ && selectedRoutePlan.selection.targetProtocol === 'openai-chat-completions') {
1316
+ log(`Applying protocol adapter openai-responses -> openai-chat-completions via provider "${selectedRoutePlan.provider}"`);
1317
+ const transformed = transformOpenAIResponsesRequestToOpenAIChat(requestForPeer);
1318
+ if (!transformed) {
1319
+ res.writeHead(502, { 'content-type': 'text/plain' });
1320
+ res.end('Failed to transform Responses API request for selected provider protocol');
1321
+ return { done: true };
1322
+ }
1323
+ requestForPeer = {
1324
+ ...transformed.request,
1325
+ headers: {
1326
+ ...transformed.request.headers,
1327
+ 'x-antseed-provider': selectedRoutePlan.provider,
1328
+ },
1329
+ };
1330
+ adaptResponse = (response) => transformOpenAIChatResponseToOpenAIResponses(response, {
1331
+ fallbackModel: transformed.requestedModel,
1332
+ streamRequested: transformed.streamRequested,
1333
+ });
1334
+ forceDisableUpstreamStreaming = true;
1335
+ }
1312
1336
  else {
1313
1337
  res.writeHead(502, { 'content-type': 'text/plain' });
1314
1338
  res.end('Unsupported protocol transformation path');
@@ -1464,11 +1488,11 @@ export class BuyerProxy {
1464
1488
  tokens: 0,
1465
1489
  });
1466
1490
  }
1467
- // Avoid poisoning routing cache from control-plane model enumeration failures.
1468
- // Some peers can time out on /v1/models while still serving inference paths.
1491
+ // Avoid poisoning routing cache from control-plane service enumeration failures.
1492
+ // Some peers can time out on /v1/models (service probe) while still serving inference paths.
1469
1493
  const normalizedPath = requestForPeer.path.toLowerCase();
1470
- const isControlPlaneModelsRequest = normalizedPath.startsWith('/v1/models');
1471
- if (isControlPlaneModelsRequest) {
1494
+ const isControlPlaneServicesRequest = normalizedPath.startsWith('/v1/models');
1495
+ if (isControlPlaneServicesRequest) {
1472
1496
  log(`Skipping peer eviction for control-plane failure on ${requestForPeer.path}`);
1473
1497
  }
1474
1498
  else if (connectionChurnError) {