mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
fix: compaction resolves undated model refs (#122422)
* fix(agents): unify tiered model resolution for chat and compaction
Manual /compact failed with Unknown_model when the agent model was
configured as an undated ref (e.g. anthropic/claude-haiku-4-5): the chat
run resolved models through a two-tier path (discovery-free lookup, then
prepared stores with bundled static-catalog fallback) while both
compaction entry points made a single bare resolveModelAsync call and
dead-ended before the static catalog could resolve the undated id.
resolveTieredModel is now the canonical resolution owner used by the
chat run, direct compaction, and queued compaction; the duplicated
chat-only tier logic and both bare compaction lookups are removed.
Regression test proves chat and manual compaction resolve the same
undated configured model through the shared owner.
* fix(agents): satisfy lint and test-types on tiered resolution call sites
Drop the redundant ?? {} spread fallbacks (spreading undefined is a
no-op) and give the test registry mock its real (provider, modelId)
signature for check:test-types.
This commit is contained in:
committed by
GitHub
parent
bad4d34982
commit
a7e4065dd7
@@ -2420,7 +2420,7 @@ describe("compactEmbeddedAgentSession hooks (ownsCompaction engine)", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("uses the acquired gateway runtime generation for queued model resolution", async () => {
|
||||
it("uses the acquired gateway runtime generation for queued tiered model resolution", async () => {
|
||||
await compactEmbeddedAgentSession(
|
||||
wrappedCompactionArgs({
|
||||
allowGatewaySubagentBinding: true,
|
||||
@@ -2434,9 +2434,8 @@ describe("compactEmbeddedAgentSession hooks (ownsCompaction engine)", () => {
|
||||
: undefined;
|
||||
expect(snapshot).toBeDefined();
|
||||
expect(mockCallArg(resolveModelAsyncMock, 0, 4)).toMatchObject({
|
||||
authStorage: {},
|
||||
modelRegistry: {},
|
||||
preparedModelRuntime: snapshot,
|
||||
skipAgentDiscovery: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ import { resolveContextEngineCapabilities } from "./context-engine-capabilities.
|
||||
import { runContextEngineMaintenance } from "./context-engine-maintenance.js";
|
||||
import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
|
||||
import { log } from "./logger.js";
|
||||
import { resolveTieredModel } from "./model-resolution.js";
|
||||
import { resolveModelAsync } from "./model.js";
|
||||
import type { EmbeddedAgentQueueHandle } from "./run-state.js";
|
||||
import {
|
||||
@@ -437,7 +438,6 @@ async function compactResolvedContextEngine(
|
||||
let preparedHarnessRuntime = selectedHarnessRuntime;
|
||||
let preparedParams = params;
|
||||
try {
|
||||
const preparedStores = preparedModelRuntime.createStores();
|
||||
// Ensure the policy-selected harness plugin so selection can pick implicit codex.
|
||||
await ensureSelectedAgentHarnessPlugin({
|
||||
config: params.config,
|
||||
@@ -450,15 +450,16 @@ async function compactResolvedContextEngine(
|
||||
workspaceDir: resolvedWorkspaceDir,
|
||||
pluginRegistry: requireActivePluginRegistry(),
|
||||
});
|
||||
const {
|
||||
model: ceModel,
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
} = await resolveModelAsync(ceRuntimeProvider, ceModelId, agentDir, params.config, {
|
||||
const { resolution: modelResolution } = await resolveTieredModel({
|
||||
provider: ceRuntimeProvider,
|
||||
modelId: ceModelId,
|
||||
agentDir,
|
||||
config: params.config,
|
||||
workspaceDir: resolvedWorkspaceDir,
|
||||
...initialModelAuth,
|
||||
...preparedStores,
|
||||
preparedModelRuntime,
|
||||
});
|
||||
const { model: ceModel, authStorage, modelRegistry } = modelResolution;
|
||||
const ceRuntimeModel = ceModel as ProviderRuntimeModel | undefined;
|
||||
// Overrides stay unset when no bound/planned/explicit harness resolved so auth-aware
|
||||
// selection can pick the credential-owning harness (codex for ChatGPT OAuth).
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
resolveCompactionRuntimeSelection,
|
||||
} from "./compaction-runtime-preparation.js";
|
||||
import { log } from "./logger.js";
|
||||
import { resolveTieredModel } from "./model-resolution.js";
|
||||
import { resolveModelAsync } from "./model.js";
|
||||
import type { EmbeddedAgentCompactResult } from "./types.js";
|
||||
|
||||
@@ -135,25 +136,26 @@ export async function prepareDirectCompactionAttempt(
|
||||
};
|
||||
};
|
||||
const preparedModelRuntime = params.preparedModelRuntime;
|
||||
const modelResolutionOptions = {
|
||||
...preparedModelRuntime.createStores(),
|
||||
preparedModelRuntime,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
};
|
||||
const { model, error, authStorage, modelRegistry } = await resolveModelAsync(
|
||||
runtimeProvider,
|
||||
const { resolution: modelResolution } = await resolveTieredModel({
|
||||
provider: runtimeProvider,
|
||||
modelId,
|
||||
agentDir,
|
||||
params.config,
|
||||
{
|
||||
...initialModelAuth,
|
||||
...modelResolutionOptions,
|
||||
},
|
||||
);
|
||||
config: params.config,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
...initialModelAuth,
|
||||
preparedModelRuntime,
|
||||
});
|
||||
const { model, error, authStorage, modelRegistry } = modelResolution;
|
||||
if (!model) {
|
||||
const reason = error ?? `Unknown model: ${runtimeProvider}/${modelId}`;
|
||||
return { ok: false as const, result: fail(reason) };
|
||||
}
|
||||
const modelResolutionOptions = {
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
preparedModelRuntime,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
};
|
||||
// Overrides stay unset when no bound/planned/explicit harness resolved so auth-aware
|
||||
// selection can pick the credential-owning harness (codex for ChatGPT OAuth); native
|
||||
// transcript compaction stays gated on the selected prepared harness.
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { resolveInitialEmbeddedRunModel } from "./run/runtime-resolution.js";
|
||||
|
||||
const STATIC_MODEL_ID = "claude-haiku-4-5";
|
||||
const PROVIDER = "anthropic";
|
||||
|
||||
const emptyModelRegistry = {
|
||||
find: vi.fn((_provider: string, _modelId: string) => null),
|
||||
};
|
||||
const authStorage = {
|
||||
setRuntimeApiKey: vi.fn(),
|
||||
};
|
||||
const staticCatalogModel = {
|
||||
provider: PROVIDER,
|
||||
id: STATIC_MODEL_ID,
|
||||
name: "Claude Haiku 4.5",
|
||||
api: "anthropic-messages",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
contextWindow: 200_000,
|
||||
maxTokens: 64_000,
|
||||
};
|
||||
|
||||
const resolveModelAsyncMock = vi.fn(
|
||||
async (
|
||||
provider: string,
|
||||
modelId: string,
|
||||
_agentDir?: string,
|
||||
_config?: unknown,
|
||||
options?: {
|
||||
allowBundledStaticCatalogFallback?: boolean;
|
||||
authStorage?: unknown;
|
||||
modelRegistry?: unknown;
|
||||
},
|
||||
) => {
|
||||
const stores = {
|
||||
authStorage: options?.authStorage ?? authStorage,
|
||||
modelRegistry: options?.modelRegistry ?? emptyModelRegistry,
|
||||
};
|
||||
if (options?.allowBundledStaticCatalogFallback) {
|
||||
return { ...stores, model: staticCatalogModel };
|
||||
}
|
||||
return {
|
||||
...stores,
|
||||
error: `Unknown model: ${provider}/${modelId}`,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
vi.mock("./model.js", () => ({
|
||||
createEmptyAgentDiscoveryStores: () => ({ authStorage, modelRegistry: emptyModelRegistry }),
|
||||
resolveModelAsync: resolveModelAsyncMock,
|
||||
}));
|
||||
|
||||
vi.mock("../harness/runtime-plugin.js", () => ({
|
||||
ensureSelectedAgentHarnessPlugin: vi.fn(async () => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("../harness/selection.js", () => ({
|
||||
selectAgentHarness: vi.fn(() => ({
|
||||
id: "openclaw",
|
||||
label: "OpenClaw",
|
||||
supports: () => ({ supported: true }),
|
||||
runAttempt: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../openai-routing.js", () => ({
|
||||
resolveSelectedOpenAIRuntimeProvider: ({ provider }: { provider: string }) => provider,
|
||||
}));
|
||||
|
||||
vi.mock("../prepared-model-runtime.js", () => ({
|
||||
prepareModelRuntimeSnapshot: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./run/setup.js", () => ({
|
||||
buildBeforeModelResolveAttachments: vi.fn(() => []),
|
||||
createNativeModelOwnedRuntimeModel: vi.fn(),
|
||||
resolveHookModelSelection: vi.fn(
|
||||
async ({ provider, modelId }: { provider: string; modelId: string }) => ({
|
||||
provider,
|
||||
modelId,
|
||||
}),
|
||||
),
|
||||
resolveNativeModelOwnedHarnessId: vi.fn(() => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("./compaction-runtime-preparation.js", () => ({
|
||||
resolveCompactionRuntimeSelection: ({
|
||||
provider,
|
||||
modelId,
|
||||
}: {
|
||||
provider: string;
|
||||
modelId: string;
|
||||
}) => ({
|
||||
runtimePolicySessionKey: "agent:main:test",
|
||||
runtimePolicyAgentId: "main",
|
||||
boundHarnessRuntime: undefined,
|
||||
selectedHarnessRuntimeOverride: undefined,
|
||||
runtimeModelAuth: { plan: undefined, authProfileId: undefined, modelAuth: undefined },
|
||||
provider,
|
||||
runtimeProvider: provider,
|
||||
contextConfigProvider: provider,
|
||||
modelId,
|
||||
}),
|
||||
prepareCompactionHarnessAuth: vi.fn(async () => ({
|
||||
runtimeAuthProfileStore: {},
|
||||
runtimeAuthPreparation: {
|
||||
plan: { selectedAuthMode: "api-key" },
|
||||
attempts: [{ kind: "direct", plan: { selectedAuthMode: "api-key" } }],
|
||||
},
|
||||
selectedPreparedHarness: { id: "openclaw" },
|
||||
providerUsesProfileScopedModelMetadata: false,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../runtime-plan/resolve-auth.js", () => ({
|
||||
resolvePreparedRuntimeAuthAttempts: vi.fn(async ({ model, attempts }) => ({
|
||||
model,
|
||||
auth: { apiKey: "test-api-key", mode: "api_key", source: "test" },
|
||||
plan: attempts[0].plan,
|
||||
})),
|
||||
resolvePreparedRuntimeModelAuth: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/provider-runtime.js", () => ({
|
||||
prepareProviderRuntimeAuth: vi.fn(async () => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("../provider-secret-egress.js", () => ({
|
||||
protectPreparedProviderRuntimeAuth: (value: unknown) => value,
|
||||
unwrapSecretSentinelsForProviderEgress: (value: unknown) => value,
|
||||
}));
|
||||
|
||||
vi.mock("../provider-request-config.js", () => ({
|
||||
applyPreparedRuntimeAuthToModel: (model: unknown) => model,
|
||||
}));
|
||||
|
||||
vi.mock("../sandbox.js", () => ({
|
||||
resolveSandboxContext: vi.fn(async () => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("./compaction-runtime-context.js", () => ({
|
||||
resolveEmbeddedCompactionThinkingLevel: vi.fn(() => "off"),
|
||||
}));
|
||||
|
||||
vi.mock("./logger.js", () => ({
|
||||
log: { warn: vi.fn() },
|
||||
}));
|
||||
|
||||
const { resolveEmbeddedRunModelSetup } = await import("./run/model-setup.js");
|
||||
const { prepareDirectCompactionAttempt } = await import("./direct-compaction-preparation.js");
|
||||
|
||||
describe("embedded model resolution consistency", () => {
|
||||
it("resolves the same undated configured model for chat and manual compaction", async () => {
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: `${PROVIDER}/${STATIC_MODEL_ID}` },
|
||||
},
|
||||
},
|
||||
};
|
||||
const target = resolveInitialEmbeddedRunModel({ config });
|
||||
const preparedModelRuntime = {
|
||||
agentDir: "/tmp/agents/main/agent",
|
||||
config,
|
||||
workspaceDir: "/tmp/openclaw-model-resolution",
|
||||
pluginRegistry: {},
|
||||
configuredRuntimeModels: [],
|
||||
inlineProviderModels: [],
|
||||
createStores: () => ({ authStorage, modelRegistry: emptyModelRegistry }),
|
||||
};
|
||||
|
||||
const chat = await resolveEmbeddedRunModelSetup({
|
||||
runParams: {
|
||||
config,
|
||||
prompt: "hello",
|
||||
sessionId: "chat-session",
|
||||
agentId: "main",
|
||||
} as never,
|
||||
...target,
|
||||
agentDir: preparedModelRuntime.agentDir,
|
||||
workspaceDir: preparedModelRuntime.workspaceDir,
|
||||
globalLane: "test",
|
||||
hookRunner: undefined,
|
||||
hookContext: {} as never,
|
||||
onHooksResolved: vi.fn(),
|
||||
preparedModelRuntime: preparedModelRuntime as never,
|
||||
});
|
||||
expect(chat.model).toMatchObject({ provider: PROVIDER, id: STATIC_MODEL_ID });
|
||||
|
||||
const compaction = await prepareDirectCompactionAttempt({
|
||||
config,
|
||||
provider: target.provider,
|
||||
model: target.modelId,
|
||||
agentId: "main",
|
||||
sessionId: "compact-session",
|
||||
sessionKey: "agent:main:compact-session",
|
||||
sessionFile: "agent:main:compact-session",
|
||||
workspaceDir: preparedModelRuntime.workspaceDir,
|
||||
preparedModelRuntime: preparedModelRuntime as never,
|
||||
});
|
||||
|
||||
expect(emptyModelRegistry.find(PROVIDER, STATIC_MODEL_ID)).toBeNull();
|
||||
if (!compaction.ok) {
|
||||
throw new Error(`manual compaction failed: ${compaction.result.reason}`);
|
||||
}
|
||||
expect(compaction.value.runtimeModel).toMatchObject({
|
||||
provider: PROVIDER,
|
||||
id: STATIC_MODEL_ID,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { resolveDefaultAgentDir } from "../agent-scope.js";
|
||||
import type { AuthProfileCredential } from "../auth-profiles/types.js";
|
||||
import {
|
||||
prepareModelRuntimeSnapshot,
|
||||
type PreparedModelRuntimeSnapshot,
|
||||
} from "../prepared-model-runtime.js";
|
||||
import { resolveModelAsync } from "./model.js";
|
||||
|
||||
type ModelResolution = Awaited<ReturnType<typeof resolveModelAsync>>;
|
||||
|
||||
/** Resolves embedded-run models through discovery first, then the prepared static catalog. */
|
||||
export async function resolveTieredModel(params: {
|
||||
provider: string;
|
||||
fallbackProvider?: string;
|
||||
modelId: string;
|
||||
agentDir: string;
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir: string;
|
||||
authProfileId?: string;
|
||||
authProfileMode?: AuthProfileCredential["type"] | "aws-sdk";
|
||||
preparedModelRuntime?: PreparedModelRuntimeSnapshot;
|
||||
staticCatalogOwnsTransport?: boolean;
|
||||
}): Promise<{ provider: string; resolution: ModelResolution }> {
|
||||
const providers =
|
||||
params.fallbackProvider && params.fallbackProvider !== params.provider
|
||||
? [params.provider, params.fallbackProvider]
|
||||
: [params.provider];
|
||||
let firstResolution: ModelResolution | undefined;
|
||||
const resolveCandidates = async (options: Parameters<typeof resolveModelAsync>[4]) => {
|
||||
for (const provider of providers) {
|
||||
const resolution = await resolveModelAsync(
|
||||
provider,
|
||||
params.modelId,
|
||||
params.agentDir,
|
||||
params.config,
|
||||
options,
|
||||
);
|
||||
firstResolution ??= resolution;
|
||||
if (resolution.model) {
|
||||
return { provider, resolution };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
const firstTier = await resolveCandidates({
|
||||
skipAgentDiscovery: true,
|
||||
allowBundledStaticCatalogFallback: params.staticCatalogOwnsTransport,
|
||||
preferBundledStaticCatalogTransport: params.staticCatalogOwnsTransport,
|
||||
preparedModelRuntime: params.preparedModelRuntime,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: params.authProfileId,
|
||||
authProfileMode: params.authProfileMode,
|
||||
});
|
||||
if (firstTier) {
|
||||
return firstTier;
|
||||
}
|
||||
if (params.staticCatalogOwnsTransport) {
|
||||
return {
|
||||
provider: params.fallbackProvider ?? params.provider,
|
||||
resolution: firstResolution!,
|
||||
};
|
||||
}
|
||||
const config = params.config ?? {};
|
||||
const preparedModelRuntime =
|
||||
params.preparedModelRuntime ??
|
||||
(await prepareModelRuntimeSnapshot({
|
||||
config,
|
||||
agentDir: params.agentDir,
|
||||
inheritedAuthDir: resolveDefaultAgentDir(config),
|
||||
workspaceDir: params.workspaceDir,
|
||||
}));
|
||||
return (
|
||||
(await resolveCandidates({
|
||||
...preparedModelRuntime.createStores(),
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: params.authProfileId,
|
||||
authProfileMode: params.authProfileMode,
|
||||
allowBundledStaticCatalogFallback: true,
|
||||
preparedModelRuntime,
|
||||
})) ?? {
|
||||
provider: params.fallbackProvider ?? params.provider,
|
||||
resolution: firstResolution!,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
import { requireActivePluginRegistry } from "../../../plugins/runtime.js";
|
||||
import { resolveDefaultAgentDir } from "../../agent-scope.js";
|
||||
import { FailoverError } from "../../failover-error.js";
|
||||
import { ensureSelectedAgentHarnessPlugin } from "../../harness/runtime-plugin.js";
|
||||
import { selectAgentHarness } from "../../harness/selection.js";
|
||||
import { resolveSelectedOpenAIRuntimeProvider } from "../../openai-routing.js";
|
||||
import {
|
||||
prepareModelRuntimeSnapshot,
|
||||
type PreparedModelRuntimeSnapshot,
|
||||
} from "../../prepared-model-runtime.js";
|
||||
import { createEmptyAgentDiscoveryStores, resolveModelAsync } from "../model.js";
|
||||
import type { PreparedModelRuntimeSnapshot } from "../../prepared-model-runtime.js";
|
||||
import { resolveTieredModel } from "../model-resolution.js";
|
||||
import { createEmptyAgentDiscoveryStores } from "../model.js";
|
||||
import type { RunEmbeddedAgentParams } from "./params.js";
|
||||
import { resolveRequestStreamTransportOverrides } from "./runtime-resolution.js";
|
||||
import {
|
||||
@@ -99,8 +96,7 @@ export async function resolveEmbeddedRunModelSetup(params: {
|
||||
const nativeModelOwned = nativeModelOwnedHarnessId !== undefined;
|
||||
const modelConfigProvider = provider;
|
||||
let resolvedModelProvider = provider;
|
||||
let firstModelResolution: Awaited<ReturnType<typeof resolveModelAsync>> | undefined;
|
||||
let modelResolution: Awaited<ReturnType<typeof resolveModelAsync>> | undefined;
|
||||
let modelResolution;
|
||||
if (nativeModelOwned) {
|
||||
modelResolution = {
|
||||
model: createNativeModelOwnedRuntimeModel({ provider, modelId }),
|
||||
@@ -116,69 +112,19 @@ export async function resolveEmbeddedRunModelSetup(params: {
|
||||
config: runParams.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
const modelResolutionProviders =
|
||||
selectedRuntimeProvider !== provider ? [selectedRuntimeProvider, provider] : [provider];
|
||||
for (const candidateProvider of modelResolutionProviders) {
|
||||
const candidateResolution = await resolveModelAsync(
|
||||
candidateProvider,
|
||||
modelId,
|
||||
params.agentDir,
|
||||
runParams.config,
|
||||
{
|
||||
// Dynamic hooks can resolve an explicit model without generating models.json first.
|
||||
skipAgentDiscovery: true,
|
||||
allowBundledStaticCatalogFallback: pluginHarnessOwnsTransport,
|
||||
preferBundledStaticCatalogTransport: pluginHarnessOwnsTransport,
|
||||
preparedModelRuntime: params.preparedModelRuntime,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: runParams.authProfileId,
|
||||
},
|
||||
);
|
||||
firstModelResolution ??= candidateResolution;
|
||||
if (candidateResolution.model) {
|
||||
resolvedModelProvider = candidateProvider;
|
||||
modelResolution = candidateResolution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!modelResolution && pluginHarnessOwnsTransport) {
|
||||
modelResolution = firstModelResolution;
|
||||
}
|
||||
if (!modelResolution) {
|
||||
const config = runParams.config ?? {};
|
||||
const preparedModelRuntime =
|
||||
params.preparedModelRuntime ??
|
||||
(await prepareModelRuntimeSnapshot({
|
||||
config,
|
||||
agentDir: params.agentDir,
|
||||
inheritedAuthDir: resolveDefaultAgentDir(config),
|
||||
workspaceDir: params.workspaceDir,
|
||||
}));
|
||||
const preparedStores = preparedModelRuntime.createStores();
|
||||
for (const candidateProvider of modelResolutionProviders) {
|
||||
const candidateResolution = await resolveModelAsync(
|
||||
candidateProvider,
|
||||
modelId,
|
||||
params.agentDir,
|
||||
runParams.config,
|
||||
{
|
||||
authStorage: preparedStores.authStorage,
|
||||
modelRegistry: preparedStores.modelRegistry,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: runParams.authProfileId,
|
||||
allowBundledStaticCatalogFallback: true,
|
||||
preparedModelRuntime,
|
||||
},
|
||||
);
|
||||
firstModelResolution ??= candidateResolution;
|
||||
if (candidateResolution.model) {
|
||||
resolvedModelProvider = candidateProvider;
|
||||
modelResolution = candidateResolution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
modelResolution ??= firstModelResolution;
|
||||
const tieredResolution = await resolveTieredModel({
|
||||
provider: selectedRuntimeProvider,
|
||||
...(selectedRuntimeProvider !== provider ? { fallbackProvider: provider } : {}),
|
||||
modelId,
|
||||
agentDir: params.agentDir,
|
||||
config: runParams.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: runParams.authProfileId,
|
||||
preparedModelRuntime: params.preparedModelRuntime,
|
||||
staticCatalogOwnsTransport: pluginHarnessOwnsTransport,
|
||||
});
|
||||
resolvedModelProvider = tieredResolution.provider;
|
||||
modelResolution = tieredResolution.resolution;
|
||||
}
|
||||
if (!modelResolution) {
|
||||
throw new FailoverError(`Unknown model: ${provider}/${modelId}`, {
|
||||
|
||||
Reference in New Issue
Block a user