mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 13:31:37 +00:00
refactor(agents): centralize model fallback resolution
This commit is contained in:
@@ -2,19 +2,13 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { FollowupRun } from "./queue.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
const resolveAgentModelFallbacksOverrideMock = vi.fn();
|
||||
const resolveAgentIdFromSessionKeyMock = vi.fn();
|
||||
return { resolveAgentModelFallbacksOverrideMock, resolveAgentIdFromSessionKeyMock };
|
||||
const resolveRunModelFallbacksOverrideMock = vi.fn();
|
||||
return { resolveRunModelFallbacksOverrideMock };
|
||||
});
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveAgentModelFallbacksOverride: (...args: unknown[]) =>
|
||||
hoisted.resolveAgentModelFallbacksOverrideMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/sessions.js", () => ({
|
||||
resolveAgentIdFromSessionKey: (...args: unknown[]) =>
|
||||
hoisted.resolveAgentIdFromSessionKeyMock(...args),
|
||||
resolveRunModelFallbacksOverride: (...args: unknown[]) =>
|
||||
hoisted.resolveRunModelFallbacksOverrideMock(...args),
|
||||
}));
|
||||
|
||||
const {
|
||||
@@ -50,22 +44,20 @@ function makeRun(overrides: Partial<FollowupRun["run"]> = {}): FollowupRun["run"
|
||||
|
||||
describe("agent-runner-utils", () => {
|
||||
beforeEach(() => {
|
||||
hoisted.resolveAgentModelFallbacksOverrideMock.mockClear();
|
||||
hoisted.resolveAgentIdFromSessionKeyMock.mockClear();
|
||||
hoisted.resolveRunModelFallbacksOverrideMock.mockClear();
|
||||
});
|
||||
|
||||
it("resolves model fallback options from run context", () => {
|
||||
hoisted.resolveAgentIdFromSessionKeyMock.mockReturnValue("agent-id");
|
||||
hoisted.resolveAgentModelFallbacksOverrideMock.mockReturnValue(["fallback-model"]);
|
||||
hoisted.resolveRunModelFallbacksOverrideMock.mockReturnValue(["fallback-model"]);
|
||||
const run = makeRun();
|
||||
|
||||
const resolved = resolveModelFallbackOptions(run);
|
||||
|
||||
expect(hoisted.resolveAgentIdFromSessionKeyMock).not.toHaveBeenCalled();
|
||||
expect(hoisted.resolveAgentModelFallbacksOverrideMock).toHaveBeenCalledWith(
|
||||
run.config,
|
||||
run.agentId,
|
||||
);
|
||||
expect(hoisted.resolveRunModelFallbacksOverrideMock).toHaveBeenCalledWith({
|
||||
cfg: run.config,
|
||||
agentId: run.agentId,
|
||||
sessionKey: run.sessionKey,
|
||||
});
|
||||
expect(resolved).toEqual({
|
||||
cfg: run.config,
|
||||
provider: run.provider,
|
||||
@@ -75,18 +67,17 @@ describe("agent-runner-utils", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to sessionKey agent id when run.agentId is missing", () => {
|
||||
hoisted.resolveAgentIdFromSessionKeyMock.mockReturnValue("agent-from-session-key");
|
||||
hoisted.resolveAgentModelFallbacksOverrideMock.mockReturnValue(["fallback-model"]);
|
||||
it("passes through missing agentId for helper-based fallback resolution", () => {
|
||||
hoisted.resolveRunModelFallbacksOverrideMock.mockReturnValue(["fallback-model"]);
|
||||
const run = makeRun({ agentId: undefined });
|
||||
|
||||
const resolved = resolveModelFallbackOptions(run);
|
||||
|
||||
expect(hoisted.resolveAgentIdFromSessionKeyMock).toHaveBeenCalledWith(run.sessionKey);
|
||||
expect(hoisted.resolveAgentModelFallbacksOverrideMock).toHaveBeenCalledWith(
|
||||
run.config,
|
||||
"agent-from-session-key",
|
||||
);
|
||||
expect(hoisted.resolveRunModelFallbacksOverrideMock).toHaveBeenCalledWith({
|
||||
cfg: run.config,
|
||||
agentId: undefined,
|
||||
sessionKey: run.sessionKey,
|
||||
});
|
||||
expect(resolved.fallbacksOverride).toEqual(["fallback-model"]);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { resolveAgentModelFallbacksOverride } from "../../agents/agent-scope.js";
|
||||
import { resolveRunModelFallbacksOverride } from "../../agents/agent-scope.js";
|
||||
import type { NormalizedUsage } from "../../agents/usage.js";
|
||||
import { getChannelDock } from "../../channels/dock.js";
|
||||
import type { ChannelId, ChannelThreadingToolContext } from "../../channels/plugins/types.js";
|
||||
import { normalizeAnyChannelId, normalizeChannelId } from "../../channels/registry.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { resolveAgentIdFromSessionKey } from "../../config/sessions.js";
|
||||
import { isReasoningTagProvider } from "../../utils/provider-utils.js";
|
||||
import { estimateUsageCost, formatTokenCount, formatUsd } from "../../utils/usage-format.js";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
@@ -147,13 +146,16 @@ export const resolveEnforceFinalTag = (run: FollowupRun["run"], provider: string
|
||||
Boolean(run.enforceFinalTag || isReasoningTagProvider(provider));
|
||||
|
||||
export function resolveModelFallbackOptions(run: FollowupRun["run"]) {
|
||||
const fallbackAgentId = run.agentId ?? resolveAgentIdFromSessionKey(run.sessionKey);
|
||||
return {
|
||||
cfg: run.config,
|
||||
provider: run.provider,
|
||||
model: run.model,
|
||||
agentDir: run.agentDir,
|
||||
fallbacksOverride: resolveAgentModelFallbacksOverride(run.config, fallbackAgentId),
|
||||
fallbacksOverride: resolveRunModelFallbacksOverride({
|
||||
cfg: run.config,
|
||||
agentId: run.agentId,
|
||||
sessionKey: run.sessionKey,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import crypto from "node:crypto";
|
||||
import { resolveAgentModelFallbacksOverride } from "../../agents/agent-scope.js";
|
||||
import { resolveRunModelFallbacksOverride } from "../../agents/agent-scope.js";
|
||||
import { lookupContextTokens } from "../../agents/context.js";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
|
||||
import { runWithModelFallback } from "../../agents/model-fallback.js";
|
||||
import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js";
|
||||
import { resolveAgentIdFromSessionKey, type SessionEntry } from "../../config/sessions.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import type { TypingMode } from "../../config/types.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { registerAgentRunContext } from "../../infra/agent-events.js";
|
||||
@@ -133,10 +133,11 @@ export function createFollowupRunner(params: {
|
||||
provider: queued.run.provider,
|
||||
model: queued.run.model,
|
||||
agentDir: queued.run.agentDir,
|
||||
fallbacksOverride: resolveAgentModelFallbacksOverride(
|
||||
queued.run.config,
|
||||
queued.run.agentId ?? resolveAgentIdFromSessionKey(queued.run.sessionKey),
|
||||
),
|
||||
fallbacksOverride: resolveRunModelFallbacksOverride({
|
||||
cfg: queued.run.config,
|
||||
agentId: queued.run.agentId,
|
||||
sessionKey: queued.run.sessionKey,
|
||||
}),
|
||||
run: (provider, model) => {
|
||||
const authProfile = resolveRunAuthProfile(queued.run, provider);
|
||||
return runEmbeddedPiAgent({
|
||||
|
||||
Reference in New Issue
Block a user