@mcoda/codali 0.1.87 → 0.1.89
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/cli/EvalCommand.d.ts +8 -0
- package/dist/cli/EvalCommand.d.ts.map +1 -1
- package/dist/cli/EvalCommand.js +93 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/docdex/DocdexClient.d.ts +8 -1
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +126 -33
- package/dist/eval/CodaliGatewayLiveHarness.d.ts +169 -0
- package/dist/eval/CodaliGatewayLiveHarness.d.ts.map +1 -0
- package/dist/eval/CodaliGatewayLiveHarness.js +824 -0
- package/dist/eval/GatewayEvalSuite.d.ts +202 -0
- package/dist/eval/GatewayEvalSuite.d.ts.map +1 -0
- package/dist/eval/GatewayEvalSuite.js +673 -0
- package/dist/gateway/AgentTierResolver.d.ts +74 -0
- package/dist/gateway/AgentTierResolver.d.ts.map +1 -0
- package/dist/gateway/AgentTierResolver.js +576 -0
- package/dist/gateway/AppToolGatewayDispatcher.d.ts +88 -0
- package/dist/gateway/AppToolGatewayDispatcher.d.ts.map +1 -0
- package/dist/gateway/AppToolGatewayDispatcher.js +381 -0
- package/dist/gateway/CodaliGateway.d.ts +73 -0
- package/dist/gateway/CodaliGateway.d.ts.map +1 -0
- package/dist/gateway/CodaliGateway.js +824 -0
- package/dist/gateway/CodaliGatewaySchemas.d.ts +21 -0
- package/dist/gateway/CodaliGatewaySchemas.d.ts.map +1 -0
- package/dist/gateway/CodaliGatewaySchemas.js +874 -0
- package/dist/gateway/CodaliGatewayStore.d.ts +157 -0
- package/dist/gateway/CodaliGatewayStore.d.ts.map +1 -0
- package/dist/gateway/CodaliGatewayStore.js +206 -0
- package/dist/gateway/CodaliGatewayTypes.d.ts +336 -0
- package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -0
- package/dist/gateway/CodaliGatewayTypes.js +1 -0
- package/dist/gateway/ContextPackBuilder.d.ts +43 -0
- package/dist/gateway/ContextPackBuilder.d.ts.map +1 -0
- package/dist/gateway/ContextPackBuilder.js +317 -0
- package/dist/gateway/EvidenceNormalizer.d.ts +42 -0
- package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -0
- package/dist/gateway/EvidenceNormalizer.js +488 -0
- package/dist/gateway/GatewayPlanner.d.ts +195 -0
- package/dist/gateway/GatewayPlanner.d.ts.map +1 -0
- package/dist/gateway/GatewayPlanner.js +379 -0
- package/dist/gateway/GatewayPolicyCompiler.d.ts +30 -0
- package/dist/gateway/GatewayPolicyCompiler.d.ts.map +1 -0
- package/dist/gateway/GatewayPolicyCompiler.js +114 -0
- package/dist/gateway/GatewaySecurityPolicy.d.ts +14 -0
- package/dist/gateway/GatewaySecurityPolicy.d.ts.map +1 -0
- package/dist/gateway/GatewaySecurityPolicy.js +350 -0
- package/dist/gateway/GatewayStateMachine.d.ts +165 -0
- package/dist/gateway/GatewayStateMachine.d.ts.map +1 -0
- package/dist/gateway/GatewayStateMachine.js +790 -0
- package/dist/gateway/GatewayTraceReplay.d.ts +120 -0
- package/dist/gateway/GatewayTraceReplay.d.ts.map +1 -0
- package/dist/gateway/GatewayTraceReplay.js +273 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts +50 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts.map +1 -0
- package/dist/gateway/ToolCapabilityCompiler.js +442 -0
- package/dist/index.d.ts +33 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/runtime/CodaliJobRuntime.d.ts +211 -0
- package/dist/runtime/CodaliJobRuntime.d.ts.map +1 -0
- package/dist/runtime/CodaliJobRuntime.js +590 -0
- package/dist/runtime/CodaliRuntime.d.ts +81 -1
- package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
- package/dist/runtime/CodaliRuntime.js +619 -4
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +4 -0
- package/dist/tools/ToolTypes.d.ts +1 -1
- package/dist/tools/ToolTypes.d.ts.map +1 -1
- package/dist/tools/ToolTypes.js +5 -1
- package/package.json +3 -3
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { CodaliRuntimeAgentInput } from "../runtime/CodaliRuntime.js";
|
|
2
|
+
import type { CodaliAgentRolePolicy, CodaliAgentTierPolicy, CodaliGatewayModelTier } from "./CodaliGatewayTypes.js";
|
|
3
|
+
export type CodaliGatewayAgentSource = "local" | "self_hosted" | "worker" | "cloud" | "unknown";
|
|
4
|
+
export type CodaliGatewayAgentHealth = "healthy" | "degraded" | "unreachable" | "limited" | "unknown";
|
|
5
|
+
export interface CodaliGatewayAgentCandidate {
|
|
6
|
+
id?: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
adapter: string;
|
|
9
|
+
provider?: string;
|
|
10
|
+
model: string;
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
runnerKind?: string;
|
|
13
|
+
source: CodaliGatewayAgentSource;
|
|
14
|
+
healthStatus: CodaliGatewayAgentHealth;
|
|
15
|
+
latencyMs?: number;
|
|
16
|
+
contextWindow?: number;
|
|
17
|
+
maxOutputTokens?: number;
|
|
18
|
+
supportsTools?: boolean;
|
|
19
|
+
supportsJsonSchema?: boolean;
|
|
20
|
+
supportsImageGeneration?: boolean;
|
|
21
|
+
supportsArtifacts?: boolean;
|
|
22
|
+
supportsStreaming?: boolean;
|
|
23
|
+
capabilities: string[];
|
|
24
|
+
bestUsage?: string;
|
|
25
|
+
rating?: number;
|
|
26
|
+
reasoningRating?: number;
|
|
27
|
+
costPerMillion?: number;
|
|
28
|
+
maxComplexity?: number;
|
|
29
|
+
tier: CodaliGatewayModelTier;
|
|
30
|
+
raw?: unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface CodaliGatewayAgentCandidateDiagnostic {
|
|
33
|
+
role: string;
|
|
34
|
+
slug: string;
|
|
35
|
+
tier: CodaliGatewayModelTier;
|
|
36
|
+
source: CodaliGatewayAgentSource;
|
|
37
|
+
eligible: boolean;
|
|
38
|
+
score?: number;
|
|
39
|
+
reasons: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface CodaliGatewayAgentTierError {
|
|
42
|
+
code: string;
|
|
43
|
+
message: string;
|
|
44
|
+
role?: string;
|
|
45
|
+
details?: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
export interface CodaliGatewayAgentAssignment {
|
|
48
|
+
role: string;
|
|
49
|
+
policy: CodaliAgentRolePolicy;
|
|
50
|
+
candidate: CodaliGatewayAgentCandidate;
|
|
51
|
+
agent: CodaliRuntimeAgentInput;
|
|
52
|
+
score: number;
|
|
53
|
+
reasons: string[];
|
|
54
|
+
}
|
|
55
|
+
export interface AgentTierResolverInput {
|
|
56
|
+
inventory: unknown[];
|
|
57
|
+
agentPolicy?: CodaliAgentTierPolicy;
|
|
58
|
+
roles?: string[];
|
|
59
|
+
allowImageWorker?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface AgentTierResolution {
|
|
62
|
+
ok: boolean;
|
|
63
|
+
assignments: Record<string, CodaliGatewayAgentAssignment>;
|
|
64
|
+
candidates: CodaliGatewayAgentCandidate[];
|
|
65
|
+
diagnostics: CodaliGatewayAgentCandidateDiagnostic[];
|
|
66
|
+
warnings: CodaliGatewayAgentTierError[];
|
|
67
|
+
errors: CodaliGatewayAgentTierError[];
|
|
68
|
+
}
|
|
69
|
+
export declare const DEFAULT_CODALI_GATEWAY_AGENT_ROLES: readonly ["classifier", "planner", "rag_worker", "tool_worker", "verifier", "context_refiner", "final_synthesizer"];
|
|
70
|
+
export declare const DEFAULT_CODALI_GATEWAY_ROLE_POLICIES: Record<string, CodaliAgentRolePolicy>;
|
|
71
|
+
export declare const normalizeCodaliGatewayAgentCandidate: (input: unknown) => CodaliGatewayAgentCandidate | undefined;
|
|
72
|
+
export declare const resolveCodaliGatewayAgentTiers: (input: AgentTierResolverInput) => AgentTierResolution;
|
|
73
|
+
export declare const resolveGatewayAgentTiers: (input: AgentTierResolverInput) => AgentTierResolution;
|
|
74
|
+
//# sourceMappingURL=AgentTierResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentTierResolver.d.ts","sourceRoot":"","sources":["../../src/gateway/AgentTierResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
export const DEFAULT_CODALI_GATEWAY_AGENT_ROLES = [
|
|
2
|
+
"classifier",
|
|
3
|
+
"planner",
|
|
4
|
+
"rag_worker",
|
|
5
|
+
"tool_worker",
|
|
6
|
+
"verifier",
|
|
7
|
+
"context_refiner",
|
|
8
|
+
"final_synthesizer",
|
|
9
|
+
];
|
|
10
|
+
export const DEFAULT_CODALI_GATEWAY_ROLE_POLICIES = {
|
|
11
|
+
classifier: { tier: "small", requiresJsonSchema: true },
|
|
12
|
+
context_refiner: { tier: "medium", requiresJsonSchema: true, minContextWindow: 8000 },
|
|
13
|
+
extractor: { tier: "small", requiresJsonSchema: true },
|
|
14
|
+
final_synthesizer: { tier: "large", minContextWindow: 16000 },
|
|
15
|
+
image_worker: { tier: "image" },
|
|
16
|
+
planner: { tier: "medium", requiresJsonSchema: true, minContextWindow: 8000 },
|
|
17
|
+
query_expander: { tier: "small", requiresJsonSchema: true },
|
|
18
|
+
rag_worker: { tier: "medium", requiresTools: true, minContextWindow: 8000 },
|
|
19
|
+
repair: { tier: "medium", requiresJsonSchema: true },
|
|
20
|
+
router: { tier: "small", requiresJsonSchema: true },
|
|
21
|
+
tool_worker: { tier: "medium", requiresTools: true },
|
|
22
|
+
verifier: { tier: "medium", requiresJsonSchema: true, minContextWindow: 8000 },
|
|
23
|
+
};
|
|
24
|
+
const LOCAL_ADAPTER_HINTS = new Set([
|
|
25
|
+
"codex-cli",
|
|
26
|
+
"llama-cpp",
|
|
27
|
+
"local-model",
|
|
28
|
+
"ollama-cli",
|
|
29
|
+
"ollama-remote",
|
|
30
|
+
"openai-compatible-local",
|
|
31
|
+
"openai-cli",
|
|
32
|
+
"vllm",
|
|
33
|
+
]);
|
|
34
|
+
const STRUCTURED_OUTPUT_CAPABILITIES = new Set([
|
|
35
|
+
"json_formatting",
|
|
36
|
+
"json_schema",
|
|
37
|
+
"schema_adherence",
|
|
38
|
+
"strict_instruction_following",
|
|
39
|
+
"structured_output",
|
|
40
|
+
]);
|
|
41
|
+
const IMAGE_CAPABILITIES = new Set([
|
|
42
|
+
"image",
|
|
43
|
+
"image_generation",
|
|
44
|
+
"image_generation_llm",
|
|
45
|
+
"text_to_image",
|
|
46
|
+
]);
|
|
47
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
48
|
+
const normalizeString = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
49
|
+
const readString = (record, keys) => {
|
|
50
|
+
for (const key of keys) {
|
|
51
|
+
const value = normalizeString(record[key]);
|
|
52
|
+
if (value) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
};
|
|
58
|
+
const readNumber = (record, keys) => {
|
|
59
|
+
for (const key of keys) {
|
|
60
|
+
const value = record[key];
|
|
61
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
};
|
|
67
|
+
const readBoolean = (record, keys) => {
|
|
68
|
+
for (const key of keys) {
|
|
69
|
+
const value = record[key];
|
|
70
|
+
if (typeof value === "boolean") {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
};
|
|
76
|
+
const readRecord = (record, keys) => {
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
const value = record[key];
|
|
79
|
+
if (isRecord(value)) {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
};
|
|
85
|
+
const normalizeCapabilities = (value) => {
|
|
86
|
+
if (!Array.isArray(value)) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const seen = new Set();
|
|
90
|
+
for (const item of value) {
|
|
91
|
+
const capability = normalizeString(item)?.toLowerCase();
|
|
92
|
+
if (capability) {
|
|
93
|
+
seen.add(capability);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return [...seen].sort();
|
|
97
|
+
};
|
|
98
|
+
const readDefaultModel = (record) => {
|
|
99
|
+
const direct = readString(record, ["model", "defaultModel", "default_model"]);
|
|
100
|
+
if (direct) {
|
|
101
|
+
return direct;
|
|
102
|
+
}
|
|
103
|
+
const models = record.models;
|
|
104
|
+
if (!Array.isArray(models)) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
for (const model of models) {
|
|
108
|
+
if (!isRecord(model)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const modelName = readString(model, ["modelName", "model_name", "name"]);
|
|
112
|
+
if (modelName && model.isDefault === true) {
|
|
113
|
+
return modelName;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
for (const model of models) {
|
|
117
|
+
if (!isRecord(model)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const modelName = readString(model, ["modelName", "model_name", "name"]);
|
|
121
|
+
if (modelName) {
|
|
122
|
+
return modelName;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
};
|
|
127
|
+
const normalizeHealth = (record) => {
|
|
128
|
+
const health = readRecord(record, ["health"]);
|
|
129
|
+
const rawStatus = readString(record, ["healthStatus", "health_status", "status"]) ??
|
|
130
|
+
(health ? readString(health, ["status"]) : undefined);
|
|
131
|
+
const normalized = rawStatus?.toLowerCase();
|
|
132
|
+
const status = normalized === "healthy" ||
|
|
133
|
+
normalized === "degraded" ||
|
|
134
|
+
normalized === "unreachable" ||
|
|
135
|
+
normalized === "limited"
|
|
136
|
+
? normalized
|
|
137
|
+
: "unknown";
|
|
138
|
+
return {
|
|
139
|
+
status,
|
|
140
|
+
latencyMs: (health ? readNumber(health, ["latencyMs", "latency_ms"]) : undefined) ??
|
|
141
|
+
readNumber(record, ["latencyMs", "latency_ms"]),
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
const normalizeSource = (record, slug, adapter) => {
|
|
145
|
+
const config = readRecord(record, ["config"]);
|
|
146
|
+
const adapterKey = adapter.toLowerCase();
|
|
147
|
+
if (slug.startsWith("mswarm-cloud-") ||
|
|
148
|
+
Boolean(config && readRecord(config, ["mswarmCloud", "mswarm_cloud"]))) {
|
|
149
|
+
return "cloud";
|
|
150
|
+
}
|
|
151
|
+
if (slug.startsWith("mswarm-self-hosted-") ||
|
|
152
|
+
Boolean(config && readRecord(config, ["mswarmSelfHosted", "mswarm_self_hosted"]))) {
|
|
153
|
+
return "self_hosted";
|
|
154
|
+
}
|
|
155
|
+
if (adapterKey === "mswarm-worker" ||
|
|
156
|
+
Boolean(config && readRecord(config, ["mswarmWorker", "mswarm_worker"]))) {
|
|
157
|
+
return "worker";
|
|
158
|
+
}
|
|
159
|
+
if (LOCAL_ADAPTER_HINTS.has(adapterKey) ||
|
|
160
|
+
Boolean(config && readRecord(config, ["localRunner", "local_runner"]))) {
|
|
161
|
+
return "local";
|
|
162
|
+
}
|
|
163
|
+
return "unknown";
|
|
164
|
+
};
|
|
165
|
+
const normalizeProvider = (adapter, explicit) => {
|
|
166
|
+
if (explicit) {
|
|
167
|
+
return explicit;
|
|
168
|
+
}
|
|
169
|
+
const normalized = adapter.toLowerCase();
|
|
170
|
+
if (normalized === "openai-api")
|
|
171
|
+
return "openai-compatible";
|
|
172
|
+
if (normalized === "mswarm-worker")
|
|
173
|
+
return "mswarm-worker";
|
|
174
|
+
if (normalized === "codex-cli" || normalized === "openai-cli")
|
|
175
|
+
return "codex-cli";
|
|
176
|
+
if (normalized === "ollama-cli" ||
|
|
177
|
+
normalized === "ollama-remote" ||
|
|
178
|
+
normalized === "local-model" ||
|
|
179
|
+
normalized === "llama-cpp" ||
|
|
180
|
+
normalized === "vllm") {
|
|
181
|
+
return "ollama-remote";
|
|
182
|
+
}
|
|
183
|
+
return undefined;
|
|
184
|
+
};
|
|
185
|
+
const containsNestedSelfHostedMarker = (value) => {
|
|
186
|
+
if (!value)
|
|
187
|
+
return false;
|
|
188
|
+
const normalized = value.toLowerCase();
|
|
189
|
+
return (normalized.includes("mswarm-self-hosted-") &&
|
|
190
|
+
(normalized.indexOf("mswarm-self-hosted-") !== normalized.lastIndexOf("mswarm-self-hosted-") ||
|
|
191
|
+
normalized.includes("/mswarm-self-hosted-") ||
|
|
192
|
+
!normalized.startsWith("mswarm-self-hosted-")));
|
|
193
|
+
};
|
|
194
|
+
const isNestedSelfHostedRelayCandidate = (candidate) => {
|
|
195
|
+
if (candidate.source !== "self_hosted") {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const raw = isRecord(candidate.raw) ? candidate.raw : undefined;
|
|
199
|
+
const config = raw ? readRecord(raw, ["config"]) : undefined;
|
|
200
|
+
const selfHosted = config
|
|
201
|
+
? readRecord(config, ["mswarmSelfHosted", "mswarm_self_hosted"])
|
|
202
|
+
: undefined;
|
|
203
|
+
return [
|
|
204
|
+
candidate.slug,
|
|
205
|
+
candidate.model,
|
|
206
|
+
selfHosted ? readString(selfHosted, ["remoteSlug", "remote_slug"]) : undefined,
|
|
207
|
+
selfHosted ? readString(selfHosted, ["agentSlug", "agent_slug"]) : undefined,
|
|
208
|
+
selfHosted ? readString(selfHosted, ["sourceAgentSlug", "source_agent_slug"]) : undefined,
|
|
209
|
+
].some(containsNestedSelfHostedMarker);
|
|
210
|
+
};
|
|
211
|
+
const readBaseUrl = (record) => {
|
|
212
|
+
const direct = readString(record, ["baseUrl", "base_url", "endpoint", "apiBaseUrl"]);
|
|
213
|
+
if (direct) {
|
|
214
|
+
return direct;
|
|
215
|
+
}
|
|
216
|
+
const config = readRecord(record, ["config"]);
|
|
217
|
+
if (!config) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
const localRunner = readRecord(config, ["localRunner", "local_runner"]);
|
|
221
|
+
const worker = readRecord(config, ["mswarmWorker", "mswarm_worker"]);
|
|
222
|
+
return ((localRunner ? readString(localRunner, ["baseUrl", "base_url"]) : undefined) ??
|
|
223
|
+
(worker ? readString(worker, ["apiRunUrl", "api_run_url", "baseUrl", "base_url"]) : undefined) ??
|
|
224
|
+
readString(config, ["baseUrl", "base_url", "endpoint", "apiBaseUrl"]));
|
|
225
|
+
};
|
|
226
|
+
const readRunnerKind = (record) => {
|
|
227
|
+
const direct = readString(record, ["runnerKind", "runner_kind"]);
|
|
228
|
+
if (direct) {
|
|
229
|
+
return direct;
|
|
230
|
+
}
|
|
231
|
+
const config = readRecord(record, ["config"]);
|
|
232
|
+
const localRunner = config ? readRecord(config, ["localRunner", "local_runner"]) : undefined;
|
|
233
|
+
return localRunner ? readString(localRunner, ["runnerKind", "runner_kind"]) : undefined;
|
|
234
|
+
};
|
|
235
|
+
const inferSupportsJsonSchema = (record, capabilities) => {
|
|
236
|
+
const explicit = readBoolean(record, ["supportsJsonSchema", "supports_json_schema"]);
|
|
237
|
+
if (explicit !== undefined) {
|
|
238
|
+
return explicit;
|
|
239
|
+
}
|
|
240
|
+
const config = readRecord(record, ["config"]);
|
|
241
|
+
const localRunner = config ? readRecord(config, ["localRunner", "local_runner"]) : undefined;
|
|
242
|
+
const local = localRunner
|
|
243
|
+
? readBoolean(localRunner, ["supportsJsonSchema", "supports_json_schema"])
|
|
244
|
+
: undefined;
|
|
245
|
+
if (local !== undefined) {
|
|
246
|
+
return local;
|
|
247
|
+
}
|
|
248
|
+
return capabilities.some((capability) => STRUCTURED_OUTPUT_CAPABILITIES.has(capability))
|
|
249
|
+
? true
|
|
250
|
+
: undefined;
|
|
251
|
+
};
|
|
252
|
+
const inferSupportsImage = (record, capabilities) => {
|
|
253
|
+
const explicit = readBoolean(record, [
|
|
254
|
+
"supportsImageGeneration",
|
|
255
|
+
"supports_image_generation",
|
|
256
|
+
"supportsImages",
|
|
257
|
+
"supports_images",
|
|
258
|
+
]);
|
|
259
|
+
if (explicit !== undefined) {
|
|
260
|
+
return explicit;
|
|
261
|
+
}
|
|
262
|
+
return capabilities.some((capability) => IMAGE_CAPABILITIES.has(capability))
|
|
263
|
+
? true
|
|
264
|
+
: undefined;
|
|
265
|
+
};
|
|
266
|
+
const inferTier = (record, capabilities, supportsImageGeneration) => {
|
|
267
|
+
const explicit = readString(record, ["tier", "modelTier", "model_tier"])?.toLowerCase();
|
|
268
|
+
if (explicit === "small" ||
|
|
269
|
+
explicit === "medium" ||
|
|
270
|
+
explicit === "large" ||
|
|
271
|
+
explicit === "image") {
|
|
272
|
+
return explicit;
|
|
273
|
+
}
|
|
274
|
+
if (supportsImageGeneration || capabilities.some((capability) => IMAGE_CAPABILITIES.has(capability))) {
|
|
275
|
+
return "image";
|
|
276
|
+
}
|
|
277
|
+
const contextWindow = readNumber(record, ["contextWindow", "context_window"]) ?? 0;
|
|
278
|
+
const reasoningRating = readNumber(record, ["reasoningRating", "reasoning_rating"]) ?? 0;
|
|
279
|
+
const maxComplexity = readNumber(record, ["maxComplexity", "max_complexity"]) ?? 0;
|
|
280
|
+
if (maxComplexity >= 8 ||
|
|
281
|
+
reasoningRating >= 8 ||
|
|
282
|
+
contextWindow >= 64000 ||
|
|
283
|
+
capabilities.includes("deep_reasoning") ||
|
|
284
|
+
capabilities.includes("final_answer_synthesis")) {
|
|
285
|
+
return "large";
|
|
286
|
+
}
|
|
287
|
+
if (maxComplexity >= 5 || reasoningRating >= 5.5 || contextWindow >= 16000) {
|
|
288
|
+
return "medium";
|
|
289
|
+
}
|
|
290
|
+
return "small";
|
|
291
|
+
};
|
|
292
|
+
export const normalizeCodaliGatewayAgentCandidate = (input) => {
|
|
293
|
+
if (!isRecord(input)) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const slug = readString(input, ["slug", "id"]);
|
|
297
|
+
const adapter = readString(input, ["adapter"]);
|
|
298
|
+
const model = readDefaultModel(input);
|
|
299
|
+
if (!slug || !adapter || !model) {
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
const capabilities = normalizeCapabilities(input.capabilities);
|
|
303
|
+
const supportsImageGeneration = inferSupportsImage(input, capabilities);
|
|
304
|
+
const { status, latencyMs } = normalizeHealth(input);
|
|
305
|
+
return {
|
|
306
|
+
id: readString(input, ["id"]),
|
|
307
|
+
slug,
|
|
308
|
+
adapter,
|
|
309
|
+
provider: normalizeProvider(adapter, readString(input, ["provider"])),
|
|
310
|
+
model,
|
|
311
|
+
baseUrl: readBaseUrl(input),
|
|
312
|
+
runnerKind: readRunnerKind(input),
|
|
313
|
+
source: normalizeSource(input, slug, adapter),
|
|
314
|
+
healthStatus: status,
|
|
315
|
+
latencyMs,
|
|
316
|
+
contextWindow: readNumber(input, ["contextWindow", "context_window"]),
|
|
317
|
+
maxOutputTokens: readNumber(input, ["maxOutputTokens", "max_output_tokens"]),
|
|
318
|
+
supportsTools: readBoolean(input, ["supportsTools", "supports_tools"]),
|
|
319
|
+
supportsJsonSchema: inferSupportsJsonSchema(input, capabilities),
|
|
320
|
+
supportsImageGeneration,
|
|
321
|
+
supportsArtifacts: readBoolean(input, ["supportsArtifacts", "supports_artifacts"]),
|
|
322
|
+
supportsStreaming: readBoolean(input, ["supportsStreaming", "supports_streaming"]),
|
|
323
|
+
capabilities,
|
|
324
|
+
bestUsage: readString(input, ["bestUsage", "best_usage"]),
|
|
325
|
+
rating: readNumber(input, ["rating"]),
|
|
326
|
+
reasoningRating: readNumber(input, ["reasoningRating", "reasoning_rating"]),
|
|
327
|
+
costPerMillion: readNumber(input, ["costPerMillion", "cost_per_million"]),
|
|
328
|
+
maxComplexity: readNumber(input, ["maxComplexity", "max_complexity"]),
|
|
329
|
+
tier: inferTier(input, capabilities, supportsImageGeneration),
|
|
330
|
+
raw: input,
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
const capabilityMisses = (candidate, required) => {
|
|
334
|
+
if (!required || required.length === 0) {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
const capabilities = new Set(candidate.capabilities.map((capability) => capability.toLowerCase()));
|
|
338
|
+
return required.filter((capability) => !capabilities.has(capability.toLowerCase()));
|
|
339
|
+
};
|
|
340
|
+
const evaluateCandidate = (role, policy, candidate, allowCloudFallback, allowImageWorker) => {
|
|
341
|
+
const reasons = [];
|
|
342
|
+
let eligible = true;
|
|
343
|
+
if (role === "image_worker" && !allowImageWorker) {
|
|
344
|
+
eligible = false;
|
|
345
|
+
reasons.push("image_worker_disabled");
|
|
346
|
+
}
|
|
347
|
+
if (candidate.healthStatus === "unreachable" || candidate.healthStatus === "limited") {
|
|
348
|
+
eligible = false;
|
|
349
|
+
reasons.push(`health_${candidate.healthStatus}`);
|
|
350
|
+
}
|
|
351
|
+
if (candidate.source === "cloud" && !allowCloudFallback) {
|
|
352
|
+
eligible = false;
|
|
353
|
+
reasons.push("cloud_fallback_disabled");
|
|
354
|
+
}
|
|
355
|
+
if (candidate.tier !== policy.tier) {
|
|
356
|
+
eligible = false;
|
|
357
|
+
reasons.push(`tier_mismatch:${candidate.tier}`);
|
|
358
|
+
}
|
|
359
|
+
if (policy.tier === "image" && candidate.supportsImageGeneration !== true) {
|
|
360
|
+
eligible = false;
|
|
361
|
+
reasons.push("image_generation_required");
|
|
362
|
+
}
|
|
363
|
+
if (policy.requiresTools === true && candidate.supportsTools !== true) {
|
|
364
|
+
eligible = false;
|
|
365
|
+
reasons.push("tools_required");
|
|
366
|
+
}
|
|
367
|
+
if (policy.requiresJsonSchema === true && candidate.supportsJsonSchema !== true) {
|
|
368
|
+
eligible = false;
|
|
369
|
+
reasons.push("json_schema_required");
|
|
370
|
+
}
|
|
371
|
+
if (policy.minContextWindow &&
|
|
372
|
+
(candidate.contextWindow ?? 0) < policy.minContextWindow) {
|
|
373
|
+
eligible = false;
|
|
374
|
+
reasons.push("context_window_too_small");
|
|
375
|
+
}
|
|
376
|
+
if (policy.maxLatencyMs &&
|
|
377
|
+
candidate.latencyMs !== undefined &&
|
|
378
|
+
candidate.latencyMs > policy.maxLatencyMs) {
|
|
379
|
+
eligible = false;
|
|
380
|
+
reasons.push("latency_too_high");
|
|
381
|
+
}
|
|
382
|
+
const missingCapabilities = capabilityMisses(candidate, policy.capabilities);
|
|
383
|
+
if (missingCapabilities.length > 0) {
|
|
384
|
+
eligible = false;
|
|
385
|
+
reasons.push(`missing_capabilities:${missingCapabilities.join(",")}`);
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
role,
|
|
389
|
+
slug: candidate.slug,
|
|
390
|
+
tier: candidate.tier,
|
|
391
|
+
source: candidate.source,
|
|
392
|
+
eligible,
|
|
393
|
+
reasons,
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
const scoreCandidate = (role, policy, candidate) => {
|
|
397
|
+
const reasons = [];
|
|
398
|
+
let score = 100;
|
|
399
|
+
const rating = candidate.rating ?? 0;
|
|
400
|
+
const reasoning = candidate.reasoningRating ?? rating;
|
|
401
|
+
score += rating * 5;
|
|
402
|
+
score += reasoning * (role === "final_synthesizer" ? 6 : 3);
|
|
403
|
+
score += (candidate.contextWindow ?? 0) / 8000;
|
|
404
|
+
if (candidate.healthStatus === "healthy") {
|
|
405
|
+
score += 25;
|
|
406
|
+
reasons.push("healthy");
|
|
407
|
+
}
|
|
408
|
+
else if (candidate.healthStatus === "degraded") {
|
|
409
|
+
score -= 20;
|
|
410
|
+
reasons.push("degraded");
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
score -= 5;
|
|
414
|
+
reasons.push("health_unknown");
|
|
415
|
+
}
|
|
416
|
+
if (candidate.source === "local") {
|
|
417
|
+
score += 25;
|
|
418
|
+
reasons.push("local");
|
|
419
|
+
}
|
|
420
|
+
else if (candidate.source === "self_hosted") {
|
|
421
|
+
score += 22;
|
|
422
|
+
reasons.push("self_hosted");
|
|
423
|
+
}
|
|
424
|
+
else if (candidate.source === "worker") {
|
|
425
|
+
score += 8;
|
|
426
|
+
reasons.push("worker");
|
|
427
|
+
}
|
|
428
|
+
else if (candidate.source === "cloud") {
|
|
429
|
+
score -= 30;
|
|
430
|
+
reasons.push("cloud_fallback");
|
|
431
|
+
}
|
|
432
|
+
if (policy.requiresTools && candidate.supportsTools) {
|
|
433
|
+
score += 12;
|
|
434
|
+
reasons.push("tools");
|
|
435
|
+
}
|
|
436
|
+
if (policy.requiresJsonSchema && candidate.supportsJsonSchema) {
|
|
437
|
+
score += 12;
|
|
438
|
+
reasons.push("json_schema");
|
|
439
|
+
}
|
|
440
|
+
if (policy.preferredRunnerKinds?.includes(candidate.runnerKind ?? "")) {
|
|
441
|
+
score += 18;
|
|
442
|
+
reasons.push("preferred_runner");
|
|
443
|
+
}
|
|
444
|
+
if (policy.capabilities && policy.capabilities.length > 0) {
|
|
445
|
+
score += policy.capabilities.length * 8;
|
|
446
|
+
reasons.push("required_capabilities");
|
|
447
|
+
}
|
|
448
|
+
if (candidate.bestUsage && candidate.bestUsage.toLowerCase().includes(role)) {
|
|
449
|
+
score += 10;
|
|
450
|
+
reasons.push("best_usage");
|
|
451
|
+
}
|
|
452
|
+
if (isNestedSelfHostedRelayCandidate(candidate)) {
|
|
453
|
+
score -= 120;
|
|
454
|
+
reasons.push("nested_self_hosted_relay_penalty");
|
|
455
|
+
}
|
|
456
|
+
if (candidate.latencyMs !== undefined) {
|
|
457
|
+
score -= candidate.latencyMs / 1000;
|
|
458
|
+
}
|
|
459
|
+
score -= (candidate.costPerMillion ?? 0) * 2;
|
|
460
|
+
return { score, reasons };
|
|
461
|
+
};
|
|
462
|
+
const toRuntimeAgentInput = (candidate) => ({
|
|
463
|
+
slug: candidate.slug,
|
|
464
|
+
adapter: candidate.adapter,
|
|
465
|
+
provider: candidate.provider,
|
|
466
|
+
model: candidate.model,
|
|
467
|
+
baseUrl: candidate.baseUrl,
|
|
468
|
+
runnerKind: candidate.runnerKind,
|
|
469
|
+
supportsTools: candidate.supportsTools,
|
|
470
|
+
capabilities: candidate.capabilities,
|
|
471
|
+
contextWindow: candidate.contextWindow,
|
|
472
|
+
maxOutputTokens: candidate.maxOutputTokens,
|
|
473
|
+
});
|
|
474
|
+
const resolveRolePolicy = (role, agentPolicy) => agentPolicy?.roles?.[role] ??
|
|
475
|
+
DEFAULT_CODALI_GATEWAY_ROLE_POLICIES[role] ??
|
|
476
|
+
{ tier: "medium" };
|
|
477
|
+
const requestedRoles = (input) => {
|
|
478
|
+
if (input.roles && input.roles.length > 0) {
|
|
479
|
+
return [...new Set(input.roles)];
|
|
480
|
+
}
|
|
481
|
+
const policyRoles = Object.keys(input.agentPolicy?.roles ?? {});
|
|
482
|
+
if (policyRoles.length > 0) {
|
|
483
|
+
return policyRoles;
|
|
484
|
+
}
|
|
485
|
+
return [...DEFAULT_CODALI_GATEWAY_AGENT_ROLES];
|
|
486
|
+
};
|
|
487
|
+
export const resolveCodaliGatewayAgentTiers = (input) => {
|
|
488
|
+
const candidates = input.inventory
|
|
489
|
+
.map(normalizeCodaliGatewayAgentCandidate)
|
|
490
|
+
.filter((candidate) => Boolean(candidate));
|
|
491
|
+
const assignments = {};
|
|
492
|
+
const diagnostics = [];
|
|
493
|
+
const errors = [];
|
|
494
|
+
const warnings = [];
|
|
495
|
+
const allowCloudFallback = input.agentPolicy?.allowCloudFallback === true;
|
|
496
|
+
const allowImageWorker = input.allowImageWorker === true;
|
|
497
|
+
for (const role of requestedRoles(input)) {
|
|
498
|
+
const policy = resolveRolePolicy(role, input.agentPolicy);
|
|
499
|
+
const roleDiagnostics = candidates.map((candidate) => evaluateCandidate(role, policy, candidate, allowCloudFallback, allowImageWorker));
|
|
500
|
+
for (const diagnostic of roleDiagnostics) {
|
|
501
|
+
if (diagnostic.eligible) {
|
|
502
|
+
const candidate = candidates.find((item) => item.slug === diagnostic.slug);
|
|
503
|
+
if (candidate) {
|
|
504
|
+
const scored = scoreCandidate(role, policy, candidate);
|
|
505
|
+
diagnostic.score = scored.score;
|
|
506
|
+
diagnostic.reasons.push(...scored.reasons);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
diagnostics.push(diagnostic);
|
|
510
|
+
}
|
|
511
|
+
const eligible = roleDiagnostics
|
|
512
|
+
.filter((diagnostic) => diagnostic.eligible)
|
|
513
|
+
.map((diagnostic) => {
|
|
514
|
+
const candidate = candidates.find((item) => item.slug === diagnostic.slug);
|
|
515
|
+
if (!candidate || diagnostic.score === undefined) {
|
|
516
|
+
return undefined;
|
|
517
|
+
}
|
|
518
|
+
return { diagnostic, candidate };
|
|
519
|
+
})
|
|
520
|
+
.filter((value) => Boolean(value))
|
|
521
|
+
.sort((left, right) => {
|
|
522
|
+
const scoreDelta = (right.diagnostic.score ?? 0) - (left.diagnostic.score ?? 0);
|
|
523
|
+
if (scoreDelta !== 0)
|
|
524
|
+
return scoreDelta;
|
|
525
|
+
const costDelta = (left.candidate.costPerMillion ?? 0) - (right.candidate.costPerMillion ?? 0);
|
|
526
|
+
if (costDelta !== 0)
|
|
527
|
+
return costDelta;
|
|
528
|
+
return left.candidate.slug.localeCompare(right.candidate.slug);
|
|
529
|
+
});
|
|
530
|
+
const selected = eligible[0];
|
|
531
|
+
if (!selected) {
|
|
532
|
+
errors.push({
|
|
533
|
+
code: role === "image_worker" && !allowImageWorker
|
|
534
|
+
? "GATEWAY_IMAGE_WORKER_DISABLED"
|
|
535
|
+
: "GATEWAY_AGENT_ROLE_UNRESOLVED",
|
|
536
|
+
message: `No eligible agent candidate found for role ${role}.`,
|
|
537
|
+
role,
|
|
538
|
+
details: { policy },
|
|
539
|
+
});
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
assignments[role] = {
|
|
543
|
+
role,
|
|
544
|
+
policy,
|
|
545
|
+
candidate: selected.candidate,
|
|
546
|
+
agent: toRuntimeAgentInput(selected.candidate),
|
|
547
|
+
score: selected.diagnostic.score ?? 0,
|
|
548
|
+
reasons: selected.diagnostic.reasons,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
if (candidates.length === 0) {
|
|
552
|
+
errors.push({
|
|
553
|
+
code: "GATEWAY_AGENT_INVENTORY_EMPTY",
|
|
554
|
+
message: "No mcoda agent candidates were available to resolve gateway roles.",
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (!allowCloudFallback) {
|
|
558
|
+
const cloudCount = candidates.filter((candidate) => candidate.source === "cloud").length;
|
|
559
|
+
if (cloudCount > 0) {
|
|
560
|
+
warnings.push({
|
|
561
|
+
code: "GATEWAY_CLOUD_FALLBACK_BLOCKED",
|
|
562
|
+
message: "Cloud candidates were present but excluded by policy.",
|
|
563
|
+
details: { cloudCount },
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
ok: errors.length === 0,
|
|
569
|
+
assignments,
|
|
570
|
+
candidates,
|
|
571
|
+
diagnostics,
|
|
572
|
+
warnings,
|
|
573
|
+
errors,
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
export const resolveGatewayAgentTiers = resolveCodaliGatewayAgentTiers;
|