From 16cd6ebb368ea2da0a502a430cc7ec4933e06b02 Mon Sep 17 00:00:00 2001 From: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 20:10:40 +0000 Subject: [PATCH] fix(clawsweeper): address review for automerge-openclaw-openclaw-73388 (2) --- extensions/arcee/index.test.ts | 76 +++++++++++++++++++++++++ extensions/arcee/index.ts | 16 ++++-- extensions/arcee/models.ts | 19 +++++-- extensions/arcee/provider-policy-api.ts | 52 +++++++++++++++++ 4 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 extensions/arcee/provider-policy-api.ts diff --git a/extensions/arcee/index.test.ts b/extensions/arcee/index.test.ts index aafa9ae2081..fbe6aeeb5ec 100644 --- a/extensions/arcee/index.test.ts +++ b/extensions/arcee/index.test.ts @@ -209,4 +209,80 @@ describe("arcee provider plugin", () => { baseUrl: "https://openrouter.ai/api/v1", }); }); + + it("repairs stale Trinity tool compat on existing Arcee configs and runtime models", async () => { + const provider = await registerSingleProviderPlugin(arceePlugin); + + expect( + provider.normalizeConfig?.({ + provider: "arcee", + providerConfig: { + api: "openai-completions", + baseUrl: "https://openrouter.ai/v1/", + models: [ + { + id: "arcee/trinity-large-thinking", + name: "Trinity Large Thinking", + reasoning: true, + input: ["text"], + contextWindow: 262144, + maxTokens: 80000, + cost: { + input: 0.25, + output: 0.9, + cacheRead: 0.25, + cacheWrite: 0.25, + }, + compat: { + supportsReasoningEffort: false, + supportsStrictMode: true, + }, + }, + ], + }, + } as never), + ).toMatchObject({ + baseUrl: "https://openrouter.ai/api/v1", + models: [ + { + id: "arcee/trinity-large-thinking", + compat: { + supportsReasoningEffort: false, + supportsStrictMode: true, + supportsTools: false, + }, + }, + ], + }); + + expect( + provider.contributeResolvedModelCompat?.({ + provider: "arcee", + modelId: "arcee/trinity-large-thinking", + model: { + provider: "arcee", + id: "arcee/trinity-large-thinking", + name: "Trinity Large Thinking", + api: "openai-completions", + baseUrl: "https://openrouter.ai/api/v1", + reasoning: true, + input: ["text"], + contextWindow: 262144, + maxTokens: 80000, + cost: { + input: 0.25, + output: 0.9, + cacheRead: 0.25, + cacheWrite: 0.25, + }, + compat: { + supportsReasoningEffort: false, + }, + }, + } as never), + ).toEqual({ + supportsReasoningEffort: false, + supportsTools: false, + }); + }); }); diff --git a/extensions/arcee/index.ts b/extensions/arcee/index.ts index e7fd0126950..29e9b2def57 100644 --- a/extensions/arcee/index.ts +++ b/extensions/arcee/index.ts @@ -5,6 +5,10 @@ import { type ProviderCatalogContext, } from "openclaw/plugin-sdk/provider-catalog-shared"; import { OPENAI_COMPATIBLE_REPLAY_HOOKS } from "openclaw/plugin-sdk/provider-model-shared"; +import { + ARCEE_TRINITY_LARGE_THINKING_COMPAT, + isArceeTrinityLargeThinkingModelId, +} from "./models.js"; import { applyArceeConfig, applyArceeOpenRouterConfig, @@ -17,6 +21,7 @@ import { normalizeArceeOpenRouterBaseUrl, toArceeOpenRouterModelId, } from "./provider-catalog.js"; +import { normalizeArceeProviderConfig } from "./provider-policy-api.js"; const PROVIDER_ID = "arcee"; const ARCEE_WIZARD_GROUP = { @@ -120,13 +125,12 @@ export default definePluginEntry({ config, providerId: PROVIDER_ID, }), - normalizeConfig: ({ providerConfig }) => { - const normalizedBaseUrl = normalizeArceeOpenRouterBaseUrl(providerConfig.baseUrl); - return normalizedBaseUrl && normalizedBaseUrl !== providerConfig.baseUrl - ? { ...providerConfig, baseUrl: normalizedBaseUrl } - : undefined; - }, + normalizeConfig: ({ providerConfig }) => normalizeArceeProviderConfig(providerConfig), normalizeResolvedModel: ({ model }) => normalizeArceeResolvedModel(model), + contributeResolvedModelCompat: ({ modelId, model }) => + isArceeTrinityLargeThinkingModelId(model.id) || isArceeTrinityLargeThinkingModelId(modelId) + ? ARCEE_TRINITY_LARGE_THINKING_COMPAT + : undefined, normalizeTransport: ({ api, baseUrl }) => { const normalizedBaseUrl = normalizeArceeOpenRouterBaseUrl(baseUrl); return normalizedBaseUrl && normalizedBaseUrl !== baseUrl diff --git a/extensions/arcee/models.ts b/extensions/arcee/models.ts index 02e522227c5..dfcb0f19f53 100644 --- a/extensions/arcee/models.ts +++ b/extensions/arcee/models.ts @@ -1,6 +1,18 @@ -import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared"; +import type { + ModelCompatConfig, + ModelDefinitionConfig, +} from "openclaw/plugin-sdk/provider-model-shared"; export const ARCEE_BASE_URL = "https://api.arcee.ai/api/v1"; +export const ARCEE_TRINITY_LARGE_THINKING_COMPAT = { + supportsReasoningEffort: false, + supportsTools: false, +} as const satisfies ModelCompatConfig; + +export function isArceeTrinityLargeThinkingModelId(modelId: string): boolean { + const normalized = modelId.trim().toLowerCase(); + return normalized === "trinity-large-thinking" || normalized === "arcee/trinity-large-thinking"; +} export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [ { @@ -44,10 +56,7 @@ export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [ cacheRead: 0.25, cacheWrite: 0.25, }, - compat: { - supportsReasoningEffort: false, - supportsTools: false, - }, + compat: ARCEE_TRINITY_LARGE_THINKING_COMPAT, }, ]; diff --git a/extensions/arcee/provider-policy-api.ts b/extensions/arcee/provider-policy-api.ts new file mode 100644 index 00000000000..2eedc85bdaa --- /dev/null +++ b/extensions/arcee/provider-policy-api.ts @@ -0,0 +1,52 @@ +import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types"; +import { + ARCEE_TRINITY_LARGE_THINKING_COMPAT, + isArceeTrinityLargeThinkingModelId, +} from "./models.js"; +import { normalizeArceeOpenRouterBaseUrl } from "./provider-catalog.js"; + +export function normalizeArceeProviderConfig( + providerConfig: ModelProviderConfig, +): ModelProviderConfig { + let changed = false; + const normalizedBaseUrl = normalizeArceeOpenRouterBaseUrl(providerConfig.baseUrl); + const baseUrl = + normalizedBaseUrl && normalizedBaseUrl !== providerConfig.baseUrl + ? normalizedBaseUrl + : providerConfig.baseUrl; + if (baseUrl !== providerConfig.baseUrl) { + changed = true; + } + + const hasModels = Array.isArray(providerConfig.models); + const models = hasModels + ? providerConfig.models.map((model) => { + if (!isArceeTrinityLargeThinkingModelId(model.id)) { + return model; + } + if ( + model.compat?.supportsReasoningEffort === + ARCEE_TRINITY_LARGE_THINKING_COMPAT.supportsReasoningEffort && + model.compat?.supportsTools === ARCEE_TRINITY_LARGE_THINKING_COMPAT.supportsTools + ) { + return model; + } + changed = true; + return { + ...model, + compat: { + ...model.compat, + ...ARCEE_TRINITY_LARGE_THINKING_COMPAT, + }, + }; + }) + : providerConfig.models; + + return changed + ? { ...providerConfig, baseUrl, ...(hasModels ? { models } : {}) } + : providerConfig; +} + +export function normalizeConfig(params: { providerConfig: ModelProviderConfig }) { + return normalizeArceeProviderConfig(params.providerConfig); +}