Skip to content

Commit dd43caa

Browse files
Fix Trinity main-session compatibility mismatch (#73388)
Summary: - The PR marks Arcee Trinity Large Thinking tool-incompatible in catalog/config/runtime paths, updates Arcee docs and changelog, and adds provider regression tests. - Reproducibility: yes. The linked reports provide concrete main-session failure logs, and current main still exposes Trinity without `compat.supportsTools:false` while the runtime sends tools unless that flag is false. ClawSweeper fixups: - Included follow-up commit: fix(arcee): disable Trinity tools in main sessions - Included follow-up commit: fix(clawsweeper): address review for automerge-openclaw-openclaw-7338… - Included follow-up commit: fix(arcee): repair Trinity main-session compatibility Validation: - ClawSweeper review passed for head 4c669d6. - Required merge gates passed before the squash merge. Prepared head SHA: 4c669d6 Review: #73388 (comment) Co-authored-by: Vincent Koc <vincentkoc@ieee.org> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
1 parent 68359ca commit dd43caa

9 files changed

Lines changed: 431 additions & 52 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Docs: https://docs.openclaw.ai
4444
- Agents/sessions: preserve terminal lifecycle state when final run metadata persists from a stale in-memory snapshot, preventing `main` sessions from staying stuck as running after completed or timed-out turns.
4545
- Gateway/CLI: make `openclaw gateway start` repair stale managed service definitions that point at old OpenClaw versions, missing binaries, or temporary installer paths before starting.
4646
- Heartbeat/scheduler: make heartbeat phase scheduling active-hours-aware so the scheduler seeks forward to the first in-window phase slot instead of arming timers for quiet-hours slots and relying solely on the runtime guard. Non-UTC `activeHours.timezone` values (e.g. `Asia/Shanghai`) now correctly influence when the next heartbeat timer fires, avoiding wasted quiet-hours ticks and long dormant gaps after gateway restarts. Fixes #75487. Thanks @amknight.
47+
- Providers/Arcee AI: mark Trinity Large Thinking as tool-incompatible so main-session runs use the same text-only request shape that made subagent runs recover, avoiding the remaining main-session response-shape mismatch after the #62848 transport failover fix. Fixes #62851 and #62847; carries forward #62848. Thanks @Adam-Researchh.
4748
- Status: show the `openai-codex` OAuth profile for `openai/gpt-*` sessions running through the native Codex runtime instead of reporting auth as unknown. (#76197) Thanks @mbelinky.
4849
- Gateway: avoid repeated plugin tool descriptor config hashing so large runtime configs do not block reply startup and trigger reconnect/timeouts. (#75944) Thanks @joshavant.
4950
- Plugins/externalization: keep diagnostics ClawHub packages and persisted bundled-plugin relocation on npm-first install metadata for launch, and omit Discord from the core package now that its external package is published. Thanks @vincentkoc.

docs/providers/arcee.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,24 @@ Arcee AI models can be accessed directly via the Arcee platform or through [Open
9898

9999
OpenClaw currently ships this bundled Arcee catalog:
100100

101-
| Model ref | Name | Input | Context | Cost (in/out per 1M) | Notes |
102-
| ------------------------------ | ---------------------- | ----- | ------- | -------------------- | ----------------------------------------- |
103-
| `arcee/trinity-large-thinking` | Trinity Large Thinking | text | 256K | $0.25 / $0.90 | Default model; reasoning enabled |
104-
| `arcee/trinity-large-preview` | Trinity Large Preview | text | 128K | $0.25 / $1.00 | General-purpose; 400B params, 13B active |
105-
| `arcee/trinity-mini` | Trinity Mini 26B | text | 128K | $0.045 / $0.15 | Fast and cost-efficient; function calling |
101+
| Model ref | Name | Input | Context | Cost (in/out per 1M) | Notes |
102+
| ------------------------------ | ---------------------- | ----- | ------- | -------------------- | ------------------------------------------ |
103+
| `arcee/trinity-large-thinking` | Trinity Large Thinking | text | 256K | $0.25 / $0.90 | Default model; reasoning enabled; no tools |
104+
| `arcee/trinity-large-preview` | Trinity Large Preview | text | 128K | $0.25 / $1.00 | General-purpose; 400B params, 13B active |
105+
| `arcee/trinity-mini` | Trinity Mini 26B | text | 128K | $0.045 / $0.15 | Fast and cost-efficient; function calling |
106106

107107
<Tip>
108-
The onboarding preset sets `arcee/trinity-large-thinking` as the default model.
108+
The onboarding preset sets `arcee/trinity-large-thinking` as the default model. It is reasoning/text-only and does not support tool use or function calling.
109109
</Tip>
110110

111111
## Supported features
112112

113-
| Feature | Supported |
114-
| --------------------------------------------- | ---------------------------- |
115-
| Streaming | Yes |
116-
| Tool use / function calling | Yes |
117-
| Structured output (JSON mode and JSON schema) | Yes |
118-
| Extended thinking | Yes (Trinity Large Thinking) |
113+
| Feature | Supported |
114+
| --------------------------------------------- | ------------------------------------------- |
115+
| Streaming | Yes |
116+
| Tool use / function calling | Model-dependent; not Trinity Large Thinking |
117+
| Structured output (JSON mode and JSON schema) | Yes |
118+
| Extended thinking | Yes (Trinity Large Thinking) |
119119

120120
<AccordionGroup>
121121
<Accordion title="Environment note">

extensions/arcee/index.test.ts

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ describe("arcee provider plugin", () => {
6969
"arcee/trinity-large-preview",
7070
"arcee/trinity-large-thinking",
7171
]);
72+
expect(
73+
config?.models?.providers?.arcee?.models?.find(
74+
(model) => model.id === "arcee/trinity-large-thinking",
75+
)?.compat,
76+
).toMatchObject({
77+
supportsReasoningEffort: false,
78+
supportsTools: false,
79+
});
7280
});
7381

7482
it("keeps direct Arcee auth env candidates separate from OpenRouter", () => {
@@ -92,6 +100,12 @@ describe("arcee provider plugin", () => {
92100
"trinity-large-preview",
93101
"trinity-large-thinking",
94102
]);
103+
expect(
104+
catalogProvider.models?.find((model) => model.id === "trinity-large-thinking")?.compat,
105+
).toMatchObject({
106+
supportsReasoningEffort: false,
107+
supportsTools: false,
108+
});
95109
});
96110

97111
it("builds the OpenRouter-backed Arcee AI model catalog", async () => {
@@ -112,6 +126,12 @@ describe("arcee provider plugin", () => {
112126
"arcee/trinity-large-preview",
113127
"arcee/trinity-large-thinking",
114128
]);
129+
expect(
130+
catalogProvider.models?.find((model) => model.id === "arcee/trinity-large-thinking")?.compat,
131+
).toMatchObject({
132+
supportsReasoningEffort: false,
133+
supportsTools: false,
134+
});
115135
});
116136

117137
it("normalizes Arcee OpenRouter models to vendor-prefixed runtime ids", async () => {
@@ -130,6 +150,10 @@ describe("arcee provider plugin", () => {
130150
} as never),
131151
).toMatchObject({
132152
id: "arcee/trinity-large-thinking",
153+
compat: {
154+
supportsReasoningEffort: false,
155+
supportsTools: false,
156+
},
133157
});
134158

135159
expect(
@@ -176,6 +200,10 @@ describe("arcee provider plugin", () => {
176200
).toMatchObject({
177201
id: "arcee/trinity-large-thinking",
178202
baseUrl: "https://openrouter.ai/api/v1",
203+
compat: {
204+
supportsReasoningEffort: false,
205+
supportsTools: false,
206+
},
179207
});
180208

181209
expect(
@@ -189,4 +217,152 @@ describe("arcee provider plugin", () => {
189217
baseUrl: "https://openrouter.ai/api/v1",
190218
});
191219
});
220+
221+
it("repairs stale Trinity tool compat on existing Arcee configs and runtime models", async () => {
222+
const provider = await registerSingleProviderPlugin(arceePlugin);
223+
224+
expect(
225+
provider.normalizeConfig?.({
226+
provider: "arcee",
227+
providerConfig: {
228+
api: "openai-completions",
229+
baseUrl: "https://openrouter.ai/v1/",
230+
models: [
231+
{
232+
id: "arcee/trinity-large-thinking",
233+
name: "Trinity Large Thinking",
234+
reasoning: true,
235+
input: ["text"],
236+
contextWindow: 262144,
237+
maxTokens: 80000,
238+
cost: {
239+
input: 0.25,
240+
output: 0.9,
241+
cacheRead: 0.25,
242+
cacheWrite: 0.25,
243+
},
244+
compat: {
245+
supportsReasoningEffort: false,
246+
supportsStrictMode: true,
247+
},
248+
},
249+
],
250+
},
251+
} as never),
252+
).toMatchObject({
253+
baseUrl: "https://openrouter.ai/api/v1",
254+
models: [
255+
{
256+
id: "arcee/trinity-large-thinking",
257+
compat: {
258+
supportsReasoningEffort: false,
259+
supportsStrictMode: true,
260+
supportsTools: false,
261+
},
262+
},
263+
],
264+
});
265+
266+
expect(
267+
provider.normalizeConfig?.({
268+
provider: "arcee",
269+
providerConfig: {
270+
api: "openai-completions",
271+
baseUrl: "https://api.arcee.ai/api/v1",
272+
models: [
273+
{
274+
id: "trinity-large-thinking",
275+
name: "Trinity Large Thinking",
276+
reasoning: true,
277+
input: ["text"],
278+
contextWindow: 262144,
279+
maxTokens: 80000,
280+
cost: {
281+
input: 0.25,
282+
output: 0.9,
283+
cacheRead: 0.25,
284+
cacheWrite: 0.25,
285+
},
286+
compat: {
287+
supportsReasoningEffort: false,
288+
},
289+
},
290+
],
291+
},
292+
} as never),
293+
).toMatchObject({
294+
baseUrl: "https://api.arcee.ai/api/v1",
295+
models: [
296+
{
297+
id: "trinity-large-thinking",
298+
compat: {
299+
supportsReasoningEffort: false,
300+
supportsTools: false,
301+
},
302+
},
303+
],
304+
});
305+
306+
const trinityRuntimeModel = {
307+
name: "Trinity Large Thinking",
308+
api: "openai-completions",
309+
reasoning: true,
310+
input: ["text"],
311+
contextWindow: 262144,
312+
maxTokens: 80000,
313+
cost: {
314+
input: 0.25,
315+
output: 0.9,
316+
cacheRead: 0.25,
317+
cacheWrite: 0.25,
318+
},
319+
compat: {
320+
supportsReasoningEffort: false,
321+
},
322+
};
323+
324+
const trinityCompat = {
325+
supportsReasoningEffort: false,
326+
supportsTools: false,
327+
};
328+
329+
expect(
330+
provider.contributeResolvedModelCompat?.({
331+
provider: "arcee",
332+
modelId: "arcee/trinity-large-thinking",
333+
model: {
334+
...trinityRuntimeModel,
335+
provider: "arcee",
336+
id: "arcee/trinity-large-thinking",
337+
baseUrl: "https://openrouter.ai/api/v1",
338+
},
339+
} as never),
340+
).toEqual(trinityCompat);
341+
342+
expect(
343+
provider.contributeResolvedModelCompat?.({
344+
provider: "arcee",
345+
modelId: "trinity-large-thinking",
346+
model: {
347+
...trinityRuntimeModel,
348+
provider: "arcee",
349+
id: "trinity-large-thinking",
350+
baseUrl: "https://api.arcee.ai/api/v1",
351+
},
352+
} as never),
353+
).toEqual(trinityCompat);
354+
355+
expect(
356+
provider.contributeResolvedModelCompat?.({
357+
provider: "openrouter",
358+
modelId: "trinity-large-thinking",
359+
model: {
360+
...trinityRuntimeModel,
361+
provider: "openrouter",
362+
id: "trinity-large-thinking",
363+
baseUrl: "https://openrouter.ai/api/v1",
364+
},
365+
} as never),
366+
).toBeUndefined();
367+
});
192368
});

extensions/arcee/index.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import {
1717
normalizeArceeOpenRouterBaseUrl,
1818
toArceeOpenRouterModelId,
1919
} from "./provider-catalog.js";
20+
import {
21+
ARCEE_TRINITY_LARGE_THINKING_COMPAT,
22+
applyArceeTrinityLargeThinkingCompat,
23+
normalizeArceeProviderConfig,
24+
shouldContributeArceeTrinityLargeThinkingCompat,
25+
} from "./provider-policy.js";
2026

2127
const PROVIDER_ID = "arcee";
2228
const ARCEE_WIZARD_GROUP = {
@@ -95,7 +101,7 @@ function normalizeArceeResolvedModel<T extends { baseUrl?: string; id: string }>
95101
return undefined;
96102
}
97103
return {
98-
...model,
104+
...applyArceeTrinityLargeThinkingCompat(model),
99105
id: normalizedId,
100106
baseUrl: normalizedBaseUrl,
101107
};
@@ -120,13 +126,12 @@ export default definePluginEntry({
120126
config,
121127
providerId: PROVIDER_ID,
122128
}),
123-
normalizeConfig: ({ providerConfig }) => {
124-
const normalizedBaseUrl = normalizeArceeOpenRouterBaseUrl(providerConfig.baseUrl);
125-
return normalizedBaseUrl && normalizedBaseUrl !== providerConfig.baseUrl
126-
? { ...providerConfig, baseUrl: normalizedBaseUrl }
127-
: undefined;
128-
},
129+
normalizeConfig: ({ providerConfig }) => normalizeArceeProviderConfig(providerConfig),
129130
normalizeResolvedModel: ({ model }) => normalizeArceeResolvedModel(model),
131+
contributeResolvedModelCompat: (ctx) =>
132+
shouldContributeArceeTrinityLargeThinkingCompat(ctx)
133+
? ARCEE_TRINITY_LARGE_THINKING_COMPAT
134+
: undefined,
130135
normalizeTransport: ({ api, baseUrl }) => {
131136
const normalizedBaseUrl = normalizeArceeOpenRouterBaseUrl(baseUrl);
132137
return normalizedBaseUrl && normalizedBaseUrl !== baseUrl

extensions/arcee/models.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
1+
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-types";
2+
import { ARCEE_BASE_URL, ARCEE_TRINITY_LARGE_THINKING_COMPAT } from "./provider-policy.js";
23

3-
export const ARCEE_BASE_URL = "https://api.arcee.ai/api/v1";
4+
export { ARCEE_BASE_URL, ARCEE_TRINITY_LARGE_THINKING_COMPAT };
45

56
export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [
67
{
@@ -44,9 +45,7 @@ export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [
4445
cacheRead: 0.25,
4546
cacheWrite: 0.25,
4647
},
47-
compat: {
48-
supportsReasoningEffort: false,
49-
},
48+
compat: ARCEE_TRINITY_LARGE_THINKING_COMPAT,
5049
},
5150
];
5251

extensions/arcee/provider-catalog.ts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,13 @@
1-
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
2-
import { buildArceeModelDefinition, ARCEE_BASE_URL, ARCEE_MODEL_CATALOG } from "./models.js";
3-
4-
export const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
5-
const OPENROUTER_LEGACY_BASE_URL = "https://openrouter.ai/v1";
6-
7-
function normalizeBaseUrl(baseUrl: string | undefined): string {
8-
return (baseUrl ?? "").trim().replace(/\/+$/, "");
9-
}
10-
11-
export function normalizeArceeOpenRouterBaseUrl(baseUrl: string | undefined): string | undefined {
12-
const normalized = normalizeBaseUrl(baseUrl);
13-
if (!normalized) {
14-
return undefined;
15-
}
16-
if (normalized === OPENROUTER_BASE_URL || normalized === OPENROUTER_LEGACY_BASE_URL) {
17-
return OPENROUTER_BASE_URL;
18-
}
19-
return undefined;
20-
}
21-
22-
export function toArceeOpenRouterModelId(modelId: string): string {
23-
const normalized = modelId.trim();
24-
if (!normalized || normalized.startsWith("arcee/")) {
25-
return normalized;
26-
}
27-
return `arcee/${normalized}`;
28-
}
1+
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types";
2+
import { buildArceeModelDefinition, ARCEE_MODEL_CATALOG } from "./models.js";
3+
import {
4+
ARCEE_BASE_URL,
5+
normalizeArceeOpenRouterBaseUrl,
6+
OPENROUTER_BASE_URL,
7+
toArceeOpenRouterModelId,
8+
} from "./provider-policy.js";
9+
10+
export { normalizeArceeOpenRouterBaseUrl, OPENROUTER_BASE_URL, toArceeOpenRouterModelId };
2911

3012
export function buildArceeCatalogModels(): NonNullable<ModelProviderConfig["models"]> {
3113
return ARCEE_MODEL_CATALOG.map(buildArceeModelDefinition);

0 commit comments

Comments
 (0)