
























11import type { OpenClawConfig } from "../config/config.js";
22import { extractModelCompat } from "../plugins/provider-model-compat.js";
3+import type { ProviderRuntimeModel } from "../plugins/provider-runtime-model.types.js";
4+import { normalizeProviderTransportWithPlugin } from "../plugins/provider-runtime.js";
35import { getActivePluginRegistry } from "../plugins/runtime.js";
46import { buildPluginToolMetadataKey, getPluginToolMeta } from "../plugins/tools.js";
57import {
@@ -10,11 +12,18 @@ import { resolveAgentDir, resolveAgentWorkspaceDir, resolveSessionAgentId } from
1012import { createOpenClawCodingTools } from "./agent-tools.js";
1113import { resolveEffectiveToolPolicy } from "./agent-tools.policy.js";
1214import { getChannelAgentToolMeta } from "./channel-tools.js";
15+import { resolveModel } from "./embedded-agent-runner/model.js";
16+import { resolveBundledStaticCatalogModel } from "./embedded-agent-runner/model.static-catalog.js";
1317import { normalizeStaticProviderModelId } from "./model-ref-shared.js";
1418import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
19+import { normalizeAgentRuntimeTools } from "./runtime-plan/tools.js";
1520import { summarizeToolDescriptionText } from "./tool-description-summary.js";
1621import { resolveToolDisplay } from "./tool-display.js";
1722import { normalizeToolName } from "./tool-policy.js";
23+import {
24+filterRuntimeCompatibleTools,
25+type RuntimeToolSchemaDiagnostic,
26+} from "./tool-schema-projection.js";
1827import type {
1928EffectiveToolInventoryNotice,
2029EffectiveToolInventoryEntry,
@@ -47,16 +56,22 @@ function summarizeToolDescription(tool: AnyAgentTool): string {
4756});
4857}
495850-function resolveEffectiveToolSource(tool: AnyAgentTool): {
59+function resolveEffectiveToolSource(
60+tool: AnyAgentTool,
61+fallbackTool?: AnyAgentTool,
62+): {
5163source: EffectiveToolSource;
5264pluginId?: string;
5365channelId?: string;
5466} {
55-const pluginMeta = getPluginToolMeta(tool);
67+const pluginMeta =
68+getPluginToolMeta(tool) ?? (fallbackTool ? getPluginToolMeta(fallbackTool) : undefined);
5669if (pluginMeta) {
5770return { source: "plugin", pluginId: pluginMeta.pluginId };
5871}
59-const channelMeta = getChannelAgentToolMeta(tool as never);
72+const channelMeta =
73+getChannelAgentToolMeta(tool as never) ??
74+(fallbackTool ? getChannelAgentToolMeta(fallbackTool as never) : undefined);
6075if (channelMeta) {
6176return { source: "channel", channelId: channelMeta.channelId };
6277}
@@ -150,6 +165,41 @@ function buildToolInventoryNotices(params: {
150165return undefined;
151166}
152167168+function buildUnsupportedToolSchemaNotice(params: {
169+diagnostic: RuntimeToolSchemaDiagnostic;
170+tool: AnyAgentTool | undefined;
171+fallbackTool: AnyAgentTool | undefined;
172+}): EffectiveToolInventoryNotice {
173+const source = params.tool
174+ ? resolveEffectiveToolSource(params.tool, params.fallbackTool)
175+ : { source: "core" as const };
176+const owner =
177+source.source === "plugin" && source.pluginId
178+ ? ` from plugin "${source.pluginId}"`
179+ : source.source === "channel" && source.channelId
180+ ? ` from channel "${source.channelId}"`
181+ : "";
182+return {
183+id: `unsupported-tool-schema:${params.diagnostic.toolName}`,
184+severity: "warning",
185+message: `Tool "${params.diagnostic.toolName}"${owner} has an unsupported runtime input schema (${params.diagnostic.violations.join(", ")}) and was quarantined before model projection. Fix or disable the owner, or remove the tool from active allowlists.`,
186+};
187+}
188+189+function buildUnsupportedToolSchemaNotices(params: {
190+diagnostics: readonly RuntimeToolSchemaDiagnostic[];
191+tools: readonly AnyAgentTool[];
192+rawToolsByName: ReadonlyMap<string, AnyAgentTool>;
193+}): EffectiveToolInventoryNotice[] {
194+return params.diagnostics.map((diagnostic) =>
195+buildUnsupportedToolSchemaNotice({
196+ diagnostic,
197+tool: params.tools[diagnostic.toolIndex],
198+fallbackTool: params.rawToolsByName.get(diagnostic.toolName),
199+}),
200+);
201+}
202+153203function disambiguateLabels(entries: EffectiveToolInventoryEntry[]): EffectiveToolInventoryEntry[] {
154204const counts = new Map<string, number>();
155205for (const entry of entries) {
@@ -164,6 +214,151 @@ function disambiguateLabels(entries: EffectiveToolInventoryEntry[]): EffectiveTo
164214});
165215}
166216217+function applyProviderTransportNormalization(params: {
218+cfg: OpenClawConfig;
219+provider: string;
220+workspaceDir?: string;
221+runtimeModel: ProviderRuntimeModel;
222+}): ProviderRuntimeModel {
223+const normalized = normalizeProviderTransportWithPlugin({
224+provider: params.provider,
225+config: params.cfg,
226+workspaceDir: params.workspaceDir,
227+context: {
228+config: params.cfg,
229+workspaceDir: params.workspaceDir,
230+provider: params.provider,
231+api: params.runtimeModel.api,
232+baseUrl: params.runtimeModel.baseUrl,
233+},
234+});
235+if (!normalized) {
236+return params.runtimeModel;
237+}
238+return {
239+ ...params.runtimeModel,
240+api: normalized.api ?? params.runtimeModel.api,
241+baseUrl: normalized.baseUrl ?? params.runtimeModel.baseUrl,
242+} as ProviderRuntimeModel;
243+}
244+245+function resolveConfiguredFallbackApi(
246+providerConfig: { api?: string; baseUrl?: string } | undefined,
247+): string {
248+const explicitApi = normalizeOptionalString(providerConfig?.api);
249+if (explicitApi) {
250+return explicitApi;
251+}
252+return normalizeOptionalString(providerConfig?.baseUrl)
253+ ? "openai-completions"
254+ : "openai-responses";
255+}
256+257+function resolveDynamicRuntimeModelContext(params: {
258+cfg: OpenClawConfig;
259+agentDir?: string;
260+workspaceDir?: string;
261+provider: string;
262+modelId: string;
263+}): { modelApi?: string; runtimeModel?: ProviderRuntimeModel } {
264+const runtimeModel = resolveModel(params.provider, params.modelId, params.agentDir, params.cfg, {
265+workspaceDir: params.workspaceDir,
266+}).model as ProviderRuntimeModel | undefined;
267+if (!runtimeModel) {
268+return {};
269+}
270+return {
271+modelApi: runtimeModel.api,
272+ runtimeModel,
273+};
274+}
275+276+export function resolveEffectiveToolInventoryRuntimeModelContext(params: {
277+cfg: OpenClawConfig;
278+agentId?: string;
279+agentDir?: string;
280+workspaceDir?: string;
281+modelProvider?: string;
282+modelId?: string;
283+}): { modelApi?: string; runtimeModel?: ProviderRuntimeModel } {
284+const provider = normalizeProviderId(params.modelProvider ?? "");
285+const modelId = params.modelId?.trim() ?? "";
286+if (!provider || !modelId) {
287+return {};
288+}
289+const agentId = params.agentId?.trim() || resolveSessionAgentId({ config: params.cfg });
290+const workspaceDir = params.workspaceDir ?? resolveAgentWorkspaceDir(params.cfg, agentId);
291+const providerConfig = findNormalizedProviderValue(params.cfg.models?.providers, provider);
292+const configuredModels = Array.isArray(providerConfig?.models) ? providerConfig.models : [];
293+const normalizedModelId = normalizeStaticProviderModelId(provider, modelId);
294+const normalizedModelKey = normalizeLowercaseStringOrEmpty(normalizedModelId);
295+const providerPrefixedModelKey = normalizeLowercaseStringOrEmpty(
296+`${provider}/${normalizedModelId}`,
297+);
298+const configuredModel = configuredModels.find((model) => {
299+const id = normalizeStaticProviderModelId(provider, model.id);
300+const key = normalizeLowercaseStringOrEmpty(id);
301+return key === normalizedModelKey || key === providerPrefixedModelKey;
302+});
303+const bundledStaticModel = resolveBundledStaticCatalogModel({
304+ provider,
305+ modelId,
306+cfg: params.cfg,
307+ workspaceDir,
308+}) as ProviderRuntimeModel | undefined;
309+if (configuredModel) {
310+const configuredApi =
311+normalizeOptionalString(configuredModel.api) ??
312+normalizeOptionalString(providerConfig?.api) ??
313+normalizeOptionalString(bundledStaticModel?.api) ??
314+resolveConfiguredFallbackApi(providerConfig);
315+const runtimeModel = applyProviderTransportNormalization({
316+cfg: params.cfg,
317+ provider,
318+ workspaceDir,
319+runtimeModel: {
320+ ...bundledStaticModel,
321+ ...configuredModel,
322+id: configuredModel.id,
323+name: configuredModel.name ?? bundledStaticModel?.name ?? configuredModel.id,
324+ provider,
325+api: configuredApi,
326+baseUrl:
327+normalizeOptionalString(configuredModel.baseUrl) ??
328+normalizeOptionalString(providerConfig?.baseUrl) ??
329+normalizeOptionalString(bundledStaticModel?.baseUrl),
330+} as ProviderRuntimeModel,
331+});
332+return {
333+modelApi: runtimeModel.api,
334+ runtimeModel,
335+};
336+}
337+if (!bundledStaticModel) {
338+return resolveDynamicRuntimeModelContext({
339+cfg: params.cfg,
340+agentDir: params.agentDir,
341+ workspaceDir,
342+ provider,
343+ modelId,
344+});
345+}
346+const runtimeModel = applyProviderTransportNormalization({
347+cfg: params.cfg,
348+ provider,
349+ workspaceDir,
350+runtimeModel: {
351+ ...bundledStaticModel,
352+api: normalizeOptionalString(providerConfig?.api) ?? bundledStaticModel.api,
353+baseUrl: normalizeOptionalString(providerConfig?.baseUrl) ?? bundledStaticModel.baseUrl,
354+} as ProviderRuntimeModel,
355+});
356+return {
357+modelApi: runtimeModel.api,
358+ runtimeModel,
359+};
360+}
361+167362function resolveEffectiveModelCompat(params: {
168363cfg: OpenClawConfig;
169364modelProvider?: string;
@@ -200,6 +395,20 @@ export function resolveEffectiveToolInventory(
200395resolveSessionAgentId({ sessionKey: params.sessionKey, config: params.cfg });
201396const workspaceDir = params.workspaceDir ?? resolveAgentWorkspaceDir(params.cfg, agentId);
202397const agentDir = params.agentDir ?? resolveAgentDir(params.cfg, agentId);
398+const runtimeModelContext =
399+params.modelApi || params.runtimeModel
400+ ? {
401+modelApi: params.modelApi ?? params.runtimeModel?.api,
402+runtimeModel: params.runtimeModel,
403+}
404+ : resolveEffectiveToolInventoryRuntimeModelContext({
405+cfg: params.cfg,
406+ agentId,
407+ agentDir,
408+ workspaceDir,
409+modelProvider: params.modelProvider,
410+modelId: params.modelId,
411+});
203412const modelCompat = resolveEffectiveModelCompat({
204413cfg: params.cfg,
205414modelProvider: params.modelProvider,
@@ -214,6 +423,7 @@ export function resolveEffectiveToolInventory(
214423config: params.cfg,
215424modelProvider: params.modelProvider,
216425modelId: params.modelId,
426+modelApi: runtimeModelContext.modelApi,
217427 modelCompat,
218428messageProvider: params.messageProvider,
219429senderId: params.senderId,
@@ -233,6 +443,17 @@ export function resolveEffectiveToolInventory(
233443requireExplicitMessageTarget: params.requireExplicitMessageTarget,
234444disableMessageTool: params.disableMessageTool,
235445});
446+const rawToolsByName = new Map(effectiveTools.map((tool) => [tool.name, tool]));
447+const normalizedEffectiveTools = normalizeAgentRuntimeTools({
448+tools: effectiveTools,
449+provider: params.modelProvider ?? "",
450+config: params.cfg,
451+ workspaceDir,
452+modelId: params.modelId,
453+modelApi: runtimeModelContext.modelApi,
454+model: runtimeModelContext.runtimeModel,
455+});
456+const toolSchemaProjection = filterRuntimeCompatibleTools(normalizedEffectiveTools);
236457const effectivePolicy = resolveEffectiveToolPolicy({
237458config: params.cfg,
238459 agentId,
@@ -251,9 +472,9 @@ export function resolveEffectiveToolInventory(
251472);
252473253474const entries = disambiguateLabels(
254-effectiveTools
475+toolSchemaProjection.tools
255476.map((tool) => {
256-const source = resolveEffectiveToolSource(tool);
477+const source = resolveEffectiveToolSource(tool, rawToolsByName.get(tool.name));
257478const metadata = source.pluginId
258479 ? pluginToolMetadata.get(buildPluginToolMetadataKey(source.pluginId, tool.name))
259480 : undefined;
@@ -276,7 +497,14 @@ export function resolveEffectiveToolInventory(
276497})
277498.toSorted((a, b) => a.label.localeCompare(b.label)),
278499);
279-const notices = buildToolInventoryNotices({ cfg: params.cfg, profile, entries, effectivePolicy });
500+const notices = [
501+ ...buildUnsupportedToolSchemaNotices({
502+diagnostics: toolSchemaProjection.diagnostics,
503+tools: normalizedEffectiveTools,
504+ rawToolsByName,
505+}),
506+ ...(buildToolInventoryNotices({ cfg: params.cfg, profile, entries, effectivePolicy }) ?? []),
507+];
280508const groupsBySource = new Map<EffectiveToolSource, EffectiveToolInventoryEntry[]>();
281509for (const entry of entries) {
282510const tools = groupsBySource.get(entry.source) ?? [];
@@ -299,5 +527,5 @@ export function resolveEffectiveToolInventory(
299527})
300528.filter((group): group is EffectiveToolInventoryGroup => group !== null);
301529302-return { agentId, profile, groups, ...(notices ? { notices } : {}) };
530+return { agentId, profile, groups, ...(notices.length > 0 ? { notices } : {}) };
303531}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。