@antseed/router-core 0.1.48 → 0.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { scoreCandidates, DEFAULT_WEIGHTS, type ScoringWeights, type ScoredCandidate, type TokenPricingUsdPerMillion, type PeerMetrics, } from './peer-scorer.js';
2
2
  export { PeerMetricsTracker, computeFailureCooldownMs, DEFAULT_MAX_FAILURES, DEFAULT_FAILURE_COOLDOWN_MS, MAX_COOLDOWN_DOUBLINGS, type PeerMetricsTrackerConfig, type FailureCooldownOptions, } from './peer-metrics.js';
3
3
  export { WELL_KNOWN_TOOL_HINTS, formatToolHints, type ToolHint, } from './tool-hints.js';
4
- export { candidatePricing, ModelRoutingClient, RouterCannotRankError, type ModelRoutingClientApi } from './model-routing-client.js';
4
+ export { ModelRoutingClient, routingProtocolOfTarget, type ModelRoutingClientApi } from './model-routing-client.js';
5
+ export { candidatePricing, findRoutingClientAdapter, InvalidRouterOptionsError, irpCandidateId, irpSuggestAdapter, RouterCannotRankError, routingClientAdapter, validateRouterOptions, type RankCandidate, type RouterOptionSpec, type RoutingClientAdapter, } from './routing-adapters/index.js';
5
6
  export { CacheObservations } from './cache-observations.js';
6
7
  export { routerModelResolver } from './router-model-names.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,KAAK,QAAQ,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AACnI,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,KAAK,QAAQ,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,KAAK,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AACnH,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  export { scoreCandidates, DEFAULT_WEIGHTS, } from './peer-scorer.js';
2
2
  export { PeerMetricsTracker, computeFailureCooldownMs, DEFAULT_MAX_FAILURES, DEFAULT_FAILURE_COOLDOWN_MS, MAX_COOLDOWN_DOUBLINGS, } from './peer-metrics.js';
3
3
  export { WELL_KNOWN_TOOL_HINTS, formatToolHints, } from './tool-hints.js';
4
- export { candidatePricing, ModelRoutingClient, RouterCannotRankError } from './model-routing-client.js';
4
+ export { ModelRoutingClient, routingProtocolOfTarget } from './model-routing-client.js';
5
+ export { candidatePricing, findRoutingClientAdapter, InvalidRouterOptionsError, irpCandidateId, irpSuggestAdapter, RouterCannotRankError, routingClientAdapter, validateRouterOptions, } from './routing-adapters/index.js';
5
6
  export { CacheObservations } from './cache-observations.js';
6
7
  export { routerModelResolver } from './router-model-names.js';
7
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,GAKhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,GAGvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,eAAe,GAEhB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,qBAAqB,EAA8B,MAAM,2BAA2B,CAAA;AACnI,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,GAKhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,GAGvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,eAAe,GAEhB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAA8B,MAAM,2BAA2B,CAAA;AACnH,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,GAItB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
@@ -1,33 +1,26 @@
1
- import { type PeerInfo, type RouteCandidate, type RouteRecommendation, type RouteSelectionContext, type RoutingModelsContext, type RoutingModelEntryV1, type RoutingPricingV1, type RoutingServiceTarget, type RoutingUsageObservation, type SerializedHttpRequest } from '@antseed/node';
1
+ import { type PeerInfo, type RouteRecommendation, type RouterModelEntry, type RouterOptions, type RouteSelectionContext, type RoutingModelsContext, type RoutingProtocol, type RoutingServiceTarget, type RoutingUsageObservation, type SerializedHttpRequest } from '@antseed/node';
2
2
  import { CacheObservations } from './cache-observations.js';
3
- /** IRP 422: no candidate could be scored. The host may refresh the model list and retry once. */
4
- export declare class RouterCannotRankError extends Error {
5
- constructor(message: string);
6
- }
7
- /**
8
- * IRP candidate `id`: `provider:model@peer`, readable in buyer and router logs. IDs longer than
9
- * 128 characters become a SHA-256 of the same string. The buyer always maps IDs back through the
10
- * candidates it sent and never parses them.
11
- */
12
- /** IRP candidate pricing; sellers without a cached-input price bill cache reads at the input price. */
13
- export declare function candidatePricing(candidate: Pick<RouteCandidate, 'inputUsdPerMillion' | 'outputUsdPerMillion' | 'cachedInputUsdPerMillion'>): RoutingPricingV1;
14
- export declare function routingCandidateId(candidate: Pick<RouteCandidate, 'peerId' | 'provider' | 'serviceId'>): string;
3
+ import { type RoutingClientAdapter } from './routing-adapters/types.js';
4
+ /** The routing protocol a target service advertises on its peer. */
5
+ export declare function routingProtocolOfTarget(peers: PeerInfo[], target: RoutingServiceTarget): RoutingProtocol | null;
15
6
  /**
16
- * Buyer side of `model-routing`, AntSeed's binding of Inference Routing Protocol suggest-only mode:
17
- * - `listModels`: free `GET /v1/routing/models`; the models the router can score, with any `max_pricing`.
18
- * - `selectRoute`: paid `POST /v1/routing/rank` once per user turn; the router orders the buyer's allowed candidates.
19
- * - `recordUsage`: observed prompt-cache reuse, sent to the router with later candidates.
20
- * It never sends inference; the buyer proxy dispatches the returned recommendations.
7
+ * Buyer side of routing services, independent of any routing protocol:
8
+ * - `listModels`: the models the router can rank (free).
9
+ * - `selectRoute`: one paid ranking per user turn; the router orders the buyer's allowed candidates.
10
+ * - `recordUsage`: observed prompt-cache reuse, given to the router with later candidates.
11
+ * The router's advertised protocol picks the adapter that speaks its wire format
12
+ * (`routing-adapters/`). It never sends inference; the buyer proxy dispatches the result.
21
13
  */
22
14
  export declare class ModelRoutingClient {
23
15
  private readonly lastRoutes;
24
16
  readonly observations: CacheObservations;
25
17
  recordUsage(observation: RoutingUsageObservation): void;
26
- listModels(target: RoutingServiceTarget, peers: PeerInfo[], context: RoutingModelsContext): Promise<RoutingModelEntryV1[]>;
18
+ /** The adapter for a target's routing protocol, e.g. to read the options it accepts. */
19
+ adapterFor(target: RoutingServiceTarget, peers: PeerInfo[]): RoutingClientAdapter;
20
+ /** Validate router options for a target before any ranking is bought; throws `InvalidRouterOptionsError`. */
21
+ validateOptions(target: RoutingServiceTarget, peers: PeerInfo[], options: RouterOptions | undefined): RouterOptions;
22
+ listModels(target: RoutingServiceTarget, peers: PeerInfo[], context: RoutingModelsContext): Promise<RouterModelEntry[]>;
27
23
  selectRoute(request: SerializedHttpRequest, peers: PeerInfo[], context: RouteSelectionContext): Promise<RouteRecommendation[] | null>;
28
- private buildRankRequest;
29
- /** The paid call. Keeps only ranked entries naming a sent candidate, in the router's order. */
30
- private rank;
31
24
  }
32
25
  /** The `ModelRoutingClient` surface the buyer proxy calls; tests can supply a stand-in. */
33
26
  export type ModelRoutingClientApi = Pick<ModelRoutingClient, 'listModels' | 'selectRoute'> & Partial<Pick<ModelRoutingClient, 'recordUsage'>>;
@@ -1 +1 @@
1
- {"version":3,"file":"model-routing-client.d.ts","sourceRoot":"","sources":["../src/model-routing-client.ts"],"names":[],"mappings":"AACA,OAAO,EAWL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAE1B,KAAK,oBAAoB,EAEzB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EAErB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAiF3D,iGAAiG;AACjG,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,uGAAuG;AACvG,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,oBAAoB,GAAG,qBAAqB,GAAG,0BAA0B,CAAC,GAAG,gBAAgB,CAM7J;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC,GAAG,MAAM,CAG/G;AAED;;;;;;GAMG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiC;IAC5D,QAAQ,CAAC,YAAY,oBAA0B;IAE/C,WAAW,CAAC,WAAW,EAAE,uBAAuB,GAAG,IAAI;IAIjD,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAW1H,WAAW,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,EAAE,GAAG,IAAI,CAAC;IA4B3I,OAAO,CAAC,gBAAgB;IAqBxB,+FAA+F;YACjF,IAAI;CAiBnB;AAED,2FAA2F;AAC3F,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"model-routing-client.d.ts","sourceRoot":"","sources":["../src/model-routing-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAE3D,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAgC9F,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,oBAAoB,GAAG,eAAe,GAAG,IAAI,CAI/G;AAqBD;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiC;IAC5D,QAAQ,CAAC,YAAY,oBAA0B;IAE/C,WAAW,CAAC,WAAW,EAAE,uBAAuB,GAAG,IAAI;IAIvD,wFAAwF;IACxF,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,oBAAoB;IAIjF,6GAA6G;IAC7G,eAAe,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,aAAa;IAI7G,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAKvH,WAAW,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,EAAE,GAAG,IAAI,CAAC;CAmC5I;AAED,2FAA2F;AAC3F,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC,CAAA"}
@@ -1,6 +1,7 @@
1
- import { createHash, randomUUID } from 'node:crypto';
2
- import { MAX_ROUTING_CANDIDATE_ID_LENGTH, MODEL_ROUTING_MODELS_PATH, MODEL_ROUTING_PROTOCOL, MODEL_ROUTING_RANK_PATH, detectRequestServiceApiProtocol, parseRoutingModelEntries, parseRoutingProblem, renderRequestBodyAsOpenAIChat, validateRoutingRankRequest, validateRoutingRankResponse, } from '@antseed/node';
1
+ import { routingProtocolOf, routingWireProtocol, } from '@antseed/node';
3
2
  import { CacheObservations } from './cache-observations.js';
3
+ import { routingClientAdapter } from './routing-adapters/index.js';
4
+ import { validateRouterOptions } from './routing-adapters/types.js';
4
5
  const MAX_CACHED_CONVERSATIONS = 500;
5
6
  const decodeJson = (body) => JSON.parse(new TextDecoder().decode(body));
6
7
  const encodeJson = (value) => new TextEncoder().encode(JSON.stringify(value));
@@ -28,89 +29,40 @@ function latestUserText(body) {
28
29
  }
29
30
  return typeof body.input === 'string' ? body.input : '';
30
31
  }
31
- /**
32
- * The request being routed as an OpenAI Chat Completions body. Anthropic Messages and Responses
33
- * bodies run through the same adapters used for dispatch. `model` and `stream` are dropped:
34
- * routers ignore them and never forward the request.
35
- */
36
- function routingInferenceRequest(request, body) {
37
- const protocol = detectRequestServiceApiProtocol(request);
38
- const chat = protocol ? renderRequestBodyAsOpenAIChat(protocol, body) : null;
39
- if (!chat)
40
- throw new Error('Routing supports Chat Completions, Anthropic Messages and Responses requests');
41
- delete chat.model;
42
- delete chat.stream;
43
- delete chat.stream_options;
44
- if (!Array.isArray(chat.messages) || chat.messages.length === 0)
45
- throw new Error('Routing requires at least one message');
46
- return chat;
32
+ /** The routing protocol a target service advertises on its peer. */
33
+ export function routingProtocolOfTarget(peers, target) {
34
+ const peer = peers.find(entry => entry.peerId === target.peerId);
35
+ const provider = peer?.metadata?.providers?.find(entry => entry.provider === target.provider && entry.services.includes(target.serviceId));
36
+ return routingProtocolOf(provider?.serviceApiProtocols?.[target.serviceId]);
47
37
  }
48
- /** The selected router peer, checked to advertise this exact model-routing service. */
38
+ /** The selected router peer, checked to advertise this exact routing service, and its protocol. */
49
39
  function findRouterPeer(peers, target) {
50
40
  const peer = peers.find(entry => entry.peerId === target.peerId);
51
41
  if (peer && !Array.isArray(peer.metadata?.providers)) {
52
42
  throw new Error('Selected router metadata is not available yet. Wait for discovery or restart the router.');
53
43
  }
54
- const provider = peer?.metadata?.providers.find(entry => entry.provider === target.provider && entry.services.includes(target.serviceId));
44
+ const provider = peer?.metadata?.providers?.find(entry => entry.provider === target.provider && entry.services.includes(target.serviceId));
55
45
  if (!peer || !provider)
56
46
  throw new Error('Selected routing service is not advertised by the selected peer');
57
- if (!provider.serviceApiProtocols?.[target.serviceId]?.includes(MODEL_ROUTING_PROTOCOL)) {
58
- throw new Error('Selected service does not advertise model-routing');
47
+ const protocol = routingProtocolOf(provider.serviceApiProtocols?.[target.serviceId]);
48
+ if (!protocol)
49
+ throw new Error('Selected service does not advertise a routing protocol');
50
+ // Protocols whose paths name no service need the router peer to offer exactly one service of that protocol.
51
+ if (routingWireProtocol(protocol).oneServicePerSeller) {
52
+ const sameProtocol = peer.metadata.providers.flatMap(entry => entry.services
53
+ .filter(service => routingProtocolOf(entry.serviceApiProtocols?.[service]) === protocol));
54
+ if (sameProtocol.length !== 1)
55
+ throw new Error(`Selected peer advertises more than one ${protocol} routing service`);
59
56
  }
60
- // IRP paths carry no service name, so a router peer must offer exactly one routing service.
61
- const routingServices = peer.metadata.providers.flatMap(entry => entry.services
62
- .filter(service => entry.serviceApiProtocols?.[service]?.includes(MODEL_ROUTING_PROTOCOL)));
63
- if (routingServices.length !== 1)
64
- throw new Error('Selected peer advertises more than one model-routing service');
65
- return peer;
66
- }
67
- /** Error text for a non-2xx router response, using IRP problem details when present. */
68
- function routerFailure(action, statusCode, body) {
69
- let problem = null;
70
- try {
71
- problem = parseRoutingProblem(decodeJson(body));
72
- }
73
- catch { /* not JSON */ }
74
- const detail = problem?.detail ?? problem?.title;
75
- const suffix = detail ? `: ${detail}` : '';
76
- switch (statusCode) {
77
- case 400: return new Error(`Router rejected the ${action} request${suffix}`);
78
- case 402: return new Error('Routing payment could not be completed. The channel may have unpaid or disputed work. Select a model or another router.');
79
- case 422: return new RouterCannotRankError(`Router cannot rank any allowed candidate. Update the allowed models or choose another router${suffix}`);
80
- case 503: return new Error(`Router is temporarily unavailable${suffix}`);
81
- default: return new Error(`Routing ${action} failed (${statusCode})${suffix}`);
82
- }
83
- }
84
- /** IRP 422: no candidate could be scored. The host may refresh the model list and retry once. */
85
- export class RouterCannotRankError extends Error {
86
- constructor(message) {
87
- super(message);
88
- this.name = 'RouterCannotRankError';
89
- }
90
- }
91
- /**
92
- * IRP candidate `id`: `provider:model@peer`, readable in buyer and router logs. IDs longer than
93
- * 128 characters become a SHA-256 of the same string. The buyer always maps IDs back through the
94
- * candidates it sent and never parses them.
95
- */
96
- /** IRP candidate pricing; sellers without a cached-input price bill cache reads at the input price. */
97
- export function candidatePricing(candidate) {
98
- return {
99
- input: candidate.inputUsdPerMillion,
100
- cache_read: candidate.cachedInputUsdPerMillion ?? candidate.inputUsdPerMillion,
101
- output: candidate.outputUsdPerMillion,
102
- };
103
- }
104
- export function routingCandidateId(candidate) {
105
- const id = `${candidate.provider}:${candidate.serviceId}@${candidate.peerId}`;
106
- return id.length <= MAX_ROUTING_CANDIDATE_ID_LENGTH ? id : `sha256:${createHash('sha256').update(id).digest('hex')}`;
57
+ return { peer, protocol };
107
58
  }
108
59
  /**
109
- * Buyer side of `model-routing`, AntSeed's binding of Inference Routing Protocol suggest-only mode:
110
- * - `listModels`: free `GET /v1/routing/models`; the models the router can score, with any `max_pricing`.
111
- * - `selectRoute`: paid `POST /v1/routing/rank` once per user turn; the router orders the buyer's allowed candidates.
112
- * - `recordUsage`: observed prompt-cache reuse, sent to the router with later candidates.
113
- * It never sends inference; the buyer proxy dispatches the returned recommendations.
60
+ * Buyer side of routing services, independent of any routing protocol:
61
+ * - `listModels`: the models the router can rank (free).
62
+ * - `selectRoute`: one paid ranking per user turn; the router orders the buyer's allowed candidates.
63
+ * - `recordUsage`: observed prompt-cache reuse, given to the router with later candidates.
64
+ * The router's advertised protocol picks the adapter that speaks its wire format
65
+ * (`routing-adapters/`). It never sends inference; the buyer proxy dispatches the result.
114
66
  */
115
67
  export class ModelRoutingClient {
116
68
  lastRoutes = new Map();
@@ -118,39 +70,49 @@ export class ModelRoutingClient {
118
70
  recordUsage(observation) {
119
71
  this.observations.record(observation);
120
72
  }
73
+ /** The adapter for a target's routing protocol, e.g. to read the options it accepts. */
74
+ adapterFor(target, peers) {
75
+ return routingClientAdapter(findRouterPeer(peers, target).protocol);
76
+ }
77
+ /** Validate router options for a target before any ranking is bought; throws `InvalidRouterOptionsError`. */
78
+ validateOptions(target, peers, options) {
79
+ return validateRouterOptions(this.adapterFor(target, peers).options, options);
80
+ }
121
81
  async listModels(target, peers, context) {
122
- const response = await context.sendRequest(findRouterPeer(peers, target), {
123
- requestId: randomUUID(), method: 'GET', path: MODEL_ROUTING_MODELS_PATH,
124
- headers: { accept: 'application/json' },
125
- body: new Uint8Array(),
126
- });
127
- context.signal.throwIfAborted();
128
- if (response.statusCode < 200 || response.statusCode >= 300)
129
- throw routerFailure('models', response.statusCode, response.body);
130
- return parseRoutingModelEntries(decodeJson(response.body));
82
+ const { peer, protocol } = findRouterPeer(peers, target);
83
+ return routingClientAdapter(protocol).listModels(peer, context);
131
84
  }
132
85
  async selectRoute(request, peers, context) {
133
86
  context.signal.throwIfAborted();
87
+ const { peer: router, protocol } = findRouterPeer(peers, context.routingService);
88
+ const adapter = routingClientAdapter(protocol);
89
+ const options = validateRouterOptions(adapter.options, context.options);
134
90
  const body = decodeJson(request.body);
135
91
  const text = latestUserText(body);
136
92
  if (!text.trim())
137
93
  throw new Error('Routing requires user text in messages or Responses input');
138
94
  if (!context.candidates.length)
139
95
  throw new Error('No eligible candidates supported by this router');
140
- const rankRequest = this.buildRankRequest(routingInferenceRequest(request, body), context);
141
- validateRoutingRankRequest(rankRequest);
142
96
  // Tool-loop continuations repeat the same user turn: reuse its recommendation instead of paying
143
97
  // again, even when the candidate set changed in between (a peer refresh reorders, adds or drops
144
98
  // sellers). A new ranking is bought only when none of the saved picks is still usable.
145
99
  const key = context.conversationKey;
146
- const fingerprint = JSON.stringify([context.routingService, context.costQualityTradeoff ?? null]);
100
+ const fingerprint = JSON.stringify([context.routingService, Object.entries(options).sort(([a], [b]) => a.localeCompare(b))]);
147
101
  const cached = key ? this.lastRoutes.get(key) : undefined;
148
102
  if (cached?.text === text && cached.fingerprint === fingerprint && context.acceptRecommendations(cached.routes)) {
149
103
  return structuredClone(cached.routes);
150
104
  }
151
105
  if (key)
152
106
  this.lastRoutes.delete(key);
153
- const routes = await this.rank(rankRequest, findRouterPeer(peers, context.routingService), context);
107
+ // Rough prompt size (characters / 4), used only to cap expected cache reuse.
108
+ const estimatedTokens = Math.ceil(encodeJson(body.messages ?? body.input ?? '').length / 4);
109
+ const candidates = context.candidates.map(candidate => ({
110
+ ...candidate,
111
+ expectedCachedInputTokens: this.observations.expectedCachedInputTokens(context.conversationKey, candidate, estimatedTokens),
112
+ }));
113
+ const routes = await adapter.rank({ request, body, router, candidates, options, context });
114
+ if (!context.acceptRecommendations(routes))
115
+ throw new Error('Router returned no usable recommendation');
154
116
  if (key) {
155
117
  this.lastRoutes.set(key, { text, fingerprint, routes: structuredClone(routes) });
156
118
  if (this.lastRoutes.size > MAX_CACHED_CONVERSATIONS)
@@ -158,45 +120,5 @@ export class ModelRoutingClient {
158
120
  }
159
121
  return routes;
160
122
  }
161
- buildRankRequest(inference, context) {
162
- // Rough prompt size (characters / 4), used only to cap expected cache reuse.
163
- const estimatedTokens = Math.ceil(encodeJson(inference.messages).length / 4);
164
- return {
165
- request: inference,
166
- routing: {
167
- ...(context.costQualityTradeoff !== undefined ? { cost_quality_tradeoff: context.costQualityTradeoff } : {}),
168
- candidates: context.candidates.map((candidate) => {
169
- const cacheReadTokens = this.observations.expectedCachedInputTokens(context.conversationKey, candidate, estimatedTokens);
170
- return {
171
- id: routingCandidateId(candidate),
172
- model: candidate.routerModel ?? candidate.serviceId,
173
- pricing: candidatePricing(candidate),
174
- // Absent means 0, so only send what was observed.
175
- ...(cacheReadTokens > 0 ? { expected_usage: { cache_read_tokens: cacheReadTokens } } : {}),
176
- };
177
- }),
178
- },
179
- };
180
- }
181
- /** The paid call. Keeps only ranked entries naming a sent candidate, in the router's order. */
182
- async rank(rankRequest, routerPeer, context) {
183
- const response = await context.sendRequest(routerPeer, {
184
- requestId: randomUUID(), method: 'POST', path: MODEL_ROUTING_RANK_PATH,
185
- headers: { 'content-type': 'application/json' },
186
- body: encodeJson(rankRequest),
187
- }, { signal: context.signal });
188
- context.signal.throwIfAborted();
189
- if (response.statusCode < 200 || response.statusCode >= 300)
190
- throw routerFailure('rank', response.statusCode, response.body);
191
- const candidates = rankRequest.routing.candidates;
192
- const byId = new Map(candidates.map((candidate, index) => [candidate.id, context.candidates[index]]));
193
- const routes = validateRoutingRankResponse(decodeJson(response.body), candidates).map((entry) => {
194
- const { serviceId, peerId, provider } = byId.get(entry.candidate_id);
195
- return { serviceId, peerId, provider, ...(entry.reasoning_effort ? { reasoningEffort: entry.reasoning_effort } : {}) };
196
- });
197
- if (!context.acceptRecommendations(routes))
198
- throw new Error('Router returned no usable recommendation');
199
- return routes;
200
- }
201
123
  }
202
124
  //# sourceMappingURL=model-routing-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-routing-client.js","sourceRoot":"","sources":["../src/model-routing-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model-routing-client.js","sourceRoot":"","sources":["../src/model-routing-client.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { type RoutingModelEntryV1, type RoutingPricingV1 } from '@antseed/node';
1
+ import { type RouterModelEntry, type RouterPricing } from '@antseed/node';
2
2
  /**
3
- * Translate seller service IDs into the model names a router lists in `GET /v1/routing/models`.
3
+ * Translate seller service IDs into the model names a router lists in its model catalog.
4
4
  * An exact name match wins; otherwise names are compared by canonical model key (the same
5
5
  * matching the savings baselines use), and the first listed router model wins a tie.
6
6
  * When the router lists `*`, unmatched service IDs pass through unchanged.
7
- * With `pricing`, a candidate priced above the matched entry's `max_pricing` is dropped.
7
+ * With `pricing`, a candidate priced above the matched entry's `maxPricing` is dropped.
8
8
  * Returns `undefined` when the router lists no matching model and no `*`, or the price is over the limit.
9
9
  */
10
- export declare function routerModelResolver(entries: readonly RoutingModelEntryV1[]): (serviceId: string, pricing?: RoutingPricingV1) => string | undefined;
10
+ export declare function routerModelResolver(entries: readonly RouterModelEntry[]): (serviceId: string, pricing?: RouterPricing) => string | undefined;
11
11
  //# sourceMappingURL=router-model-names.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router-model-names.d.ts","sourceRoot":"","sources":["../src/router-model-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAG9H;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,MAAM,GAAG,SAAS,CAoBlJ"}
1
+ {"version":3,"file":"router-model-names.d.ts","sourceRoot":"","sources":["../src/router-model-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAGhH;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,MAAM,GAAG,SAAS,CAoB5I"}
@@ -1,11 +1,11 @@
1
- import { MODEL_ROUTING_ANY_MODEL, withinRoutingPricing } from '@antseed/node';
1
+ import { ROUTER_ANY_MODEL, withinRouterPricing } from '@antseed/node';
2
2
  import { canonicalModelKey } from '@antseed/node/model-identity';
3
3
  /**
4
- * Translate seller service IDs into the model names a router lists in `GET /v1/routing/models`.
4
+ * Translate seller service IDs into the model names a router lists in its model catalog.
5
5
  * An exact name match wins; otherwise names are compared by canonical model key (the same
6
6
  * matching the savings baselines use), and the first listed router model wins a tie.
7
7
  * When the router lists `*`, unmatched service IDs pass through unchanged.
8
- * With `pricing`, a candidate priced above the matched entry's `max_pricing` is dropped.
8
+ * With `pricing`, a candidate priced above the matched entry's `maxPricing` is dropped.
9
9
  * Returns `undefined` when the router lists no matching model and no `*`, or the price is over the limit.
10
10
  */
11
11
  export function routerModelResolver(entries) {
@@ -13,7 +13,7 @@ export function routerModelResolver(entries) {
13
13
  const byKey = new Map();
14
14
  let anyModel;
15
15
  for (const entry of entries) {
16
- if (entry.id === MODEL_ROUTING_ANY_MODEL) {
16
+ if (entry.id === ROUTER_ANY_MODEL) {
17
17
  anyModel = entry;
18
18
  continue;
19
19
  }
@@ -26,7 +26,7 @@ export function routerModelResolver(entries) {
26
26
  const key = canonicalModelKey(serviceId);
27
27
  const named = exact.get(serviceId) ?? (key ? byKey.get(key) : undefined);
28
28
  const entry = named ?? anyModel;
29
- if (!entry || (pricing && !withinRoutingPricing(pricing, entry.max_pricing)))
29
+ if (!entry || (pricing && !withinRouterPricing(pricing, entry.maxPricing)))
30
30
  return undefined;
31
31
  return named ? named.id : serviceId;
32
32
  };
@@ -1 +1 @@
1
- {"version":3,"file":"router-model-names.js","sourceRoot":"","sources":["../src/router-model-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAmD,MAAM,eAAe,CAAA;AAC9H,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAEhE;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAuC;IACzE,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAA;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAA;IACpD,IAAI,QAAyC,CAAA;IAC7C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,EAAE,KAAK,uBAAuB,EAAE,CAAC;YACzC,QAAQ,GAAG,KAAK,CAAA;YAChB,SAAQ;QACV,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;QAC1B,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACvC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACxE,MAAM,KAAK,GAAG,KAAK,IAAI,QAAQ,CAAA;QAC/B,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;QAC9F,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACrC,CAAC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"router-model-names.js","sourceRoot":"","sources":["../src/router-model-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAA6C,MAAM,eAAe,CAAA;AAChH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAEhE;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAoC;IACtE,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAA;IACjD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAA;IACjD,IAAI,QAAsC,CAAA;IAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,EAAE,KAAK,gBAAgB,EAAE,CAAC;YAClC,QAAQ,GAAG,KAAK,CAAA;YAChB,SAAQ;QACV,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;QAC1B,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACvC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACxE,MAAM,KAAK,GAAG,KAAK,IAAI,QAAQ,CAAA;QAC/B,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;QAC5F,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACrC,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type RoutingProtocol } from '@antseed/node';
2
+ import type { RoutingClientAdapter } from './types.js';
3
+ export declare function routingClientAdapter(protocol: RoutingProtocol): RoutingClientAdapter;
4
+ /** The adapter for a protocol string, or null when it is not a routing protocol this buyer speaks. */
5
+ export declare function findRoutingClientAdapter(protocol: string | null | undefined): RoutingClientAdapter | null;
6
+ export { irpSuggestAdapter, irpCandidateId } from './irp.js';
7
+ export { candidatePricing, InvalidRouterOptionsError, RouterCannotRankError, validateRouterOptions, type RankCandidate, type RouterOptionSpec, type RoutingClientAdapter, } from './types.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing-adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAEvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAOtD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,oBAAoB,CAEpF;AAED,sGAAsG;AACtG,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,oBAAoB,GAAG,IAAI,CAEzG;AAED,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC5D,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { isRoutingProtocol } from '@antseed/node';
2
+ import { irpSuggestAdapter } from './irp.js';
3
+ /** One buyer adapter per routing protocol. None is preferred: the router's advertised protocol picks it. */
4
+ const ROUTING_CLIENT_ADAPTERS = {
5
+ 'irp-suggest': irpSuggestAdapter,
6
+ };
7
+ export function routingClientAdapter(protocol) {
8
+ return ROUTING_CLIENT_ADAPTERS[protocol];
9
+ }
10
+ /** The adapter for a protocol string, or null when it is not a routing protocol this buyer speaks. */
11
+ export function findRoutingClientAdapter(protocol) {
12
+ return isRoutingProtocol(protocol) ? ROUTING_CLIENT_ADAPTERS[protocol] : null;
13
+ }
14
+ export { irpSuggestAdapter, irpCandidateId } from './irp.js';
15
+ export { candidatePricing, InvalidRouterOptionsError, RouterCannotRankError, validateRouterOptions, } from './types.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing-adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAwB,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAG5C,4GAA4G;AAC5G,MAAM,uBAAuB,GAAkD;IAC7E,aAAa,EAAE,iBAAiB;CACjC,CAAA;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAyB;IAC5D,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAA;AAC1C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,wBAAwB,CAAC,QAAmC;IAC1E,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAC/E,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC5D,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,GAItB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { type RouteCandidate } from '@antseed/node';
2
+ import { type RoutingClientAdapter } from './types.js';
3
+ /**
4
+ * IRP candidate `id`: `provider:model@peer`, readable in buyer and router logs. IDs longer than
5
+ * 128 characters become a SHA-256 of the same string. The buyer always maps IDs back through the
6
+ * candidates it sent and never parses them.
7
+ */
8
+ export declare function irpCandidateId(candidate: Pick<RouteCandidate, 'peerId' | 'provider' | 'serviceId'>): string;
9
+ /**
10
+ * `irp-suggest`: Inference Routing Protocol suggest-only mode.
11
+ * - catalog: free `GET /v1/routing/models`; `*` means any model, with optional `max_pricing`.
12
+ * - rank: paid `POST /v1/routing/rank`; the router orders the buyer's allowed candidates.
13
+ * - options: `cost_quality_tradeoff`, 0 (best quality) to 10 (cheapest), router default 5.
14
+ */
15
+ export declare const irpSuggestAdapter: RoutingClientAdapter;
16
+ //# sourceMappingURL=irp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"irp.d.ts","sourceRoot":"","sources":["../../src/routing-adapters/irp.ts"],"names":[],"mappings":"AACA,OAAO,EAkBL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AACtB,OAAO,EAA2C,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAK/F;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC,GAAG,MAAM,CAG3G;AA8CD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,oBAuD/B,CAAA"}
@@ -0,0 +1,122 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { IRP_ANY_MODEL, IRP_DEFAULT_COST_QUALITY_TRADEOFF, IRP_MAX_CANDIDATE_ID_LENGTH, IRP_MODELS_PATH, IRP_RANK_PATH, IRP_SUGGEST_PROTOCOL, ROUTER_ANY_MODEL, detectRequestServiceApiProtocol, parseIrpModelEntries, parseIrpProblem, renderRequestBodyAsOpenAIChat, validateIrpRankRequest, validateIrpRankResponse, } from '@antseed/node';
3
+ import { candidatePricing, RouterCannotRankError } from './types.js';
4
+ const decodeJson = (body) => JSON.parse(new TextDecoder().decode(body));
5
+ const encodeJson = (value) => new TextEncoder().encode(JSON.stringify(value));
6
+ /**
7
+ * IRP candidate `id`: `provider:model@peer`, readable in buyer and router logs. IDs longer than
8
+ * 128 characters become a SHA-256 of the same string. The buyer always maps IDs back through the
9
+ * candidates it sent and never parses them.
10
+ */
11
+ export function irpCandidateId(candidate) {
12
+ const id = `${candidate.provider}:${candidate.serviceId}@${candidate.peerId}`;
13
+ return id.length <= IRP_MAX_CANDIDATE_ID_LENGTH ? id : `sha256:${createHash('sha256').update(id).digest('hex')}`;
14
+ }
15
+ function toIrpPricing(pricing) {
16
+ return { input: pricing.inputUsdPerMillion, cache_read: pricing.cachedInputUsdPerMillion, output: pricing.outputUsdPerMillion };
17
+ }
18
+ function fromIrpPricing(pricing) {
19
+ return {
20
+ ...(pricing.input !== undefined ? { inputUsdPerMillion: pricing.input } : {}),
21
+ ...(pricing.cache_read !== undefined ? { cachedInputUsdPerMillion: pricing.cache_read } : {}),
22
+ ...(pricing.output !== undefined ? { outputUsdPerMillion: pricing.output } : {}),
23
+ };
24
+ }
25
+ /**
26
+ * The request being routed as an OpenAI Chat Completions body (IRP `request`). Anthropic Messages
27
+ * and Responses bodies run through the same adapters used for dispatch. `model` and `stream` are
28
+ * dropped: routers ignore them and never forward the request.
29
+ */
30
+ function irpInferenceRequest(request, body) {
31
+ const protocol = detectRequestServiceApiProtocol(request);
32
+ const chat = protocol ? renderRequestBodyAsOpenAIChat(protocol, body) : null;
33
+ if (!chat)
34
+ throw new Error('Routing supports Chat Completions, Anthropic Messages and Responses requests');
35
+ delete chat.model;
36
+ delete chat.stream;
37
+ delete chat.stream_options;
38
+ if (!Array.isArray(chat.messages) || chat.messages.length === 0)
39
+ throw new Error('Routing requires at least one message');
40
+ return chat;
41
+ }
42
+ /** Error text for a non-2xx router response, using IRP problem details when present. */
43
+ function routerFailure(action, statusCode, body) {
44
+ let problem = null;
45
+ try {
46
+ problem = parseIrpProblem(decodeJson(body));
47
+ }
48
+ catch { /* not JSON */ }
49
+ const detail = problem?.detail ?? problem?.title;
50
+ const suffix = detail ? `: ${detail}` : '';
51
+ switch (statusCode) {
52
+ case 400: return new Error(`Router rejected the ${action} request${suffix}`);
53
+ case 402: return new Error('Routing payment could not be completed. The channel may have unpaid or disputed work. Select a model or another router.');
54
+ // IRP 422: no candidate could be scored; the buyer's model list may be stale.
55
+ case 422: return new RouterCannotRankError(`Router cannot rank any allowed candidate. Update the allowed models or choose another router${suffix}`);
56
+ case 503: return new Error(`Router is temporarily unavailable${suffix}`);
57
+ default: return new Error(`Routing ${action} failed (${statusCode})${suffix}`);
58
+ }
59
+ }
60
+ /**
61
+ * `irp-suggest`: Inference Routing Protocol suggest-only mode.
62
+ * - catalog: free `GET /v1/routing/models`; `*` means any model, with optional `max_pricing`.
63
+ * - rank: paid `POST /v1/routing/rank`; the router orders the buyer's allowed candidates.
64
+ * - options: `cost_quality_tradeoff`, 0 (best quality) to 10 (cheapest), router default 5.
65
+ */
66
+ export const irpSuggestAdapter = {
67
+ protocol: IRP_SUGGEST_PROTOCOL,
68
+ options: [{
69
+ key: 'cost_quality_tradeoff',
70
+ type: 'integer',
71
+ label: 'Cost vs. quality',
72
+ description: '0 picks the best quality regardless of price; 10 picks the cheapest acceptable model.',
73
+ minimum: 0,
74
+ maximum: 10,
75
+ default: IRP_DEFAULT_COST_QUALITY_TRADEOFF,
76
+ }],
77
+ async listModels(router, context) {
78
+ const response = await context.sendRequest(router, {
79
+ requestId: randomUUID(), method: 'GET', path: IRP_MODELS_PATH,
80
+ headers: { accept: 'application/json' },
81
+ body: new Uint8Array(),
82
+ });
83
+ context.signal.throwIfAborted();
84
+ if (response.statusCode < 200 || response.statusCode >= 300)
85
+ throw routerFailure('models', response.statusCode, response.body);
86
+ return parseIrpModelEntries(decodeJson(response.body)).map((entry) => ({
87
+ id: entry.id === IRP_ANY_MODEL ? ROUTER_ANY_MODEL : entry.id,
88
+ ...(entry.max_pricing ? { maxPricing: fromIrpPricing(entry.max_pricing) } : {}),
89
+ }));
90
+ },
91
+ async rank({ request, body, router, candidates, options, context }) {
92
+ const sent = candidates.map((candidate) => ({
93
+ id: irpCandidateId(candidate),
94
+ model: candidate.routerModel ?? candidate.serviceId,
95
+ pricing: toIrpPricing(candidatePricing(candidate)),
96
+ // Absent means 0, so only send what was observed.
97
+ ...(candidate.expectedCachedInputTokens > 0 ? { expected_usage: { cache_read_tokens: candidate.expectedCachedInputTokens } } : {}),
98
+ }));
99
+ const rankRequest = {
100
+ request: irpInferenceRequest(request, body),
101
+ routing: {
102
+ ...(typeof options.cost_quality_tradeoff === 'number' ? { cost_quality_tradeoff: options.cost_quality_tradeoff } : {}),
103
+ candidates: sent,
104
+ },
105
+ };
106
+ validateIrpRankRequest(rankRequest);
107
+ const response = await context.sendRequest(router, {
108
+ requestId: randomUUID(), method: 'POST', path: IRP_RANK_PATH,
109
+ headers: { 'content-type': 'application/json' },
110
+ body: encodeJson(rankRequest),
111
+ }, { signal: context.signal });
112
+ context.signal.throwIfAborted();
113
+ if (response.statusCode < 200 || response.statusCode >= 300)
114
+ throw routerFailure('rank', response.statusCode, response.body);
115
+ const byId = new Map(sent.map((candidate, index) => [candidate.id, candidates[index]]));
116
+ return validateIrpRankResponse(decodeJson(response.body), sent).map((entry) => {
117
+ const { serviceId, peerId, provider } = byId.get(entry.candidate_id);
118
+ return { serviceId, peerId, provider, ...(entry.reasoning_effort ? { reasoningEffort: entry.reasoning_effort } : {}) };
119
+ });
120
+ },
121
+ };
122
+ //# sourceMappingURL=irp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"irp.js","sourceRoot":"","sources":["../../src/routing-adapters/irp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { PeerInfo, RouteCandidate, RouteRecommendation, RouterModelEntry, RouterOptions, RouterPricing, RoutingModelsContext, RoutingProtocol, RouteSelectionContext, SerializedHttpRequest } from '@antseed/node';
2
+ /**
3
+ * One setting a router accepts with each ranking, described so a host (CLI, desktop) can
4
+ * validate it and render a control for it without knowing the routing protocol.
5
+ */
6
+ export type RouterOptionSpec = {
7
+ key: string;
8
+ label: string;
9
+ description?: string;
10
+ } & ({
11
+ type: 'integer' | 'number';
12
+ minimum?: number;
13
+ maximum?: number;
14
+ default?: number;
15
+ } | {
16
+ type: 'boolean';
17
+ default?: boolean;
18
+ } | {
19
+ type: 'string';
20
+ values?: readonly string[];
21
+ default?: string;
22
+ });
23
+ /** A candidate the buyer allows, with the prompt-cache reuse it observed for it. */
24
+ export type RankCandidate = RouteCandidate & {
25
+ expectedCachedInputTokens: number;
26
+ };
27
+ /**
28
+ * Buyer side of one routing protocol. The protocol-neutral `ModelRoutingClient` picks the
29
+ * adapter by the protocol the router advertises; adding a routing API means adding an adapter
30
+ * here, its wire binding in `@antseed/protocol/routing`, and a provider plugin for sellers.
31
+ */
32
+ export interface RoutingClientAdapter {
33
+ readonly protocol: RoutingProtocol;
34
+ /** Settings this protocol accepts with each ranking. Unknown keys are rejected. */
35
+ readonly options: readonly RouterOptionSpec[];
36
+ /** The models the router can rank: a free control-plane request. */
37
+ listModels(router: PeerInfo, context: RoutingModelsContext): Promise<RouterModelEntry[]>;
38
+ /**
39
+ * The paid ranking. Returns allowed candidates in the router's order, each at most once;
40
+ * a router can never name a destination the buyer did not send.
41
+ */
42
+ rank(args: {
43
+ request: SerializedHttpRequest;
44
+ /** `request.body`, decoded. */
45
+ body: Record<string, unknown>;
46
+ router: PeerInfo;
47
+ candidates: readonly RankCandidate[];
48
+ options: RouterOptions;
49
+ context: RouteSelectionContext;
50
+ }): Promise<RouteRecommendation[]>;
51
+ }
52
+ /** The router says none of the candidates can be ranked; the host may refresh the model list and retry once. */
53
+ export declare class RouterCannotRankError extends Error {
54
+ constructor(message: string);
55
+ }
56
+ /** Router settings that the router's protocol does not accept; nothing was paid. */
57
+ export declare class InvalidRouterOptionsError extends Error {
58
+ constructor(message: string);
59
+ }
60
+ /** Check `options` against a protocol's option specs; throws `InvalidRouterOptionsError`. */
61
+ export declare function validateRouterOptions(specs: readonly RouterOptionSpec[], options: RouterOptions | undefined): RouterOptions;
62
+ /** Candidate pricing for a router; sellers without a cached-input price bill cache reads at the input price. */
63
+ export declare function candidatePricing(candidate: Pick<RouteCandidate, 'inputUsdPerMillion' | 'outputUsdPerMillion' | 'cachedInputUsdPerMillion'>): RouterPricing;
64
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/routing-adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAA;AAEtB;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,CACA;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CACnE,CAAA;AAED,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG;IAAE,yBAAyB,EAAE,MAAM,CAAA;CAAE,CAAA;AAElF;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;IAClC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC7C,oEAAoE;IACpE,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IACxF;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE;QACT,OAAO,EAAE,qBAAqB,CAAA;QAC9B,+BAA+B;QAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC7B,MAAM,EAAE,QAAQ,CAAA;QAChB,UAAU,EAAE,SAAS,aAAa,EAAE,CAAA;QACpC,OAAO,EAAE,aAAa,CAAA;QACtB,OAAO,EAAE,qBAAqB,CAAA;KAC/B,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAA;CACnC;AAED,gHAAgH;AAChH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,oFAAoF;AACpF,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,OAAO,EAAE,MAAM;CAI5B;AAED,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,aAAa,CAwB3H;AAED,gHAAgH;AAChH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,oBAAoB,GAAG,qBAAqB,GAAG,0BAA0B,CAAC,GAAG,aAAa,CAM1J"}
@@ -0,0 +1,54 @@
1
+ /** The router says none of the candidates can be ranked; the host may refresh the model list and retry once. */
2
+ export class RouterCannotRankError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = 'RouterCannotRankError';
6
+ }
7
+ }
8
+ /** Router settings that the router's protocol does not accept; nothing was paid. */
9
+ export class InvalidRouterOptionsError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'InvalidRouterOptionsError';
13
+ }
14
+ }
15
+ /** Check `options` against a protocol's option specs; throws `InvalidRouterOptionsError`. */
16
+ export function validateRouterOptions(specs, options) {
17
+ if (options === undefined)
18
+ return {};
19
+ const byKey = new Map(specs.map(spec => [spec.key, spec]));
20
+ const unknown = Object.keys(options).filter(key => !byKey.has(key));
21
+ if (unknown.length) {
22
+ const supported = specs.map(spec => spec.key).join(', ') || 'none';
23
+ throw new InvalidRouterOptionsError(`Unknown router option(s): ${unknown.join(', ')}. Supported: ${supported}.`);
24
+ }
25
+ for (const [key, value] of Object.entries(options)) {
26
+ const spec = byKey.get(key);
27
+ const fail = (expected) => { throw new InvalidRouterOptionsError(`Router option ${key} must be ${expected}.`); };
28
+ if (spec.type === 'boolean') {
29
+ if (typeof value !== 'boolean')
30
+ fail('true or false');
31
+ }
32
+ else if (spec.type === 'string') {
33
+ if (typeof value !== 'string' || (spec.values && !spec.values.includes(value)))
34
+ fail(spec.values ? `one of ${spec.values.join(', ')}` : 'a string');
35
+ }
36
+ else {
37
+ const range = spec.minimum !== undefined && spec.maximum !== undefined ? ` from ${spec.minimum} to ${spec.maximum}` : '';
38
+ if (typeof value !== 'number' || !Number.isFinite(value) || (spec.type === 'integer' && !Number.isInteger(value))
39
+ || (spec.minimum !== undefined && value < spec.minimum) || (spec.maximum !== undefined && value > spec.maximum)) {
40
+ fail(`${spec.type === 'integer' ? 'an integer' : 'a number'}${range}`);
41
+ }
42
+ }
43
+ }
44
+ return { ...options };
45
+ }
46
+ /** Candidate pricing for a router; sellers without a cached-input price bill cache reads at the input price. */
47
+ export function candidatePricing(candidate) {
48
+ return {
49
+ inputUsdPerMillion: candidate.inputUsdPerMillion,
50
+ cachedInputUsdPerMillion: candidate.cachedInputUsdPerMillion ?? candidate.inputUsdPerMillion,
51
+ outputUsdPerMillion: candidate.outputUsdPerMillion,
52
+ };
53
+ }
54
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/routing-adapters/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antseed/router-core",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "description": "Shared router infrastructure for Antseed plugins",
5
5
  "type": "module",
6
6
  "files": [
@@ -14,7 +14,7 @@
14
14
  "devDependencies": {
15
15
  "typescript": "^5.5.0",
16
16
  "vitest": "^2.0.0",
17
- "@antseed/node": "0.2.141"
17
+ "@antseed/node": "0.2.143"
18
18
  },
19
19
  "scripts": {
20
20
  "prebuild": "node ../../scripts/remove-paths.mjs dist",