FN-7719: derive implicit planning fallback model when no fallback configured
Triage planning-model retries no longer dead-end on "no fallback configured" when a provider primary-model call fails (e.g. a 404 wrapped in a 429 for nvidia/moonshotai/kimi-k2.6). - Add resolveImplicitPlanningFallbackModel() to agent-session-helpers.ts: derives a fallback from the resolved project/global default (execution) model when neither planningFallback*/global fallback* is set, guarding against self-swap and skipping in test mode. - Wire the implicit fallback into TriageProcessor.specifyTask() in triage.ts so a retryable primary planner-model failure swaps once via the derived fallback instead of failing triage outright. - Add unit test coverage in agent-session-helpers.test.ts and triage.test.ts for the new implicit-fallback resolution and its triage integration. - Document the new implicit-fallback behavior in docs/settings-reference.md. - Add a patch changeset for @runfusion/fusion describing the fix. Files changed: .changeset/fn-7719-triage-planning-implicit-fallback.md | 7 + docs/settings-reference.md | 2 + packages/engine/src/__tests__/agent-session-helpers.test.ts | 75 +++++++ packages/engine/src/__tests__/triage.test.ts | 237 +++++++++++++++++++++ packages/engine/src/agent-session-helpers.ts | 41 ++++ packages/engine/src/triage.ts | 31 ++- 6 files changed, 389 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-7719 Fusion-Task-Lineage: 69e797e1-5bac-47f3-8dce-505b9d64d83c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7719-triage-planning-implicit-fallback.md
Normal file
7
.changeset/fn-7719-triage-planning-implicit-fallback.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Triage recovers automatically when the planning model hits a provider 404/429 and no fallback is set.
|
||||
category: fix
|
||||
dev: TriageProcessor.specifyTask now derives an implicit fallback from the project/global default (execution) model when no planningFallback*/global fallback* pair is configured, so a retryable primary planner-model failure swaps once instead of failing triage with "no fallback configured". Test mode and self-swap are excluded; the single-swap ModelFallbackExhaustedError terminal path is preserved.
|
||||
@@ -964,6 +964,8 @@ Short-lived token bounds are enforced server-side:
|
||||
|
||||
Fusion resolves task models through workflow-backed lane values first, then global lane defaults, then the project/global default model fallback. The common workflow lanes are stored as setting values on the project's default workflow and can be edited with dropdown controls from Settings -> Project Models -> Default workflow model lanes (persisted by the Settings modal's primary Save) or from workflow editor -> Settings -> Values for declared workflow lanes and fallbacks. General-scope fallback selection remains the global Fallback Model picker in Settings -> General Models.
|
||||
|
||||
When the planning lane has neither `planningFallback*` nor a global `fallback*` pair configured, triage now derives an **implicit fallback** from the resolved project/global default (execution) model (FN-7719). This lets a retryable primary planner-model failure (e.g. a provider 404/429) recover via one distinct swap instead of permanently failing triage with "no fallback configured" — the operator's chosen primary planner lane is unchanged, and the implicit fallback is skipped when it would equal the primary model or when test mode is active.
|
||||
|
||||
Z.ai's built-in provider uses the existing `zai` auth entry / `ZAI_API_KEY` environment variable and includes `zai/glm-5.2` as a selectable model in the same dropdowns and workflow lane controls as the other built-in GLM models. If a pi extension also registers the `zai` provider, Fusion preserves the extension's models and re-adds any missing built-in Z.ai models so built-in GLM choices remain available.
|
||||
|
||||
Grok (`grok-cli`) is likewise seeded as a built-in provider — xAI's OpenAI-compatible endpoint (`https://api.x.ai/v1`, api type `openai-completions`), API key `GROK_API_KEY` — into every model registry Fusion seeds (task execution, dashboard `/api/models`, and CLI `serve`/`daemon`/`dashboard`), mirroring the Z.ai pattern above. This makes `grok-cli/<model>` selections (e.g. `grok-cli/grok-4.5`) resolvable for execution even before the `grok` CLI binary is discovered or the picker surfaces additional Grok models (see the CLI-discovery paragraph below); a missing `GROK_API_KEY` surfaces only as a normal auth error at stream time, not a model-resolution failure. If `GROK_API_KEY` is not set in the environment, provider registration falls back to `~/.grok/user-settings.json`'s `apiKey` field (the same file the `grok` CLI itself writes on login) and hydrates `process.env.GROK_API_KEY` from it, so an operator who authenticated via the `grok` CLI but never exported the env var still resolves a key; an already-set env var always wins, and a missing/malformed/empty settings file is fail-soft (no error, no env mutation).
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
extractRuntimeModel,
|
||||
resolveExecutorSessionModel,
|
||||
resolveHeartbeatSessionModels,
|
||||
resolveImplicitPlanningFallbackModel,
|
||||
resolveMergerSessionModel,
|
||||
resolvePlanningSessionModel,
|
||||
resolveValidatorSessionModel,
|
||||
@@ -629,3 +630,77 @@ describe("resolveMergerSessionModel", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveImplicitPlanningFallbackModel (FN-7719)", () => {
|
||||
it("derives a distinct implicit fallback from the project/global default model", () => {
|
||||
expect(
|
||||
resolveImplicitPlanningFallbackModel(
|
||||
{
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
},
|
||||
"9router",
|
||||
"nvidia/moonshotai/kimi-k2.6",
|
||||
),
|
||||
).toEqual({
|
||||
provider: "openai",
|
||||
modelId: "gpt-4o",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns undefined/undefined when the implicit fallback would equal the primary (self-swap guard)", () => {
|
||||
expect(
|
||||
resolveImplicitPlanningFallbackModel(
|
||||
{
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
},
|
||||
"openai",
|
||||
"gpt-4o",
|
||||
),
|
||||
).toEqual({
|
||||
provider: undefined,
|
||||
modelId: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns undefined/undefined when no project/global default model is configured", () => {
|
||||
expect(
|
||||
resolveImplicitPlanningFallbackModel({}, "9router", "nvidia/moonshotai/kimi-k2.6"),
|
||||
).toEqual({
|
||||
provider: undefined,
|
||||
modelId: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not inject an implicit fallback in test mode", () => {
|
||||
expect(
|
||||
resolveImplicitPlanningFallbackModel(
|
||||
{
|
||||
testMode: true,
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
},
|
||||
"9router",
|
||||
"nvidia/moonshotai/kimi-k2.6",
|
||||
),
|
||||
).toEqual({
|
||||
provider: undefined,
|
||||
modelId: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers the assigned agent runtime model when no default model pair is configured", () => {
|
||||
expect(
|
||||
resolveImplicitPlanningFallbackModel(
|
||||
{},
|
||||
"9router",
|
||||
"nvidia/moonshotai/kimi-k2.6",
|
||||
{ model: "anthropic/claude-3-5-sonnet-20241022" },
|
||||
),
|
||||
).toEqual({
|
||||
provider: "anthropic",
|
||||
modelId: "claude-3-5-sonnet-20241022",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4263,6 +4263,243 @@ describe("taskCreate tool model inheritance", () => {
|
||||
}));
|
||||
});
|
||||
|
||||
describe("implicit planning fallback (FN-7719)", () => {
|
||||
const baseSession = () => ({
|
||||
prompt: vi.fn().mockResolvedValue(undefined),
|
||||
dispose: vi.fn(),
|
||||
sessionManager: {
|
||||
getLeafId: vi.fn().mockReturnValue(null),
|
||||
navigateTree: vi.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
it("recovers from the reported 404/429 planner failure via a derived implicit fallback", async () => {
|
||||
const task = {
|
||||
id: "FN-7719",
|
||||
description: "Bug: 9router/Planning 404 on nvidia/moonshotai/kimi-k2.6",
|
||||
column: "triage",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
} as unknown as Task;
|
||||
const onSpecifyError = vi.fn();
|
||||
const store = createMockStore({
|
||||
getTask: vi.fn().mockResolvedValue({ ...task, attachments: [] }),
|
||||
getSettings: vi.fn().mockResolvedValue({
|
||||
maxConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
pollIntervalMs: 10000,
|
||||
groupOverlappingFiles: false,
|
||||
autoMerge: true,
|
||||
// Primary planner lane ("9router/Planning") — distinct from the project default.
|
||||
planningProvider: "9router",
|
||||
planningModelId: "nvidia/moonshotai/kimi-k2.6",
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
// No planningFallback*/global fallback* configured — this is the reported gap.
|
||||
defaultThinkingLevel: "low",
|
||||
} as Settings),
|
||||
});
|
||||
mockCreateFnAgent.mockResolvedValue({ session: baseSession() });
|
||||
|
||||
const { promptWithFallback } = await import("../pi.js");
|
||||
// With a distinct implicit fallback now supplied, pi.ts's real single-swap
|
||||
// loop (covered by pi.test.ts) recovers instead of throwing
|
||||
// ModelFallbackExhaustedError — simulate that recovered outcome here.
|
||||
(promptWithFallback as ReturnType<typeof vi.fn>).mockResolvedValueOnce(undefined);
|
||||
|
||||
const processor = new TriageProcessor(store, "/test/root", {
|
||||
pollIntervalMs: 100_000,
|
||||
onSpecifyError,
|
||||
});
|
||||
|
||||
await processor.specifyTask(task);
|
||||
|
||||
expect(mockCreateFnAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
defaultProvider: "9router",
|
||||
defaultModelId: "nvidia/moonshotai/kimi-k2.6",
|
||||
fallbackProvider: "openai",
|
||||
fallbackModelId: "gpt-4o",
|
||||
}),
|
||||
);
|
||||
expect(store.updateTask).not.toHaveBeenCalledWith("FN-7719", expect.objectContaining({
|
||||
status: "failed",
|
||||
error: expect.stringContaining("no fallback configured"),
|
||||
}));
|
||||
});
|
||||
|
||||
it("stays terminal when the implicit fallback would equal the primary planner model (self-swap guard)", async () => {
|
||||
const task = {
|
||||
id: "FN-7719-SELF-SWAP",
|
||||
description: "No distinct default model available for implicit fallback",
|
||||
column: "triage",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
} as unknown as Task;
|
||||
const onSpecifyError = vi.fn();
|
||||
const store = createMockStore({
|
||||
getTask: vi.fn().mockResolvedValue({ ...task, attachments: [] }),
|
||||
getSettings: vi.fn().mockResolvedValue({
|
||||
maxConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
pollIntervalMs: 10000,
|
||||
groupOverlappingFiles: false,
|
||||
autoMerge: true,
|
||||
// No planningProvider/planningModelId — the primary planning model
|
||||
// resolves through to the project default itself, so the implicit
|
||||
// fallback would equal the primary. Must NOT self-swap.
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
defaultThinkingLevel: "low",
|
||||
} as Settings),
|
||||
});
|
||||
mockCreateFnAgent.mockResolvedValue({ session: baseSession() });
|
||||
|
||||
const { ModelFallbackExhaustedError, promptWithFallback } = await import("../pi.js");
|
||||
(promptWithFallback as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
new ModelFallbackExhaustedError({
|
||||
primaryModel: "openai/gpt-4o",
|
||||
triggerPoint: "prompt-time",
|
||||
attempts: 1,
|
||||
underlyingReason: "model not found: no distinct fallback available",
|
||||
}),
|
||||
);
|
||||
|
||||
const processor = new TriageProcessor(store, "/test/root", {
|
||||
pollIntervalMs: 100_000,
|
||||
onSpecifyError,
|
||||
});
|
||||
|
||||
await processor.specifyTask(task);
|
||||
|
||||
expect(mockCreateFnAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
fallbackProvider: undefined,
|
||||
fallbackModelId: undefined,
|
||||
}),
|
||||
);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-7719-SELF-SWAP", expect.objectContaining({
|
||||
status: "failed",
|
||||
recoveryRetryCount: null,
|
||||
nextRecoveryAt: null,
|
||||
}));
|
||||
expect(onSpecifyError).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not override an explicitly configured planningFallback* pair", async () => {
|
||||
const task = {
|
||||
id: "FN-7719-EXPLICIT-PLANNING",
|
||||
description: "Explicit planning fallback stays authoritative",
|
||||
column: "triage",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
} as unknown as Task;
|
||||
const store = createMockStore({
|
||||
getTask: vi.fn().mockResolvedValue({ ...task, attachments: [] }),
|
||||
getSettings: vi.fn().mockResolvedValue({
|
||||
maxConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
pollIntervalMs: 10000,
|
||||
groupOverlappingFiles: false,
|
||||
autoMerge: true,
|
||||
planningProvider: "9router",
|
||||
planningModelId: "nvidia/moonshotai/kimi-k2.6",
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
planningFallbackProvider: "anthropic",
|
||||
planningFallbackModelId: "claude-3-5-haiku-20241022",
|
||||
} as Settings),
|
||||
});
|
||||
mockCreateFnAgent.mockResolvedValue({ session: baseSession() });
|
||||
|
||||
const { promptWithFallback } = await import("../pi.js");
|
||||
(promptWithFallback as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
new Error("test stop after model check"),
|
||||
);
|
||||
|
||||
const processor = new TriageProcessor(store, "/test/root", { pollIntervalMs: 100_000 });
|
||||
await processor.specifyTask(task);
|
||||
|
||||
expect(mockCreateFnAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
defaultProvider: "9router",
|
||||
defaultModelId: "nvidia/moonshotai/kimi-k2.6",
|
||||
fallbackProvider: "anthropic",
|
||||
fallbackModelId: "claude-3-5-haiku-20241022",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not override an explicitly configured global fallback* pair", async () => {
|
||||
const task = {
|
||||
id: "FN-7719-EXPLICIT-GLOBAL",
|
||||
description: "Explicit global fallback stays authoritative",
|
||||
column: "triage",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
} as unknown as Task;
|
||||
const store = createMockStore({
|
||||
getTask: vi.fn().mockResolvedValue({ ...task, attachments: [] }),
|
||||
getSettings: vi.fn().mockResolvedValue({
|
||||
maxConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
pollIntervalMs: 10000,
|
||||
groupOverlappingFiles: false,
|
||||
autoMerge: true,
|
||||
planningProvider: "9router",
|
||||
planningModelId: "nvidia/moonshotai/kimi-k2.6",
|
||||
defaultProvider: "openai",
|
||||
defaultModelId: "gpt-4o",
|
||||
fallbackProvider: "google",
|
||||
fallbackModelId: "gemini-2.5-pro",
|
||||
} as Settings),
|
||||
});
|
||||
mockCreateFnAgent.mockResolvedValue({ session: baseSession() });
|
||||
|
||||
const { promptWithFallback } = await import("../pi.js");
|
||||
(promptWithFallback as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
|
||||
new Error("test stop after model check"),
|
||||
);
|
||||
|
||||
const processor = new TriageProcessor(store, "/test/root", { pollIntervalMs: 100_000 });
|
||||
await processor.specifyTask(task);
|
||||
|
||||
expect(mockCreateFnAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
defaultProvider: "9router",
|
||||
defaultModelId: "nvidia/moonshotai/kimi-k2.6",
|
||||
fallbackProvider: "google",
|
||||
fallbackModelId: "gemini-2.5-pro",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// NOTE: test-mode exclusion (isTestModeActive -> no implicit fallback
|
||||
// injected) is covered directly at the resolver-unit level in
|
||||
// agent-session-helpers.test.ts ("resolveImplicitPlanningFallbackModel
|
||||
// (FN-7719)"). The mock runtime used by createResolvedAgentSession in
|
||||
// test mode does not route through createFnAgent, so it cannot assert
|
||||
// fallbackProvider/fallbackModelId via mockCreateFnAgent call args here.
|
||||
});
|
||||
|
||||
it("escalates to error state when triage retries are exhausted via specifyTask", async () => {
|
||||
const task = {
|
||||
id: "FN-201",
|
||||
|
||||
@@ -212,6 +212,47 @@ export function resolvePlanningSessionModel(
|
||||
return pickSettingsThenRuntimeModel(resolvedTaskPlanningModel, assignedAgentRuntimeConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* FNXC:TriageModelFallback 2026-07-09-00:00:
|
||||
* When no explicit `planningFallback*`/global `fallback*` pair is configured, the
|
||||
* planning lane must still get a working fallback. Derive one implicitly from the
|
||||
* resolved project/global default (execution) model — the same resolver
|
||||
* `resolveHeartbeatSessionModels`/`resolveMergerSessionModel` use — so a retryable
|
||||
* primary-planner failure (e.g. provider 404/429) recovers via one distinct swap
|
||||
* instead of failing triage permanently (see FN-7719). Guard against self-swap
|
||||
* (implicit fallback === primary planning model) and skip entirely in test mode,
|
||||
* so the single-swap `usingFallback` ceiling in pi.ts and the terminal
|
||||
* ModelFallbackExhaustedError path are preserved unchanged.
|
||||
*/
|
||||
export function resolveImplicitPlanningFallbackModel(
|
||||
settings: Partial<Settings> | undefined,
|
||||
primaryProvider: string | undefined,
|
||||
primaryModelId: string | undefined,
|
||||
assignedAgentRuntimeConfig?: Record<string, unknown>,
|
||||
): { provider: string | undefined; modelId: string | undefined } {
|
||||
if (isTestModeActive(settings)) {
|
||||
return { provider: undefined, modelId: undefined };
|
||||
}
|
||||
|
||||
const defaultModel = resolveProjectDefaultModel(settings);
|
||||
const resolvedModel = pickSettingsThenRuntimeModel(defaultModel, assignedAgentRuntimeConfig);
|
||||
|
||||
if (!resolvedModel.provider || !resolvedModel.modelId) {
|
||||
return { provider: undefined, modelId: undefined };
|
||||
}
|
||||
|
||||
// Self-swap guard: an implicit fallback identical to the primary planner
|
||||
// model would produce a misleading "fallback configured" message while
|
||||
// still hitting the terminal ModelFallbackExhaustedError path in pi.ts
|
||||
// (hasDistinctFallback requires the models to differ). Leave both fields
|
||||
// undefined so the existing terminal behavior is preserved cleanly.
|
||||
if (resolvedModel.provider === primaryProvider && resolvedModel.modelId === primaryModelId) {
|
||||
return { provider: undefined, modelId: undefined };
|
||||
}
|
||||
|
||||
return resolvedModel;
|
||||
}
|
||||
|
||||
export function resolveValidatorSessionModel(
|
||||
taskValidatorModelProvider: string | undefined,
|
||||
taskValidatorModelId: string | undefined,
|
||||
|
||||
@@ -86,6 +86,7 @@ import { ModelFallbackExhaustedError, describeModel, formatModelMarkerDetails, p
|
||||
import {
|
||||
createResolvedAgentSession,
|
||||
extractRuntimeHint,
|
||||
resolveImplicitPlanningFallbackModel,
|
||||
resolvePlanningSessionModel,
|
||||
} from "./agent-session-helpers.js";
|
||||
import { mergeEffectiveSettings } from "./effective-settings.js";
|
||||
@@ -1117,6 +1118,28 @@ export class TriageProcessor {
|
||||
defaultModelId: planningModel.modelId,
|
||||
};
|
||||
|
||||
/*
|
||||
* FNXC:TriageModelFallback 2026-07-09-00:00:
|
||||
* When neither `planningFallback*` nor global `fallback*` is configured,
|
||||
* derive an implicit fallback from the project/global default (execution)
|
||||
* model so a retryable primary-planner failure (e.g. provider 404/429)
|
||||
* recovers via one distinct swap instead of failing triage permanently
|
||||
* (FN-7719: nvidia/moonshotai/kimi-k2.6 404 wrapped in a 429 stalled a
|
||||
* whole board's triage with "no fallback configured"). Self-swap (implicit
|
||||
* fallback === primary) and test mode are excluded so the single-swap,
|
||||
* no-loop invariant and the mock lane stay unchanged.
|
||||
*/
|
||||
const hasExplicitPlanningFallback = Boolean(settings.planningFallbackProvider && settings.planningFallbackModelId);
|
||||
const hasExplicitGlobalFallback = Boolean(settings.fallbackProvider && settings.fallbackModelId);
|
||||
const implicitPlanningFallback = (!hasExplicitPlanningFallback && !hasExplicitGlobalFallback)
|
||||
? resolveImplicitPlanningFallbackModel(
|
||||
settings,
|
||||
planningModel.provider,
|
||||
planningModel.modelId,
|
||||
assignedAgent?.runtimeConfig,
|
||||
)
|
||||
: { provider: undefined, modelId: undefined };
|
||||
|
||||
const { session } = await createResolvedAgentSession({
|
||||
sessionPurpose: "triage",
|
||||
runtimeHint: triageRuntimeHint,
|
||||
@@ -1131,12 +1154,12 @@ export class TriageProcessor {
|
||||
onToolStart: agentLogger.onToolStart,
|
||||
onToolEnd: agentLogger.onToolEnd,
|
||||
...planningSessionModelOptions,
|
||||
fallbackProvider: settings.planningFallbackProvider && settings.planningFallbackModelId
|
||||
fallbackProvider: hasExplicitPlanningFallback
|
||||
? settings.planningFallbackProvider
|
||||
: settings.fallbackProvider,
|
||||
fallbackModelId: settings.planningFallbackProvider && settings.planningFallbackModelId
|
||||
: (hasExplicitGlobalFallback ? settings.fallbackProvider : implicitPlanningFallback.provider),
|
||||
fallbackModelId: hasExplicitPlanningFallback
|
||||
? settings.planningFallbackModelId
|
||||
: settings.fallbackModelId,
|
||||
: (hasExplicitGlobalFallback ? settings.fallbackModelId : implicitPlanningFallback.modelId),
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
runAuditor,
|
||||
settings,
|
||||
|
||||
Reference in New Issue
Block a user