@mcoda/agents 0.1.71 → 0.1.73
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/AgentService/AgentService.d.ts.map +1 -1
- package/dist/AgentService/AgentService.js +10 -3
- package/dist/adapters/AdapterTypes.d.ts +15 -1
- package/dist/adapters/AdapterTypes.d.ts.map +1 -1
- package/dist/adapters/codali/CodaliAdapter.d.ts +7 -4
- package/dist/adapters/codali/CodaliAdapter.d.ts.map +1 -1
- package/dist/adapters/codali/CodaliAdapter.js +54 -5
- package/dist/adapters/codali/CodaliCliRunner.d.ts +15 -0
- package/dist/adapters/codali/CodaliCliRunner.d.ts.map +1 -1
- package/dist/adapters/codali/CodaliCliRunner.js +45 -0
- package/dist/adapters/openai/OpenAiAdapter.d.ts +32 -2
- package/dist/adapters/openai/OpenAiAdapter.d.ts.map +1 -1
- package/dist/adapters/openai/OpenAiAdapter.js +256 -32
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentService.d.ts","sourceRoot":"","sources":["../../src/AgentService/AgentService.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,mBAAmB,
|
|
1
|
+
{"version":3,"file":"AgentService.d.ts","sourceRoot":"","sources":["../../src/AgentService/AgentService.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EAOpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAa7C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAuOhG,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,OAAO;gBADP,IAAI,EAAE,gBAAgB,EACtB,OAAO,GAAE,mBAAwB;WAG9B,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAKtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAUhD,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAIrE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAInD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAIpD,kBAAkB;YAMlB,kBAAkB;IA6BhC,OAAO,CAAC,kBAAkB;IA+CpB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA+C/E,OAAO,CAAC,KAAK;YAIC,OAAO;YASP,UAAU;IAQxB,OAAO,CAAC,6BAA6B;YAOvB,mBAAmB;YAgCnB,uBAAuB;YAiBvB,gBAAgB;IAM9B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,8BAA8B;IAStC,OAAO,CAAC,iBAAiB;YAOX,oBAAoB;YA6BpB,oBAAoB;YA6CpB,sBAAsB;YAkBtB,mBAAmB;YAUnB,4BAA4B;IAkCpC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAmBlD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA6H9E,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;YAwP5F,mBAAmB;YAkBnB,uBAAuB;YAwBvB,mBAAmB;CAYlC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { CryptoHelper, } from "@mcoda/shared";
|
|
4
|
+
import { CryptoHelper, LOCAL_OPENAI_COMPATIBLE_ADAPTER_ALIASES, isLocalOpenAiCompatibleAdapter, } from "@mcoda/shared";
|
|
5
5
|
import { GlobalRepository } from "@mcoda/db";
|
|
6
6
|
import { CodexAdapter } from "../adapters/codex/CodexAdapter.js";
|
|
7
7
|
import { GeminiAdapter } from "../adapters/gemini/GeminiAdapter.js";
|
|
@@ -18,7 +18,13 @@ import { ClaudeAdapter } from "../adapters/claude/ClaudeAdapter.js";
|
|
|
18
18
|
import { parseInvocationFailure } from "./InvocationFailureParser.js";
|
|
19
19
|
const CLI_BASED_ADAPTERS = new Set(["codex-cli", "gemini-cli", "openai-cli", "ollama-cli", "codali-cli", "claude-cli"]);
|
|
20
20
|
const LOCAL_ADAPTERS = new Set(["local-model"]);
|
|
21
|
-
const
|
|
21
|
+
const LOCAL_OPENAI_COMPATIBLE_ADAPTERS = new Set(LOCAL_OPENAI_COMPATIBLE_ADAPTER_ALIASES);
|
|
22
|
+
const OFFLINE_CAPABLE_ADAPTERS = new Set([
|
|
23
|
+
"local-model",
|
|
24
|
+
"ollama-cli",
|
|
25
|
+
"qa-cli",
|
|
26
|
+
...LOCAL_OPENAI_COMPATIBLE_ADAPTERS,
|
|
27
|
+
]);
|
|
22
28
|
const SUPPORTED_ADAPTERS = new Set([
|
|
23
29
|
"openai-api",
|
|
24
30
|
"codex-cli",
|
|
@@ -32,6 +38,7 @@ const SUPPORTED_ADAPTERS = new Set([
|
|
|
32
38
|
"ollama-cli",
|
|
33
39
|
"codali-cli",
|
|
34
40
|
"mswarm-worker",
|
|
41
|
+
...LOCAL_OPENAI_COMPATIBLE_ADAPTERS,
|
|
35
42
|
]);
|
|
36
43
|
const DEFAULT_JOB_PROMPT = "You are an mcoda agent that follows workspace runbooks and responds with actionable, concise output.";
|
|
37
44
|
const DEFAULT_CHARACTER_PROMPT = "Write clearly, avoid hallucinations, cite assumptions, and prioritize risk mitigation for the user.";
|
|
@@ -332,7 +339,7 @@ export class AgentService {
|
|
|
332
339
|
const config = await this.buildAdapterConfig(agent);
|
|
333
340
|
const adapterType = this.resolveAdapterType(agent, config.apiKey, adapterOverride);
|
|
334
341
|
const configWithAdapter = { ...config, adapter: adapterType };
|
|
335
|
-
if (adapterType === "openai-api") {
|
|
342
|
+
if (adapterType === "openai-api" || isLocalOpenAiCompatibleAdapter(adapterType)) {
|
|
336
343
|
return new OpenAiAdapter(configWithAdapter);
|
|
337
344
|
}
|
|
338
345
|
if (adapterType === "mswarm-worker") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent, AgentAuthMetadata, AgentHealth, AgentPromptManifest } from "@mcoda/shared";
|
|
1
|
+
import type { Agent, AgentAuthMetadata, AgentHealth, AgentPromptManifest, LocalOpenAiCompatibleRunnerConfig, LocalRunnerAuthMode, LocalRunnerKind, LocalRunnerResponseFormatStrategy } from "@mcoda/shared";
|
|
2
2
|
export interface AdapterConfig {
|
|
3
3
|
agent: Agent;
|
|
4
4
|
capabilities: string[];
|
|
@@ -13,6 +13,20 @@ export interface AdapterConfig {
|
|
|
13
13
|
prompts?: AgentPromptManifest;
|
|
14
14
|
authMetadata?: AgentAuthMetadata;
|
|
15
15
|
adapter?: string;
|
|
16
|
+
localRunner?: LocalOpenAiCompatibleRunnerConfig;
|
|
17
|
+
runnerKind?: LocalRunnerKind;
|
|
18
|
+
authMode?: LocalRunnerAuthMode;
|
|
19
|
+
dummyBearerToken?: string;
|
|
20
|
+
headers?: Record<string, string>;
|
|
21
|
+
extraBody?: Record<string, unknown>;
|
|
22
|
+
responseFormatStrategy?: LocalRunnerResponseFormatStrategy;
|
|
23
|
+
healthPath?: string;
|
|
24
|
+
modelsPath?: string;
|
|
25
|
+
requireModelInRequest?: boolean;
|
|
26
|
+
supportsStreaming?: boolean;
|
|
27
|
+
supportsTools?: boolean;
|
|
28
|
+
supportsJsonSchema?: boolean;
|
|
29
|
+
supportsGbnf?: boolean;
|
|
16
30
|
}
|
|
17
31
|
export interface DocdexRuntimeContext {
|
|
18
32
|
enabled?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdapterTypes.d.ts","sourceRoot":"","sources":["../../src/adapters/AdapterTypes.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"AdapterTypes.d.ts","sourceRoot":"","sources":["../../src/adapters/AdapterTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { AgentHealth } from "@mcoda/shared";
|
|
2
2
|
import { AdapterConfig, AgentAdapter, InvocationRequest, InvocationResult } from "../AdapterTypes.js";
|
|
3
|
-
|
|
4
|
-
sourceAdapter?: string;
|
|
5
|
-
explicitProvider?: string;
|
|
6
|
-
}) => {
|
|
3
|
+
type CodaliProviderResolution = {
|
|
7
4
|
provider: string;
|
|
8
5
|
sourceAdapter?: string;
|
|
9
6
|
requiresApiKey: boolean;
|
|
7
|
+
localOpenAiCompatible: boolean;
|
|
10
8
|
};
|
|
9
|
+
export declare const resolveCodaliProviderFromAdapter: (params: {
|
|
10
|
+
sourceAdapter?: string;
|
|
11
|
+
explicitProvider?: string;
|
|
12
|
+
}) => CodaliProviderResolution;
|
|
11
13
|
export declare class CodaliAdapter implements AgentAdapter {
|
|
12
14
|
private config;
|
|
13
15
|
constructor(config: AdapterConfig);
|
|
@@ -16,4 +18,5 @@ export declare class CodaliAdapter implements AgentAdapter {
|
|
|
16
18
|
invoke(request: InvocationRequest): Promise<InvocationResult>;
|
|
17
19
|
invokeStream(request: InvocationRequest): AsyncGenerator<InvocationResult, void, unknown>;
|
|
18
20
|
}
|
|
21
|
+
export {};
|
|
19
22
|
//# sourceMappingURL=CodaliAdapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodaliAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/codali/CodaliAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"CodaliAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/codali/CodaliAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAIZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA4CtG,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC;AA+BF,eAAO,MAAM,gCAAgC,GAAI,QAAQ;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,KAAG,wBAuCH,CAAC;AAiIF,qBAAa,aAAc,YAAW,YAAY;IACpC,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEnC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAanC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2E5D,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC;CA4EjG"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isLocalOpenAiCompatibleAdapter, normalizeLocalOpenAiCompatibleRunnerConfig, } from "@mcoda/shared";
|
|
1
2
|
import { cliHealthy, runCodaliExec, runCodaliStream } from "./CodaliCliRunner.js";
|
|
2
3
|
const resolveString = (value) => (typeof value === "string" && value.trim() ? value : undefined);
|
|
3
4
|
const resolveMetadataValue = (metadata, keys) => {
|
|
@@ -60,13 +61,16 @@ const resolveAgentSlug = (request, config) => {
|
|
|
60
61
|
export const resolveCodaliProviderFromAdapter = (params) => {
|
|
61
62
|
const sourceAdapter = params.sourceAdapter;
|
|
62
63
|
const explicitProvider = params.explicitProvider;
|
|
64
|
+
const localOpenAiCompatible = isLocalOpenAiCompatibleAdapter(sourceAdapter);
|
|
63
65
|
if (explicitProvider) {
|
|
64
66
|
const providerRequires = PROVIDERS_REQUIRING_API_KEY.has(explicitProvider);
|
|
65
|
-
const
|
|
67
|
+
const sourceUsesSessionAuth = SESSION_AUTH_ADAPTERS.has(sourceAdapter ?? "") || localOpenAiCompatible;
|
|
68
|
+
const requiresApiKey = providerRequires && !sourceUsesSessionAuth;
|
|
66
69
|
return {
|
|
67
70
|
provider: explicitProvider,
|
|
68
71
|
sourceAdapter,
|
|
69
72
|
requiresApiKey,
|
|
73
|
+
localOpenAiCompatible,
|
|
70
74
|
};
|
|
71
75
|
}
|
|
72
76
|
if (sourceAdapter) {
|
|
@@ -74,17 +78,20 @@ export const resolveCodaliProviderFromAdapter = (params) => {
|
|
|
74
78
|
throw new Error(`CODALI_UNSUPPORTED_ADAPTER: ${sourceAdapter} is not supported; configure a codali provider explicitly or use an openai/ollama adapter.`);
|
|
75
79
|
}
|
|
76
80
|
if (sourceAdapter === "openai-api") {
|
|
77
|
-
return { provider: "openai-compatible", sourceAdapter, requiresApiKey: true };
|
|
81
|
+
return { provider: "openai-compatible", sourceAdapter, requiresApiKey: true, localOpenAiCompatible };
|
|
82
|
+
}
|
|
83
|
+
if (localOpenAiCompatible) {
|
|
84
|
+
return { provider: "openai-compatible", sourceAdapter, requiresApiKey: false, localOpenAiCompatible };
|
|
78
85
|
}
|
|
79
86
|
if (["openai-cli", "codex-cli"].includes(sourceAdapter)) {
|
|
80
|
-
return { provider: "codex-cli", sourceAdapter, requiresApiKey: false };
|
|
87
|
+
return { provider: "codex-cli", sourceAdapter, requiresApiKey: false, localOpenAiCompatible };
|
|
81
88
|
}
|
|
82
89
|
if (["ollama-remote", "ollama-cli", "local-model"].includes(sourceAdapter)) {
|
|
83
|
-
return { provider: "ollama-remote", sourceAdapter, requiresApiKey: false };
|
|
90
|
+
return { provider: "ollama-remote", sourceAdapter, requiresApiKey: false, localOpenAiCompatible };
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
const requiresApiKey = PROVIDERS_REQUIRING_API_KEY.has("openai-compatible") && !SESSION_AUTH_ADAPTERS.has(sourceAdapter ?? "");
|
|
87
|
-
return { provider: "openai-compatible", sourceAdapter, requiresApiKey };
|
|
94
|
+
return { provider: "openai-compatible", sourceAdapter, requiresApiKey, localOpenAiCompatible };
|
|
88
95
|
};
|
|
89
96
|
const resolveProviderInfo = (request, config) => {
|
|
90
97
|
const anyConfig = config;
|
|
@@ -101,6 +108,12 @@ const ensureApiKey = (provider, sourceAdapter, requiresApiKey, config) => {
|
|
|
101
108
|
const sourceLabel = sourceAdapter ? ` (source adapter: ${sourceAdapter})` : "";
|
|
102
109
|
throw new Error(`AUTH_REQUIRED: API key missing for codali provider ${provider}${sourceLabel}; set CODALI_API_KEY or run \\\"mcoda agent auth set ${agentLabel}\\\".`);
|
|
103
110
|
};
|
|
111
|
+
const ensureLocalOpenAiCompatibleBaseUrl = (providerInfo, baseUrl) => {
|
|
112
|
+
if (providerInfo.provider !== "openai-compatible" || !providerInfo.localOpenAiCompatible || baseUrl)
|
|
113
|
+
return;
|
|
114
|
+
const sourceLabel = providerInfo.sourceAdapter ? ` (source adapter: ${providerInfo.sourceAdapter})` : "";
|
|
115
|
+
throw new Error(`CONFIG_REQUIRED: baseUrl missing for local OpenAI-compatible codali provider${sourceLabel}; set config.baseUrl or agent.config.baseUrl.`);
|
|
116
|
+
};
|
|
104
117
|
const resolveBaseUrl = (config) => {
|
|
105
118
|
const anyConfig = config;
|
|
106
119
|
const agentConfig = config.agent?.config;
|
|
@@ -111,6 +124,36 @@ const resolveBaseUrl = (config) => {
|
|
|
111
124
|
resolveString(agentConfig?.endpoint) ??
|
|
112
125
|
resolveString(agentConfig?.apiBaseUrl));
|
|
113
126
|
};
|
|
127
|
+
const compactLocalRunnerConfig = (config) => {
|
|
128
|
+
const normalized = normalizeLocalOpenAiCompatibleRunnerConfig({
|
|
129
|
+
adapter: config.adapter ?? config.agent.adapter,
|
|
130
|
+
config,
|
|
131
|
+
agentConfig: config.agent.config,
|
|
132
|
+
});
|
|
133
|
+
const entries = Object.entries(normalized.config).filter(([, value]) => value !== undefined);
|
|
134
|
+
if (!normalized.isLocalOpenAiCompatible && entries.length === 0)
|
|
135
|
+
return undefined;
|
|
136
|
+
return Object.fromEntries(entries);
|
|
137
|
+
};
|
|
138
|
+
const resolveLocalRunnerCliOptions = (config) => {
|
|
139
|
+
const localRunner = compactLocalRunnerConfig(config);
|
|
140
|
+
return {
|
|
141
|
+
localRunner,
|
|
142
|
+
runnerKind: localRunner?.runnerKind,
|
|
143
|
+
authMode: localRunner?.authMode,
|
|
144
|
+
dummyBearerToken: localRunner?.dummyBearerToken,
|
|
145
|
+
headers: localRunner?.headers,
|
|
146
|
+
extraBody: localRunner?.extraBody,
|
|
147
|
+
responseFormatStrategy: localRunner?.responseFormatStrategy,
|
|
148
|
+
healthPath: localRunner?.healthPath,
|
|
149
|
+
modelsPath: localRunner?.modelsPath,
|
|
150
|
+
requireModelInRequest: localRunner?.requireModelInRequest,
|
|
151
|
+
supportsStreaming: localRunner?.supportsStreaming,
|
|
152
|
+
supportsTools: localRunner?.supportsTools,
|
|
153
|
+
supportsJsonSchema: localRunner?.supportsJsonSchema,
|
|
154
|
+
supportsGbnf: localRunner?.supportsGbnf,
|
|
155
|
+
};
|
|
156
|
+
};
|
|
114
157
|
const resolveDocdexBaseUrl = (config) => {
|
|
115
158
|
const anyConfig = config;
|
|
116
159
|
return resolveString(anyConfig.docdexBaseUrl) ?? resolveString(anyConfig.docdex?.baseUrl);
|
|
@@ -162,6 +205,8 @@ export class CodaliAdapter {
|
|
|
162
205
|
const agentId = resolveAgentId(request, this.config);
|
|
163
206
|
const agentSlug = resolveAgentSlug(request, this.config);
|
|
164
207
|
const baseUrl = resolveBaseUrl(this.config);
|
|
208
|
+
ensureLocalOpenAiCompatibleBaseUrl(providerInfo, baseUrl);
|
|
209
|
+
const localRunnerOptions = resolveLocalRunnerCliOptions(this.config);
|
|
165
210
|
const docdexBaseUrl = resolveMetadataValue(metadata, ["docdexBaseUrl", "docdex_base_url"]) ?? resolveDocdexBaseUrl(this.config);
|
|
166
211
|
const docdexRepoId = resolveMetadataValue(metadata, ["docdexRepoId", "docdex_repo_id"]) ?? resolveDocdexRepoId(this.config);
|
|
167
212
|
const docdexRepoRoot = resolveMetadataValue(metadata, ["docdexRepoRoot", "docdex_repo_root"]) ?? resolveDocdexRepoRoot(this.config);
|
|
@@ -188,6 +233,7 @@ export class CodaliAdapter {
|
|
|
188
233
|
model: this.config.model ?? "default",
|
|
189
234
|
apiKey: this.config.apiKey,
|
|
190
235
|
baseUrl,
|
|
236
|
+
...localRunnerOptions,
|
|
191
237
|
docdexBaseUrl,
|
|
192
238
|
docdexRepoId,
|
|
193
239
|
docdexRepoRoot,
|
|
@@ -229,6 +275,8 @@ export class CodaliAdapter {
|
|
|
229
275
|
const agentId = resolveAgentId(request, this.config);
|
|
230
276
|
const agentSlug = resolveAgentSlug(request, this.config);
|
|
231
277
|
const baseUrl = resolveBaseUrl(this.config);
|
|
278
|
+
ensureLocalOpenAiCompatibleBaseUrl(providerInfo, baseUrl);
|
|
279
|
+
const localRunnerOptions = resolveLocalRunnerCliOptions(this.config);
|
|
232
280
|
const docdexBaseUrl = resolveMetadataValue(metadata, ["docdexBaseUrl", "docdex_base_url"]) ?? resolveDocdexBaseUrl(this.config);
|
|
233
281
|
const docdexRepoId = resolveMetadataValue(metadata, ["docdexRepoId", "docdex_repo_id"]) ?? resolveDocdexRepoId(this.config);
|
|
234
282
|
const docdexRepoRoot = resolveMetadataValue(metadata, ["docdexRepoRoot", "docdex_repo_root"]) ?? resolveDocdexRepoRoot(this.config);
|
|
@@ -272,6 +320,7 @@ export class CodaliAdapter {
|
|
|
272
320
|
model: this.config.model ?? "default",
|
|
273
321
|
apiKey: this.config.apiKey,
|
|
274
322
|
baseUrl,
|
|
323
|
+
...localRunnerOptions,
|
|
275
324
|
docdexBaseUrl,
|
|
276
325
|
docdexRepoId,
|
|
277
326
|
docdexRepoRoot,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LocalOpenAiCompatibleRunnerConfig, LocalRunnerAuthMode, LocalRunnerKind, LocalRunnerResponseFormatStrategy } from "@mcoda/shared";
|
|
1
2
|
export interface CodaliCliOptions {
|
|
2
3
|
workspaceRoot: string;
|
|
3
4
|
project?: string;
|
|
@@ -13,6 +14,20 @@ export interface CodaliCliOptions {
|
|
|
13
14
|
model: string;
|
|
14
15
|
apiKey?: string;
|
|
15
16
|
baseUrl?: string;
|
|
17
|
+
localRunner?: LocalOpenAiCompatibleRunnerConfig;
|
|
18
|
+
runnerKind?: LocalRunnerKind;
|
|
19
|
+
authMode?: LocalRunnerAuthMode;
|
|
20
|
+
dummyBearerToken?: string;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
extraBody?: Record<string, unknown>;
|
|
23
|
+
responseFormatStrategy?: LocalRunnerResponseFormatStrategy;
|
|
24
|
+
healthPath?: string;
|
|
25
|
+
modelsPath?: string;
|
|
26
|
+
requireModelInRequest?: boolean;
|
|
27
|
+
supportsStreaming?: boolean;
|
|
28
|
+
supportsTools?: boolean;
|
|
29
|
+
supportsJsonSchema?: boolean;
|
|
30
|
+
supportsGbnf?: boolean;
|
|
16
31
|
docdexBaseUrl?: string;
|
|
17
32
|
docdexRepoId?: string;
|
|
18
33
|
docdexRepoRoot?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodaliCliRunner.d.ts","sourceRoot":"","sources":["../../../src/adapters/codali/CodaliCliRunner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CodaliCliRunner.d.ts","sourceRoot":"","sources":["../../../src/adapters/codali/CodaliCliRunner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iCAAiC,EACjC,mBAAmB,EACnB,eAAe,EACf,iCAAiC,EAClC,MAAM,eAAe,CAAC;AAMvB,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,iCAAiC,CAAC;IAChD,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,sBAAsB,CAAC,EAAE,iCAAiC,CAAC;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,UAAU,GAAI,sBAAoB,KAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAiBjG,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,SAAS,gBAAgB,KAAG,MAAM,EAoF3D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,SAAS,gBAAgB,KAAG,MAAM,CAAC,UAkB3D,CAAC;AAwBF,wBAAuB,eAAe,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,gBAAgB,GACxB,cAAc,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAuGpE;AAED,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,EACb,SAAS,gBAAgB,KACxB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAgC/D,CAAC"}
|
|
@@ -53,6 +53,48 @@ export const buildArgs = (options) => {
|
|
|
53
53
|
if (options.baseUrl) {
|
|
54
54
|
args.push("--base-url", options.baseUrl);
|
|
55
55
|
}
|
|
56
|
+
if (options.localRunner) {
|
|
57
|
+
args.push("--local-runner-json", JSON.stringify(options.localRunner));
|
|
58
|
+
}
|
|
59
|
+
if (options.runnerKind) {
|
|
60
|
+
args.push("--runner-kind", options.runnerKind);
|
|
61
|
+
}
|
|
62
|
+
if (options.authMode) {
|
|
63
|
+
args.push("--auth-mode", options.authMode);
|
|
64
|
+
}
|
|
65
|
+
if (options.dummyBearerToken) {
|
|
66
|
+
args.push("--dummy-bearer-token", options.dummyBearerToken);
|
|
67
|
+
}
|
|
68
|
+
if (options.headers) {
|
|
69
|
+
args.push("--headers-json", JSON.stringify(options.headers));
|
|
70
|
+
}
|
|
71
|
+
if (options.extraBody) {
|
|
72
|
+
args.push("--extra-body-json", JSON.stringify(options.extraBody));
|
|
73
|
+
}
|
|
74
|
+
if (options.responseFormatStrategy) {
|
|
75
|
+
args.push("--response-format-strategy", options.responseFormatStrategy);
|
|
76
|
+
}
|
|
77
|
+
if (options.healthPath) {
|
|
78
|
+
args.push("--health-path", options.healthPath);
|
|
79
|
+
}
|
|
80
|
+
if (options.modelsPath) {
|
|
81
|
+
args.push("--models-path", options.modelsPath);
|
|
82
|
+
}
|
|
83
|
+
if (options.requireModelInRequest !== undefined) {
|
|
84
|
+
args.push("--require-model-in-request", String(options.requireModelInRequest));
|
|
85
|
+
}
|
|
86
|
+
if (options.supportsStreaming !== undefined) {
|
|
87
|
+
args.push("--supports-streaming", String(options.supportsStreaming));
|
|
88
|
+
}
|
|
89
|
+
if (options.supportsTools !== undefined) {
|
|
90
|
+
args.push("--supports-tools", String(options.supportsTools));
|
|
91
|
+
}
|
|
92
|
+
if (options.supportsJsonSchema !== undefined) {
|
|
93
|
+
args.push("--supports-json-schema", String(options.supportsJsonSchema));
|
|
94
|
+
}
|
|
95
|
+
if (options.supportsGbnf !== undefined) {
|
|
96
|
+
args.push("--supports-gbnf", String(options.supportsGbnf));
|
|
97
|
+
}
|
|
56
98
|
if (options.docdexBaseUrl) {
|
|
57
99
|
args.push("--docdex-base-url", options.docdexBaseUrl);
|
|
58
100
|
}
|
|
@@ -78,6 +120,9 @@ export const buildEnv = (options) => {
|
|
|
78
120
|
if (options.baseUrl && !env.CODALI_BASE_URL) {
|
|
79
121
|
env.CODALI_BASE_URL = options.baseUrl;
|
|
80
122
|
}
|
|
123
|
+
if (options.localRunner && !env.CODALI_LOCAL_RUNNER_JSON) {
|
|
124
|
+
env.CODALI_LOCAL_RUNNER_JSON = JSON.stringify(options.localRunner);
|
|
125
|
+
}
|
|
81
126
|
return env;
|
|
82
127
|
};
|
|
83
128
|
const parseRunMetaLine = (line) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentHealth } from "@mcoda/shared";
|
|
1
|
+
import { AgentHealth, type LocalOpenAiCompatibleRunnerConfig, type LocalRunnerAuthMode, type LocalRunnerKind } from "@mcoda/shared";
|
|
2
2
|
import { AdapterConfig, AgentAdapter, InvocationRequest, InvocationResult } from "../AdapterTypes.js";
|
|
3
3
|
type OpenAiConfig = AdapterConfig & {
|
|
4
4
|
baseUrl?: string;
|
|
@@ -7,6 +7,18 @@ type OpenAiConfig = AdapterConfig & {
|
|
|
7
7
|
headers?: Record<string, string>;
|
|
8
8
|
temperature?: number;
|
|
9
9
|
extraBody?: Record<string, unknown>;
|
|
10
|
+
localRunner?: LocalOpenAiCompatibleRunnerConfig;
|
|
11
|
+
runnerKind?: LocalRunnerKind;
|
|
12
|
+
authMode?: LocalRunnerAuthMode;
|
|
13
|
+
dummyBearerToken?: string;
|
|
14
|
+
responseFormatStrategy?: string;
|
|
15
|
+
healthPath?: string;
|
|
16
|
+
modelsPath?: string;
|
|
17
|
+
requireModelInRequest?: boolean;
|
|
18
|
+
supportsStreaming?: boolean;
|
|
19
|
+
supportsTools?: boolean;
|
|
20
|
+
supportsJsonSchema?: boolean;
|
|
21
|
+
supportsGbnf?: boolean;
|
|
10
22
|
};
|
|
11
23
|
export declare class OpenAiAdapter implements AgentAdapter {
|
|
12
24
|
private config;
|
|
@@ -14,6 +26,13 @@ export declare class OpenAiAdapter implements AgentAdapter {
|
|
|
14
26
|
private headers;
|
|
15
27
|
private temperature;
|
|
16
28
|
private extraBody;
|
|
29
|
+
private runnerKind;
|
|
30
|
+
private authMode;
|
|
31
|
+
private dummyBearerToken;
|
|
32
|
+
private healthPath;
|
|
33
|
+
private modelsPath;
|
|
34
|
+
private localConfigIssues;
|
|
35
|
+
private isLocalOpenAiCompatible;
|
|
17
36
|
constructor(config: OpenAiConfig);
|
|
18
37
|
getCapabilities(): Promise<string[]>;
|
|
19
38
|
healthCheck(): Promise<AgentHealth>;
|
|
@@ -22,10 +41,21 @@ export declare class OpenAiAdapter implements AgentAdapter {
|
|
|
22
41
|
private assertConfig;
|
|
23
42
|
private ensureBaseUrl;
|
|
24
43
|
private ensureModel;
|
|
25
|
-
private
|
|
44
|
+
private resolveAuth;
|
|
26
45
|
private buildHeaders;
|
|
27
46
|
private buildBody;
|
|
28
47
|
private buildHealthCheckBody;
|
|
48
|
+
private healthCheckLocal;
|
|
49
|
+
private buildHealthResult;
|
|
50
|
+
private fetchHealthPath;
|
|
51
|
+
private fetchModels;
|
|
52
|
+
private fetchChatProbe;
|
|
53
|
+
private fetchJsonProbe;
|
|
54
|
+
private resolveRunnerUrl;
|
|
55
|
+
private resolveModelListUrls;
|
|
56
|
+
private extractModelIds;
|
|
57
|
+
private summarizeProbe;
|
|
58
|
+
private summarizeModelListing;
|
|
29
59
|
}
|
|
30
60
|
export {};
|
|
31
61
|
//# sourceMappingURL=OpenAiAdapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAiAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"OpenAiAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAEX,KAAK,iCAAiC,EACtC,KAAK,mBAAmB,EAExB,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,oBAAoB,CAAC;AAwW5B,KAAK,YAAY,GAAG,aAAa,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,iCAAiC,CAAC;IAChD,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAsBF,qBAAa,aAAc,YAAW,YAAY;IAapC,OAAO,CAAC,MAAM;IAZ1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,uBAAuB,CAAU;gBAErB,MAAM,EAAE,YAAY;IAoBlC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAkGnC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyC5D,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC;IAyFhG,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,SAAS;IA4BjB,OAAO,CAAC,oBAAoB;YAWd,gBAAgB;IAwE9B,OAAO,CAAC,iBAAiB;YAqBX,eAAe;YAKf,WAAW;YAyBX,cAAc;YAQd,cAAc;IAyC5B,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,qBAAqB;CAQ9B"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { normalizeLocalOpenAiCompatibleRunnerConfig, } from "@mcoda/shared";
|
|
1
2
|
import { parseUsageLimitError } from "../../AgentService/UsageLimitParser.js";
|
|
2
3
|
const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
3
4
|
const MAX_RESPONSE_DETAIL_CHARS = 500;
|
|
5
|
+
const LOCAL_HEALTH_TIMEOUT_MS = 10000;
|
|
4
6
|
const RATE_LIMIT_HEADER_NAMES = [
|
|
5
7
|
"retry-after",
|
|
6
8
|
"x-ratelimit-reset-after",
|
|
@@ -269,40 +271,39 @@ const extractResponseText = (data) => {
|
|
|
269
271
|
export class OpenAiAdapter {
|
|
270
272
|
constructor(config) {
|
|
271
273
|
this.config = config;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
+
const normalizedLocal = normalizeLocalOpenAiCompatibleRunnerConfig({
|
|
275
|
+
adapter: config.adapter ?? config.agent.adapter,
|
|
276
|
+
config,
|
|
277
|
+
agentConfig: config.agent.config,
|
|
278
|
+
});
|
|
279
|
+
this.baseUrl = normalizeBaseUrl(normalizedLocal.config.baseUrl) ?? resolveBaseUrl(config);
|
|
280
|
+
this.headers = normalizedLocal.config.headers;
|
|
274
281
|
this.temperature = typeof config.temperature === "number" ? config.temperature : undefined;
|
|
275
|
-
this.extraBody =
|
|
276
|
-
|
|
277
|
-
|
|
282
|
+
this.extraBody = normalizedLocal.config.extraBody;
|
|
283
|
+
this.runnerKind = normalizedLocal.config.runnerKind;
|
|
284
|
+
this.authMode = normalizedLocal.config.authMode ?? "bearer";
|
|
285
|
+
this.dummyBearerToken = normalizedLocal.config.dummyBearerToken;
|
|
286
|
+
this.healthPath = normalizedLocal.config.healthPath;
|
|
287
|
+
this.modelsPath = normalizedLocal.config.modelsPath;
|
|
288
|
+
this.localConfigIssues = normalizedLocal.issues;
|
|
289
|
+
this.isLocalOpenAiCompatible = normalizedLocal.isLocalOpenAiCompatible || this.authMode !== "bearer";
|
|
278
290
|
this.assertConfig();
|
|
279
291
|
}
|
|
280
292
|
async getCapabilities() {
|
|
281
293
|
return this.config.capabilities;
|
|
282
294
|
}
|
|
283
295
|
async healthCheck() {
|
|
284
|
-
if (!this.config.apiKey) {
|
|
285
|
-
return {
|
|
286
|
-
agentId: this.config.agent.id,
|
|
287
|
-
status: "unreachable",
|
|
288
|
-
lastCheckedAt: new Date().toISOString(),
|
|
289
|
-
details: {
|
|
290
|
-
adapter: "openai-api",
|
|
291
|
-
source: "openai_probe",
|
|
292
|
-
model: this.config.model,
|
|
293
|
-
baseUrl: this.baseUrl,
|
|
294
|
-
reason: "missing_api_key",
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
296
|
const startedAt = Date.now();
|
|
299
297
|
try {
|
|
300
298
|
const model = this.ensureModel();
|
|
301
|
-
const
|
|
299
|
+
const auth = this.resolveAuth();
|
|
302
300
|
const url = this.ensureBaseUrl();
|
|
301
|
+
if (this.isLocalOpenAiCompatible) {
|
|
302
|
+
return await this.healthCheckLocal({ model, auth, url, startedAt });
|
|
303
|
+
}
|
|
303
304
|
const response = await fetch(`${url}/chat/completions`, {
|
|
304
305
|
method: "POST",
|
|
305
|
-
headers: this.buildHeaders(
|
|
306
|
+
headers: this.buildHeaders(auth, false),
|
|
306
307
|
body: JSON.stringify(this.buildHealthCheckBody(model)),
|
|
307
308
|
});
|
|
308
309
|
const responseText = await response.text().catch(() => "");
|
|
@@ -367,7 +368,7 @@ export class OpenAiAdapter {
|
|
|
367
368
|
const message = error instanceof Error ? error.message : String(error);
|
|
368
369
|
const reason = /model is not configured/i.test(message)
|
|
369
370
|
? "missing_model"
|
|
370
|
-
: /missing api key/i.test(message)
|
|
371
|
+
: /missing api key|api key missing/i.test(message)
|
|
371
372
|
? "missing_api_key"
|
|
372
373
|
: "probe_failed";
|
|
373
374
|
return {
|
|
@@ -389,11 +390,11 @@ export class OpenAiAdapter {
|
|
|
389
390
|
async invoke(request) {
|
|
390
391
|
const url = this.ensureBaseUrl();
|
|
391
392
|
const model = this.ensureModel();
|
|
392
|
-
const
|
|
393
|
+
const auth = this.resolveAuth();
|
|
393
394
|
const docdex = resolveDocdexContext(this.config, request.metadata);
|
|
394
395
|
const resp = await fetch(`${url}/chat/completions`, {
|
|
395
396
|
method: "POST",
|
|
396
|
-
headers: this.buildHeaders(
|
|
397
|
+
headers: this.buildHeaders(auth, false, docdex),
|
|
397
398
|
body: JSON.stringify(this.buildBody(request.input, model, false, docdex)),
|
|
398
399
|
});
|
|
399
400
|
if (!resp.ok) {
|
|
@@ -411,7 +412,7 @@ export class OpenAiAdapter {
|
|
|
411
412
|
mode: "api",
|
|
412
413
|
capabilities: this.config.capabilities,
|
|
413
414
|
prompts: this.config.prompts,
|
|
414
|
-
authMode:
|
|
415
|
+
authMode: auth.metadataAuthMode,
|
|
415
416
|
adapterType: this.config.adapter ?? "openai-api",
|
|
416
417
|
baseUrl: url,
|
|
417
418
|
usage: isRecord(data) ? data.usage : undefined,
|
|
@@ -428,11 +429,11 @@ export class OpenAiAdapter {
|
|
|
428
429
|
async *invokeStream(request) {
|
|
429
430
|
const url = this.ensureBaseUrl();
|
|
430
431
|
const model = this.ensureModel();
|
|
431
|
-
const
|
|
432
|
+
const auth = this.resolveAuth();
|
|
432
433
|
const docdex = resolveDocdexContext(this.config, request.metadata);
|
|
433
434
|
const resp = await fetch(`${url}/chat/completions`, {
|
|
434
435
|
method: "POST",
|
|
435
|
-
headers: this.buildHeaders(
|
|
436
|
+
headers: this.buildHeaders(auth, true, docdex),
|
|
436
437
|
body: JSON.stringify(this.buildBody(request.input, model, true, docdex)),
|
|
437
438
|
});
|
|
438
439
|
if (!resp.ok || !resp.body) {
|
|
@@ -457,7 +458,7 @@ export class OpenAiAdapter {
|
|
|
457
458
|
model,
|
|
458
459
|
metadata: {
|
|
459
460
|
mode: "api",
|
|
460
|
-
authMode:
|
|
461
|
+
authMode: auth.metadataAuthMode,
|
|
461
462
|
adapterType: this.config.adapter ?? "openai-api",
|
|
462
463
|
baseUrl: url,
|
|
463
464
|
capabilities: this.config.capabilities,
|
|
@@ -536,15 +537,29 @@ export class OpenAiAdapter {
|
|
|
536
537
|
}
|
|
537
538
|
return this.config.model;
|
|
538
539
|
}
|
|
539
|
-
|
|
540
|
+
resolveAuth() {
|
|
541
|
+
if (this.authMode === "none") {
|
|
542
|
+
return { mode: "none", metadataAuthMode: "none" };
|
|
543
|
+
}
|
|
544
|
+
if (this.authMode === "dummy-bearer") {
|
|
545
|
+
return {
|
|
546
|
+
mode: "dummy-bearer",
|
|
547
|
+
authorization: `Bearer ${this.dummyBearerToken ?? "local"}`,
|
|
548
|
+
metadataAuthMode: "dummy-bearer",
|
|
549
|
+
};
|
|
550
|
+
}
|
|
540
551
|
if (!this.config.apiKey) {
|
|
541
552
|
throw new Error(`AUTH_REQUIRED: OpenAI API key missing; run \`mcoda agent auth set ${this.config.agent.slug ?? this.config.agent.id}\``);
|
|
542
553
|
}
|
|
543
|
-
return
|
|
554
|
+
return {
|
|
555
|
+
mode: "bearer",
|
|
556
|
+
authorization: `Bearer ${this.config.apiKey}`,
|
|
557
|
+
metadataAuthMode: "api",
|
|
558
|
+
};
|
|
544
559
|
}
|
|
545
|
-
buildHeaders(
|
|
560
|
+
buildHeaders(auth, streaming, docdex) {
|
|
546
561
|
return {
|
|
547
|
-
Authorization:
|
|
562
|
+
...(auth.authorization ? { Authorization: auth.authorization } : {}),
|
|
548
563
|
"Content-Type": "application/json",
|
|
549
564
|
Accept: streaming ? "text/event-stream" : "application/json",
|
|
550
565
|
...(docdex?.repoId ? { "x-docdex-repo-id": docdex.repoId } : {}),
|
|
@@ -586,4 +601,213 @@ export class OpenAiAdapter {
|
|
|
586
601
|
}
|
|
587
602
|
return body;
|
|
588
603
|
}
|
|
604
|
+
async healthCheckLocal(params) {
|
|
605
|
+
const { model, auth, url, startedAt } = params;
|
|
606
|
+
if (this.healthPath) {
|
|
607
|
+
const result = await this.fetchHealthPath(url, auth, this.healthPath);
|
|
608
|
+
const checkedAtMs = Date.now();
|
|
609
|
+
if (result.ok) {
|
|
610
|
+
return this.buildHealthResult("healthy", checkedAtMs, startedAt, {
|
|
611
|
+
source: "local_health_path",
|
|
612
|
+
model,
|
|
613
|
+
baseUrl: url,
|
|
614
|
+
healthPath: this.healthPath,
|
|
615
|
+
health: this.summarizeProbe(result),
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
return this.buildHealthResult("unreachable", checkedAtMs, startedAt, {
|
|
619
|
+
source: "local_health_path",
|
|
620
|
+
model,
|
|
621
|
+
baseUrl: url,
|
|
622
|
+
reason: result.error ? "health_path_failed" : "health_path_http_error",
|
|
623
|
+
healthPath: this.healthPath,
|
|
624
|
+
health: this.summarizeProbe(result),
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
const modelListing = await this.fetchModels(url, auth, model);
|
|
628
|
+
const checkedAtMs = Date.now();
|
|
629
|
+
if (modelListing.ok) {
|
|
630
|
+
if (modelListing.modelFound === false) {
|
|
631
|
+
return this.buildHealthResult("degraded", checkedAtMs, startedAt, {
|
|
632
|
+
source: "local_models",
|
|
633
|
+
model,
|
|
634
|
+
baseUrl: url,
|
|
635
|
+
reason: "model_not_listed",
|
|
636
|
+
modelListing: this.summarizeModelListing(modelListing),
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
return this.buildHealthResult("healthy", checkedAtMs, startedAt, {
|
|
640
|
+
source: "local_models",
|
|
641
|
+
model,
|
|
642
|
+
baseUrl: url,
|
|
643
|
+
modelListing: this.summarizeModelListing(modelListing),
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const chatProbe = await this.fetchChatProbe(url, auth, model);
|
|
647
|
+
const afterChatMs = Date.now();
|
|
648
|
+
if (chatProbe.ok) {
|
|
649
|
+
return this.buildHealthResult("degraded", afterChatMs, startedAt, {
|
|
650
|
+
source: "local_chat_probe",
|
|
651
|
+
model,
|
|
652
|
+
baseUrl: url,
|
|
653
|
+
reason: "model_listing_failed",
|
|
654
|
+
modelListing: this.summarizeModelListing(modelListing),
|
|
655
|
+
chatProbe: this.summarizeProbe(chatProbe),
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return this.buildHealthResult("unreachable", afterChatMs, startedAt, {
|
|
659
|
+
source: "local_models",
|
|
660
|
+
model,
|
|
661
|
+
baseUrl: url,
|
|
662
|
+
reason: modelListing.error ? "model_listing_failed" : "model_listing_http_error",
|
|
663
|
+
modelListing: this.summarizeModelListing(modelListing),
|
|
664
|
+
chatProbe: this.summarizeProbe(chatProbe),
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
buildHealthResult(status, checkedAtMs, startedAt, details) {
|
|
668
|
+
return {
|
|
669
|
+
agentId: this.config.agent.id,
|
|
670
|
+
status,
|
|
671
|
+
lastCheckedAt: new Date(checkedAtMs).toISOString(),
|
|
672
|
+
latencyMs: checkedAtMs - startedAt,
|
|
673
|
+
details: {
|
|
674
|
+
adapter: this.config.adapter ?? "openai-api",
|
|
675
|
+
runnerKind: this.runnerKind,
|
|
676
|
+
authMode: this.authMode,
|
|
677
|
+
configIssues: this.localConfigIssues.length ? this.localConfigIssues : undefined,
|
|
678
|
+
...details,
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
async fetchHealthPath(baseUrl, auth, healthPath) {
|
|
683
|
+
const url = this.resolveRunnerUrl(baseUrl, healthPath);
|
|
684
|
+
return this.fetchJsonProbe(url, auth);
|
|
685
|
+
}
|
|
686
|
+
async fetchModels(baseUrl, auth, model) {
|
|
687
|
+
const candidates = this.resolveModelListUrls(baseUrl);
|
|
688
|
+
let last;
|
|
689
|
+
for (const url of candidates) {
|
|
690
|
+
const result = await this.fetchJsonProbe(url, auth);
|
|
691
|
+
if (!result.ok) {
|
|
692
|
+
last = { ...result, models: [], modelFound: false };
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
const models = this.extractModelIds(result.data);
|
|
696
|
+
return {
|
|
697
|
+
...result,
|
|
698
|
+
models,
|
|
699
|
+
modelFound: models.length === 0 ? undefined : models.includes(model),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
return last ?? {
|
|
703
|
+
ok: false,
|
|
704
|
+
url: candidates[0] ?? baseUrl,
|
|
705
|
+
error: "No model listing URL candidates were available.",
|
|
706
|
+
models: [],
|
|
707
|
+
modelFound: false,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
async fetchChatProbe(baseUrl, auth, model) {
|
|
711
|
+
const url = `${baseUrl}/chat/completions`;
|
|
712
|
+
return this.fetchJsonProbe(url, auth, {
|
|
713
|
+
method: "POST",
|
|
714
|
+
body: JSON.stringify(this.buildHealthCheckBody(model)),
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
async fetchJsonProbe(url, auth, init = {}) {
|
|
718
|
+
const controller = new AbortController();
|
|
719
|
+
const timeout = setTimeout(() => controller.abort(), LOCAL_HEALTH_TIMEOUT_MS);
|
|
720
|
+
try {
|
|
721
|
+
const response = await fetch(url, {
|
|
722
|
+
method: init.method ?? "GET",
|
|
723
|
+
headers: this.buildHeaders(auth, false),
|
|
724
|
+
body: init.body,
|
|
725
|
+
signal: controller.signal,
|
|
726
|
+
});
|
|
727
|
+
const responseText = await response.text().catch(() => "");
|
|
728
|
+
let data;
|
|
729
|
+
if (responseText.trim()) {
|
|
730
|
+
try {
|
|
731
|
+
data = JSON.parse(responseText);
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
data = undefined;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
ok: response.ok,
|
|
739
|
+
status: response.status,
|
|
740
|
+
url,
|
|
741
|
+
response: responseText.slice(0, MAX_RESPONSE_DETAIL_CHARS),
|
|
742
|
+
data,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
return {
|
|
747
|
+
ok: false,
|
|
748
|
+
url,
|
|
749
|
+
error: error instanceof Error ? error.message : String(error),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
finally {
|
|
753
|
+
clearTimeout(timeout);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
resolveRunnerUrl(baseUrl, rawPath) {
|
|
757
|
+
try {
|
|
758
|
+
return new URL(rawPath).toString();
|
|
759
|
+
}
|
|
760
|
+
catch {
|
|
761
|
+
// Continue with relative runner paths below.
|
|
762
|
+
}
|
|
763
|
+
if (rawPath.startsWith("/")) {
|
|
764
|
+
const root = new URL(baseUrl);
|
|
765
|
+
return new URL(rawPath, root.origin).toString();
|
|
766
|
+
}
|
|
767
|
+
return new URL(rawPath, `${baseUrl}/`).toString();
|
|
768
|
+
}
|
|
769
|
+
resolveModelListUrls(baseUrl) {
|
|
770
|
+
if (this.modelsPath)
|
|
771
|
+
return [this.resolveRunnerUrl(baseUrl, this.modelsPath)];
|
|
772
|
+
const root = new URL(baseUrl);
|
|
773
|
+
const urls = new Set();
|
|
774
|
+
if (!root.pathname.replace(/\/+$/, "").endsWith("/v1")) {
|
|
775
|
+
urls.add(new URL("/v1/models", root.origin).toString());
|
|
776
|
+
}
|
|
777
|
+
urls.add(new URL("models", `${baseUrl}/`).toString());
|
|
778
|
+
urls.add(new URL("/models", root.origin).toString());
|
|
779
|
+
return Array.from(urls);
|
|
780
|
+
}
|
|
781
|
+
extractModelIds(data) {
|
|
782
|
+
if (!isRecord(data))
|
|
783
|
+
return [];
|
|
784
|
+
const entries = Array.isArray(data.data) ? data.data : Array.isArray(data.models) ? data.models : [];
|
|
785
|
+
const ids = entries
|
|
786
|
+
.map((entry) => {
|
|
787
|
+
if (typeof entry === "string")
|
|
788
|
+
return entry.trim();
|
|
789
|
+
if (isRecord(entry))
|
|
790
|
+
return resolveString(entry.id) ?? resolveString(entry.name);
|
|
791
|
+
return undefined;
|
|
792
|
+
})
|
|
793
|
+
.filter((entry) => Boolean(entry));
|
|
794
|
+
return Array.from(new Set(ids));
|
|
795
|
+
}
|
|
796
|
+
summarizeProbe(result) {
|
|
797
|
+
return {
|
|
798
|
+
ok: result.ok,
|
|
799
|
+
url: result.url,
|
|
800
|
+
httpStatus: result.status,
|
|
801
|
+
response: result.response,
|
|
802
|
+
error: result.error,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
summarizeModelListing(result) {
|
|
806
|
+
return {
|
|
807
|
+
...this.summarizeProbe(result),
|
|
808
|
+
models: result.models?.slice(0, 25),
|
|
809
|
+
modelCount: result.models?.length,
|
|
810
|
+
modelFound: result.modelFound,
|
|
811
|
+
};
|
|
812
|
+
}
|
|
589
813
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcoda/agents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.73",
|
|
4
4
|
"description": "Agent registry and capabilities for mcoda.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@mcoda/shared": "0.1.
|
|
34
|
-
"@mcoda/db": "0.1.
|
|
33
|
+
"@mcoda/shared": "0.1.73",
|
|
34
|
+
"@mcoda/db": "0.1.73"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -p tsconfig.json",
|