@hasna/switcher 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -8
- package/dist/cli/index.js +2725 -621
- package/dist/cli.d.ts +2 -0
- package/dist/codex-model-policy.d.ts +65 -0
- package/dist/credentials.d.ts +71 -8
- package/dist/direct-launch.d.ts +2 -2
- package/dist/domain.d.ts +259 -2
- package/dist/gemini-model-policy.d.ts +56 -0
- package/dist/generated/api.d.ts +192 -0
- package/dist/harness-types.d.ts +9 -0
- package/dist/hermes-model-policy.d.ts +30 -0
- package/dist/index.js +138 -48
- package/dist/inference-gateway.d.ts +24 -0
- package/dist/launcher.d.ts +8 -3
- package/dist/mcp/index.js +153 -62
- package/dist/model-policy-schema.d.ts +137 -0
- package/dist/model-policy.d.ts +31 -0
- package/dist/native-model-policy.d.ts +23 -0
- package/dist/opencode-model-policy.d.ts +33 -0
- package/dist/ori-model-policy.d.ts +8 -0
- package/dist/sdk.d.ts +248 -6
- package/dist/sdk.js +138 -48
- package/dist/serve/index.js +1044 -88
- package/docs/MODEL-POLICY.md +82 -0
- package/openapi.json +836 -1
- package/package.json +3 -2
package/dist/serve/index.js
CHANGED
|
@@ -11,13 +11,63 @@ import { chmod, mkdir } from "fs/promises";
|
|
|
11
11
|
import { dirname, resolve } from "path";
|
|
12
12
|
|
|
13
13
|
// src/domain.ts
|
|
14
|
+
import { z as z2 } from "zod";
|
|
15
|
+
|
|
16
|
+
// src/model-policy-schema.ts
|
|
14
17
|
import { z } from "zod";
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
var
|
|
20
|
-
|
|
18
|
+
var policyModelIdSchema = z.string().min(1).max(300).regex(/^[^\u0000-\u001f\u007f]+$/);
|
|
19
|
+
var modelPolicyRoleSchema = z.enum(["subagent", "fast", "planning", "review", "summary", "compaction", "weak", "editor"]);
|
|
20
|
+
var boundedModelList = z.array(policyModelIdSchema).max(500);
|
|
21
|
+
var aliasSchema = z.string().regex(/^[A-Za-z0-9._/-]{1,120}$/).refine((v) => !["__proto__", "prototype", "constructor"].includes(v));
|
|
22
|
+
var boundedModelMap = z.record(aliasSchema, policyModelIdSchema).superRefine((value, ctx) => {
|
|
23
|
+
if (Object.keys(value).length > 200)
|
|
24
|
+
ctx.addIssue({ code: "custom", message: "Model policy maps may contain at most 200 entries." });
|
|
25
|
+
});
|
|
26
|
+
var fallbacksSchema = z.record(policyModelIdSchema, z.array(policyModelIdSchema).max(20)).superRefine((value, ctx) => {
|
|
27
|
+
if (Object.keys(value).length > 200)
|
|
28
|
+
ctx.addIssue({ code: "custom", message: "Model policy fallback maps may contain at most 200 entries." });
|
|
29
|
+
});
|
|
30
|
+
var modelPolicySchema = z.object({
|
|
31
|
+
version: z.literal(1).default(1),
|
|
32
|
+
roles: z.object({
|
|
33
|
+
subagent: policyModelIdSchema.optional(),
|
|
34
|
+
fast: policyModelIdSchema.optional(),
|
|
35
|
+
planning: policyModelIdSchema.optional(),
|
|
36
|
+
review: policyModelIdSchema.optional(),
|
|
37
|
+
summary: policyModelIdSchema.optional(),
|
|
38
|
+
compaction: policyModelIdSchema.optional(),
|
|
39
|
+
weak: policyModelIdSchema.optional(),
|
|
40
|
+
editor: policyModelIdSchema.optional()
|
|
41
|
+
}).strict().optional(),
|
|
42
|
+
allowedModels: boundedModelList.optional(),
|
|
43
|
+
aliases: boundedModelMap.optional(),
|
|
44
|
+
fallbacks: fallbacksSchema.optional()
|
|
45
|
+
}).strict();
|
|
46
|
+
var routingDecisionSchema = z.enum(["allow", "alias", "reject", "fallback"]);
|
|
47
|
+
var routingEventRoleSchema = z.enum(["main", ...modelPolicyRoleSchema.options]);
|
|
48
|
+
var routingEventSchema = z.object({
|
|
49
|
+
at: z.string().datetime({ offset: true }),
|
|
50
|
+
requestId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/),
|
|
51
|
+
requestedModel: policyModelIdSchema,
|
|
52
|
+
resolvedModel: policyModelIdSchema.optional(),
|
|
53
|
+
reportedModel: policyModelIdSchema.optional(),
|
|
54
|
+
decision: routingDecisionSchema,
|
|
55
|
+
role: routingEventRoleSchema.optional(),
|
|
56
|
+
reason: z.string().regex(/^[a-z][a-z0-9_]{0,63}$/).optional(),
|
|
57
|
+
upstreamStatus: z.number().int().min(100).max(599).optional()
|
|
58
|
+
}).strict();
|
|
59
|
+
var routingEventsSchema = z.array(routingEventSchema).max(1000);
|
|
60
|
+
function canonicalPolicyJSON(value) {
|
|
61
|
+
return JSON.stringify(value, (_key, item) => item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b))) : item);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/domain.ts
|
|
65
|
+
var VERSION = "0.1.4";
|
|
66
|
+
var harnessSchema = z2.enum(["claude", "codex", "grok", "opencode", "opencode2", "pi", "omp", "dsh", "cline", "hermes", "prime-agent", "gemini", "aider", "kilo"]);
|
|
67
|
+
var protocolSchema = z2.enum(["anthropic-messages", "openai-responses", "openai-chat", "gemini-generate-content"]);
|
|
68
|
+
var idSchema = z2.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/);
|
|
69
|
+
var label = z2.string().min(1).max(200);
|
|
70
|
+
var envRef = z2.string().regex(/^SWITCHER_PROVIDER_[A-Z0-9_]+$/);
|
|
21
71
|
function endpoint(value) {
|
|
22
72
|
let url;
|
|
23
73
|
try {
|
|
@@ -30,74 +80,79 @@ function endpoint(value) {
|
|
|
30
80
|
throw new Fault(400, "invalid_url", "URL must use HTTPS, contain no credentials/query/fragment, or use HTTP on loopback.");
|
|
31
81
|
return url.href.replace(/\/+$/, "");
|
|
32
82
|
}
|
|
33
|
-
var urlSchema =
|
|
83
|
+
var urlSchema = z2.string().max(2000).superRefine((v, ctx) => {
|
|
34
84
|
try {
|
|
35
85
|
endpoint(v);
|
|
36
86
|
} catch {
|
|
37
87
|
ctx.addIssue({ code: "custom", message: "Invalid endpoint URL" });
|
|
38
88
|
}
|
|
39
89
|
}).transform(endpoint);
|
|
40
|
-
var modelSchema =
|
|
41
|
-
id:
|
|
90
|
+
var modelSchema = z2.object({
|
|
91
|
+
id: z2.string().min(1).max(300),
|
|
42
92
|
name: label,
|
|
43
|
-
description:
|
|
44
|
-
available:
|
|
45
|
-
contextWindow:
|
|
46
|
-
maxOutputTokens:
|
|
47
|
-
inputModalities:
|
|
48
|
-
outputModalities:
|
|
49
|
-
supportedParameters:
|
|
50
|
-
supportedGenerationMethods:
|
|
93
|
+
description: z2.string().max(8000).optional(),
|
|
94
|
+
available: z2.boolean().optional(),
|
|
95
|
+
contextWindow: z2.number().int().positive().optional(),
|
|
96
|
+
maxOutputTokens: z2.number().int().positive().optional(),
|
|
97
|
+
inputModalities: z2.array(z2.string().max(50)).max(20).optional(),
|
|
98
|
+
outputModalities: z2.array(z2.string().max(50)).max(20).optional(),
|
|
99
|
+
supportedParameters: z2.array(z2.string().max(100)).max(100).optional(),
|
|
100
|
+
supportedGenerationMethods: z2.array(z2.string().min(1).max(100)).max(100).optional()
|
|
51
101
|
}).strict();
|
|
52
|
-
var providerInputSchema =
|
|
102
|
+
var providerInputSchema = z2.object({
|
|
53
103
|
id: idSchema,
|
|
54
104
|
name: label,
|
|
55
105
|
baseUrl: urlSchema,
|
|
56
106
|
protocol: protocolSchema,
|
|
57
107
|
credentialEnv: envRef.optional(),
|
|
58
|
-
authStyle:
|
|
108
|
+
authStyle: z2.enum(["bearer", "x-api-key", "api-key"]).default("bearer"),
|
|
59
109
|
catalogBaseUrl: urlSchema.optional(),
|
|
60
|
-
catalogFormat:
|
|
61
|
-
catalogAuthStyle:
|
|
110
|
+
catalogFormat: z2.enum(["openai", "ollama", "mistral", "together", "fireworks", "dashscope", "gemini", "none"]).optional(),
|
|
111
|
+
catalogAuthStyle: z2.enum(["bearer", "x-api-key", "api-key", "none"]).optional(),
|
|
62
112
|
catalogCredentialEnv: envRef.optional(),
|
|
63
|
-
catalogAccountId:
|
|
64
|
-
modelsPath:
|
|
65
|
-
manualModels:
|
|
113
|
+
catalogAccountId: z2.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/).optional(),
|
|
114
|
+
modelsPath: z2.string().regex(/^[a-zA-Z0-9_/-]+$/).max(200).default("models"),
|
|
115
|
+
manualModels: z2.array(modelSchema).max(1e4).default([])
|
|
66
116
|
}).strict().refine((p) => !p.modelsPath.split("/").includes("..") && !p.modelsPath.startsWith("/"), "modelsPath must be relative");
|
|
67
|
-
var providerPresetSchema =
|
|
117
|
+
var providerPresetSchema = z2.object({
|
|
68
118
|
id: idSchema,
|
|
69
119
|
name: label,
|
|
70
120
|
credentialEnv: envRef.optional(),
|
|
71
|
-
credentialAliases:
|
|
72
|
-
protocols:
|
|
121
|
+
credentialAliases: z2.array(z2.string().regex(/^[A-Z][A-Z0-9_]+$/)),
|
|
122
|
+
protocols: z2.array(z2.object({
|
|
73
123
|
protocol: protocolSchema,
|
|
74
124
|
baseUrl: urlSchema.optional(),
|
|
75
|
-
authStyle:
|
|
125
|
+
authStyle: z2.enum(["bearer", "x-api-key", "api-key"]),
|
|
76
126
|
catalogBaseUrl: urlSchema.optional(),
|
|
77
|
-
catalogFormat:
|
|
78
|
-
catalogAuthStyle:
|
|
79
|
-
modelsPath:
|
|
80
|
-
notes:
|
|
127
|
+
catalogFormat: z2.enum(["openai", "ollama", "mistral", "together", "fireworks", "dashscope", "gemini", "none"]),
|
|
128
|
+
catalogAuthStyle: z2.enum(["bearer", "x-api-key", "api-key", "none"]).optional(),
|
|
129
|
+
modelsPath: z2.string(),
|
|
130
|
+
notes: z2.array(z2.string())
|
|
81
131
|
}).strict()).min(1),
|
|
82
|
-
sources:
|
|
83
|
-
verification:
|
|
132
|
+
sources: z2.array(z2.string().url()),
|
|
133
|
+
verification: z2.literal("documented")
|
|
84
134
|
}).strict();
|
|
85
|
-
var profileInputSchema =
|
|
135
|
+
var profileInputSchema = z2.object({
|
|
86
136
|
id: idSchema,
|
|
87
137
|
name: label,
|
|
88
138
|
providerId: idSchema,
|
|
89
139
|
harness: harnessSchema,
|
|
90
|
-
model:
|
|
140
|
+
model: z2.string().min(1).max(300),
|
|
141
|
+
modelPolicy: modelPolicySchema.optional()
|
|
91
142
|
}).strict();
|
|
92
|
-
var runInputSchema =
|
|
143
|
+
var runInputSchema = z2.object({
|
|
144
|
+
modelPolicyVersion: z2.literal(1),
|
|
93
145
|
profileId: idSchema,
|
|
94
146
|
harness: harnessSchema,
|
|
95
|
-
model:
|
|
96
|
-
|
|
147
|
+
model: z2.string().min(1).max(300),
|
|
148
|
+
modelPolicy: modelPolicySchema.optional(),
|
|
149
|
+
planToken: z2.string().regex(/^[a-f0-9]{64}$/)
|
|
97
150
|
}).strict();
|
|
98
|
-
var runUpdateSchema =
|
|
99
|
-
status:
|
|
100
|
-
exitCode:
|
|
151
|
+
var runUpdateSchema = z2.object({
|
|
152
|
+
status: z2.enum(["exited", "failed", "interrupted"]),
|
|
153
|
+
exitCode: z2.number().int().min(0).max(255),
|
|
154
|
+
routingEvents: routingEventsSchema.optional(),
|
|
155
|
+
routingEventsDropped: z2.number().int().min(0).max(1e6).optional()
|
|
101
156
|
}).strict();
|
|
102
157
|
|
|
103
158
|
class Fault extends Error {
|
|
@@ -298,8 +353,8 @@ class Store {
|
|
|
298
353
|
}
|
|
299
354
|
|
|
300
355
|
// src/service.ts
|
|
301
|
-
import { createHash, timingSafeEqual } from "crypto";
|
|
302
|
-
import { z as
|
|
356
|
+
import { createHash as createHash2, timingSafeEqual } from "crypto";
|
|
357
|
+
import { z as z3 } from "zod";
|
|
303
358
|
|
|
304
359
|
// src/http.ts
|
|
305
360
|
var MAX_BYTES = 16 * 1024 * 1024;
|
|
@@ -595,6 +650,69 @@ async function discover(provider, env = process.env, resolveCredential) {
|
|
|
595
650
|
throw new Fault(502, "catalog_too_large", "Provider catalog pagination exceeded 100 pages.");
|
|
596
651
|
}
|
|
597
652
|
|
|
653
|
+
// src/model-policy.ts
|
|
654
|
+
import { createHash } from "crypto";
|
|
655
|
+
var roles = ["subagent", "fast", "planning", "review", "summary", "compaction", "weak", "editor"];
|
|
656
|
+
var id = (v, label2) => {
|
|
657
|
+
if (typeof v !== "string" || !v || v.length > 300 || /[\u0000-\u001f\u007f]/.test(v))
|
|
658
|
+
throw new Fault(400, "invalid_model_policy", `${label2} is invalid.`);
|
|
659
|
+
return v;
|
|
660
|
+
};
|
|
661
|
+
var uniq = (xs) => [...new Set(xs)];
|
|
662
|
+
function stable(value) {
|
|
663
|
+
return JSON.stringify(value, (_k, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort(([a], [b]) => a.localeCompare(b))) : v);
|
|
664
|
+
}
|
|
665
|
+
function compileModelPolicy(model, catalog, policy) {
|
|
666
|
+
const selected = id(model, "model");
|
|
667
|
+
const available = new Set(catalog.filter((m) => m.available !== false).map((m) => m.id));
|
|
668
|
+
if (!available.has(selected))
|
|
669
|
+
throw new Fault(422, "model_unavailable", "Selected model is not present in the eligible catalog.");
|
|
670
|
+
if (policy && policy.version !== undefined && policy.version !== 1)
|
|
671
|
+
throw new Fault(400, "invalid_model_policy", "Unsupported model policy version.");
|
|
672
|
+
const p = parse(modelPolicySchema, policy ?? {});
|
|
673
|
+
const roleInput = p.roles ?? {};
|
|
674
|
+
if (Object.keys(roleInput).some((k) => !roles.includes(k)))
|
|
675
|
+
throw new Fault(400, "invalid_model_policy", "Unknown model policy role.");
|
|
676
|
+
const compiledRoles = Object.fromEntries(roles.map((role) => [role, id(roleInput[role] ?? selected, `roles.${role}`)]));
|
|
677
|
+
for (const value of Object.values(compiledRoles))
|
|
678
|
+
if (!available.has(value))
|
|
679
|
+
throw new Fault(422, "model_unavailable", "A model policy role is not present in the eligible catalog.");
|
|
680
|
+
const explicitAllowed = (p.allowedModels ?? []).map((v, i) => id(v, `allowedModels[${i}]`));
|
|
681
|
+
if (explicitAllowed.length > 500)
|
|
682
|
+
throw new Fault(400, "invalid_model_policy", "Model policy allowedModels is too large.");
|
|
683
|
+
for (const value of explicitAllowed)
|
|
684
|
+
if (!available.has(value))
|
|
685
|
+
throw new Fault(422, "model_unavailable", "An allowed model is not present in the eligible catalog.");
|
|
686
|
+
const aliases = Object.create(null);
|
|
687
|
+
for (const [name, target] of Object.entries(p.aliases ?? {})) {
|
|
688
|
+
if (Object.keys(aliases).length >= 200 || !/^[A-Za-z0-9._/-]{1,120}$/.test(name) || ["__proto__", "prototype", "constructor"].includes(name))
|
|
689
|
+
throw new Fault(400, "invalid_model_policy", "A model alias is invalid or too numerous.");
|
|
690
|
+
const canonical = id(target, `aliases.${name}`);
|
|
691
|
+
if (available.has(name) && name !== canonical)
|
|
692
|
+
throw new Fault(400, "invalid_model_policy", "A model alias cannot shadow a real model ID.");
|
|
693
|
+
if (!available.has(canonical))
|
|
694
|
+
throw new Fault(422, "model_unavailable", "A model alias target is not present in the eligible catalog.");
|
|
695
|
+
aliases[name] = canonical;
|
|
696
|
+
}
|
|
697
|
+
const fallbacks = Object.create(null);
|
|
698
|
+
const allowedSources = new Set([selected, ...Object.values(compiledRoles), ...explicitAllowed]);
|
|
699
|
+
for (const [source, targets] of Object.entries(p.fallbacks ?? {})) {
|
|
700
|
+
if (!allowedSources.has(source) || !available.has(source) || !Array.isArray(targets) || Object.keys(fallbacks).length >= 200 || targets.length > 20)
|
|
701
|
+
throw new Fault(400, "invalid_model_policy", "Fallback source or list is invalid.");
|
|
702
|
+
const values = targets.map((v, i) => id(v, `fallbacks.${source}[${i}]`));
|
|
703
|
+
if (values.some((v) => !available.has(v)))
|
|
704
|
+
throw new Fault(422, "model_unavailable", "A fallback model is not present in the eligible catalog.");
|
|
705
|
+
if (values.includes(source))
|
|
706
|
+
throw new Fault(400, "invalid_model_policy", "A model cannot fall back to itself.");
|
|
707
|
+
fallbacks[source] = uniq(values);
|
|
708
|
+
}
|
|
709
|
+
const allowedModels = uniq([selected, ...Object.values(compiledRoles), ...explicitAllowed, ...Object.values(fallbacks).flat()]).sort();
|
|
710
|
+
if (Object.values(aliases).some((value) => !allowedModels.includes(value)))
|
|
711
|
+
throw new Fault(422, "model_not_allowed", "A model alias target must be explicitly allowed by the launch policy.");
|
|
712
|
+
const result = { version: 1, model: selected, roles: compiledRoles, allowedModels, aliases, fallbacks };
|
|
713
|
+
return { ...result, digest: createHash("sha256").update(stable(result)).digest("hex") };
|
|
714
|
+
}
|
|
715
|
+
|
|
598
716
|
// src/presets.ts
|
|
599
717
|
var route = (protocol, baseUrl, options = {}) => ({
|
|
600
718
|
protocol,
|
|
@@ -605,13 +723,13 @@ var route = (protocol, baseUrl, options = {}) => ({
|
|
|
605
723
|
notes: [],
|
|
606
724
|
...options
|
|
607
725
|
});
|
|
608
|
-
var preset = (
|
|
609
|
-
id,
|
|
726
|
+
var preset = (id2, name, protocols, sources, alias) => parse(providerPresetSchema, {
|
|
727
|
+
id: id2,
|
|
610
728
|
name,
|
|
611
729
|
protocols,
|
|
612
730
|
sources,
|
|
613
731
|
credentialAliases: alias ? [alias] : [],
|
|
614
|
-
credentialEnv: alias ? `SWITCHER_PROVIDER_${
|
|
732
|
+
credentialEnv: alias ? `SWITCHER_PROVIDER_${id2.toUpperCase().replace(/-/g, "_")}` : undefined,
|
|
615
733
|
verification: "documented"
|
|
616
734
|
});
|
|
617
735
|
var providerPresets = [
|
|
@@ -679,8 +797,8 @@ var providerPresets = [
|
|
|
679
797
|
preset("siliconflow", "SiliconFlow", [route("openai-chat", "https://api.siliconflow.cn/v1", { catalogBaseUrl: "https://api.siliconflow.cn/v1", catalogFormat: "openai", notes: ["The official SiliconCloud OpenAPI contract defines GET /models with Bearer auth and data[] model rows; optional type and sub_type filters are available at the upstream endpoint."] })], ["https://github.com/siliconflow/siliconcloud/blob/main/openapi.yaml", "https://docs.siliconflow.cn/docs/userguide/quickstart", "https://docs.siliconflow.cn/docs/api/chat-completions-post"], "SILICONFLOW_API_KEY"),
|
|
680
798
|
...["anthropic-messages", "openai-responses", "openai-chat"].map((protocol) => preset(`generic-${protocol}`, `Custom ${protocol}`, [route(protocol)], []))
|
|
681
799
|
];
|
|
682
|
-
function getProviderPreset(
|
|
683
|
-
const entry = providerPresets.find((p) => p.id ===
|
|
800
|
+
function getProviderPreset(id2) {
|
|
801
|
+
const entry = providerPresets.find((p) => p.id === id2);
|
|
684
802
|
if (!entry)
|
|
685
803
|
throw new Fault(404, "preset_not_found", "Unknown provider preset. Use switcher providers presets to list available presets.");
|
|
686
804
|
return structuredClone(entry);
|
|
@@ -690,7 +808,7 @@ var openapi_default = {
|
|
|
690
808
|
openapi: "3.0.3",
|
|
691
809
|
info: {
|
|
692
810
|
title: "Switcher API",
|
|
693
|
-
version: "0.1.
|
|
811
|
+
version: "0.1.4",
|
|
694
812
|
description: "Authenticated provider/profile/catalog control plane. Launches run locally; the API never returns provider credentials."
|
|
695
813
|
},
|
|
696
814
|
security: [
|
|
@@ -2364,6 +2482,105 @@ var openapi_default = {
|
|
|
2364
2482
|
type: "string",
|
|
2365
2483
|
minLength: 1,
|
|
2366
2484
|
maxLength: 300
|
|
2485
|
+
},
|
|
2486
|
+
modelPolicy: {
|
|
2487
|
+
type: "object",
|
|
2488
|
+
properties: {
|
|
2489
|
+
version: {
|
|
2490
|
+
type: "number",
|
|
2491
|
+
enum: [
|
|
2492
|
+
1
|
|
2493
|
+
],
|
|
2494
|
+
default: 1
|
|
2495
|
+
},
|
|
2496
|
+
roles: {
|
|
2497
|
+
type: "object",
|
|
2498
|
+
properties: {
|
|
2499
|
+
subagent: {
|
|
2500
|
+
type: "string",
|
|
2501
|
+
minLength: 1,
|
|
2502
|
+
maxLength: 300,
|
|
2503
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2504
|
+
},
|
|
2505
|
+
fast: {
|
|
2506
|
+
type: "string",
|
|
2507
|
+
minLength: 1,
|
|
2508
|
+
maxLength: 300,
|
|
2509
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2510
|
+
},
|
|
2511
|
+
planning: {
|
|
2512
|
+
type: "string",
|
|
2513
|
+
minLength: 1,
|
|
2514
|
+
maxLength: 300,
|
|
2515
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2516
|
+
},
|
|
2517
|
+
review: {
|
|
2518
|
+
type: "string",
|
|
2519
|
+
minLength: 1,
|
|
2520
|
+
maxLength: 300,
|
|
2521
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2522
|
+
},
|
|
2523
|
+
summary: {
|
|
2524
|
+
type: "string",
|
|
2525
|
+
minLength: 1,
|
|
2526
|
+
maxLength: 300,
|
|
2527
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2528
|
+
},
|
|
2529
|
+
compaction: {
|
|
2530
|
+
type: "string",
|
|
2531
|
+
minLength: 1,
|
|
2532
|
+
maxLength: 300,
|
|
2533
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2534
|
+
},
|
|
2535
|
+
weak: {
|
|
2536
|
+
type: "string",
|
|
2537
|
+
minLength: 1,
|
|
2538
|
+
maxLength: 300,
|
|
2539
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2540
|
+
},
|
|
2541
|
+
editor: {
|
|
2542
|
+
type: "string",
|
|
2543
|
+
minLength: 1,
|
|
2544
|
+
maxLength: 300,
|
|
2545
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2546
|
+
}
|
|
2547
|
+
},
|
|
2548
|
+
additionalProperties: false
|
|
2549
|
+
},
|
|
2550
|
+
allowedModels: {
|
|
2551
|
+
type: "array",
|
|
2552
|
+
items: {
|
|
2553
|
+
type: "string",
|
|
2554
|
+
minLength: 1,
|
|
2555
|
+
maxLength: 300,
|
|
2556
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2557
|
+
},
|
|
2558
|
+
maxItems: 500
|
|
2559
|
+
},
|
|
2560
|
+
aliases: {
|
|
2561
|
+
type: "object",
|
|
2562
|
+
additionalProperties: {
|
|
2563
|
+
type: "string",
|
|
2564
|
+
minLength: 1,
|
|
2565
|
+
maxLength: 300,
|
|
2566
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2567
|
+
}
|
|
2568
|
+
},
|
|
2569
|
+
fallbacks: {
|
|
2570
|
+
type: "object",
|
|
2571
|
+
additionalProperties: {
|
|
2572
|
+
type: "array",
|
|
2573
|
+
items: {
|
|
2574
|
+
type: "string",
|
|
2575
|
+
minLength: 1,
|
|
2576
|
+
maxLength: 300,
|
|
2577
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2578
|
+
},
|
|
2579
|
+
maxItems: 20
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
},
|
|
2583
|
+
additionalProperties: false
|
|
2367
2584
|
}
|
|
2368
2585
|
},
|
|
2369
2586
|
required: [
|
|
@@ -2415,6 +2632,105 @@ var openapi_default = {
|
|
|
2415
2632
|
minLength: 1,
|
|
2416
2633
|
maxLength: 300
|
|
2417
2634
|
},
|
|
2635
|
+
modelPolicy: {
|
|
2636
|
+
type: "object",
|
|
2637
|
+
properties: {
|
|
2638
|
+
version: {
|
|
2639
|
+
type: "number",
|
|
2640
|
+
enum: [
|
|
2641
|
+
1
|
|
2642
|
+
],
|
|
2643
|
+
default: 1
|
|
2644
|
+
},
|
|
2645
|
+
roles: {
|
|
2646
|
+
type: "object",
|
|
2647
|
+
properties: {
|
|
2648
|
+
subagent: {
|
|
2649
|
+
type: "string",
|
|
2650
|
+
minLength: 1,
|
|
2651
|
+
maxLength: 300,
|
|
2652
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2653
|
+
},
|
|
2654
|
+
fast: {
|
|
2655
|
+
type: "string",
|
|
2656
|
+
minLength: 1,
|
|
2657
|
+
maxLength: 300,
|
|
2658
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2659
|
+
},
|
|
2660
|
+
planning: {
|
|
2661
|
+
type: "string",
|
|
2662
|
+
minLength: 1,
|
|
2663
|
+
maxLength: 300,
|
|
2664
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2665
|
+
},
|
|
2666
|
+
review: {
|
|
2667
|
+
type: "string",
|
|
2668
|
+
minLength: 1,
|
|
2669
|
+
maxLength: 300,
|
|
2670
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2671
|
+
},
|
|
2672
|
+
summary: {
|
|
2673
|
+
type: "string",
|
|
2674
|
+
minLength: 1,
|
|
2675
|
+
maxLength: 300,
|
|
2676
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2677
|
+
},
|
|
2678
|
+
compaction: {
|
|
2679
|
+
type: "string",
|
|
2680
|
+
minLength: 1,
|
|
2681
|
+
maxLength: 300,
|
|
2682
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2683
|
+
},
|
|
2684
|
+
weak: {
|
|
2685
|
+
type: "string",
|
|
2686
|
+
minLength: 1,
|
|
2687
|
+
maxLength: 300,
|
|
2688
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2689
|
+
},
|
|
2690
|
+
editor: {
|
|
2691
|
+
type: "string",
|
|
2692
|
+
minLength: 1,
|
|
2693
|
+
maxLength: 300,
|
|
2694
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2695
|
+
}
|
|
2696
|
+
},
|
|
2697
|
+
additionalProperties: false
|
|
2698
|
+
},
|
|
2699
|
+
allowedModels: {
|
|
2700
|
+
type: "array",
|
|
2701
|
+
items: {
|
|
2702
|
+
type: "string",
|
|
2703
|
+
minLength: 1,
|
|
2704
|
+
maxLength: 300,
|
|
2705
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2706
|
+
},
|
|
2707
|
+
maxItems: 500
|
|
2708
|
+
},
|
|
2709
|
+
aliases: {
|
|
2710
|
+
type: "object",
|
|
2711
|
+
additionalProperties: {
|
|
2712
|
+
type: "string",
|
|
2713
|
+
minLength: 1,
|
|
2714
|
+
maxLength: 300,
|
|
2715
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2716
|
+
}
|
|
2717
|
+
},
|
|
2718
|
+
fallbacks: {
|
|
2719
|
+
type: "object",
|
|
2720
|
+
additionalProperties: {
|
|
2721
|
+
type: "array",
|
|
2722
|
+
items: {
|
|
2723
|
+
type: "string",
|
|
2724
|
+
minLength: 1,
|
|
2725
|
+
maxLength: 300,
|
|
2726
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2727
|
+
},
|
|
2728
|
+
maxItems: 20
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
additionalProperties: false
|
|
2733
|
+
},
|
|
2418
2734
|
version: {
|
|
2419
2735
|
type: "integer",
|
|
2420
2736
|
exclusiveMinimum: true,
|
|
@@ -2505,6 +2821,175 @@ var openapi_default = {
|
|
|
2505
2821
|
],
|
|
2506
2822
|
additionalProperties: false
|
|
2507
2823
|
},
|
|
2824
|
+
ModelPolicy: {
|
|
2825
|
+
type: "object",
|
|
2826
|
+
properties: {
|
|
2827
|
+
version: {
|
|
2828
|
+
type: "number",
|
|
2829
|
+
enum: [
|
|
2830
|
+
1
|
|
2831
|
+
],
|
|
2832
|
+
default: 1
|
|
2833
|
+
},
|
|
2834
|
+
roles: {
|
|
2835
|
+
type: "object",
|
|
2836
|
+
properties: {
|
|
2837
|
+
subagent: {
|
|
2838
|
+
type: "string",
|
|
2839
|
+
minLength: 1,
|
|
2840
|
+
maxLength: 300,
|
|
2841
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2842
|
+
},
|
|
2843
|
+
fast: {
|
|
2844
|
+
type: "string",
|
|
2845
|
+
minLength: 1,
|
|
2846
|
+
maxLength: 300,
|
|
2847
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2848
|
+
},
|
|
2849
|
+
planning: {
|
|
2850
|
+
type: "string",
|
|
2851
|
+
minLength: 1,
|
|
2852
|
+
maxLength: 300,
|
|
2853
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2854
|
+
},
|
|
2855
|
+
review: {
|
|
2856
|
+
type: "string",
|
|
2857
|
+
minLength: 1,
|
|
2858
|
+
maxLength: 300,
|
|
2859
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2860
|
+
},
|
|
2861
|
+
summary: {
|
|
2862
|
+
type: "string",
|
|
2863
|
+
minLength: 1,
|
|
2864
|
+
maxLength: 300,
|
|
2865
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2866
|
+
},
|
|
2867
|
+
compaction: {
|
|
2868
|
+
type: "string",
|
|
2869
|
+
minLength: 1,
|
|
2870
|
+
maxLength: 300,
|
|
2871
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2872
|
+
},
|
|
2873
|
+
weak: {
|
|
2874
|
+
type: "string",
|
|
2875
|
+
minLength: 1,
|
|
2876
|
+
maxLength: 300,
|
|
2877
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2878
|
+
},
|
|
2879
|
+
editor: {
|
|
2880
|
+
type: "string",
|
|
2881
|
+
minLength: 1,
|
|
2882
|
+
maxLength: 300,
|
|
2883
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2884
|
+
}
|
|
2885
|
+
},
|
|
2886
|
+
additionalProperties: false
|
|
2887
|
+
},
|
|
2888
|
+
allowedModels: {
|
|
2889
|
+
type: "array",
|
|
2890
|
+
items: {
|
|
2891
|
+
type: "string",
|
|
2892
|
+
minLength: 1,
|
|
2893
|
+
maxLength: 300,
|
|
2894
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2895
|
+
},
|
|
2896
|
+
maxItems: 500
|
|
2897
|
+
},
|
|
2898
|
+
aliases: {
|
|
2899
|
+
type: "object",
|
|
2900
|
+
additionalProperties: {
|
|
2901
|
+
type: "string",
|
|
2902
|
+
minLength: 1,
|
|
2903
|
+
maxLength: 300,
|
|
2904
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2905
|
+
}
|
|
2906
|
+
},
|
|
2907
|
+
fallbacks: {
|
|
2908
|
+
type: "object",
|
|
2909
|
+
additionalProperties: {
|
|
2910
|
+
type: "array",
|
|
2911
|
+
items: {
|
|
2912
|
+
type: "string",
|
|
2913
|
+
minLength: 1,
|
|
2914
|
+
maxLength: 300,
|
|
2915
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2916
|
+
},
|
|
2917
|
+
maxItems: 20
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
},
|
|
2921
|
+
additionalProperties: false
|
|
2922
|
+
},
|
|
2923
|
+
RoutingEvent: {
|
|
2924
|
+
type: "object",
|
|
2925
|
+
properties: {
|
|
2926
|
+
at: {
|
|
2927
|
+
type: "string",
|
|
2928
|
+
format: "date-time"
|
|
2929
|
+
},
|
|
2930
|
+
requestId: {
|
|
2931
|
+
type: "string",
|
|
2932
|
+
pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
|
|
2933
|
+
},
|
|
2934
|
+
requestedModel: {
|
|
2935
|
+
type: "string",
|
|
2936
|
+
minLength: 1,
|
|
2937
|
+
maxLength: 300,
|
|
2938
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2939
|
+
},
|
|
2940
|
+
resolvedModel: {
|
|
2941
|
+
type: "string",
|
|
2942
|
+
minLength: 1,
|
|
2943
|
+
maxLength: 300,
|
|
2944
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2945
|
+
},
|
|
2946
|
+
reportedModel: {
|
|
2947
|
+
type: "string",
|
|
2948
|
+
minLength: 1,
|
|
2949
|
+
maxLength: 300,
|
|
2950
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
2951
|
+
},
|
|
2952
|
+
decision: {
|
|
2953
|
+
type: "string",
|
|
2954
|
+
enum: [
|
|
2955
|
+
"allow",
|
|
2956
|
+
"alias",
|
|
2957
|
+
"reject",
|
|
2958
|
+
"fallback"
|
|
2959
|
+
]
|
|
2960
|
+
},
|
|
2961
|
+
role: {
|
|
2962
|
+
type: "string",
|
|
2963
|
+
enum: [
|
|
2964
|
+
"main",
|
|
2965
|
+
"subagent",
|
|
2966
|
+
"fast",
|
|
2967
|
+
"planning",
|
|
2968
|
+
"review",
|
|
2969
|
+
"summary",
|
|
2970
|
+
"compaction",
|
|
2971
|
+
"weak",
|
|
2972
|
+
"editor"
|
|
2973
|
+
]
|
|
2974
|
+
},
|
|
2975
|
+
reason: {
|
|
2976
|
+
type: "string",
|
|
2977
|
+
pattern: "^[a-z][a-z0-9_]{0,63}$"
|
|
2978
|
+
},
|
|
2979
|
+
upstreamStatus: {
|
|
2980
|
+
type: "integer",
|
|
2981
|
+
minimum: 100,
|
|
2982
|
+
maximum: 599
|
|
2983
|
+
}
|
|
2984
|
+
},
|
|
2985
|
+
required: [
|
|
2986
|
+
"at",
|
|
2987
|
+
"requestId",
|
|
2988
|
+
"requestedModel",
|
|
2989
|
+
"decision"
|
|
2990
|
+
],
|
|
2991
|
+
additionalProperties: false
|
|
2992
|
+
},
|
|
2508
2993
|
ModelPage: {
|
|
2509
2994
|
type: "object",
|
|
2510
2995
|
properties: {
|
|
@@ -2924,6 +3409,105 @@ var openapi_default = {
|
|
|
2924
3409
|
minLength: 1,
|
|
2925
3410
|
maxLength: 300
|
|
2926
3411
|
},
|
|
3412
|
+
modelPolicy: {
|
|
3413
|
+
type: "object",
|
|
3414
|
+
properties: {
|
|
3415
|
+
version: {
|
|
3416
|
+
type: "number",
|
|
3417
|
+
enum: [
|
|
3418
|
+
1
|
|
3419
|
+
],
|
|
3420
|
+
default: 1
|
|
3421
|
+
},
|
|
3422
|
+
roles: {
|
|
3423
|
+
type: "object",
|
|
3424
|
+
properties: {
|
|
3425
|
+
subagent: {
|
|
3426
|
+
type: "string",
|
|
3427
|
+
minLength: 1,
|
|
3428
|
+
maxLength: 300,
|
|
3429
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3430
|
+
},
|
|
3431
|
+
fast: {
|
|
3432
|
+
type: "string",
|
|
3433
|
+
minLength: 1,
|
|
3434
|
+
maxLength: 300,
|
|
3435
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3436
|
+
},
|
|
3437
|
+
planning: {
|
|
3438
|
+
type: "string",
|
|
3439
|
+
minLength: 1,
|
|
3440
|
+
maxLength: 300,
|
|
3441
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3442
|
+
},
|
|
3443
|
+
review: {
|
|
3444
|
+
type: "string",
|
|
3445
|
+
minLength: 1,
|
|
3446
|
+
maxLength: 300,
|
|
3447
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3448
|
+
},
|
|
3449
|
+
summary: {
|
|
3450
|
+
type: "string",
|
|
3451
|
+
minLength: 1,
|
|
3452
|
+
maxLength: 300,
|
|
3453
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3454
|
+
},
|
|
3455
|
+
compaction: {
|
|
3456
|
+
type: "string",
|
|
3457
|
+
minLength: 1,
|
|
3458
|
+
maxLength: 300,
|
|
3459
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3460
|
+
},
|
|
3461
|
+
weak: {
|
|
3462
|
+
type: "string",
|
|
3463
|
+
minLength: 1,
|
|
3464
|
+
maxLength: 300,
|
|
3465
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3466
|
+
},
|
|
3467
|
+
editor: {
|
|
3468
|
+
type: "string",
|
|
3469
|
+
minLength: 1,
|
|
3470
|
+
maxLength: 300,
|
|
3471
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3472
|
+
}
|
|
3473
|
+
},
|
|
3474
|
+
additionalProperties: false
|
|
3475
|
+
},
|
|
3476
|
+
allowedModels: {
|
|
3477
|
+
type: "array",
|
|
3478
|
+
items: {
|
|
3479
|
+
type: "string",
|
|
3480
|
+
minLength: 1,
|
|
3481
|
+
maxLength: 300,
|
|
3482
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3483
|
+
},
|
|
3484
|
+
maxItems: 500
|
|
3485
|
+
},
|
|
3486
|
+
aliases: {
|
|
3487
|
+
type: "object",
|
|
3488
|
+
additionalProperties: {
|
|
3489
|
+
type: "string",
|
|
3490
|
+
minLength: 1,
|
|
3491
|
+
maxLength: 300,
|
|
3492
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3493
|
+
}
|
|
3494
|
+
},
|
|
3495
|
+
fallbacks: {
|
|
3496
|
+
type: "object",
|
|
3497
|
+
additionalProperties: {
|
|
3498
|
+
type: "array",
|
|
3499
|
+
items: {
|
|
3500
|
+
type: "string",
|
|
3501
|
+
minLength: 1,
|
|
3502
|
+
maxLength: 300,
|
|
3503
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3504
|
+
},
|
|
3505
|
+
maxItems: 20
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
},
|
|
3509
|
+
additionalProperties: false
|
|
3510
|
+
},
|
|
2927
3511
|
version: {
|
|
2928
3512
|
type: "integer",
|
|
2929
3513
|
exclusiveMinimum: true,
|
|
@@ -3060,6 +3644,12 @@ var openapi_default = {
|
|
|
3060
3644
|
RunInput: {
|
|
3061
3645
|
type: "object",
|
|
3062
3646
|
properties: {
|
|
3647
|
+
modelPolicyVersion: {
|
|
3648
|
+
type: "number",
|
|
3649
|
+
enum: [
|
|
3650
|
+
1
|
|
3651
|
+
]
|
|
3652
|
+
},
|
|
3063
3653
|
profileId: {
|
|
3064
3654
|
type: "string",
|
|
3065
3655
|
pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$"
|
|
@@ -3088,12 +3678,112 @@ var openapi_default = {
|
|
|
3088
3678
|
minLength: 1,
|
|
3089
3679
|
maxLength: 300
|
|
3090
3680
|
},
|
|
3681
|
+
modelPolicy: {
|
|
3682
|
+
type: "object",
|
|
3683
|
+
properties: {
|
|
3684
|
+
version: {
|
|
3685
|
+
type: "number",
|
|
3686
|
+
enum: [
|
|
3687
|
+
1
|
|
3688
|
+
],
|
|
3689
|
+
default: 1
|
|
3690
|
+
},
|
|
3691
|
+
roles: {
|
|
3692
|
+
type: "object",
|
|
3693
|
+
properties: {
|
|
3694
|
+
subagent: {
|
|
3695
|
+
type: "string",
|
|
3696
|
+
minLength: 1,
|
|
3697
|
+
maxLength: 300,
|
|
3698
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3699
|
+
},
|
|
3700
|
+
fast: {
|
|
3701
|
+
type: "string",
|
|
3702
|
+
minLength: 1,
|
|
3703
|
+
maxLength: 300,
|
|
3704
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3705
|
+
},
|
|
3706
|
+
planning: {
|
|
3707
|
+
type: "string",
|
|
3708
|
+
minLength: 1,
|
|
3709
|
+
maxLength: 300,
|
|
3710
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3711
|
+
},
|
|
3712
|
+
review: {
|
|
3713
|
+
type: "string",
|
|
3714
|
+
minLength: 1,
|
|
3715
|
+
maxLength: 300,
|
|
3716
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3717
|
+
},
|
|
3718
|
+
summary: {
|
|
3719
|
+
type: "string",
|
|
3720
|
+
minLength: 1,
|
|
3721
|
+
maxLength: 300,
|
|
3722
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3723
|
+
},
|
|
3724
|
+
compaction: {
|
|
3725
|
+
type: "string",
|
|
3726
|
+
minLength: 1,
|
|
3727
|
+
maxLength: 300,
|
|
3728
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3729
|
+
},
|
|
3730
|
+
weak: {
|
|
3731
|
+
type: "string",
|
|
3732
|
+
minLength: 1,
|
|
3733
|
+
maxLength: 300,
|
|
3734
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3735
|
+
},
|
|
3736
|
+
editor: {
|
|
3737
|
+
type: "string",
|
|
3738
|
+
minLength: 1,
|
|
3739
|
+
maxLength: 300,
|
|
3740
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3741
|
+
}
|
|
3742
|
+
},
|
|
3743
|
+
additionalProperties: false
|
|
3744
|
+
},
|
|
3745
|
+
allowedModels: {
|
|
3746
|
+
type: "array",
|
|
3747
|
+
items: {
|
|
3748
|
+
type: "string",
|
|
3749
|
+
minLength: 1,
|
|
3750
|
+
maxLength: 300,
|
|
3751
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3752
|
+
},
|
|
3753
|
+
maxItems: 500
|
|
3754
|
+
},
|
|
3755
|
+
aliases: {
|
|
3756
|
+
type: "object",
|
|
3757
|
+
additionalProperties: {
|
|
3758
|
+
type: "string",
|
|
3759
|
+
minLength: 1,
|
|
3760
|
+
maxLength: 300,
|
|
3761
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3762
|
+
}
|
|
3763
|
+
},
|
|
3764
|
+
fallbacks: {
|
|
3765
|
+
type: "object",
|
|
3766
|
+
additionalProperties: {
|
|
3767
|
+
type: "array",
|
|
3768
|
+
items: {
|
|
3769
|
+
type: "string",
|
|
3770
|
+
minLength: 1,
|
|
3771
|
+
maxLength: 300,
|
|
3772
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3773
|
+
},
|
|
3774
|
+
maxItems: 20
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
},
|
|
3778
|
+
additionalProperties: false
|
|
3779
|
+
},
|
|
3091
3780
|
planToken: {
|
|
3092
3781
|
type: "string",
|
|
3093
3782
|
pattern: "^[a-f0-9]{64}$"
|
|
3094
3783
|
}
|
|
3095
3784
|
},
|
|
3096
3785
|
required: [
|
|
3786
|
+
"modelPolicyVersion",
|
|
3097
3787
|
"profileId",
|
|
3098
3788
|
"harness",
|
|
3099
3789
|
"model",
|
|
@@ -3116,6 +3806,85 @@ var openapi_default = {
|
|
|
3116
3806
|
type: "integer",
|
|
3117
3807
|
minimum: 0,
|
|
3118
3808
|
maximum: 255
|
|
3809
|
+
},
|
|
3810
|
+
routingEvents: {
|
|
3811
|
+
type: "array",
|
|
3812
|
+
items: {
|
|
3813
|
+
type: "object",
|
|
3814
|
+
properties: {
|
|
3815
|
+
at: {
|
|
3816
|
+
type: "string",
|
|
3817
|
+
format: "date-time"
|
|
3818
|
+
},
|
|
3819
|
+
requestId: {
|
|
3820
|
+
type: "string",
|
|
3821
|
+
pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
|
|
3822
|
+
},
|
|
3823
|
+
requestedModel: {
|
|
3824
|
+
type: "string",
|
|
3825
|
+
minLength: 1,
|
|
3826
|
+
maxLength: 300,
|
|
3827
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3828
|
+
},
|
|
3829
|
+
resolvedModel: {
|
|
3830
|
+
type: "string",
|
|
3831
|
+
minLength: 1,
|
|
3832
|
+
maxLength: 300,
|
|
3833
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3834
|
+
},
|
|
3835
|
+
reportedModel: {
|
|
3836
|
+
type: "string",
|
|
3837
|
+
minLength: 1,
|
|
3838
|
+
maxLength: 300,
|
|
3839
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3840
|
+
},
|
|
3841
|
+
decision: {
|
|
3842
|
+
type: "string",
|
|
3843
|
+
enum: [
|
|
3844
|
+
"allow",
|
|
3845
|
+
"alias",
|
|
3846
|
+
"reject",
|
|
3847
|
+
"fallback"
|
|
3848
|
+
]
|
|
3849
|
+
},
|
|
3850
|
+
role: {
|
|
3851
|
+
type: "string",
|
|
3852
|
+
enum: [
|
|
3853
|
+
"main",
|
|
3854
|
+
"subagent",
|
|
3855
|
+
"fast",
|
|
3856
|
+
"planning",
|
|
3857
|
+
"review",
|
|
3858
|
+
"summary",
|
|
3859
|
+
"compaction",
|
|
3860
|
+
"weak",
|
|
3861
|
+
"editor"
|
|
3862
|
+
]
|
|
3863
|
+
},
|
|
3864
|
+
reason: {
|
|
3865
|
+
type: "string",
|
|
3866
|
+
pattern: "^[a-z][a-z0-9_]{0,63}$"
|
|
3867
|
+
},
|
|
3868
|
+
upstreamStatus: {
|
|
3869
|
+
type: "integer",
|
|
3870
|
+
minimum: 100,
|
|
3871
|
+
maximum: 599
|
|
3872
|
+
}
|
|
3873
|
+
},
|
|
3874
|
+
required: [
|
|
3875
|
+
"at",
|
|
3876
|
+
"requestId",
|
|
3877
|
+
"requestedModel",
|
|
3878
|
+
"decision"
|
|
3879
|
+
],
|
|
3880
|
+
additionalProperties: false
|
|
3881
|
+
},
|
|
3882
|
+
maxItems: 1000
|
|
3883
|
+
},
|
|
3884
|
+
routingEventsDropped: {
|
|
3885
|
+
type: "integer",
|
|
3886
|
+
minimum: 0,
|
|
3887
|
+
maximum: 1e6
|
|
3119
3888
|
}
|
|
3120
3889
|
},
|
|
3121
3890
|
required: [
|
|
@@ -3127,6 +3896,12 @@ var openapi_default = {
|
|
|
3127
3896
|
Run: {
|
|
3128
3897
|
type: "object",
|
|
3129
3898
|
properties: {
|
|
3899
|
+
modelPolicyVersion: {
|
|
3900
|
+
type: "number",
|
|
3901
|
+
enum: [
|
|
3902
|
+
1
|
|
3903
|
+
]
|
|
3904
|
+
},
|
|
3130
3905
|
profileId: {
|
|
3131
3906
|
type: "string",
|
|
3132
3907
|
pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$"
|
|
@@ -3155,6 +3930,105 @@ var openapi_default = {
|
|
|
3155
3930
|
minLength: 1,
|
|
3156
3931
|
maxLength: 300
|
|
3157
3932
|
},
|
|
3933
|
+
modelPolicy: {
|
|
3934
|
+
type: "object",
|
|
3935
|
+
properties: {
|
|
3936
|
+
version: {
|
|
3937
|
+
type: "number",
|
|
3938
|
+
enum: [
|
|
3939
|
+
1
|
|
3940
|
+
],
|
|
3941
|
+
default: 1
|
|
3942
|
+
},
|
|
3943
|
+
roles: {
|
|
3944
|
+
type: "object",
|
|
3945
|
+
properties: {
|
|
3946
|
+
subagent: {
|
|
3947
|
+
type: "string",
|
|
3948
|
+
minLength: 1,
|
|
3949
|
+
maxLength: 300,
|
|
3950
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3951
|
+
},
|
|
3952
|
+
fast: {
|
|
3953
|
+
type: "string",
|
|
3954
|
+
minLength: 1,
|
|
3955
|
+
maxLength: 300,
|
|
3956
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3957
|
+
},
|
|
3958
|
+
planning: {
|
|
3959
|
+
type: "string",
|
|
3960
|
+
minLength: 1,
|
|
3961
|
+
maxLength: 300,
|
|
3962
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3963
|
+
},
|
|
3964
|
+
review: {
|
|
3965
|
+
type: "string",
|
|
3966
|
+
minLength: 1,
|
|
3967
|
+
maxLength: 300,
|
|
3968
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3969
|
+
},
|
|
3970
|
+
summary: {
|
|
3971
|
+
type: "string",
|
|
3972
|
+
minLength: 1,
|
|
3973
|
+
maxLength: 300,
|
|
3974
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3975
|
+
},
|
|
3976
|
+
compaction: {
|
|
3977
|
+
type: "string",
|
|
3978
|
+
minLength: 1,
|
|
3979
|
+
maxLength: 300,
|
|
3980
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3981
|
+
},
|
|
3982
|
+
weak: {
|
|
3983
|
+
type: "string",
|
|
3984
|
+
minLength: 1,
|
|
3985
|
+
maxLength: 300,
|
|
3986
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3987
|
+
},
|
|
3988
|
+
editor: {
|
|
3989
|
+
type: "string",
|
|
3990
|
+
minLength: 1,
|
|
3991
|
+
maxLength: 300,
|
|
3992
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
3993
|
+
}
|
|
3994
|
+
},
|
|
3995
|
+
additionalProperties: false
|
|
3996
|
+
},
|
|
3997
|
+
allowedModels: {
|
|
3998
|
+
type: "array",
|
|
3999
|
+
items: {
|
|
4000
|
+
type: "string",
|
|
4001
|
+
minLength: 1,
|
|
4002
|
+
maxLength: 300,
|
|
4003
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
4004
|
+
},
|
|
4005
|
+
maxItems: 500
|
|
4006
|
+
},
|
|
4007
|
+
aliases: {
|
|
4008
|
+
type: "object",
|
|
4009
|
+
additionalProperties: {
|
|
4010
|
+
type: "string",
|
|
4011
|
+
minLength: 1,
|
|
4012
|
+
maxLength: 300,
|
|
4013
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
4014
|
+
}
|
|
4015
|
+
},
|
|
4016
|
+
fallbacks: {
|
|
4017
|
+
type: "object",
|
|
4018
|
+
additionalProperties: {
|
|
4019
|
+
type: "array",
|
|
4020
|
+
items: {
|
|
4021
|
+
type: "string",
|
|
4022
|
+
minLength: 1,
|
|
4023
|
+
maxLength: 300,
|
|
4024
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
4025
|
+
},
|
|
4026
|
+
maxItems: 20
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
},
|
|
4030
|
+
additionalProperties: false
|
|
4031
|
+
},
|
|
3158
4032
|
planToken: {
|
|
3159
4033
|
type: "string",
|
|
3160
4034
|
pattern: "^[a-f0-9]{64}$"
|
|
@@ -3202,6 +4076,85 @@ var openapi_default = {
|
|
|
3202
4076
|
},
|
|
3203
4077
|
exitCode: {
|
|
3204
4078
|
type: "integer"
|
|
4079
|
+
},
|
|
4080
|
+
routingEvents: {
|
|
4081
|
+
type: "array",
|
|
4082
|
+
items: {
|
|
4083
|
+
type: "object",
|
|
4084
|
+
properties: {
|
|
4085
|
+
at: {
|
|
4086
|
+
type: "string",
|
|
4087
|
+
format: "date-time"
|
|
4088
|
+
},
|
|
4089
|
+
requestId: {
|
|
4090
|
+
type: "string",
|
|
4091
|
+
pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
|
|
4092
|
+
},
|
|
4093
|
+
requestedModel: {
|
|
4094
|
+
type: "string",
|
|
4095
|
+
minLength: 1,
|
|
4096
|
+
maxLength: 300,
|
|
4097
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
4098
|
+
},
|
|
4099
|
+
resolvedModel: {
|
|
4100
|
+
type: "string",
|
|
4101
|
+
minLength: 1,
|
|
4102
|
+
maxLength: 300,
|
|
4103
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
4104
|
+
},
|
|
4105
|
+
reportedModel: {
|
|
4106
|
+
type: "string",
|
|
4107
|
+
minLength: 1,
|
|
4108
|
+
maxLength: 300,
|
|
4109
|
+
pattern: "^[^\\u0000-\\u001f\\u007f]+$"
|
|
4110
|
+
},
|
|
4111
|
+
decision: {
|
|
4112
|
+
type: "string",
|
|
4113
|
+
enum: [
|
|
4114
|
+
"allow",
|
|
4115
|
+
"alias",
|
|
4116
|
+
"reject",
|
|
4117
|
+
"fallback"
|
|
4118
|
+
]
|
|
4119
|
+
},
|
|
4120
|
+
role: {
|
|
4121
|
+
type: "string",
|
|
4122
|
+
enum: [
|
|
4123
|
+
"main",
|
|
4124
|
+
"subagent",
|
|
4125
|
+
"fast",
|
|
4126
|
+
"planning",
|
|
4127
|
+
"review",
|
|
4128
|
+
"summary",
|
|
4129
|
+
"compaction",
|
|
4130
|
+
"weak",
|
|
4131
|
+
"editor"
|
|
4132
|
+
]
|
|
4133
|
+
},
|
|
4134
|
+
reason: {
|
|
4135
|
+
type: "string",
|
|
4136
|
+
pattern: "^[a-z][a-z0-9_]{0,63}$"
|
|
4137
|
+
},
|
|
4138
|
+
upstreamStatus: {
|
|
4139
|
+
type: "integer",
|
|
4140
|
+
minimum: 100,
|
|
4141
|
+
maximum: 599
|
|
4142
|
+
}
|
|
4143
|
+
},
|
|
4144
|
+
required: [
|
|
4145
|
+
"at",
|
|
4146
|
+
"requestId",
|
|
4147
|
+
"requestedModel",
|
|
4148
|
+
"decision"
|
|
4149
|
+
],
|
|
4150
|
+
additionalProperties: false
|
|
4151
|
+
},
|
|
4152
|
+
maxItems: 1000
|
|
4153
|
+
},
|
|
4154
|
+
routingEventsDropped: {
|
|
4155
|
+
type: "integer",
|
|
4156
|
+
minimum: 0,
|
|
4157
|
+
maximum: 1e6
|
|
3205
4158
|
}
|
|
3206
4159
|
},
|
|
3207
4160
|
required: [
|
|
@@ -3328,8 +4281,8 @@ var openapi_default = {
|
|
|
3328
4281
|
};
|
|
3329
4282
|
|
|
3330
4283
|
// src/service.ts
|
|
3331
|
-
var snapshot = (profile, provider, catalog) =>
|
|
3332
|
-
var hash = (s) =>
|
|
4284
|
+
var snapshot = (profile, provider, catalog) => createHash2("sha256").update(JSON.stringify([profile, provider, { models: catalog.models, source: catalog.source }])).digest("hex");
|
|
4285
|
+
var hash = (s) => createHash2("sha256").update(s).digest();
|
|
3333
4286
|
function createHandler(store, apiKey, providerEnv = process.env, resolveCredential) {
|
|
3334
4287
|
if (!apiKey || apiKey.length < 24)
|
|
3335
4288
|
throw new Fault(500, "auth_config", "Set HASNA_SWITCHER_API_KEY to a random token of at least 24 characters.");
|
|
@@ -3360,23 +4313,23 @@ function createHandler(store, apiKey, providerEnv = process.env, resolveCredenti
|
|
|
3360
4313
|
if (parts[0] !== "v1")
|
|
3361
4314
|
throw new Fault(404, "not_found", "Route was not found.");
|
|
3362
4315
|
const resource = parts[1];
|
|
3363
|
-
const
|
|
3364
|
-
if (
|
|
3365
|
-
parse(idSchema,
|
|
3366
|
-
const page = () => parse(
|
|
3367
|
-
limit:
|
|
3368
|
-
offset:
|
|
3369
|
-
search:
|
|
4316
|
+
const id2 = parts[2];
|
|
4317
|
+
if (id2)
|
|
4318
|
+
parse(idSchema, id2);
|
|
4319
|
+
const page = () => parse(z3.object({
|
|
4320
|
+
limit: z3.coerce.number().int().min(1).max(1000).default(100),
|
|
4321
|
+
offset: z3.coerce.number().int().min(0).max(1e6).default(0),
|
|
4322
|
+
search: z3.string().max(200).default("")
|
|
3370
4323
|
}).strict(), Object.fromEntries(url.searchParams));
|
|
3371
4324
|
if (request.method === "GET") {
|
|
3372
4325
|
if (resource === "provider-presets" && parts.length <= 3)
|
|
3373
|
-
return json(
|
|
4326
|
+
return json(id2 ? getProviderPreset(id2) : { data: providerPresets });
|
|
3374
4327
|
if (["providers", "profiles", "runs"].includes(resource) && parts.length <= 3) {
|
|
3375
4328
|
const kind = resource;
|
|
3376
|
-
return json(
|
|
4329
|
+
return json(id2 ? await store.get(kind, id2) : await store.list(kind, page()));
|
|
3377
4330
|
}
|
|
3378
|
-
if (resource === "providers" &&
|
|
3379
|
-
const catalog = await store.get("catalogs",
|
|
4331
|
+
if (resource === "providers" && id2 && parts[3] === "models" && parts.length === 4) {
|
|
4332
|
+
const catalog = await store.get("catalogs", id2);
|
|
3380
4333
|
const p = page();
|
|
3381
4334
|
const filtered = catalog.models.filter((m) => [m.id, m.name].some((s) => s.toLowerCase().includes(p.search.toLowerCase())));
|
|
3382
4335
|
return json({ ...catalog, models: undefined, data: filtered.slice(p.offset, p.offset + p.limit).map((m) => ({ ...m, codingEligible: codingEligible(m) })), total: filtered.length, ...p });
|
|
@@ -3407,50 +4360,50 @@ function createHandler(store, apiKey, providerEnv = process.env, resolveCredenti
|
|
|
3407
4360
|
};
|
|
3408
4361
|
const replay = await store.replay(key, fingerprint);
|
|
3409
4362
|
if (replay.found)
|
|
3410
|
-
return json(replay.value, request.method === "POST" && ["providers", "profiles", "runs"].includes(resource) && !
|
|
4363
|
+
return json(replay.value, request.method === "POST" && ["providers", "profiles", "runs"].includes(resource) && !id2 ? 201 : 200);
|
|
3411
4364
|
let refreshed;
|
|
3412
|
-
if (resource === "providers" &&
|
|
3413
|
-
parse(
|
|
3414
|
-
const provider = await store.get("providers",
|
|
4365
|
+
if (resource === "providers" && id2 && parts[3] === "refresh" && parts.length === 4 && request.method === "POST") {
|
|
4366
|
+
parse(z3.object({}).strict(), body);
|
|
4367
|
+
const provider = await store.get("providers", id2);
|
|
3415
4368
|
refreshed = { provider, catalog: await discover(provider, providerEnv, resolveCredential) };
|
|
3416
4369
|
}
|
|
3417
4370
|
const result = await store.mutate(key, fingerprint, async (db) => {
|
|
3418
4371
|
if ((resource === "providers" || resource === "profiles") && parts.length <= 3) {
|
|
3419
|
-
if (request.method === "DELETE" &&
|
|
3420
|
-
return store.remove(resource,
|
|
3421
|
-
if (request.method === "POST" && !
|
|
4372
|
+
if (request.method === "DELETE" && id2)
|
|
4373
|
+
return store.remove(resource, id2, version(), db);
|
|
4374
|
+
if (request.method === "POST" && !id2 || request.method === "PUT" && id2) {
|
|
3422
4375
|
const value = resource === "providers" ? parse(providerInputSchema, body) : parse(profileInputSchema, body);
|
|
3423
|
-
if (
|
|
4376
|
+
if (id2 && value.id !== id2)
|
|
3424
4377
|
throw new Fault(400, "id_mismatch", "Path and body IDs must match.");
|
|
3425
4378
|
if (resource === "profiles") {
|
|
3426
4379
|
const profile = value;
|
|
3427
4380
|
const provider = await store.get("providers", profile.providerId, db);
|
|
3428
4381
|
validateHarnessProvider(profile.harness, provider);
|
|
3429
4382
|
}
|
|
3430
|
-
const saved = await store.put(resource, value,
|
|
3431
|
-
if (resource === "providers" &&
|
|
3432
|
-
await db.unsafe("DELETE FROM switcher_catalogs WHERE id = $1", [
|
|
4383
|
+
const saved = await store.put(resource, value, id2 ? version() : undefined, db);
|
|
4384
|
+
if (resource === "providers" && id2)
|
|
4385
|
+
await db.unsafe("DELETE FROM switcher_catalogs WHERE id = $1", [id2]);
|
|
3433
4386
|
return saved;
|
|
3434
4387
|
}
|
|
3435
4388
|
}
|
|
3436
|
-
if (resource === "providers" &&
|
|
4389
|
+
if (resource === "providers" && id2 && parts[3] === "refresh" && parts.length === 4 && request.method === "POST") {
|
|
3437
4390
|
if (store.engine === "postgresql")
|
|
3438
|
-
await db.unsafe("SELECT id FROM switcher_providers WHERE id = $1 FOR SHARE", [
|
|
3439
|
-
const provider = await store.get("providers",
|
|
4391
|
+
await db.unsafe("SELECT id FROM switcher_providers WHERE id = $1 FOR SHARE", [id2]);
|
|
4392
|
+
const provider = await store.get("providers", id2, db);
|
|
3440
4393
|
if (!refreshed || provider.version !== refreshed.provider.version)
|
|
3441
4394
|
throw new Fault(409, "provider_changed", "Provider changed during discovery; refresh again.");
|
|
3442
4395
|
const catalog = refreshed.catalog;
|
|
3443
4396
|
let old;
|
|
3444
4397
|
try {
|
|
3445
|
-
old = await store.get("catalogs",
|
|
4398
|
+
old = await store.get("catalogs", id2, db);
|
|
3446
4399
|
} catch (e) {
|
|
3447
4400
|
if (!(e instanceof Fault && e.status === 404))
|
|
3448
4401
|
throw e;
|
|
3449
4402
|
}
|
|
3450
|
-
return store.put("catalogs", { id, ...catalog }, old?.version, db);
|
|
4403
|
+
return store.put("catalogs", { id: id2, ...catalog }, old?.version, db);
|
|
3451
4404
|
}
|
|
3452
|
-
if (resource === "launch-plans" && !
|
|
3453
|
-
const { profileId } = parse(
|
|
4405
|
+
if (resource === "launch-plans" && !id2 && request.method === "POST") {
|
|
4406
|
+
const { profileId } = parse(z3.object({ profileId: idSchema }).strict(), body);
|
|
3454
4407
|
const profile = await store.get("profiles", profileId, db);
|
|
3455
4408
|
const provider = await store.get("providers", profile.providerId, db);
|
|
3456
4409
|
validateHarnessProvider(profile.harness, provider);
|
|
@@ -3467,6 +4420,7 @@ function createHandler(store, apiKey, providerEnv = process.env, resolveCredenti
|
|
|
3467
4420
|
throw new Fault(422, "model_missing", "Selected model is not in the provider catalog.");
|
|
3468
4421
|
if (!harnessEligible(selected, profile.harness))
|
|
3469
4422
|
throw new Fault(422, "model_ineligible", "Selected model is unavailable or explicitly lacks a required generation method, text output or tool support.");
|
|
4423
|
+
compileModelPolicy(profile.model, catalog.models.filter((model) => harnessEligible(model, profile.harness)), profile.modelPolicy);
|
|
3470
4424
|
const warnings = [];
|
|
3471
4425
|
if (profile.harness !== "aider" && !selected.supportedParameters)
|
|
3472
4426
|
warnings.push("Provider does not declare tool capabilities; execution compatibility is unverified.");
|
|
@@ -3476,7 +4430,9 @@ function createHandler(store, apiKey, providerEnv = process.env, resolveCredenti
|
|
|
3476
4430
|
warnings.push("Catalog snapshot is older than five minutes; refresh before launching.");
|
|
3477
4431
|
return { profile, provider, catalog, warnings, planToken: snapshot(profile, provider, catalog) };
|
|
3478
4432
|
}
|
|
3479
|
-
if (resource === "runs" && !
|
|
4433
|
+
if (resource === "runs" && !id2 && request.method === "POST") {
|
|
4434
|
+
if (body?.modelPolicyVersion !== 1)
|
|
4435
|
+
throw new Fault(409, "launcher_upgrade_required", "This API requires a launcher with automatic model policy version 1; upgrade the Switcher CLI/SDK.");
|
|
3480
4436
|
const input = parse(runInputSchema, body);
|
|
3481
4437
|
if (store.engine === "postgresql")
|
|
3482
4438
|
await db.unsafe("SELECT id FROM switcher_profiles WHERE id = $1 FOR SHARE", [input.profileId]);
|
|
@@ -3494,20 +4450,20 @@ function createHandler(store, apiKey, providerEnv = process.env, resolveCredenti
|
|
|
3494
4450
|
throw new Fault(409, "plan_changed", "Catalog changed; request a fresh launch plan.");
|
|
3495
4451
|
throw error;
|
|
3496
4452
|
}
|
|
3497
|
-
if (profile.harness !== input.harness || profile.model !== input.model || snapshot(profile, provider, catalog) !== input.planToken)
|
|
3498
|
-
throw new Fault(409, "plan_changed", "Provider, profile or catalog changed; request a fresh launch plan.");
|
|
3499
|
-
return store.put("runs", { ...input, providerId: provider.id, providerVersion: provider.version, profileVersion: profile.version, id: crypto.randomUUID(), status: "running", startedAt: new Date().toISOString() }, undefined, db);
|
|
4453
|
+
if (profile.harness !== input.harness || profile.model !== input.model || canonicalPolicyJSON(profile.modelPolicy ?? null) !== canonicalPolicyJSON(input.modelPolicy ?? profile.modelPolicy ?? null) || snapshot(profile, provider, catalog) !== input.planToken)
|
|
4454
|
+
throw new Fault(409, "plan_changed", "Provider, profile, model policy or catalog changed; request a fresh launch plan.");
|
|
4455
|
+
return store.put("runs", { ...input, modelPolicy: profile.modelPolicy, providerId: provider.id, providerVersion: provider.version, profileVersion: profile.version, id: crypto.randomUUID(), status: "running", startedAt: new Date().toISOString() }, undefined, db);
|
|
3500
4456
|
}
|
|
3501
|
-
if (resource === "runs" &&
|
|
4457
|
+
if (resource === "runs" && id2 && request.method === "PATCH" && parts.length === 3) {
|
|
3502
4458
|
const input = parse(runUpdateSchema, body);
|
|
3503
|
-
const run = await store.get("runs",
|
|
4459
|
+
const run = await store.get("runs", id2, db);
|
|
3504
4460
|
if (run.status !== "running")
|
|
3505
4461
|
throw new Fault(409, "run_finished", "Run has already finished.");
|
|
3506
4462
|
return store.put("runs", { ...run, ...input, endedAt: new Date().toISOString() }, version(), db);
|
|
3507
4463
|
}
|
|
3508
4464
|
throw new Fault(404, "not_found", "Route was not found.");
|
|
3509
4465
|
});
|
|
3510
|
-
return json(result, request.method === "POST" && ["providers", "profiles", "runs"].includes(resource) && !
|
|
4466
|
+
return json(result, request.method === "POST" && ["providers", "profiles", "runs"].includes(resource) && !id2 ? 201 : 200);
|
|
3511
4467
|
} catch (error) {
|
|
3512
4468
|
const safe = error instanceof Fault ? error : new Fault(500, "internal_error", "Request failed.");
|
|
3513
4469
|
return json({ error: { code: safe.code, message: safe.message, requestId } }, safe.status);
|