@juno-ai/bind 1.0.0
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/LICENSE +21 -0
- package/README.md +102 -0
- package/contracts/index.d.ts +1 -0
- package/contracts/index.js +1 -0
- package/contracts/turn.d.ts +79 -0
- package/contracts/turn.js +39 -0
- package/index.d.ts +13 -0
- package/index.js +13 -0
- package/package.json +43 -0
- package/routing/canonical-model.d.ts +17 -0
- package/routing/canonical-model.js +22 -0
- package/routing/circuit-breaker.d.ts +67 -0
- package/routing/circuit-breaker.js +75 -0
- package/routing/errors.d.ts +95 -0
- package/routing/errors.js +141 -0
- package/routing/executor.d.ts +64 -0
- package/routing/executor.js +173 -0
- package/routing/index.d.ts +9 -0
- package/routing/index.js +9 -0
- package/routing/plan.d.ts +98 -0
- package/routing/plan.js +22 -0
- package/routing/planner.d.ts +36 -0
- package/routing/planner.js +102 -0
- package/routing/policy.d.ts +81 -0
- package/routing/policy.js +99 -0
- package/routing/transport.d.ts +40 -0
- package/routing/transport.js +1 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type CanonicalModelId, type ProviderId } from "./canonical-model";
|
|
3
|
+
/**
|
|
4
|
+
* Policy order and semantics are versioned; the version string travels into
|
|
5
|
+
* telemetry (LLM Provider Routing PRD §4.2) so a routing-behavior change is
|
|
6
|
+
* distinguishable from a configuration change after the fact.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ROUTE_POLICY_VERSION = "provider-router-v1";
|
|
9
|
+
export type RoutePolicyVersion = typeof ROUTE_POLICY_VERSION;
|
|
10
|
+
/**
|
|
11
|
+
* `ordered` tries each listed provider in order, skipping unavailable or
|
|
12
|
+
* incapable endpoints. `only` is a hard fence: the marked canonical model is
|
|
13
|
+
* never attempted through another provider. A separately configured fallback
|
|
14
|
+
* model still expands under its own policy (PRD §4.2).
|
|
15
|
+
*/
|
|
16
|
+
export declare const providerPolicySchema: z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
17
|
+
mode: z.ZodLiteral<"ordered">;
|
|
18
|
+
providers: z.ZodArray<z.core.$ZodBranded<z.ZodString, "ProviderId", "out">>;
|
|
19
|
+
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
20
|
+
mode: z.ZodLiteral<"only">;
|
|
21
|
+
provider: z.core.$ZodBranded<z.ZodString, "ProviderId", "out">;
|
|
22
|
+
}, z.core.$strict>>], "mode">;
|
|
23
|
+
export type ProviderPolicy = z.infer<typeof providerPolicySchema>;
|
|
24
|
+
/**
|
|
25
|
+
* The checked-in route registry. It contains policy only — never endpoints,
|
|
26
|
+
* deployment names, credentials, prices, quota, or tenant-specific values.
|
|
27
|
+
*
|
|
28
|
+
* Naming note: the PRD draft called the bound default `azureBoundDefault`.
|
|
29
|
+
* The harness is provider-agnostic, so the registry generalizes it to
|
|
30
|
+
* `boundDefault`, applied when the host reports at least one provider
|
|
31
|
+
* binding for the model (see `resolveProviderPolicy`).
|
|
32
|
+
*/
|
|
33
|
+
export declare const modelRouteRegistrySchema: z.ZodReadonly<z.ZodObject<{
|
|
34
|
+
version: z.ZodLiteral<"provider-router-v1">;
|
|
35
|
+
/** Used when a model has no explicit policy and no provider binding. */
|
|
36
|
+
unboundDefault: z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
37
|
+
mode: z.ZodLiteral<"ordered">;
|
|
38
|
+
providers: z.ZodArray<z.core.$ZodBranded<z.ZodString, "ProviderId", "out">>;
|
|
39
|
+
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
40
|
+
mode: z.ZodLiteral<"only">;
|
|
41
|
+
provider: z.core.$ZodBranded<z.ZodString, "ProviderId", "out">;
|
|
42
|
+
}, z.core.$strict>>], "mode">;
|
|
43
|
+
/** Used when a model has no explicit policy but has ≥1 provider binding. */
|
|
44
|
+
boundDefault: z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
45
|
+
mode: z.ZodLiteral<"ordered">;
|
|
46
|
+
providers: z.ZodArray<z.core.$ZodBranded<z.ZodString, "ProviderId", "out">>;
|
|
47
|
+
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
48
|
+
mode: z.ZodLiteral<"only">;
|
|
49
|
+
provider: z.core.$ZodBranded<z.ZodString, "ProviderId", "out">;
|
|
50
|
+
}, z.core.$strict>>], "mode">;
|
|
51
|
+
/** Keys are canonical model ids; parsing rejects deployment names. */
|
|
52
|
+
models: z.ZodRecord<z.core.$ZodBranded<z.ZodString, "CanonicalModelId", "out">, z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
53
|
+
mode: z.ZodLiteral<"ordered">;
|
|
54
|
+
providers: z.ZodArray<z.core.$ZodBranded<z.ZodString, "ProviderId", "out">>;
|
|
55
|
+
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
56
|
+
mode: z.ZodLiteral<"only">;
|
|
57
|
+
provider: z.core.$ZodBranded<z.ZodString, "ProviderId", "out">;
|
|
58
|
+
}, z.core.$strict>>], "mode">>;
|
|
59
|
+
}, z.core.$strict>>;
|
|
60
|
+
export type ModelRouteRegistry = z.infer<typeof modelRouteRegistrySchema>;
|
|
61
|
+
/**
|
|
62
|
+
* Parse and freeze a checked-in registry object at module initialization.
|
|
63
|
+
* `satisfies` on the literal is not a runtime-validation substitute
|
|
64
|
+
* (PRD §4.2) — hosts must call this on their checked-in object.
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseModelRouteRegistry(input: unknown): ModelRouteRegistry;
|
|
67
|
+
/**
|
|
68
|
+
* Policy precedence (PRD §4.2): exact canonical-model entry, then the bound
|
|
69
|
+
* default when the host reports a provider binding for this model, then the
|
|
70
|
+
* unbound default. An explicit policy always wins — in particular, an
|
|
71
|
+
* explicit single-provider model does not become bound-first merely because
|
|
72
|
+
* a binding is later added.
|
|
73
|
+
*
|
|
74
|
+
* `boundProviders` is the host's report of which providers have an explicit
|
|
75
|
+
* binding for this model (e.g. an Azure deployment mapping). A catch-all
|
|
76
|
+
* transport that serves every model without per-model configuration should
|
|
77
|
+
* not be reported as a binding.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveProviderPolicy(registry: ModelRouteRegistry, model: CanonicalModelId, boundProviders: ReadonlySet<ProviderId>): ProviderPolicy;
|
|
80
|
+
/** The provider order a policy prescribes; `only` is a single-entry order. */
|
|
81
|
+
export declare function policyProviderOrder(policy: ProviderPolicy): readonly ProviderId[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { canonicalModelIdSchema, providerIdSchema, } from "./canonical-model";
|
|
3
|
+
/**
|
|
4
|
+
* Policy order and semantics are versioned; the version string travels into
|
|
5
|
+
* telemetry (LLM Provider Routing PRD §4.2) so a routing-behavior change is
|
|
6
|
+
* distinguishable from a configuration change after the fact.
|
|
7
|
+
*/
|
|
8
|
+
export const ROUTE_POLICY_VERSION = "provider-router-v1";
|
|
9
|
+
const orderedPolicySchema = z
|
|
10
|
+
.strictObject({
|
|
11
|
+
mode: z.literal("ordered"),
|
|
12
|
+
providers: z
|
|
13
|
+
.array(providerIdSchema)
|
|
14
|
+
.min(1)
|
|
15
|
+
.refine((providers) => new Set(providers).size === providers.length, "duplicate provider ids in ordered policy"),
|
|
16
|
+
})
|
|
17
|
+
.readonly();
|
|
18
|
+
const onlyPolicySchema = z
|
|
19
|
+
.strictObject({
|
|
20
|
+
mode: z.literal("only"),
|
|
21
|
+
provider: providerIdSchema,
|
|
22
|
+
})
|
|
23
|
+
.readonly();
|
|
24
|
+
/**
|
|
25
|
+
* `ordered` tries each listed provider in order, skipping unavailable or
|
|
26
|
+
* incapable endpoints. `only` is a hard fence: the marked canonical model is
|
|
27
|
+
* never attempted through another provider. A separately configured fallback
|
|
28
|
+
* model still expands under its own policy (PRD §4.2).
|
|
29
|
+
*/
|
|
30
|
+
export const providerPolicySchema = z.discriminatedUnion("mode", [
|
|
31
|
+
orderedPolicySchema,
|
|
32
|
+
onlyPolicySchema,
|
|
33
|
+
]);
|
|
34
|
+
/**
|
|
35
|
+
* The checked-in route registry. It contains policy only — never endpoints,
|
|
36
|
+
* deployment names, credentials, prices, quota, or tenant-specific values.
|
|
37
|
+
*
|
|
38
|
+
* Naming note: the PRD draft called the bound default `azureBoundDefault`.
|
|
39
|
+
* The harness is provider-agnostic, so the registry generalizes it to
|
|
40
|
+
* `boundDefault`, applied when the host reports at least one provider
|
|
41
|
+
* binding for the model (see `resolveProviderPolicy`).
|
|
42
|
+
*/
|
|
43
|
+
export const modelRouteRegistrySchema = z
|
|
44
|
+
.strictObject({
|
|
45
|
+
version: z.literal(ROUTE_POLICY_VERSION),
|
|
46
|
+
/** Used when a model has no explicit policy and no provider binding. */
|
|
47
|
+
unboundDefault: providerPolicySchema,
|
|
48
|
+
/** Used when a model has no explicit policy but has ≥1 provider binding. */
|
|
49
|
+
boundDefault: providerPolicySchema,
|
|
50
|
+
/** Keys are canonical model ids; parsing rejects deployment names. */
|
|
51
|
+
models: z.record(canonicalModelIdSchema, providerPolicySchema),
|
|
52
|
+
})
|
|
53
|
+
.readonly();
|
|
54
|
+
/**
|
|
55
|
+
* Parse and freeze a checked-in registry object at module initialization.
|
|
56
|
+
* `satisfies` on the literal is not a runtime-validation substitute
|
|
57
|
+
* (PRD §4.2) — hosts must call this on their checked-in object.
|
|
58
|
+
*/
|
|
59
|
+
export function parseModelRouteRegistry(input) {
|
|
60
|
+
const registry = modelRouteRegistrySchema.parse(input);
|
|
61
|
+
for (const policy of Object.values(registry.models)) {
|
|
62
|
+
if (policy.mode === "ordered")
|
|
63
|
+
Object.freeze(policy.providers);
|
|
64
|
+
Object.freeze(policy);
|
|
65
|
+
}
|
|
66
|
+
Object.freeze(registry.models);
|
|
67
|
+
return Object.freeze(registry);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Policy precedence (PRD §4.2): exact canonical-model entry, then the bound
|
|
71
|
+
* default when the host reports a provider binding for this model, then the
|
|
72
|
+
* unbound default. An explicit policy always wins — in particular, an
|
|
73
|
+
* explicit single-provider model does not become bound-first merely because
|
|
74
|
+
* a binding is later added.
|
|
75
|
+
*
|
|
76
|
+
* `boundProviders` is the host's report of which providers have an explicit
|
|
77
|
+
* binding for this model (e.g. an Azure deployment mapping). A catch-all
|
|
78
|
+
* transport that serves every model without per-model configuration should
|
|
79
|
+
* not be reported as a binding.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveProviderPolicy(registry, model, boundProviders) {
|
|
82
|
+
const explicit = registry.models[model];
|
|
83
|
+
if (explicit !== undefined)
|
|
84
|
+
return explicit;
|
|
85
|
+
return boundProviders.size > 0 ? registry.boundDefault : registry.unboundDefault;
|
|
86
|
+
}
|
|
87
|
+
/** The provider order a policy prescribes; `only` is a single-entry order. */
|
|
88
|
+
export function policyProviderOrder(policy) {
|
|
89
|
+
switch (policy.mode) {
|
|
90
|
+
case "ordered":
|
|
91
|
+
return policy.providers;
|
|
92
|
+
case "only":
|
|
93
|
+
return [policy.provider];
|
|
94
|
+
default: {
|
|
95
|
+
const _exhaustive = policy;
|
|
96
|
+
throw new Error(`unknown policy mode: ${JSON.stringify(_exhaustive)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CanonicalModelId, ProviderId } from "./canonical-model";
|
|
2
|
+
import type { InferenceRequirements, RouteCandidate, RouteSkip } from "./plan";
|
|
3
|
+
export type TransportAvailability = Readonly<{
|
|
4
|
+
available: true;
|
|
5
|
+
}> | Readonly<{
|
|
6
|
+
available: false;
|
|
7
|
+
reason: string;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* What a transport reports when the planner asks it for an endpoint:
|
|
11
|
+
* - `candidate` — a concrete, attemptable endpoint.
|
|
12
|
+
* - `skip` — the transport knows about the model but cannot serve it
|
|
13
|
+
* (malformed binding, capability gap it detects itself); recorded as a
|
|
14
|
+
* plan diagnostic.
|
|
15
|
+
* - `unserved` — the transport has no knowledge of this model (e.g. no Azure
|
|
16
|
+
* deployment mapping). Omitted silently, matching PRD §5.1's "wholly
|
|
17
|
+
* absent configuration is omitted" behavior.
|
|
18
|
+
*/
|
|
19
|
+
export type CandidateResolution = Readonly<{
|
|
20
|
+
kind: "candidate";
|
|
21
|
+
candidate: RouteCandidate;
|
|
22
|
+
}> | Readonly<{
|
|
23
|
+
kind: "skip";
|
|
24
|
+
skip: RouteSkip;
|
|
25
|
+
}> | Readonly<{
|
|
26
|
+
kind: "unserved";
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* The planner-facing surface of an inference transport. This is the harness
|
|
30
|
+
* subset of the PRD's `InferenceTransportContribution` (§4.3): request
|
|
31
|
+
* construction (`createClient` / `buildRequest`) and provider error
|
|
32
|
+
* classification stay with the host's transport adapters, because they
|
|
33
|
+
* depend on the wire client. The planner needs only availability and
|
|
34
|
+
* endpoint resolution.
|
|
35
|
+
*/
|
|
36
|
+
export interface PlannerTransport {
|
|
37
|
+
readonly id: ProviderId;
|
|
38
|
+
getAvailability(): TransportAvailability;
|
|
39
|
+
resolveCandidate(model: CanonicalModelId, requirements: InferenceRequirements): CandidateResolution;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|