mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
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 head4c669d66cb. - Required merge gates passed before the squash merge. Prepared head SHA:4c669d66cbReview: https://github.com/openclaw/openclaw/pull/73388#issuecomment-4338585215 Co-authored-by: Vincent Koc <vincentkoc@ieee.org> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
This commit is contained in:
@@ -44,6 +44,7 @@ Docs: https://docs.openclaw.ai
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- Gateway: avoid repeated plugin tool descriptor config hashing so large runtime configs do not block reply startup and trigger reconnect/timeouts. (#75944) Thanks @joshavant.
|
||||
- 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.
|
||||
|
||||
@@ -98,24 +98,24 @@ Arcee AI models can be accessed directly via the Arcee platform or through [Open
|
||||
|
||||
OpenClaw currently ships this bundled Arcee catalog:
|
||||
|
||||
| Model ref | Name | Input | Context | Cost (in/out per 1M) | Notes |
|
||||
| ------------------------------ | ---------------------- | ----- | ------- | -------------------- | ----------------------------------------- |
|
||||
| `arcee/trinity-large-thinking` | Trinity Large Thinking | text | 256K | $0.25 / $0.90 | Default model; reasoning enabled |
|
||||
| `arcee/trinity-large-preview` | Trinity Large Preview | text | 128K | $0.25 / $1.00 | General-purpose; 400B params, 13B active |
|
||||
| `arcee/trinity-mini` | Trinity Mini 26B | text | 128K | $0.045 / $0.15 | Fast and cost-efficient; function calling |
|
||||
| Model ref | Name | Input | Context | Cost (in/out per 1M) | Notes |
|
||||
| ------------------------------ | ---------------------- | ----- | ------- | -------------------- | ------------------------------------------ |
|
||||
| `arcee/trinity-large-thinking` | Trinity Large Thinking | text | 256K | $0.25 / $0.90 | Default model; reasoning enabled; no tools |
|
||||
| `arcee/trinity-large-preview` | Trinity Large Preview | text | 128K | $0.25 / $1.00 | General-purpose; 400B params, 13B active |
|
||||
| `arcee/trinity-mini` | Trinity Mini 26B | text | 128K | $0.045 / $0.15 | Fast and cost-efficient; function calling |
|
||||
|
||||
<Tip>
|
||||
The onboarding preset sets `arcee/trinity-large-thinking` as the default model.
|
||||
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.
|
||||
</Tip>
|
||||
|
||||
## Supported features
|
||||
|
||||
| Feature | Supported |
|
||||
| --------------------------------------------- | ---------------------------- |
|
||||
| Streaming | Yes |
|
||||
| Tool use / function calling | Yes |
|
||||
| Structured output (JSON mode and JSON schema) | Yes |
|
||||
| Extended thinking | Yes (Trinity Large Thinking) |
|
||||
| Feature | Supported |
|
||||
| --------------------------------------------- | ------------------------------------------- |
|
||||
| Streaming | Yes |
|
||||
| Tool use / function calling | Model-dependent; not Trinity Large Thinking |
|
||||
| Structured output (JSON mode and JSON schema) | Yes |
|
||||
| Extended thinking | Yes (Trinity Large Thinking) |
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Environment note">
|
||||
|
||||
@@ -69,6 +69,14 @@ describe("arcee provider plugin", () => {
|
||||
"arcee/trinity-large-preview",
|
||||
"arcee/trinity-large-thinking",
|
||||
]);
|
||||
expect(
|
||||
config?.models?.providers?.arcee?.models?.find(
|
||||
(model) => model.id === "arcee/trinity-large-thinking",
|
||||
)?.compat,
|
||||
).toMatchObject({
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps direct Arcee auth env candidates separate from OpenRouter", () => {
|
||||
@@ -92,6 +100,12 @@ describe("arcee provider plugin", () => {
|
||||
"trinity-large-preview",
|
||||
"trinity-large-thinking",
|
||||
]);
|
||||
expect(
|
||||
catalogProvider.models?.find((model) => model.id === "trinity-large-thinking")?.compat,
|
||||
).toMatchObject({
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("builds the OpenRouter-backed Arcee AI model catalog", async () => {
|
||||
@@ -112,6 +126,12 @@ describe("arcee provider plugin", () => {
|
||||
"arcee/trinity-large-preview",
|
||||
"arcee/trinity-large-thinking",
|
||||
]);
|
||||
expect(
|
||||
catalogProvider.models?.find((model) => model.id === "arcee/trinity-large-thinking")?.compat,
|
||||
).toMatchObject({
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes Arcee OpenRouter models to vendor-prefixed runtime ids", async () => {
|
||||
@@ -130,6 +150,10 @@ describe("arcee provider plugin", () => {
|
||||
} as never),
|
||||
).toMatchObject({
|
||||
id: "arcee/trinity-large-thinking",
|
||||
compat: {
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
@@ -176,6 +200,10 @@ describe("arcee provider plugin", () => {
|
||||
).toMatchObject({
|
||||
id: "arcee/trinity-large-thinking",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
compat: {
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
@@ -189,4 +217,152 @@ 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.normalizeConfig?.({
|
||||
provider: "arcee",
|
||||
providerConfig: {
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://api.arcee.ai/api/v1",
|
||||
models: [
|
||||
{
|
||||
id: "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,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as never),
|
||||
).toMatchObject({
|
||||
baseUrl: "https://api.arcee.ai/api/v1",
|
||||
models: [
|
||||
{
|
||||
id: "trinity-large-thinking",
|
||||
compat: {
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const trinityRuntimeModel = {
|
||||
name: "Trinity Large Thinking",
|
||||
api: "openai-completions",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
contextWindow: 262144,
|
||||
maxTokens: 80000,
|
||||
cost: {
|
||||
input: 0.25,
|
||||
output: 0.9,
|
||||
cacheRead: 0.25,
|
||||
cacheWrite: 0.25,
|
||||
},
|
||||
compat: {
|
||||
supportsReasoningEffort: false,
|
||||
},
|
||||
};
|
||||
|
||||
const trinityCompat = {
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
};
|
||||
|
||||
expect(
|
||||
provider.contributeResolvedModelCompat?.({
|
||||
provider: "arcee",
|
||||
modelId: "arcee/trinity-large-thinking",
|
||||
model: {
|
||||
...trinityRuntimeModel,
|
||||
provider: "arcee",
|
||||
id: "arcee/trinity-large-thinking",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
},
|
||||
} as never),
|
||||
).toEqual(trinityCompat);
|
||||
|
||||
expect(
|
||||
provider.contributeResolvedModelCompat?.({
|
||||
provider: "arcee",
|
||||
modelId: "trinity-large-thinking",
|
||||
model: {
|
||||
...trinityRuntimeModel,
|
||||
provider: "arcee",
|
||||
id: "trinity-large-thinking",
|
||||
baseUrl: "https://api.arcee.ai/api/v1",
|
||||
},
|
||||
} as never),
|
||||
).toEqual(trinityCompat);
|
||||
|
||||
expect(
|
||||
provider.contributeResolvedModelCompat?.({
|
||||
provider: "openrouter",
|
||||
modelId: "trinity-large-thinking",
|
||||
model: {
|
||||
...trinityRuntimeModel,
|
||||
provider: "openrouter",
|
||||
id: "trinity-large-thinking",
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
},
|
||||
} as never),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,12 @@ import {
|
||||
normalizeArceeOpenRouterBaseUrl,
|
||||
toArceeOpenRouterModelId,
|
||||
} from "./provider-catalog.js";
|
||||
import {
|
||||
ARCEE_TRINITY_LARGE_THINKING_COMPAT,
|
||||
applyArceeTrinityLargeThinkingCompat,
|
||||
normalizeArceeProviderConfig,
|
||||
shouldContributeArceeTrinityLargeThinkingCompat,
|
||||
} from "./provider-policy.js";
|
||||
|
||||
const PROVIDER_ID = "arcee";
|
||||
const ARCEE_WIZARD_GROUP = {
|
||||
@@ -95,7 +101,7 @@ function normalizeArceeResolvedModel<T extends { baseUrl?: string; id: string }>
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
...model,
|
||||
...applyArceeTrinityLargeThinkingCompat(model),
|
||||
id: normalizedId,
|
||||
baseUrl: normalizedBaseUrl,
|
||||
};
|
||||
@@ -120,13 +126,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: (ctx) =>
|
||||
shouldContributeArceeTrinityLargeThinkingCompat(ctx)
|
||||
? ARCEE_TRINITY_LARGE_THINKING_COMPAT
|
||||
: undefined,
|
||||
normalizeTransport: ({ api, baseUrl }) => {
|
||||
const normalizedBaseUrl = normalizeArceeOpenRouterBaseUrl(baseUrl);
|
||||
return normalizedBaseUrl && normalizedBaseUrl !== baseUrl
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-types";
|
||||
import { ARCEE_BASE_URL, ARCEE_TRINITY_LARGE_THINKING_COMPAT } from "./provider-policy.js";
|
||||
|
||||
export const ARCEE_BASE_URL = "https://api.arcee.ai/api/v1";
|
||||
export { ARCEE_BASE_URL, ARCEE_TRINITY_LARGE_THINKING_COMPAT };
|
||||
|
||||
export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [
|
||||
{
|
||||
@@ -44,9 +45,7 @@ export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [
|
||||
cacheRead: 0.25,
|
||||
cacheWrite: 0.25,
|
||||
},
|
||||
compat: {
|
||||
supportsReasoningEffort: false,
|
||||
},
|
||||
compat: ARCEE_TRINITY_LARGE_THINKING_COMPAT,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,31 +1,13 @@
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildArceeModelDefinition, ARCEE_BASE_URL, ARCEE_MODEL_CATALOG } from "./models.js";
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types";
|
||||
import { buildArceeModelDefinition, ARCEE_MODEL_CATALOG } from "./models.js";
|
||||
import {
|
||||
ARCEE_BASE_URL,
|
||||
normalizeArceeOpenRouterBaseUrl,
|
||||
OPENROUTER_BASE_URL,
|
||||
toArceeOpenRouterModelId,
|
||||
} from "./provider-policy.js";
|
||||
|
||||
export const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
||||
const OPENROUTER_LEGACY_BASE_URL = "https://openrouter.ai/v1";
|
||||
|
||||
function normalizeBaseUrl(baseUrl: string | undefined): string {
|
||||
return (baseUrl ?? "").trim().replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
export function normalizeArceeOpenRouterBaseUrl(baseUrl: string | undefined): string | undefined {
|
||||
const normalized = normalizeBaseUrl(baseUrl);
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
if (normalized === OPENROUTER_BASE_URL || normalized === OPENROUTER_LEGACY_BASE_URL) {
|
||||
return OPENROUTER_BASE_URL;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function toArceeOpenRouterModelId(modelId: string): string {
|
||||
const normalized = modelId.trim();
|
||||
if (!normalized || normalized.startsWith("arcee/")) {
|
||||
return normalized;
|
||||
}
|
||||
return `arcee/${normalized}`;
|
||||
}
|
||||
export { normalizeArceeOpenRouterBaseUrl, OPENROUTER_BASE_URL, toArceeOpenRouterModelId };
|
||||
|
||||
export function buildArceeCatalogModels(): NonNullable<ModelProviderConfig["models"]> {
|
||||
return ARCEE_MODEL_CATALOG.map(buildArceeModelDefinition);
|
||||
|
||||
73
extensions/arcee/provider-policy-api.test.ts
Normal file
73
extensions/arcee/provider-policy-api.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { normalizeConfig } from "./provider-policy-api.js";
|
||||
|
||||
describe("arcee provider policy public artifact", () => {
|
||||
it("normalizes stale OpenRouter base URLs and Trinity compat without loading the full plugin", () => {
|
||||
expect(
|
||||
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,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
).toMatchObject({
|
||||
baseUrl: "https://openrouter.ai/api/v1",
|
||||
models: [
|
||||
{
|
||||
id: "arcee/trinity-large-thinking",
|
||||
compat: {
|
||||
supportsReasoningEffort: false,
|
||||
supportsStrictMode: true,
|
||||
supportsTools: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("returns unchanged non-Trinity configs by identity", () => {
|
||||
const providerConfig = {
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://api.arcee.ai/api/v1",
|
||||
models: [
|
||||
{
|
||||
id: "trinity-mini",
|
||||
name: "Trinity Mini 26B",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
contextWindow: 131072,
|
||||
maxTokens: 80000,
|
||||
cost: {
|
||||
input: 0.045,
|
||||
output: 0.15,
|
||||
cacheRead: 0.045,
|
||||
cacheWrite: 0.045,
|
||||
},
|
||||
},
|
||||
],
|
||||
} satisfies Parameters<typeof normalizeConfig>[0]["providerConfig"];
|
||||
|
||||
expect(normalizeConfig({ provider: "arcee", providerConfig })).toBe(providerConfig);
|
||||
});
|
||||
});
|
||||
11
extensions/arcee/provider-policy-api.ts
Normal file
11
extensions/arcee/provider-policy-api.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types";
|
||||
import { normalizeArceeProviderConfig } from "./provider-policy.js";
|
||||
|
||||
export { normalizeArceeProviderConfig };
|
||||
|
||||
export function normalizeConfig(params: {
|
||||
provider?: string;
|
||||
providerConfig: ModelProviderConfig;
|
||||
}): ModelProviderConfig {
|
||||
return normalizeArceeProviderConfig(params.providerConfig);
|
||||
}
|
||||
132
extensions/arcee/provider-policy.ts
Normal file
132
extensions/arcee/provider-policy.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type {
|
||||
ModelCompatConfig,
|
||||
ModelProviderConfig,
|
||||
} from "openclaw/plugin-sdk/provider-model-types";
|
||||
|
||||
export const ARCEE_BASE_URL = "https://api.arcee.ai/api/v1";
|
||||
export const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
||||
export const ARCEE_TRINITY_LARGE_THINKING_COMPAT = {
|
||||
supportsReasoningEffort: false,
|
||||
supportsTools: false,
|
||||
} as const satisfies ModelCompatConfig;
|
||||
|
||||
const ARCEE_PROVIDER_ID = "arcee";
|
||||
const OPENROUTER_LEGACY_BASE_URL = "https://openrouter.ai/v1";
|
||||
const ARCEE_TRINITY_LARGE_THINKING_ID = "trinity-large-thinking";
|
||||
const ARCEE_TRINITY_LARGE_THINKING_REF = `${ARCEE_PROVIDER_ID}/${ARCEE_TRINITY_LARGE_THINKING_ID}`;
|
||||
|
||||
function normalizeModelId(modelId: string): string {
|
||||
return modelId.trim().toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeBaseUrl(baseUrl: unknown): string {
|
||||
return typeof baseUrl === "string" ? baseUrl.trim().replace(/\/+$/, "") : "";
|
||||
}
|
||||
|
||||
export function normalizeArceeOpenRouterBaseUrl(baseUrl: string | undefined): string | undefined {
|
||||
const normalized = normalizeBaseUrl(baseUrl);
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
if (normalized === OPENROUTER_BASE_URL || normalized === OPENROUTER_LEGACY_BASE_URL) {
|
||||
return OPENROUTER_BASE_URL;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function toArceeOpenRouterModelId(modelId: string): string {
|
||||
const normalized = modelId.trim();
|
||||
if (!normalized || normalized.startsWith("arcee/")) {
|
||||
return normalized;
|
||||
}
|
||||
return `arcee/${normalized}`;
|
||||
}
|
||||
|
||||
export function isArceeTrinityLargeThinkingModelId(modelId: string): boolean {
|
||||
const normalized = normalizeModelId(modelId);
|
||||
return (
|
||||
normalized === ARCEE_TRINITY_LARGE_THINKING_ID ||
|
||||
normalized === ARCEE_TRINITY_LARGE_THINKING_REF
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldContributeArceeTrinityLargeThinkingCompat(params: {
|
||||
provider?: unknown;
|
||||
modelId: string;
|
||||
model: { id: string; provider?: unknown; baseUrl?: unknown };
|
||||
}): boolean {
|
||||
const modelId = normalizeModelId(params.modelId);
|
||||
const resolvedId = normalizeModelId(params.model.id);
|
||||
if (
|
||||
modelId === ARCEE_TRINITY_LARGE_THINKING_REF ||
|
||||
resolvedId === ARCEE_TRINITY_LARGE_THINKING_REF
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
modelId !== ARCEE_TRINITY_LARGE_THINKING_ID &&
|
||||
resolvedId !== ARCEE_TRINITY_LARGE_THINKING_ID
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (params.provider === ARCEE_PROVIDER_ID || params.model.provider === ARCEE_PROVIDER_ID) {
|
||||
return true;
|
||||
}
|
||||
return normalizeBaseUrl(params.model.baseUrl) === normalizeBaseUrl(ARCEE_BASE_URL);
|
||||
}
|
||||
|
||||
export function applyArceeTrinityLargeThinkingCompat<T extends { id: string; compat?: unknown }>(
|
||||
model: T,
|
||||
): T {
|
||||
if (!isArceeTrinityLargeThinkingModelId(model.id)) {
|
||||
return model;
|
||||
}
|
||||
const compat =
|
||||
model.compat && typeof model.compat === "object"
|
||||
? (model.compat as Record<string, unknown>)
|
||||
: undefined;
|
||||
if (
|
||||
compat?.supportsReasoningEffort ===
|
||||
ARCEE_TRINITY_LARGE_THINKING_COMPAT.supportsReasoningEffort &&
|
||||
compat?.supportsTools === ARCEE_TRINITY_LARGE_THINKING_COMPAT.supportsTools
|
||||
) {
|
||||
return model;
|
||||
}
|
||||
return {
|
||||
...model,
|
||||
compat: {
|
||||
...compat,
|
||||
...ARCEE_TRINITY_LARGE_THINKING_COMPAT,
|
||||
} as T extends { compat?: infer TCompat } ? TCompat : never,
|
||||
} as T;
|
||||
}
|
||||
|
||||
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) => {
|
||||
const normalizedModel = applyArceeTrinityLargeThinkingCompat(model);
|
||||
if (normalizedModel === model) {
|
||||
return model;
|
||||
}
|
||||
changed = true;
|
||||
return normalizedModel;
|
||||
})
|
||||
: providerConfig.models;
|
||||
|
||||
return changed
|
||||
? { ...providerConfig, baseUrl, ...(hasModels ? { models } : {}) }
|
||||
: providerConfig;
|
||||
}
|
||||
Reference in New Issue
Block a user