@mcoda/codali 0.1.134 → 0.1.135-codali.2
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/CHANGELOG.md +26 -0
- package/ORCHESTRATION.md +387 -0
- package/README.md +103 -1
- package/config/alternate-local.example.json +49 -0
- package/config/docdex-runtime.json +21 -0
- package/config/orchestration.schema.json +548 -0
- package/config/raijin-suku.example.json +172 -0
- package/config/terminal-long-scenarios.v1.json +70 -0
- package/dist/agents/RoleResolution.d.ts.map +1 -1
- package/dist/agents/RoleResolution.js +4 -4
- package/dist/api/CodaliApi.d.ts +16 -0
- package/dist/api/CodaliApi.d.ts.map +1 -1
- package/dist/api/CodaliApi.js +39 -10
- package/dist/cli/AskCommand.d.ts +3 -0
- package/dist/cli/AskCommand.d.ts.map +1 -1
- package/dist/cli/AskCommand.js +76 -13
- package/dist/cli/RunCommand.d.ts +4 -0
- package/dist/cli/RunCommand.d.ts.map +1 -1
- package/dist/cli/RunCommand.js +110 -24
- package/dist/cli/TerminalCommand.d.ts +35 -0
- package/dist/cli/TerminalCommand.d.ts.map +1 -0
- package/dist/cli/TerminalCommand.js +616 -0
- package/dist/cli/TerminalCredentials.d.ts +3 -0
- package/dist/cli/TerminalCredentials.d.ts.map +1 -0
- package/dist/cli/TerminalCredentials.js +89 -0
- package/dist/cli/TerminalInput.d.ts +24 -0
- package/dist/cli/TerminalInput.d.ts.map +1 -0
- package/dist/cli/TerminalInput.js +98 -0
- package/dist/cli/TerminalText.d.ts +10 -0
- package/dist/cli/TerminalText.d.ts.map +1 -0
- package/dist/cli/TerminalText.js +53 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +33 -4
- package/dist/cognitive/ConfiguredPipeline.d.ts +34 -0
- package/dist/cognitive/ConfiguredPipeline.d.ts.map +1 -0
- package/dist/cognitive/ConfiguredPipeline.js +50 -0
- package/dist/cognitive/ValidationRunner.d.ts +4 -0
- package/dist/cognitive/ValidationRunner.d.ts.map +1 -1
- package/dist/cognitive/ValidationRunner.js +18 -41
- package/dist/config/NodeOrchestration.d.ts +21 -0
- package/dist/config/NodeOrchestration.d.ts.map +1 -0
- package/dist/config/NodeOrchestration.js +54 -0
- package/dist/config/OrchestrationConfig.d.ts +100 -0
- package/dist/config/OrchestrationConfig.d.ts.map +1 -0
- package/dist/config/OrchestrationConfig.js +245 -0
- package/dist/connectors/mcp/McpClient.d.ts +3 -1
- package/dist/connectors/mcp/McpClient.d.ts.map +1 -1
- package/dist/connectors/mcp/McpClient.js +3 -2
- package/dist/connectors/mcp/McpServerRegistry.d.ts +2 -2
- package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -1
- package/dist/connectors/mcp/McpServerRegistry.js +39 -16
- package/dist/connectors/mcp/McpToolAdapter.js +2 -2
- package/dist/connectors/media/ConfiguredMediaTools.d.ts +6 -0
- package/dist/connectors/media/ConfiguredMediaTools.d.ts.map +1 -0
- package/dist/connectors/media/ConfiguredMediaTools.js +41 -0
- package/dist/connectors/media/ImageGenerationTool.js +3 -3
- package/dist/connectors/media/MediaArtifactReceipt.d.ts +5 -0
- package/dist/connectors/media/MediaArtifactReceipt.d.ts.map +1 -0
- package/dist/connectors/media/MediaArtifactReceipt.js +65 -0
- package/dist/connectors/media/MediaJobStore.d.ts +68 -0
- package/dist/connectors/media/MediaJobStore.d.ts.map +1 -0
- package/dist/connectors/media/MediaJobStore.js +199 -0
- package/dist/connectors/media/MediaJobWorker.d.ts +2 -0
- package/dist/connectors/media/MediaJobWorker.d.ts.map +1 -0
- package/dist/connectors/media/MediaJobWorker.js +65 -0
- package/dist/connectors/media/RemoteMediaJob.d.ts +6 -0
- package/dist/connectors/media/RemoteMediaJob.d.ts.map +1 -0
- package/dist/connectors/media/RemoteMediaJob.js +80 -0
- package/dist/docdex/DocdexClient.d.ts +18 -1
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +135 -19
- package/dist/docdex/ManagedDocdex.d.ts +29 -0
- package/dist/docdex/ManagedDocdex.d.ts.map +1 -0
- package/dist/docdex/ManagedDocdex.js +215 -0
- package/dist/docdex/ManagedRuntime.d.ts +28 -0
- package/dist/docdex/ManagedRuntime.d.ts.map +1 -0
- package/dist/docdex/ManagedRuntime.js +212 -0
- package/dist/eval/EvaluationProcess.d.ts +13 -0
- package/dist/eval/EvaluationProcess.d.ts.map +1 -0
- package/dist/eval/EvaluationProcess.js +76 -0
- package/dist/eval/LongWorkflowJourney.d.ts +34 -0
- package/dist/eval/LongWorkflowJourney.d.ts.map +1 -0
- package/dist/eval/LongWorkflowJourney.js +243 -0
- package/dist/eval/PlanningCorpus.d.ts +45 -0
- package/dist/eval/PlanningCorpus.d.ts.map +1 -0
- package/dist/eval/PlanningCorpus.js +189 -0
- package/dist/eval/PlanningCorpusRunner.d.ts +13 -0
- package/dist/eval/PlanningCorpusRunner.d.ts.map +1 -0
- package/dist/eval/PlanningCorpusRunner.js +89 -0
- package/dist/eval/TerminalCorpusRunner.d.ts +36 -0
- package/dist/eval/TerminalCorpusRunner.d.ts.map +1 -0
- package/dist/eval/TerminalCorpusRunner.js +222 -0
- package/dist/eval/TerminalTaskCorpus.d.ts +26 -0
- package/dist/eval/TerminalTaskCorpus.d.ts.map +1 -0
- package/dist/eval/TerminalTaskCorpus.js +105 -0
- package/dist/eval/TerminalWorkflowCorpus.d.ts +11 -0
- package/dist/eval/TerminalWorkflowCorpus.d.ts.map +1 -0
- package/dist/eval/TerminalWorkflowCorpus.js +116 -0
- package/dist/eval/fixtures/TerminalRecoveryJourney.d.ts +4 -0
- package/dist/eval/fixtures/TerminalRecoveryJourney.d.ts.map +1 -0
- package/dist/eval/fixtures/TerminalRecoveryJourney.js +132 -0
- package/dist/gateway/CodaliGateway.d.ts +2 -0
- package/dist/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/gateway/CodaliGateway.js +11 -1
- package/dist/gateway/ConfiguredGateway.d.ts +23 -0
- package/dist/gateway/ConfiguredGateway.d.ts.map +1 -0
- package/dist/gateway/ConfiguredGateway.js +44 -0
- package/dist/gateway/GatewayTracer.d.ts +1 -1
- package/dist/gateway/GatewayTracer.d.ts.map +1 -1
- package/dist/gateway/GatewayTracer.js +2 -27
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/permissions/PermissionGate.d.ts +44 -0
- package/dist/permissions/PermissionGate.d.ts.map +1 -0
- package/dist/permissions/PermissionGate.js +97 -0
- package/dist/providers/ConfiguredEmbeddings.d.ts +43 -0
- package/dist/providers/ConfiguredEmbeddings.d.ts.map +1 -0
- package/dist/providers/ConfiguredEmbeddings.js +87 -0
- package/dist/providers/ConfiguredModelScope.d.ts +69 -0
- package/dist/providers/ConfiguredModelScope.d.ts.map +1 -0
- package/dist/providers/ConfiguredModelScope.js +113 -0
- package/dist/providers/ConfiguredProvider.d.ts +79 -0
- package/dist/providers/ConfiguredProvider.d.ts.map +1 -0
- package/dist/providers/ConfiguredProvider.js +238 -0
- package/dist/providers/ModelServiceError.d.ts +10 -0
- package/dist/providers/ModelServiceError.d.ts.map +1 -0
- package/dist/providers/ModelServiceError.js +45 -0
- package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/providers/MswarmWorkerProvider.js +4 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAiCompatibleProvider.js +91 -50
- package/dist/providers/ProviderTypes.d.ts +12 -0
- package/dist/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/runtime/CodaliJobRuntime.d.ts +6 -0
- package/dist/runtime/CodaliJobRuntime.d.ts.map +1 -1
- package/dist/runtime/CodaliJobRuntime.js +36 -1
- package/dist/runtime/CodaliRuntime.d.ts +28 -0
- package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
- package/dist/runtime/CodaliRuntime.js +69 -1
- package/dist/runtime/CompletionReport.d.ts +5 -0
- package/dist/runtime/CompletionReport.d.ts.map +1 -0
- package/dist/runtime/CompletionReport.js +12 -0
- package/dist/runtime/ProgressTracker.d.ts +17 -0
- package/dist/runtime/ProgressTracker.d.ts.map +1 -0
- package/dist/runtime/ProgressTracker.js +27 -0
- package/dist/runtime/RequestedTestEvidence.d.ts +12 -0
- package/dist/runtime/RequestedTestEvidence.d.ts.map +1 -0
- package/dist/runtime/RequestedTestEvidence.js +16 -0
- package/dist/runtime/RequiredRetrieval.d.ts +13 -0
- package/dist/runtime/RequiredRetrieval.d.ts.map +1 -0
- package/dist/runtime/RequiredRetrieval.js +25 -0
- package/dist/runtime/ReviewSource.d.ts +6 -0
- package/dist/runtime/ReviewSource.d.ts.map +1 -0
- package/dist/runtime/ReviewSource.js +38 -0
- package/dist/runtime/Runner.d.ts +7 -0
- package/dist/runtime/Runner.d.ts.map +1 -1
- package/dist/runtime/Runner.js +215 -171
- package/dist/runtime/SecretRedaction.d.ts +11 -0
- package/dist/runtime/SecretRedaction.d.ts.map +1 -0
- package/dist/runtime/SecretRedaction.js +85 -0
- package/dist/runtime/SessionController.d.ts +148 -0
- package/dist/runtime/SessionController.d.ts.map +1 -0
- package/dist/runtime/SessionController.js +1356 -0
- package/dist/runtime/StructuralEvidence.d.ts +22 -0
- package/dist/runtime/StructuralEvidence.d.ts.map +1 -0
- package/dist/runtime/StructuralEvidence.js +87 -0
- package/dist/runtime/TaskReviewContext.d.ts +34 -0
- package/dist/runtime/TaskReviewContext.d.ts.map +1 -0
- package/dist/runtime/TaskReviewContext.js +34 -0
- package/dist/runtime/ValidationOutput.d.ts +5 -0
- package/dist/runtime/ValidationOutput.d.ts.map +1 -0
- package/dist/runtime/ValidationOutput.js +32 -0
- package/dist/runtime/VerificationCommand.d.ts +3 -0
- package/dist/runtime/VerificationCommand.d.ts.map +1 -0
- package/dist/runtime/VerificationCommand.js +48 -0
- package/dist/runtime/WorkStatusPage.d.ts +15 -0
- package/dist/runtime/WorkStatusPage.d.ts.map +1 -0
- package/dist/runtime/WorkStatusPage.js +58 -0
- package/dist/runtime/WorkspaceLedger.d.ts +13 -0
- package/dist/runtime/WorkspaceLedger.d.ts.map +1 -0
- package/dist/runtime/WorkspaceLedger.js +145 -0
- package/dist/runtime/WorkspaceLock.d.ts.map +1 -1
- package/dist/runtime/WorkspaceLock.js +20 -17
- package/dist/runtime/WorkspaceSnapshot.d.ts +13 -0
- package/dist/runtime/WorkspaceSnapshot.d.ts.map +1 -0
- package/dist/runtime/WorkspaceSnapshot.js +88 -0
- package/dist/session/AtomicState.d.ts +13 -0
- package/dist/session/AtomicState.d.ts.map +1 -0
- package/dist/session/AtomicState.js +109 -0
- package/dist/session/SessionStoragePath.d.ts +15 -0
- package/dist/session/SessionStoragePath.d.ts.map +1 -0
- package/dist/session/SessionStoragePath.js +89 -0
- package/dist/session/SessionStore.d.ts +9 -0
- package/dist/session/SessionStore.d.ts.map +1 -1
- package/dist/session/SessionStore.js +181 -47
- package/dist/session/WorkState.d.ts +23 -0
- package/dist/session/WorkState.d.ts.map +1 -0
- package/dist/session/WorkState.js +367 -0
- package/dist/session/WorkTypes.d.ts +94 -0
- package/dist/session/WorkTypes.d.ts.map +1 -0
- package/dist/session/WorkTypes.js +1 -0
- package/dist/tools/ToolRegistry.d.ts +2 -0
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +12 -2
- package/dist/tools/ToolTypes.d.ts +16 -0
- package/dist/tools/ToolTypes.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.d.ts +2 -0
- package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.js +17 -2
- package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/tools/filesystem/FileTools.js +30 -10
- package/dist/tools/shell/AsyncProcess.d.ts +16 -0
- package/dist/tools/shell/AsyncProcess.d.ts.map +1 -0
- package/dist/tools/shell/AsyncProcess.js +94 -0
- package/dist/tools/shell/DarwinRuntimeLibraries.d.ts +13 -0
- package/dist/tools/shell/DarwinRuntimeLibraries.d.ts.map +1 -0
- package/dist/tools/shell/DarwinRuntimeLibraries.js +130 -0
- package/dist/tools/shell/HostToolchain.d.ts +19 -0
- package/dist/tools/shell/HostToolchain.d.ts.map +1 -0
- package/dist/tools/shell/HostToolchain.js +264 -0
- package/dist/tools/shell/IsolatedExecutor.d.ts +15 -0
- package/dist/tools/shell/IsolatedExecutor.d.ts.map +1 -0
- package/dist/tools/shell/IsolatedExecutor.js +134 -0
- package/dist/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/tools/shell/ShellTool.js +9 -29
- package/dist/tools/shell/ShellWorkingDirectory.d.ts +3 -0
- package/dist/tools/shell/ShellWorkingDirectory.d.ts.map +1 -0
- package/dist/tools/shell/ShellWorkingDirectory.js +30 -0
- package/package.json +27 -24
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { CryptoHelper, PathHelper, mswarmClientProductHeaders } from "@mcoda/shared";
|
|
4
|
+
import { resolveCredential } from "../runcontext/CredentialFile.js";
|
|
5
|
+
import { resolveBinding } from "../config/OrchestrationConfig.js";
|
|
6
|
+
import { OpenAiCompatibleProvider } from "./OpenAiCompatibleProvider.js";
|
|
7
|
+
import { ModelServiceError } from "./ModelServiceError.js";
|
|
8
|
+
import { registerSecretValue } from "../runtime/SecretRedaction.js";
|
|
9
|
+
export const normalizeModelCaller = (caller = {}) => {
|
|
10
|
+
const clientIdentity = caller.clientIdentity?.trim();
|
|
11
|
+
const clientProduct = caller.clientProduct?.trim().toLowerCase() ?? "codali";
|
|
12
|
+
if (clientIdentity !== undefined && !/^[A-Za-z0-9._:-]{1,128}$/.test(clientIdentity))
|
|
13
|
+
throw new Error("Invalid model client identity");
|
|
14
|
+
if (!/^[a-z0-9](?:[a-z0-9_-]{0,62}[a-z0-9])?$/.test(clientProduct))
|
|
15
|
+
throw new Error("Invalid model client product");
|
|
16
|
+
return { ...(clientIdentity ? { clientIdentity } : {}), clientProduct };
|
|
17
|
+
};
|
|
18
|
+
const modelCallerHeaders = (caller) => ({
|
|
19
|
+
...mswarmClientProductHeaders(caller.clientProduct),
|
|
20
|
+
...(caller.clientIdentity ? { "x-mswarm-client-identity": caller.clientIdentity, "x-mswarm-client": caller.clientIdentity } : {}),
|
|
21
|
+
});
|
|
22
|
+
const record = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
23
|
+
/** Discovery failed before inference; a caller may release its local token reservation. */
|
|
24
|
+
export class NativeReasoningTransportError extends Error {
|
|
25
|
+
constructor(message) { super(message); this.name = "NativeReasoningTransportError"; }
|
|
26
|
+
}
|
|
27
|
+
export const providerCredential = async (provider) => {
|
|
28
|
+
if (!provider.credentialRef)
|
|
29
|
+
return undefined;
|
|
30
|
+
const name = provider.credentialRef.slice(4);
|
|
31
|
+
const direct = resolveCredential(name);
|
|
32
|
+
if (direct) {
|
|
33
|
+
registerSecretValue(direct);
|
|
34
|
+
return direct;
|
|
35
|
+
}
|
|
36
|
+
if (provider.adapter === "mswarm" && resolveCredential("CODALI_MSWARM_LOGGED_OUT") === "1")
|
|
37
|
+
throw new Error("Logged out; use codali login");
|
|
38
|
+
// Migrate by reading the existing user store; never read repository credentials.
|
|
39
|
+
if (provider.adapter === "mswarm" && name === "MCODA_MSWARM_API_KEY") {
|
|
40
|
+
try {
|
|
41
|
+
const config = JSON.parse(await readFile(PathHelper.getGlobalConfigPath(), "utf8"));
|
|
42
|
+
const existing = config.mswarm;
|
|
43
|
+
if (existing?.encryptedApiKey && new URL(existing.baseUrl ?? "https://api.mswarm.org").origin === new URL(provider.baseUrl).origin) {
|
|
44
|
+
const key = await CryptoHelper.decryptSecret(existing.encryptedApiKey);
|
|
45
|
+
registerSecretValue(key);
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error.code !== "ENOENT")
|
|
51
|
+
throw new Error("Stored mSwarm credentials could not be loaded; use codali login");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Missing credential ${name}; use codali login or set the environment variable`);
|
|
55
|
+
};
|
|
56
|
+
export class ModelServiceClient {
|
|
57
|
+
constructor(provider, credentialOverride, caller) {
|
|
58
|
+
this.provider = provider;
|
|
59
|
+
this.credentialOverride = credentialOverride;
|
|
60
|
+
this.caller = normalizeModelCaller(caller);
|
|
61
|
+
if (credentialOverride)
|
|
62
|
+
registerSecretValue(credentialOverride);
|
|
63
|
+
}
|
|
64
|
+
credential() { return this.credentialOverride === undefined ? providerCredential(this.provider) : Promise.resolve(this.credentialOverride); }
|
|
65
|
+
async json(url, options = {}) {
|
|
66
|
+
if (new URL(url).origin !== new URL(this.provider.baseUrl).origin)
|
|
67
|
+
throw new Error("Refusing credentials to an unconfigured origin");
|
|
68
|
+
const key = await this.credential();
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const abort = () => controller.abort(options.signal?.reason);
|
|
71
|
+
options.signal?.throwIfAborted();
|
|
72
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
73
|
+
const timer = setTimeout(() => controller.abort(new Error("Service request timed out")), options.timeoutMs ?? 30000);
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
redirect: "error", method: options.method ?? "GET", signal: controller.signal,
|
|
77
|
+
headers: { accept: "application/json", ...(key ? this.provider.adapter === "mswarm" ? { "x-api-key": key } : { authorization: `Bearer ${key}` } : {}), ...(this.provider.adapter === "mswarm" ? modelCallerHeaders(this.caller) : {}), ...(options.body ? { "content-type": "application/json" } : {}) },
|
|
78
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok)
|
|
81
|
+
throw await ModelServiceError.fromResponse(response);
|
|
82
|
+
if (!response.body)
|
|
83
|
+
throw new Error("Empty service response");
|
|
84
|
+
const reader = response.body.getReader();
|
|
85
|
+
const chunks = [];
|
|
86
|
+
let size = 0;
|
|
87
|
+
try {
|
|
88
|
+
while (true) {
|
|
89
|
+
const next = await reader.read();
|
|
90
|
+
if (next.done)
|
|
91
|
+
break;
|
|
92
|
+
size += next.value.byteLength;
|
|
93
|
+
if (size > 96 * 1024 * 1024) {
|
|
94
|
+
await reader.cancel();
|
|
95
|
+
throw new Error("Service response exceeds size limit");
|
|
96
|
+
}
|
|
97
|
+
chunks.push(next.value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
reader.releaseLock();
|
|
102
|
+
}
|
|
103
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
options.signal?.removeEventListener("abort", abort);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async catalog(signal, refresh = false) {
|
|
111
|
+
const credential = await this.credential();
|
|
112
|
+
const key = createHash("sha256").update(credential ?? "anonymous").digest("hex");
|
|
113
|
+
if (!refresh && this.cache?.key === key && this.cache.expires > Date.now())
|
|
114
|
+
return this.cache.entries;
|
|
115
|
+
const base = new URL(this.provider.baseUrl);
|
|
116
|
+
const url = this.provider.catalogUrl ?? (this.provider.adapter === "mswarm" ? new URL("/v1/swarm/self-hosted/agents", base).toString() : `${this.provider.baseUrl.replace(/\/$/, "")}/models`);
|
|
117
|
+
const payload = record(await this.json(url, { signal }));
|
|
118
|
+
const items = payload.agents ?? payload.data;
|
|
119
|
+
if (!Array.isArray(items))
|
|
120
|
+
throw new Error("Invalid model catalog response");
|
|
121
|
+
const entries = items.map(item => {
|
|
122
|
+
const raw = record(item);
|
|
123
|
+
const aliases = [raw.slug, raw.public_model_id, raw.default_model, raw.id].filter((v) => typeof v === "string" && !!v);
|
|
124
|
+
return { id: aliases[0] ?? "", aliases, backend: raw.adapter === "strata-local" ? "strata" : typeof raw.backend === "string" ? raw.backend : typeof raw.adapter === "string" ? raw.adapter : undefined,
|
|
125
|
+
health: String(raw.health_status ?? "unknown"), supportsTools: raw.supports_tools === true,
|
|
126
|
+
contextWindow: typeof raw.context_window === "number" ? raw.context_window : undefined,
|
|
127
|
+
operations: Array.isArray(raw.operations) ? raw.operations.map(v => typeof v === "string" ? v : String(record(v).type ?? "")) : [], raw };
|
|
128
|
+
}).filter(x => x.id);
|
|
129
|
+
this.cache = { key, expires: Date.now() + 30000, entries };
|
|
130
|
+
return entries;
|
|
131
|
+
}
|
|
132
|
+
async usage(signal) {
|
|
133
|
+
if (this.provider.adapter !== "mswarm" && !this.provider.usageUrl)
|
|
134
|
+
return { available: false };
|
|
135
|
+
return this.json(this.provider.usageUrl ?? new URL("/v1/swarm/runtime/usage-limits", this.provider.baseUrl).toString(), { signal });
|
|
136
|
+
}
|
|
137
|
+
/** The proxy and selected node must both advertise this transport contract.
|
|
138
|
+
* A model's supports_reasoning flag alone says nothing about request forwarding. */
|
|
139
|
+
async requireNativeReasoning(modelId, parameters, signal) {
|
|
140
|
+
if (this.provider.adapter !== "mswarm" || parameters.length === 0)
|
|
141
|
+
return;
|
|
142
|
+
const endpoint = new URL(this.provider.baseUrl);
|
|
143
|
+
endpoint.pathname = endpoint.pathname.replace(/\/$/, "");
|
|
144
|
+
endpoint.pathname = ["", "/v1"].includes(endpoint.pathname)
|
|
145
|
+
? "/v1/swarm/self-hosted/openai/models" : `${endpoint.pathname}/models`;
|
|
146
|
+
const payload = record(await this.json(endpoint.href, { signal }).catch(error => {
|
|
147
|
+
throw new NativeReasoningTransportError(`Native reasoning preflight failed before inference: ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
+
}));
|
|
149
|
+
const models = Array.isArray(payload.data) ? payload.data.map(record) : [];
|
|
150
|
+
const model = models.find(item => [item.id, item.agent_slug, item.remote_slug].includes(modelId));
|
|
151
|
+
const contract = record(model?.native_reasoning);
|
|
152
|
+
const supported = Array.isArray(contract.parameters) ? contract.parameters : [];
|
|
153
|
+
if (contract.transport_version !== 1 || parameters.some(parameter => !supported.includes(parameter))) {
|
|
154
|
+
throw new NativeReasoningTransportError(`Native reasoning transport is unavailable for ${modelId}; upgrade the mSwarm proxy and selected node, or remove unsupported native reasoning settings from the configuration`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export class ConfiguredProvider {
|
|
159
|
+
constructor(resolved, binding = "lead", options = {}) {
|
|
160
|
+
this.resolved = resolved;
|
|
161
|
+
this.binding = binding;
|
|
162
|
+
this.options = options;
|
|
163
|
+
this.name = "configured";
|
|
164
|
+
this.supportsToolCalls = true;
|
|
165
|
+
this.clients = new Map();
|
|
166
|
+
this.caller = normalizeModelCaller(options.caller);
|
|
167
|
+
this.credentials = Object.freeze(Object.assign(Object.create(null), options.credentials));
|
|
168
|
+
for (const [id, credential] of Object.entries(this.credentials)) {
|
|
169
|
+
if (!Object.prototype.hasOwnProperty.call(resolved.config.providers, id) || typeof credential !== "string" || !credential.trim())
|
|
170
|
+
throw new Error("Invalid host provider credential mapping");
|
|
171
|
+
registerSecretValue(credential);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
client(providerId) {
|
|
175
|
+
const provider = this.resolved.config.providers[providerId];
|
|
176
|
+
if (!provider)
|
|
177
|
+
throw new Error(`Unknown configured provider: ${providerId}`);
|
|
178
|
+
if (!this.clients.has(providerId))
|
|
179
|
+
this.clients.set(providerId, new ModelServiceClient(provider, this.credentials[providerId], this.caller));
|
|
180
|
+
return this.clients.get(providerId);
|
|
181
|
+
}
|
|
182
|
+
async select(operation, signal, requireTools = false) {
|
|
183
|
+
const candidates = resolveBinding(this.resolved.config, this.binding, operation);
|
|
184
|
+
const reasons = [];
|
|
185
|
+
for (const candidate of candidates) {
|
|
186
|
+
const { id, model } = candidate;
|
|
187
|
+
const catalog = await this.client(model.provider).catalog(signal);
|
|
188
|
+
const actual = catalog.find(entry => entry.aliases.includes(model.catalogId));
|
|
189
|
+
if (!actual) {
|
|
190
|
+
reasons.push(`${id}: not entitled/advertised`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!["healthy", "unknown", "ready"].includes(actual.health)) {
|
|
194
|
+
reasons.push(`${id}: ${actual.health}`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (model.backend && actual.backend !== model.backend) {
|
|
198
|
+
reasons.push(`${id}: backend identity not verified`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (actual.operations.length && !actual.operations.includes(operation)) {
|
|
202
|
+
reasons.push(`${id}: unsupported operation`);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (requireTools && (!model.supportsTools || this.resolved.config.providers[model.provider].adapter === "mswarm" && !actual.supportsTools)) {
|
|
206
|
+
reasons.push(`${id}: tools unavailable`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
return { id, model, actual, provider: this.resolved.config.providers[model.provider] };
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`No eligible configured ${this.binding} route: ${reasons.join("; ")}`);
|
|
212
|
+
}
|
|
213
|
+
async generate(request) {
|
|
214
|
+
request.signal?.throwIfAborted();
|
|
215
|
+
const { id, model, provider, actual } = await this.select("chat.completions", request.signal, !!request.tools?.length);
|
|
216
|
+
const invocation = { id: randomUUID(), binding: this.binding, configHash: this.resolved.hash, modelId: id, catalogId: model.catalogId, backend: actual.backend, host: model.host };
|
|
217
|
+
const nativeReasoning = this.options.effort ? model.reasoning?.[this.options.effort] : undefined;
|
|
218
|
+
if (nativeReasoning)
|
|
219
|
+
invocation.nativeReasoningRequest = { ...nativeReasoning };
|
|
220
|
+
if (nativeReasoning)
|
|
221
|
+
await this.client(model.provider).requireNativeReasoning(model.catalogId, Object.keys(nativeReasoning), request.signal);
|
|
222
|
+
const maxTokens = Math.min(request.maxTokens ?? model.maxOutputTokens, model.maxOutputTokens);
|
|
223
|
+
const inputEstimate = Math.ceil(JSON.stringify([request.messages, request.tools]).length / 3);
|
|
224
|
+
if (inputEstimate + maxTokens > Math.min(model.contextWindow, actual.contextWindow ?? model.contextWindow))
|
|
225
|
+
throw new Error("Context budget exceeded; checkpoint and compact before continuing");
|
|
226
|
+
const bounded = { ...request, maxTokens, stream: request.stream === true && model.supportsStreaming === true };
|
|
227
|
+
await this.options.beforeRequest?.(invocation, bounded);
|
|
228
|
+
const adapter = new OpenAiCompatibleProvider({
|
|
229
|
+
model: model.catalogId, baseUrl: provider.baseUrl, apiKey: this.credentials[model.provider] ?? await providerCredential(provider), timeoutMs: 300000,
|
|
230
|
+
supportsStreaming: model.supportsStreaming ?? true, supportsTools: model.supportsTools,
|
|
231
|
+
headers: provider.adapter === "mswarm" ? modelCallerHeaders(this.caller) : undefined, retryTransient: false,
|
|
232
|
+
extraBody: nativeReasoning,
|
|
233
|
+
});
|
|
234
|
+
const result = await adapter.generate(bounded);
|
|
235
|
+
await this.options.afterRequest?.(invocation, result);
|
|
236
|
+
return { ...result, route: invocation };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ModelServiceErrorCode = "authentication_failed" | "entitlement_missing" | "quota_exhausted" | "rate_limited" | "service_unavailable" | "model_unavailable" | "invalid_request";
|
|
2
|
+
/** Transport diagnostics contain stable classifications, never an upstream body. */
|
|
3
|
+
export declare class ModelServiceError extends Error {
|
|
4
|
+
readonly code: ModelServiceErrorCode;
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly retryAfterSeconds?: number | undefined;
|
|
7
|
+
constructor(code: ModelServiceErrorCode, status: number, retryAfterSeconds?: number | undefined);
|
|
8
|
+
static fromResponse(response: Response): Promise<ModelServiceError>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ModelServiceError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelServiceError.d.ts","sourceRoot":"","sources":["../../src/providers/ModelServiceError.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,uBAAuB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,cAAc,GAAG,qBAAqB,GAAG,mBAAmB,GAAG,iBAAiB,CAAC;AAE3L,oFAAoF;AACpF,qBAAa,iBAAkB,SAAQ,KAAK;IAC9B,QAAQ,CAAC,IAAI,EAAE,qBAAqB;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM;gBAAzF,IAAI,EAAE,qBAAqB,EAAW,MAAM,EAAE,MAAM,EAAW,iBAAiB,CAAC,EAAE,MAAM,YAAA;WAKjG,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAoB1E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Transport diagnostics contain stable classifications, never an upstream body. */
|
|
2
|
+
export class ModelServiceError extends Error {
|
|
3
|
+
constructor(code, status, retryAfterSeconds) {
|
|
4
|
+
super(`Model service HTTP ${status}: ${code}${code === "authentication_failed" ? "; authenticate with codali login" : ""}`);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
8
|
+
this.name = "ModelServiceError";
|
|
9
|
+
}
|
|
10
|
+
static async fromResponse(response) {
|
|
11
|
+
let serverCode = "";
|
|
12
|
+
if (response.body) {
|
|
13
|
+
const reader = response.body.getReader();
|
|
14
|
+
const chunks = [];
|
|
15
|
+
let bytes = 0;
|
|
16
|
+
try {
|
|
17
|
+
while (true) {
|
|
18
|
+
const part = await reader.read();
|
|
19
|
+
if (part.done)
|
|
20
|
+
break;
|
|
21
|
+
bytes += part.value.byteLength;
|
|
22
|
+
if (bytes > 8192)
|
|
23
|
+
break;
|
|
24
|
+
chunks.push(part.value);
|
|
25
|
+
}
|
|
26
|
+
const payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
27
|
+
if (typeof (payload.error?.code ?? payload.code) === "string")
|
|
28
|
+
serverCode = String(payload.error?.code ?? payload.code);
|
|
29
|
+
}
|
|
30
|
+
catch { /* The status still supplies a safe classification. */ }
|
|
31
|
+
finally {
|
|
32
|
+
await reader.cancel().catch(() => undefined);
|
|
33
|
+
reader.releaseLock();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const code = response.status === 401 ? "authentication_failed"
|
|
37
|
+
: response.status === 403 ? "entitlement_missing"
|
|
38
|
+
: ["quota_exceeded", "quota_exhausted", "insufficient_quota", "credits_exhausted", "insufficient_credits"].includes(serverCode) || response.status === 402 ? "quota_exhausted"
|
|
39
|
+
: response.status === 429 ? "rate_limited"
|
|
40
|
+
: response.status === 404 ? "model_unavailable"
|
|
41
|
+
: response.status >= 500 ? "service_unavailable" : "invalid_request";
|
|
42
|
+
const retry = Number(response.headers.get("retry-after"));
|
|
43
|
+
return new ModelServiceError(code, response.status, response.headers.has("retry-after") && Number.isFinite(retry) && retry >= 0 ? Math.min(retry, 3600) : undefined);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MswarmWorkerProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MswarmWorkerProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAER,eAAe,EACf,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;AA8C5B,qBAAa,oBAAqB,YAAW,QAAQ;IAQvC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,SAAS;IAEnC,QAAQ,CAAC,IAAI,mBAAmB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAWvG,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"MswarmWorkerProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MswarmWorkerProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAER,eAAe,EACf,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;AA8C5B,qBAAa,oBAAqB,YAAW,QAAQ;IAQvC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,SAAS;IAEnC,QAAQ,CAAC,IAAI,mBAAmB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAWvG,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAmEpE"}
|
|
@@ -52,7 +52,10 @@ export class MswarmWorkerProvider {
|
|
|
52
52
|
this.apiKey = config.apiKey;
|
|
53
53
|
}
|
|
54
54
|
async generate(request) {
|
|
55
|
+
request.signal?.throwIfAborted();
|
|
55
56
|
const controller = new AbortController();
|
|
57
|
+
const onAbort = () => controller.abort(request.signal?.reason);
|
|
58
|
+
request.signal?.addEventListener("abort", onAbort, { once: true });
|
|
56
59
|
const timeout = this.config.timeoutMs && this.config.timeoutMs > 0
|
|
57
60
|
? setTimeout(() => controller.abort(), this.config.timeoutMs)
|
|
58
61
|
: undefined;
|
|
@@ -106,6 +109,7 @@ export class MswarmWorkerProvider {
|
|
|
106
109
|
};
|
|
107
110
|
}
|
|
108
111
|
finally {
|
|
112
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
109
113
|
if (timeout) {
|
|
110
114
|
clearTimeout(timeout);
|
|
111
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAiCompatibleProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OpenAiCompatibleProvider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OpenAiCompatibleProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OpenAiCompatibleProvider.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EAEd,eAAe,EACf,gBAAgB,EAGjB,MAAM,oBAAoB,CAAC;AAkK5B,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,EACX,MAAM,WAAW,EACjB,aAAa,eAAe,KAC3B,OAAO,CAAC,QAAQ,CAclB,CAAC;AAEF,qBAAa,wBAAyB,YAAW,QAAQ;IAS3C,OAAO,CAAC,MAAM;IAR1B,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,QAAQ;IAElC,IAAI,SAAuB;IAC3B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,sBAAsB,CAAoC;gBAE9C,MAAM,EAAE,cAAc;IAWpC,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoPnE,OAAO,CAAC,WAAW;CA0BpB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeLocalOpenAiCompatibleRunnerConfig, } from "@mcoda/shared";
|
|
2
|
+
import { ModelServiceError } from "./ModelServiceError.js";
|
|
2
3
|
const parseToolArgs = (raw) => {
|
|
3
4
|
try {
|
|
4
5
|
return JSON.parse(raw);
|
|
@@ -7,6 +8,11 @@ const parseToolArgs = (raw) => {
|
|
|
7
8
|
return raw;
|
|
8
9
|
}
|
|
9
10
|
};
|
|
11
|
+
// JSON permits lone UTF-16 surrogates, but native tokenizers often require
|
|
12
|
+
// Unicode scalar values. Keep their visible source spelling in model context
|
|
13
|
+
// instead of losing them to replacement characters. With /u, valid pairs are
|
|
14
|
+
// matched as one astral code point and are therefore outside this range.
|
|
15
|
+
const modelContextText = (value) => value.replace(/[\uD800-\uDFFF]/gu, (unit) => `\\u${unit.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
10
16
|
const normalizeBaseUrl = (baseUrl) => {
|
|
11
17
|
const root = baseUrl ?? "https://api.openai.com/v1";
|
|
12
18
|
return root.endsWith("/") ? root : `${root}/`;
|
|
@@ -148,6 +154,7 @@ export class OpenAiCompatibleProvider {
|
|
|
148
154
|
this.responseFormatStrategy = normalizedLocal.config.responseFormatStrategy ?? "openai";
|
|
149
155
|
}
|
|
150
156
|
async generate(request) {
|
|
157
|
+
request.signal?.throwIfAborted();
|
|
151
158
|
const baseUrl = normalizeBaseUrl(this.localRunner.baseUrl ?? this.config.baseUrl);
|
|
152
159
|
const url = new URL("chat/completions", baseUrl).toString();
|
|
153
160
|
const auth = this.resolveAuth();
|
|
@@ -168,7 +175,7 @@ export class OpenAiCompatibleProvider {
|
|
|
168
175
|
: undefined;
|
|
169
176
|
const messages = request.messages.map((message) => ({
|
|
170
177
|
role: message.role,
|
|
171
|
-
content: message.content,
|
|
178
|
+
content: modelContextText(message.content),
|
|
172
179
|
name: message.name,
|
|
173
180
|
tool_call_id: message.toolCallId,
|
|
174
181
|
tool_calls: message.toolCalls?.map((call) => ({
|
|
@@ -176,7 +183,7 @@ export class OpenAiCompatibleProvider {
|
|
|
176
183
|
type: "function",
|
|
177
184
|
function: {
|
|
178
185
|
name: call.name,
|
|
179
|
-
arguments: typeof call.args === "string" ? call.args : JSON.stringify(call.args ?? {}),
|
|
186
|
+
arguments: typeof call.args === "string" ? modelContextText(call.args) : JSON.stringify(call.args ?? {}),
|
|
180
187
|
},
|
|
181
188
|
})),
|
|
182
189
|
}));
|
|
@@ -199,7 +206,7 @@ export class OpenAiCompatibleProvider {
|
|
|
199
206
|
type: "function",
|
|
200
207
|
function: {
|
|
201
208
|
name: tool.name,
|
|
202
|
-
description: tool.description,
|
|
209
|
+
description: tool.description === undefined ? undefined : modelContextText(tool.description),
|
|
203
210
|
parameters: tool.inputSchema ?? {},
|
|
204
211
|
},
|
|
205
212
|
})),
|
|
@@ -228,20 +235,23 @@ export class OpenAiCompatibleProvider {
|
|
|
228
235
|
}
|
|
229
236
|
}
|
|
230
237
|
const controller = new AbortController();
|
|
238
|
+
const onAbort = () => controller.abort(request.signal?.reason);
|
|
239
|
+
request.signal?.addEventListener("abort", onAbort, { once: true });
|
|
231
240
|
const timeoutMs = this.config.timeoutMs ?? 60000;
|
|
232
241
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
233
242
|
try {
|
|
234
|
-
const
|
|
243
|
+
const requestFetch = this.config.retryTransient === false ? (url, init) => fetch(url, init) : fetchWithTransientRetry;
|
|
244
|
+
const response = await requestFetch(url, {
|
|
235
245
|
method: "POST",
|
|
236
246
|
headers,
|
|
237
247
|
body: JSON.stringify(body),
|
|
238
248
|
signal: controller.signal,
|
|
249
|
+
redirect: "error",
|
|
239
250
|
}, controller);
|
|
240
251
|
if (!response.ok) {
|
|
241
|
-
|
|
242
|
-
throw new Error(`OpenAI-compatible error ${response.status}: ${errorBody}`);
|
|
252
|
+
throw await ModelServiceError.fromResponse(response);
|
|
243
253
|
}
|
|
244
|
-
if (request.stream) {
|
|
254
|
+
if (request.stream && !response.headers.get("content-type")?.includes("application/json")) {
|
|
245
255
|
if (!response.body) {
|
|
246
256
|
throw new Error("OpenAI-compatible streaming response missing body");
|
|
247
257
|
}
|
|
@@ -249,55 +259,85 @@ export class OpenAiCompatibleProvider {
|
|
|
249
259
|
const decoder = new TextDecoder();
|
|
250
260
|
let buffer = "";
|
|
251
261
|
let content = "";
|
|
262
|
+
let ended = false;
|
|
263
|
+
let finishReason;
|
|
252
264
|
const toolCallMap = new Map();
|
|
253
265
|
let usage;
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
266
|
+
const consume = (line) => {
|
|
267
|
+
const trimmed = line.trim();
|
|
268
|
+
if (!trimmed.startsWith("data:"))
|
|
269
|
+
return;
|
|
270
|
+
const data = trimmed.slice(5).trim();
|
|
271
|
+
if (!data)
|
|
272
|
+
return;
|
|
273
|
+
if (data === "[DONE]") {
|
|
274
|
+
ended = true;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const chunk = JSON.parse(data);
|
|
278
|
+
if (chunk.error) {
|
|
279
|
+
const detail = typeof chunk.error === "string" ? chunk.error : JSON.stringify(chunk.error);
|
|
280
|
+
throw new Error(`OpenAI-compatible stream error: ${this.config.apiKey ? detail.split(this.config.apiKey).join("[redacted]").slice(0, 600) : detail.slice(0, 600)}`);
|
|
281
|
+
}
|
|
282
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
283
|
+
if (typeof chunk.choices?.[0]?.finish_reason === "string")
|
|
284
|
+
finishReason = chunk.choices[0].finish_reason;
|
|
285
|
+
if (typeof delta?.content === "string") {
|
|
286
|
+
content += delta.content;
|
|
287
|
+
if (content.length > 4000000)
|
|
288
|
+
throw new Error("Model stream exceeds output limit");
|
|
289
|
+
emitToken(delta.content);
|
|
290
|
+
}
|
|
291
|
+
if (Array.isArray(delta?.tool_calls))
|
|
292
|
+
for (const call of delta.tool_calls) {
|
|
293
|
+
const index = call.index ?? 0;
|
|
294
|
+
if (!Number.isInteger(index) || index < 0 || index > 128)
|
|
295
|
+
throw new Error("Invalid streamed tool index");
|
|
296
|
+
const existing = toolCallMap.get(index) ?? { id: call.id ?? `call_${index + 1}`, name: "", args: "" };
|
|
297
|
+
if (typeof call.id === "string")
|
|
298
|
+
existing.id = call.id;
|
|
299
|
+
if (typeof call.function?.name === "string")
|
|
300
|
+
existing.name = call.function.name;
|
|
301
|
+
if (typeof call.function?.arguments === "string")
|
|
302
|
+
existing.args += call.function.arguments;
|
|
303
|
+
if (existing.args.length > 2000000)
|
|
304
|
+
throw new Error("Tool arguments exceed stream limit");
|
|
305
|
+
toolCallMap.set(index, existing);
|
|
295
306
|
}
|
|
296
|
-
|
|
297
|
-
|
|
307
|
+
if (chunk.usage)
|
|
308
|
+
usage = chunk.usage;
|
|
309
|
+
};
|
|
310
|
+
try {
|
|
311
|
+
while (true) {
|
|
312
|
+
const { value, done } = await reader.read();
|
|
313
|
+
if (done) {
|
|
314
|
+
buffer += decoder.decode();
|
|
315
|
+
if (buffer.trim())
|
|
316
|
+
consume(buffer);
|
|
317
|
+
break;
|
|
298
318
|
}
|
|
319
|
+
buffer += decoder.decode(value, { stream: true });
|
|
320
|
+
if (buffer.length > 2000000)
|
|
321
|
+
throw new Error("Model stream frame exceeds limit");
|
|
322
|
+
const lines = buffer.split("\n");
|
|
323
|
+
buffer = lines.pop() ?? "";
|
|
324
|
+
for (const line of lines)
|
|
325
|
+
consume(line);
|
|
299
326
|
}
|
|
300
327
|
}
|
|
328
|
+
finally {
|
|
329
|
+
await reader.cancel().catch(() => undefined);
|
|
330
|
+
reader.releaseLock();
|
|
331
|
+
}
|
|
332
|
+
if ((!ended && !finishReason) || finishReason === "length")
|
|
333
|
+
throw new Error("Model stream ended before a complete response");
|
|
334
|
+
if (!content && !toolCallMap.size)
|
|
335
|
+
throw new Error("Model stream returned no content or tool calls");
|
|
336
|
+
for (const call of toolCallMap.values()) {
|
|
337
|
+
if (!call.name || !call.id)
|
|
338
|
+
throw new Error("Incomplete streamed tool call");
|
|
339
|
+
JSON.parse(call.args);
|
|
340
|
+
}
|
|
301
341
|
const toolCalls = toolCallMap.size > 0
|
|
302
342
|
? Array.from(toolCallMap.entries()).map(([index, call]) => ({
|
|
303
343
|
id: call.id ?? `call_${index + 1}`,
|
|
@@ -360,6 +400,7 @@ export class OpenAiCompatibleProvider {
|
|
|
360
400
|
};
|
|
361
401
|
}
|
|
362
402
|
finally {
|
|
403
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
363
404
|
clearTimeout(timeout);
|
|
364
405
|
}
|
|
365
406
|
}
|
|
@@ -52,6 +52,7 @@ export interface ProviderUsage {
|
|
|
52
52
|
totalTokens?: number;
|
|
53
53
|
}
|
|
54
54
|
export interface ProviderRequest {
|
|
55
|
+
signal?: AbortSignal;
|
|
55
56
|
messages: ProviderMessage[];
|
|
56
57
|
tools?: ProviderToolDefinition[];
|
|
57
58
|
toolChoice?: "auto" | "none" | {
|
|
@@ -70,8 +71,19 @@ export interface ProviderResponse {
|
|
|
70
71
|
toolCalls?: ProviderToolCall[];
|
|
71
72
|
usage?: ProviderUsage;
|
|
72
73
|
raw?: unknown;
|
|
74
|
+
/** Selected configured route; host labels are configuration metadata, not hardware attestation. */
|
|
75
|
+
route?: {
|
|
76
|
+
id: string;
|
|
77
|
+
binding: string;
|
|
78
|
+
configHash: string;
|
|
79
|
+
modelId: string;
|
|
80
|
+
catalogId: string;
|
|
81
|
+
backend?: string;
|
|
82
|
+
host?: string;
|
|
83
|
+
};
|
|
73
84
|
}
|
|
74
85
|
export interface ProviderConfig {
|
|
86
|
+
retryTransient?: boolean;
|
|
75
87
|
model: string;
|
|
76
88
|
/** Agent this provider serves, so failures can name it. */
|
|
77
89
|
agentSlug?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderTypes.d.ts","sourceRoot":"","sources":["../../src/providers/ProviderTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iCAAiC,EACjC,mBAAmB,EACnB,eAAe,EACf,iCAAiC,EAClC,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAE9E,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAClD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ProviderTypes.d.ts","sourceRoot":"","sources":["../../src/providers/ProviderTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type CodaliRuntimeAgentInput, type CodaliRuntimeEvent, type CodaliRuntimeInput, type CodaliRuntimePolicy, type CodaliRuntimeProviderInput, type CodaliRuntimeResult, type CodaliRuntimeTelemetry, type CodaliRuntimeToolManifest } from "./CodaliRuntime.js";
|
|
2
2
|
import type { ProviderMessage, ProviderUsage } from "../providers/ProviderTypes.js";
|
|
3
|
+
import { type ScopedModelUsage } from "../providers/ConfiguredModelScope.js";
|
|
3
4
|
export type CodaliJobStageKind = "router" | "planner" | "worker" | "adjudicator" | "synthesizer" | "verifier" | "repair" | (string & {});
|
|
4
5
|
export type CodaliJobStageStatus = "completed" | "failed" | "skipped";
|
|
5
6
|
export type CodaliJobStatus = "succeeded" | "failed" | "partial" | "needs_clarification";
|
|
@@ -178,6 +179,11 @@ export interface CodaliJobTelemetry {
|
|
|
178
179
|
stages: CodaliJobTelemetryStage[];
|
|
179
180
|
}
|
|
180
181
|
export interface CodaliJobRuntimeResult {
|
|
182
|
+
orchestration?: {
|
|
183
|
+
configHash: string;
|
|
184
|
+
usage: ScopedModelUsage;
|
|
185
|
+
toolCalls: number;
|
|
186
|
+
};
|
|
181
187
|
output: string;
|
|
182
188
|
status: CodaliJobStatus;
|
|
183
189
|
runId: string;
|