mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
refactor(agents): split embedded runner model setup (#107646)
This commit is contained in:
committed by
GitHub
parent
7f36f49052
commit
4ea573edd2
@@ -35,7 +35,6 @@ import { formatErrorMessage, toErrorObject } from "../../infra/errors.js";
|
||||
import { redactIdentifier } from "../../logging/redact-identifier.js";
|
||||
import { buildAgentHookContextChannelFields } from "../../plugins/hook-agent-context.js";
|
||||
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
||||
import { resolveProviderAuthProfileId } from "../../plugins/provider-runtime.js";
|
||||
import { looksLikeSecretSentinel, resolveSecretSentinel } from "../../secrets/sentinel.js";
|
||||
import { createAgentHarnessTaskRuntimeScope } from "../../tasks/agent-harness-task-runtime-scope.js";
|
||||
import { createTrajectoryRuntimeRecorder } from "../../trajectory/runtime.js";
|
||||
@@ -59,7 +58,6 @@ import {
|
||||
markAuthProfileFailure,
|
||||
markAuthProfileSuccess,
|
||||
} from "../auth-profiles.js";
|
||||
import { resolveExternalCliAuthOverlayScopeFromSelection } from "../auth-profiles/external-cli-auth-selection.js";
|
||||
import { listActiveProcessSessionReferences } from "../bash-process-references.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js";
|
||||
import {
|
||||
@@ -94,31 +92,14 @@ import {
|
||||
FailoverError,
|
||||
resolveFailoverStatus,
|
||||
} from "../failover-error.js";
|
||||
import { ensureSelectedAgentHarnessPlugin } from "../harness/runtime-plugin.js";
|
||||
import {
|
||||
agentHarnessBuildsOpenClawTools,
|
||||
selectAgentHarness,
|
||||
selectAgentHarnessForPreparedModelProviders,
|
||||
} from "../harness/selection.js";
|
||||
import {
|
||||
resolveAgentHarnessPreparedAuthSupport,
|
||||
resolveAgentHarnessPreparedRouteSupport,
|
||||
} from "../harness/support.js";
|
||||
import { agentHarnessBuildsOpenClawTools } from "../harness/selection.js";
|
||||
import { LiveSessionModelSwitchError } from "../live-model-switch-error.js";
|
||||
import { shouldSwitchToLiveModel, clearLiveModelSwitchPending } from "../live-model-switch.js";
|
||||
import {
|
||||
applyAuthHeaderOverride,
|
||||
applyLocalNoAuthHeaderOverride,
|
||||
ensureAuthProfileStore,
|
||||
ensureAuthProfileStoreWithoutExternalProfiles,
|
||||
type ResolvedProviderAuth,
|
||||
} from "../model-auth.js";
|
||||
import { ensureOpenClawModelsJson } from "../models-config.js";
|
||||
import {
|
||||
OPENAI_PROVIDER_ID,
|
||||
resolveContextConfigProviderForRuntime,
|
||||
resolveSelectedOpenAIRuntimeProvider,
|
||||
} from "../openai-routing.js";
|
||||
import { hasOnlyAssistantReasoningContent } from "../replay-turn-classification.js";
|
||||
import { runAgentCleanupStep } from "../run-cleanup-timeout.js";
|
||||
import {
|
||||
@@ -128,14 +109,11 @@ import {
|
||||
import { createAgentRunDirectAbortError } from "../run-termination.js";
|
||||
import { buildAgentRuntimePlan } from "../runtime-plan/build.js";
|
||||
import {
|
||||
createPreparedRuntimeModelMaterializer,
|
||||
hasPreparedAuthAttemptModelMetadata,
|
||||
providerUsesCredentialScopedModelMetadata,
|
||||
resolveCredentialScopedAuthAttemptModelDecision,
|
||||
} from "../runtime-plan/credential-scoped-model.js";
|
||||
import {
|
||||
canRunPreparedAgentRuntimeAuthAttempt,
|
||||
prepareAgentRuntimeAuth,
|
||||
type PreparedAgentRuntimeAuthAttempt,
|
||||
} from "../runtime-plan/prepare-auth.js";
|
||||
import type { AgentRuntimeAuthPlan } from "../runtime-plan/types.js";
|
||||
@@ -168,7 +146,6 @@ import {
|
||||
import { resolveEmbeddedRunFailureSignal } from "./failure-signal.js";
|
||||
import { resolveGlobalLane, resolveSessionLane } from "./lanes.js";
|
||||
import { log } from "./logger.js";
|
||||
import { createEmptyAgentDiscoveryStores, resolveModelAsync } from "./model.js";
|
||||
import {
|
||||
createPostCompactionLoopGuard,
|
||||
PostCompactionLoopPersistedError,
|
||||
@@ -189,6 +166,7 @@ import {
|
||||
createEmbeddedRunAuthController,
|
||||
resolveEmbeddedAuthCooldownProbePolicy,
|
||||
} from "./run/auth-controller.js";
|
||||
import { prepareEmbeddedRunAuthPlan } from "./run/auth-plan.js";
|
||||
import { resolveAuthProfileFailureReason } from "./run/auth-profile-failure-policy.js";
|
||||
import { createScopedAuthProfileStore, resolveAttemptDispatchApiKey } from "./run/auth-store.js";
|
||||
import { runEmbeddedAttemptWithBackend } from "./run/backend.js";
|
||||
@@ -246,6 +224,12 @@ import {
|
||||
EMBEDDED_RUN_LANE_TIMEOUT_GRACE_MS,
|
||||
resolveEmbeddedRunLaneTimeoutMs,
|
||||
} from "./run/lane-runtime.js";
|
||||
import {
|
||||
resolveEmbeddedRunEffectiveModel,
|
||||
selectEmbeddedRunHarness,
|
||||
selectEmbeddedRunHarnessForPreparedAttempts,
|
||||
} from "./run/model-harness.js";
|
||||
import { resolveEmbeddedRunModelSetup } from "./run/model-setup.js";
|
||||
import type { RunEmbeddedAgentParams } from "./run/params.js";
|
||||
import { buildEmbeddedRunPayloads } from "./run/payloads.js";
|
||||
import { createEmbeddedRunProgressController } from "./run/progress-controller.js";
|
||||
@@ -260,7 +244,6 @@ import {
|
||||
resolveAttemptTrajectoryAttribution,
|
||||
resolveInitialEmbeddedRunModel,
|
||||
resolveInitialThinkLevel,
|
||||
resolveRequestStreamTransportOverrides,
|
||||
} from "./run/runtime-resolution.js";
|
||||
import {
|
||||
assertAgentHarnessRunAdmission,
|
||||
@@ -269,13 +252,6 @@ import {
|
||||
isNoRealConversationCompactionNoop,
|
||||
resetNoRealConversationTokenSnapshot,
|
||||
} from "./run/session-bootstrap.js";
|
||||
import {
|
||||
buildBeforeModelResolveAttachments,
|
||||
createNativeModelOwnedRuntimeModel,
|
||||
resolveEmbeddedRuntimeModelPolicy,
|
||||
resolveHookModelSelection,
|
||||
resolveNativeModelOwnedHarnessId,
|
||||
} from "./run/setup.js";
|
||||
import { resolveSkillWorkshopAttemptParams } from "./run/skill-workshop-attempt-params.js";
|
||||
import {
|
||||
isEmbeddedRunTerminalAbort,
|
||||
@@ -553,180 +529,46 @@ async function runEmbeddedAgentInternal(
|
||||
notifyExecutionPhase("runtime_plugins", { provider, model: modelId });
|
||||
}
|
||||
|
||||
const hookSelection = await resolveHookModelSelection({
|
||||
prompt: params.prompt,
|
||||
attachments: buildBeforeModelResolveAttachments(params.images),
|
||||
const modelSetup = await resolveEmbeddedRunModelSetup({
|
||||
runParams: params,
|
||||
provider,
|
||||
modelId,
|
||||
modelSelectionLocked: params.modelSelectionLocked,
|
||||
agentDir,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
globalLane,
|
||||
hookRunner,
|
||||
hookContext: hookCtx,
|
||||
onHooksResolved: () => startupStages.mark("hooks"),
|
||||
});
|
||||
const modelSelectionChangedByHook =
|
||||
hookSelection.provider !== provider || hookSelection.modelId !== modelId;
|
||||
provider = hookSelection.provider;
|
||||
modelId = hookSelection.modelId;
|
||||
const requestedModelId = modelId;
|
||||
const beforeAgentStartResult = hookSelection.beforeAgentStartResult;
|
||||
const requestStreamTransportOverrides = resolveRequestStreamTransportOverrides(
|
||||
params.streamParams,
|
||||
);
|
||||
startupStages.mark("hooks");
|
||||
await ensureSelectedAgentHarnessPlugin({
|
||||
provider,
|
||||
modelId,
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
agentHarnessId: params.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: params.agentHarnessRuntimeOverride,
|
||||
requestTransportOverrides: requestStreamTransportOverrides,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
});
|
||||
let agentHarness = selectAgentHarness({
|
||||
provider,
|
||||
modelId,
|
||||
...(requestStreamTransportOverrides
|
||||
? {
|
||||
modelProvider: {
|
||||
requestTransportOverrides: requestStreamTransportOverrides,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
agentHarnessId: params.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: params.agentHarnessRuntimeOverride,
|
||||
});
|
||||
let pluginHarnessOwnsTransport = agentHarness.id !== "openclaw";
|
||||
const expectedHarnessArtifact = params.expectedAgentHarnessRuntimeArtifact;
|
||||
if (expectedHarnessArtifact && expectedHarnessArtifact.harnessId !== agentHarness.id) {
|
||||
throw new Error(
|
||||
`Verified inference requires agent harness ${expectedHarnessArtifact.harnessId}, but ${agentHarness.id} was selected.`,
|
||||
);
|
||||
}
|
||||
if (expectedHarnessArtifact && !agentHarness.runtimeArtifact) {
|
||||
throw new Error(
|
||||
`Agent harness ${agentHarness.id} cannot attest the verified inference runtime artifact.`,
|
||||
);
|
||||
}
|
||||
const nativeModelOwnedHarnessId = resolveNativeModelOwnedHarnessId({
|
||||
agentHarnessId: params.agentHarnessId,
|
||||
modelSelectionLocked: params.modelSelectionLocked,
|
||||
selectedHarnessId: agentHarness.id,
|
||||
});
|
||||
const nativeModelOwned = nativeModelOwnedHarnessId !== undefined;
|
||||
const modelConfigProvider = provider;
|
||||
let resolvedModelProvider = provider;
|
||||
let firstModelResolution: Awaited<ReturnType<typeof resolveModelAsync>> | undefined;
|
||||
let modelResolution: Awaited<ReturnType<typeof resolveModelAsync>> | undefined;
|
||||
if (nativeModelOwned) {
|
||||
modelResolution = {
|
||||
model: createNativeModelOwnedRuntimeModel({ provider, modelId }),
|
||||
...createEmptyAgentDiscoveryStores(),
|
||||
};
|
||||
} else {
|
||||
const selectedRuntimeProvider = resolveSelectedOpenAIRuntimeProvider({
|
||||
provider,
|
||||
harnessRuntime: agentHarness.id,
|
||||
agentHarnessId: agentHarness.id,
|
||||
authProfileProvider: params.authProfileId?.split(":", 1)[0],
|
||||
authProfileId: params.authProfileId,
|
||||
config: params.config,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
});
|
||||
const modelResolutionProviders =
|
||||
selectedRuntimeProvider !== provider ? [selectedRuntimeProvider, provider] : [provider];
|
||||
for (const candidateProvider of modelResolutionProviders) {
|
||||
const candidateResolution = await resolveModelAsync(
|
||||
candidateProvider,
|
||||
modelId,
|
||||
agentDir,
|
||||
params.config,
|
||||
{
|
||||
// Plugin dynamic model hooks can resolve explicit model refs without
|
||||
// first generating OpenClaw models.json. This keeps one-shot model runs from
|
||||
// blocking on unrelated provider discovery.
|
||||
skipAgentDiscovery: true,
|
||||
allowBundledStaticCatalogFallback: pluginHarnessOwnsTransport,
|
||||
preferBundledStaticCatalogTransport: pluginHarnessOwnsTransport,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
authProfileId: params.authProfileId,
|
||||
},
|
||||
);
|
||||
firstModelResolution ??= candidateResolution;
|
||||
if (candidateResolution.model) {
|
||||
resolvedModelProvider = candidateProvider;
|
||||
modelResolution = candidateResolution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!modelResolution && pluginHarnessOwnsTransport) {
|
||||
modelResolution ??= firstModelResolution;
|
||||
}
|
||||
if (!modelResolution) {
|
||||
await ensureOpenClawModelsJson(params.config, agentDir, {
|
||||
workspaceDir: resolvedWorkspace,
|
||||
});
|
||||
for (const candidateProvider of modelResolutionProviders) {
|
||||
const candidateResolution = await resolveModelAsync(
|
||||
candidateProvider,
|
||||
modelId,
|
||||
agentDir,
|
||||
params.config,
|
||||
{
|
||||
workspaceDir: resolvedWorkspace,
|
||||
authProfileId: params.authProfileId,
|
||||
// Enable bundled static catalog fallback so plugin-provided
|
||||
// models that are not discoverable via agent model discovery
|
||||
// can still be resolved from the static catalog.
|
||||
allowBundledStaticCatalogFallback: true,
|
||||
},
|
||||
);
|
||||
firstModelResolution ??= candidateResolution;
|
||||
if (candidateResolution.model) {
|
||||
resolvedModelProvider = candidateProvider;
|
||||
modelResolution = candidateResolution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
modelResolution ??= firstModelResolution;
|
||||
}
|
||||
if (!modelResolution) {
|
||||
throw new FailoverError(`Unknown model: ${provider}/${modelId}`, {
|
||||
reason: "model_not_found",
|
||||
provider,
|
||||
model: modelId,
|
||||
sessionId: params.sessionId,
|
||||
lane: globalLane,
|
||||
});
|
||||
}
|
||||
provider = resolvedModelProvider;
|
||||
const { model, error, authStorage, modelRegistry } = modelResolution;
|
||||
if (!model) {
|
||||
throw new FailoverError(error ?? `Unknown model: ${provider}/${modelId}`, {
|
||||
reason: "model_not_found",
|
||||
provider,
|
||||
model: modelId,
|
||||
sessionId: params.sessionId,
|
||||
lane: globalLane,
|
||||
});
|
||||
}
|
||||
provider = modelSetup.provider;
|
||||
modelId = modelSetup.modelId;
|
||||
const {
|
||||
requestedModelId,
|
||||
modelSelectionChangedByHook,
|
||||
beforeAgentStartResult,
|
||||
requestStreamTransportOverrides,
|
||||
expectedHarnessArtifact,
|
||||
nativeModelOwnedHarnessId,
|
||||
nativeModelOwned,
|
||||
modelConfigProvider,
|
||||
model,
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
} = modelSetup;
|
||||
let agentHarness = modelSetup.agentHarness;
|
||||
let pluginHarnessOwnsTransport = modelSetup.pluginHarnessOwnsTransport;
|
||||
let runtimeModel = model;
|
||||
const resolveEffectiveModel = (candidate: typeof runtimeModel) =>
|
||||
resolveEmbeddedRuntimeModelPolicy({
|
||||
cfg: params.config,
|
||||
resolveEmbeddedRunEffectiveModel({
|
||||
runParams: params,
|
||||
provider,
|
||||
contextConfigProvider: resolveContextConfigProviderForRuntime({
|
||||
provider: modelConfigProvider,
|
||||
runtimeId: agentHarness.id,
|
||||
config: params.config,
|
||||
}),
|
||||
modelConfigProvider,
|
||||
modelId,
|
||||
agentHarnessId: agentHarness.id,
|
||||
runtimeModel: candidate,
|
||||
nativeModelOwned,
|
||||
requestStreamTransportOverrides,
|
||||
nativeModelOwnedHarnessId,
|
||||
});
|
||||
const initialResolvedRuntimeModel = resolveEffectiveModel(runtimeModel);
|
||||
let contextTokenBudget = initialResolvedRuntimeModel.contextTokenBudget;
|
||||
@@ -745,82 +587,34 @@ async function runEmbeddedAgentInternal(
|
||||
outerContextTokenMeta =
|
||||
contextTokenBudget === undefined ? {} : { contextTokens: contextTokenBudget };
|
||||
};
|
||||
const buildHarnessModelProvider = (
|
||||
candidate: typeof effectiveModel,
|
||||
plan?: AgentRuntimeAuthPlan,
|
||||
preparedAuthAttempt?: PreparedAgentRuntimeAuthAttempt,
|
||||
) => {
|
||||
const route = plan?.modelRoute;
|
||||
const routeSupport = resolveAgentHarnessPreparedRouteSupport(plan);
|
||||
const requestTransportOverrides =
|
||||
requestStreamTransportOverrides ?? routeSupport.requestTransportOverrides;
|
||||
return {
|
||||
api: route?.api ?? candidate.api,
|
||||
baseUrl: route?.baseUrl ?? candidate.baseUrl,
|
||||
...(requestTransportOverrides ? { requestTransportOverrides } : {}),
|
||||
...(routeSupport.runtimePolicy ? { runtimePolicy: routeSupport.runtimePolicy } : {}),
|
||||
...(plan
|
||||
? {
|
||||
preparedAuth: resolveAgentHarnessPreparedAuthSupport({
|
||||
plan,
|
||||
...(preparedAuthAttempt?.kind === "profile" ||
|
||||
preparedAuthAttempt?.kind === "direct"
|
||||
? { source: preparedAuthAttempt.kind }
|
||||
: {}),
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
};
|
||||
const selectHarnessForModel = (
|
||||
candidate: typeof effectiveModel,
|
||||
plan?: AgentRuntimeAuthPlan,
|
||||
preparedAuthAttempt?: PreparedAgentRuntimeAuthAttempt,
|
||||
) => {
|
||||
const selected = selectAgentHarness({
|
||||
) =>
|
||||
selectEmbeddedRunHarness({
|
||||
runParams: params,
|
||||
provider,
|
||||
modelId,
|
||||
modelProvider: buildHarnessModelProvider(candidate, plan, preparedAuthAttempt),
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
agentHarnessId: params.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: params.agentHarnessRuntimeOverride,
|
||||
model: candidate,
|
||||
plan,
|
||||
preparedAuthAttempt,
|
||||
requestStreamTransportOverrides,
|
||||
nativeModelOwnedHarnessId,
|
||||
});
|
||||
if (nativeModelOwnedHarnessId && selected.id !== nativeModelOwnedHarnessId) {
|
||||
throw new Error(
|
||||
`Prepared model route changed the session-pinned agent harness from "${nativeModelOwnedHarnessId}" to "${selected.id}".`,
|
||||
);
|
||||
}
|
||||
return selected;
|
||||
};
|
||||
const selectHarnessForPreparedAttempts = (
|
||||
candidate: typeof effectiveModel,
|
||||
attempts: readonly PreparedAgentRuntimeAuthAttempt[],
|
||||
) => {
|
||||
const selected = selectAgentHarnessForPreparedModelProviders({
|
||||
) =>
|
||||
selectEmbeddedRunHarnessForPreparedAttempts({
|
||||
runParams: params,
|
||||
provider,
|
||||
modelId,
|
||||
modelProviders: attempts.map((attempt) => {
|
||||
const route = attempt.plan.modelRoute;
|
||||
const attemptModel = route
|
||||
? { ...candidate, api: route.api, baseUrl: route.baseUrl }
|
||||
: candidate;
|
||||
return buildHarnessModelProvider(attemptModel, attempt.plan, attempt);
|
||||
}),
|
||||
config: params.config,
|
||||
agentId: params.agentId,
|
||||
sessionKey: params.sessionKey,
|
||||
agentHarnessId: params.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: params.agentHarnessRuntimeOverride,
|
||||
model: candidate,
|
||||
attempts,
|
||||
requestStreamTransportOverrides,
|
||||
nativeModelOwnedHarnessId,
|
||||
});
|
||||
if (nativeModelOwnedHarnessId && selected.id !== nativeModelOwnedHarnessId) {
|
||||
throw new Error(
|
||||
`Prepared auth routes changed the session-pinned agent harness from "${nativeModelOwnedHarnessId}" to "${selected.id}".`,
|
||||
);
|
||||
}
|
||||
return selected;
|
||||
};
|
||||
startupStages.mark("model-resolution");
|
||||
notifyExecutionPhase("model_resolution", { provider, model: modelId });
|
||||
|
||||
@@ -831,154 +625,39 @@ async function runEmbeddedAgentInternal(
|
||||
pluginHarnessOwnsTransport = agentHarness.id !== "openclaw";
|
||||
|
||||
const authStages = log.isEnabled("trace") ? createEmbeddedRunStageTracker() : undefined;
|
||||
const usesOpenAIAuthRouting = provider === OPENAI_PROVIDER_ID;
|
||||
const openClawNativeCodexResponsesNeedsAuthBootstrap =
|
||||
!pluginHarnessOwnsTransport &&
|
||||
provider === OPENAI_PROVIDER_ID &&
|
||||
effectiveModel.api === "openai-chatgpt-responses";
|
||||
let piExternalCliAuthScope = pluginHarnessOwnsTransport
|
||||
? { ignoreAutoPreferredProfile: false }
|
||||
: openClawNativeCodexResponsesNeedsAuthBootstrap
|
||||
? {
|
||||
providerIds: [OPENAI_PROVIDER_ID],
|
||||
ignoreAutoPreferredProfile: false,
|
||||
}
|
||||
: resolveExternalCliAuthOverlayScopeFromSelection({
|
||||
provider,
|
||||
cfg: params.config,
|
||||
agentId: params.agentId,
|
||||
modelId,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
userLockedAuthProfileId:
|
||||
params.authProfileIdSource === "user" ? params.authProfileId : undefined,
|
||||
});
|
||||
let noExternalAuthStore: AuthProfileStore | undefined;
|
||||
if (!pluginHarnessOwnsTransport && !piExternalCliAuthScope.providerIds) {
|
||||
noExternalAuthStore = ensureAuthProfileStoreWithoutExternalProfiles(agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
piExternalCliAuthScope = resolveExternalCliAuthOverlayScopeFromSelection({
|
||||
provider,
|
||||
cfg: params.config,
|
||||
agentId: params.agentId,
|
||||
modelId,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
store: noExternalAuthStore,
|
||||
userLockedAuthProfileId:
|
||||
params.authProfileIdSource === "user" ? params.authProfileId : undefined,
|
||||
});
|
||||
}
|
||||
authStages?.mark("scope");
|
||||
const attemptAuthProfileStore = usesOpenAIAuthRouting
|
||||
? ensureAuthProfileStore(agentDir, {
|
||||
externalCliProviderIds: [OPENAI_PROVIDER_ID],
|
||||
allowKeychainPrompt: false,
|
||||
})
|
||||
: pluginHarnessOwnsTransport
|
||||
? ensureAuthProfileStoreWithoutExternalProfiles(agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
})
|
||||
: piExternalCliAuthScope.providerIds
|
||||
? ensureAuthProfileStore(agentDir, {
|
||||
externalCliProviderIds: piExternalCliAuthScope.providerIds,
|
||||
allowKeychainPrompt: false,
|
||||
})
|
||||
: (noExternalAuthStore ??
|
||||
ensureAuthProfileStoreWithoutExternalProfiles(agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
}));
|
||||
authStages?.mark("store");
|
||||
const requestedProfileId = params.authProfileId?.trim() || undefined;
|
||||
const lockedProfileId =
|
||||
params.authProfileIdSource === "user" ? requestedProfileId : undefined;
|
||||
const preferredProfileId =
|
||||
piExternalCliAuthScope.ignoreAutoPreferredProfile && !lockedProfileId
|
||||
? undefined
|
||||
: requestedProfileId;
|
||||
const createAuthPreparation = () =>
|
||||
prepareAgentRuntimeAuth({
|
||||
provider,
|
||||
modelId,
|
||||
modelApi: model.api,
|
||||
modelBaseUrl: model.baseUrl,
|
||||
requestTransportOverrides: requestStreamTransportOverrides,
|
||||
config: params.config,
|
||||
env: process.env,
|
||||
agentDir,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
authProfileStore: attemptAuthProfileStore,
|
||||
sessionAuthProfileId: preferredProfileId,
|
||||
sessionAuthProfileSource: params.authProfileIdSource,
|
||||
harnessId: agentHarness.id,
|
||||
harnessRuntime: agentHarness.id,
|
||||
harnessAuthBootstrap: agentHarness.authBootstrap,
|
||||
allowHarnessAuthProfileForwarding: true,
|
||||
allowTransientCooldownProbe: params.allowTransientCooldownProbe === true,
|
||||
resolveProviderPreferredProfileId: (context) =>
|
||||
resolveProviderAuthProfileId({
|
||||
provider,
|
||||
config: params.config,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
env: process.env,
|
||||
context,
|
||||
}),
|
||||
});
|
||||
const providerUsesProfileScopedModelMetadata = providerUsesCredentialScopedModelMetadata({
|
||||
const preparedAuthPlan = await prepareEmbeddedRunAuthPlan({
|
||||
runParams: params,
|
||||
provider,
|
||||
modelId,
|
||||
config: params.config,
|
||||
model,
|
||||
agentDir,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
requestStreamTransportOverrides,
|
||||
nativeModelOwned,
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
getAgentHarness: () => agentHarness,
|
||||
setAgentHarness: (nextHarness) => {
|
||||
agentHarness = nextHarness;
|
||||
pluginHarnessOwnsTransport = agentHarness.id !== "openclaw";
|
||||
},
|
||||
getRuntimeModel: () => runtimeModel,
|
||||
getEffectiveModel: () => effectiveModel,
|
||||
applyResolvedRuntimeModel,
|
||||
selectHarnessForPreparedAttempts,
|
||||
markStage: (stage) => authStages?.mark(stage),
|
||||
});
|
||||
const { materialize: materializeAuthPlan, materializeUncached: materializeAuthPlanUncached } =
|
||||
createPreparedRuntimeModelMaterializer({
|
||||
provider,
|
||||
modelId,
|
||||
config: params.config,
|
||||
getModel: () => runtimeModel,
|
||||
nativeModelOwned,
|
||||
requestedProfileId: params.authProfileId,
|
||||
providerUsesProfileScopedModelMetadata,
|
||||
resolveModel: ({ config, authProfileId, authProfileMode }) =>
|
||||
resolveModelAsync(provider, modelId, agentDir, config, {
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
skipAgentDiscovery: true,
|
||||
allowBundledStaticCatalogFallback: true,
|
||||
preferBundledStaticCatalogTransport: true,
|
||||
workspaceDir: resolvedWorkspace,
|
||||
authProfileId,
|
||||
authProfileMode,
|
||||
}),
|
||||
});
|
||||
let resolvedAuthPreparation = createAuthPreparation();
|
||||
let preparedAuthAttempts = resolvedAuthPreparation.attempts;
|
||||
let activePreparedAuthPlan = resolvedAuthPreparation.plan;
|
||||
applyResolvedRuntimeModel(await materializeAuthPlan(activePreparedAuthPlan));
|
||||
authStages?.mark("prepare-plan");
|
||||
|
||||
const finalizedHarness = selectHarnessForPreparedAttempts(
|
||||
effectiveModel,
|
||||
const {
|
||||
usesOpenAIAuthRouting,
|
||||
attemptAuthProfileStore,
|
||||
lockedProfileId,
|
||||
preferredProfileId,
|
||||
providerUsesProfileScopedModelMetadata,
|
||||
materializeAuthPlan,
|
||||
materializeAuthPlanUncached,
|
||||
preparedAuthAttempts,
|
||||
);
|
||||
if (finalizedHarness.id !== agentHarness.id) {
|
||||
agentHarness = finalizedHarness;
|
||||
pluginHarnessOwnsTransport = agentHarness.id !== "openclaw";
|
||||
resolvedAuthPreparation = createAuthPreparation();
|
||||
preparedAuthAttempts = resolvedAuthPreparation.attempts;
|
||||
activePreparedAuthPlan = resolvedAuthPreparation.plan;
|
||||
applyResolvedRuntimeModel(await materializeAuthPlan(activePreparedAuthPlan));
|
||||
const confirmedHarness = selectHarnessForPreparedAttempts(
|
||||
effectiveModel,
|
||||
preparedAuthAttempts,
|
||||
);
|
||||
if (confirmedHarness.id !== agentHarness.id) {
|
||||
throw new Error(
|
||||
`Prepared auth route did not converge on one agent harness for ${provider}/${modelId}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
authStages?.mark("harness");
|
||||
} = preparedAuthPlan;
|
||||
let { activePreparedAuthPlan } = preparedAuthPlan;
|
||||
// A selected plugin harness owns context pressure with its native transcript,
|
||||
// even if it cannot expose manual compaction. Generic recovery is OpenClaw-only.
|
||||
const genericCompactionRecoveryAllowed = !pluginHarnessOwnsTransport;
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
import { resolveProviderAuthProfileId } from "../../../plugins/provider-runtime.js";
|
||||
import type { AuthProfileStore } from "../../auth-profiles.js";
|
||||
import { resolveExternalCliAuthOverlayScopeFromSelection } from "../../auth-profiles/external-cli-auth-selection.js";
|
||||
import type { AgentHarness } from "../../harness/types.js";
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
ensureAuthProfileStoreWithoutExternalProfiles,
|
||||
} from "../../model-auth.js";
|
||||
import { OPENAI_PROVIDER_ID } from "../../openai-routing.js";
|
||||
import {
|
||||
createPreparedRuntimeModelMaterializer,
|
||||
providerUsesCredentialScopedModelMetadata,
|
||||
} from "../../runtime-plan/credential-scoped-model.js";
|
||||
import {
|
||||
prepareAgentRuntimeAuth,
|
||||
type PreparedAgentRuntimeAuthAttempt,
|
||||
} from "../../runtime-plan/prepare-auth.js";
|
||||
import { resolveModelAsync } from "../model.js";
|
||||
import type { RunEmbeddedAgentParams } from "./params.js";
|
||||
|
||||
type ModelResolution = Awaited<ReturnType<typeof resolveModelAsync>>;
|
||||
type RuntimeModel = NonNullable<ModelResolution["model"]>;
|
||||
|
||||
export async function prepareEmbeddedRunAuthPlan(params: {
|
||||
runParams: RunEmbeddedAgentParams;
|
||||
provider: string;
|
||||
modelId: string;
|
||||
model: RuntimeModel;
|
||||
agentDir: string;
|
||||
workspaceDir: string;
|
||||
requestStreamTransportOverrides?: "present";
|
||||
nativeModelOwned: boolean;
|
||||
authStorage: ModelResolution["authStorage"];
|
||||
modelRegistry: ModelResolution["modelRegistry"];
|
||||
getAgentHarness: () => AgentHarness;
|
||||
setAgentHarness: (harness: AgentHarness) => void;
|
||||
getRuntimeModel: () => RuntimeModel;
|
||||
getEffectiveModel: () => RuntimeModel;
|
||||
applyResolvedRuntimeModel: (model: RuntimeModel) => void;
|
||||
selectHarnessForPreparedAttempts: (
|
||||
model: RuntimeModel,
|
||||
attempts: readonly PreparedAgentRuntimeAuthAttempt[],
|
||||
) => AgentHarness;
|
||||
markStage?: (stage: string) => void;
|
||||
}) {
|
||||
const runParams = params.runParams;
|
||||
const usesOpenAIAuthRouting = params.provider === OPENAI_PROVIDER_ID;
|
||||
const initialHarness = params.getAgentHarness();
|
||||
const initialPluginHarnessOwnsTransport = initialHarness.id !== "openclaw";
|
||||
const openClawNativeCodexResponsesNeedsAuthBootstrap =
|
||||
!initialPluginHarnessOwnsTransport &&
|
||||
usesOpenAIAuthRouting &&
|
||||
params.getEffectiveModel().api === "openai-chatgpt-responses";
|
||||
let externalCliAuthScope = initialPluginHarnessOwnsTransport
|
||||
? { ignoreAutoPreferredProfile: false }
|
||||
: openClawNativeCodexResponsesNeedsAuthBootstrap
|
||||
? {
|
||||
providerIds: [OPENAI_PROVIDER_ID],
|
||||
ignoreAutoPreferredProfile: false,
|
||||
}
|
||||
: resolveExternalCliAuthOverlayScopeFromSelection({
|
||||
provider: params.provider,
|
||||
cfg: runParams.config,
|
||||
agentId: runParams.agentId,
|
||||
modelId: params.modelId,
|
||||
workspaceDir: params.workspaceDir,
|
||||
userLockedAuthProfileId:
|
||||
runParams.authProfileIdSource === "user" ? runParams.authProfileId : undefined,
|
||||
});
|
||||
let noExternalAuthStore: AuthProfileStore | undefined;
|
||||
if (!initialPluginHarnessOwnsTransport && !externalCliAuthScope.providerIds) {
|
||||
noExternalAuthStore = ensureAuthProfileStoreWithoutExternalProfiles(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
externalCliAuthScope = resolveExternalCliAuthOverlayScopeFromSelection({
|
||||
provider: params.provider,
|
||||
cfg: runParams.config,
|
||||
agentId: runParams.agentId,
|
||||
modelId: params.modelId,
|
||||
workspaceDir: params.workspaceDir,
|
||||
store: noExternalAuthStore,
|
||||
userLockedAuthProfileId:
|
||||
runParams.authProfileIdSource === "user" ? runParams.authProfileId : undefined,
|
||||
});
|
||||
}
|
||||
params.markStage?.("scope");
|
||||
|
||||
const attemptAuthProfileStore = usesOpenAIAuthRouting
|
||||
? ensureAuthProfileStore(params.agentDir, {
|
||||
externalCliProviderIds: [OPENAI_PROVIDER_ID],
|
||||
allowKeychainPrompt: false,
|
||||
})
|
||||
: initialPluginHarnessOwnsTransport
|
||||
? ensureAuthProfileStoreWithoutExternalProfiles(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
})
|
||||
: externalCliAuthScope.providerIds
|
||||
? ensureAuthProfileStore(params.agentDir, {
|
||||
externalCliProviderIds: externalCliAuthScope.providerIds,
|
||||
allowKeychainPrompt: false,
|
||||
})
|
||||
: (noExternalAuthStore ??
|
||||
ensureAuthProfileStoreWithoutExternalProfiles(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
}));
|
||||
params.markStage?.("store");
|
||||
|
||||
const requestedProfileId = runParams.authProfileId?.trim() || undefined;
|
||||
const lockedProfileId = runParams.authProfileIdSource === "user" ? requestedProfileId : undefined;
|
||||
const preferredProfileId =
|
||||
externalCliAuthScope.ignoreAutoPreferredProfile && !lockedProfileId
|
||||
? undefined
|
||||
: requestedProfileId;
|
||||
const createAuthPreparation = () => {
|
||||
const harness = params.getAgentHarness();
|
||||
return prepareAgentRuntimeAuth({
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
modelApi: params.model.api,
|
||||
modelBaseUrl: params.model.baseUrl,
|
||||
requestTransportOverrides: params.requestStreamTransportOverrides,
|
||||
config: runParams.config,
|
||||
env: process.env,
|
||||
agentDir: params.agentDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileStore: attemptAuthProfileStore,
|
||||
sessionAuthProfileId: preferredProfileId,
|
||||
sessionAuthProfileSource: runParams.authProfileIdSource,
|
||||
harnessId: harness.id,
|
||||
harnessRuntime: harness.id,
|
||||
harnessAuthBootstrap: harness.authBootstrap,
|
||||
allowHarnessAuthProfileForwarding: true,
|
||||
allowTransientCooldownProbe: runParams.allowTransientCooldownProbe === true,
|
||||
resolveProviderPreferredProfileId: (context) =>
|
||||
resolveProviderAuthProfileId({
|
||||
provider: params.provider,
|
||||
config: runParams.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: process.env,
|
||||
context,
|
||||
}),
|
||||
});
|
||||
};
|
||||
const providerUsesProfileScopedModelMetadata = providerUsesCredentialScopedModelMetadata({
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
config: runParams.config,
|
||||
agentDir: params.agentDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
const { materialize: materializeAuthPlan, materializeUncached: materializeAuthPlanUncached } =
|
||||
createPreparedRuntimeModelMaterializer({
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
config: runParams.config,
|
||||
getModel: params.getRuntimeModel,
|
||||
nativeModelOwned: params.nativeModelOwned,
|
||||
requestedProfileId: runParams.authProfileId,
|
||||
providerUsesProfileScopedModelMetadata,
|
||||
resolveModel: ({ config, authProfileId, authProfileMode }) =>
|
||||
resolveModelAsync(params.provider, params.modelId, params.agentDir, config, {
|
||||
authStorage: params.authStorage,
|
||||
modelRegistry: params.modelRegistry,
|
||||
skipAgentDiscovery: true,
|
||||
allowBundledStaticCatalogFallback: true,
|
||||
preferBundledStaticCatalogTransport: true,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId,
|
||||
authProfileMode,
|
||||
}),
|
||||
});
|
||||
|
||||
let resolvedAuthPreparation = createAuthPreparation();
|
||||
let preparedAuthAttempts = resolvedAuthPreparation.attempts;
|
||||
let activePreparedAuthPlan = resolvedAuthPreparation.plan;
|
||||
params.applyResolvedRuntimeModel(await materializeAuthPlan(activePreparedAuthPlan));
|
||||
params.markStage?.("prepare-plan");
|
||||
|
||||
const finalizedHarness = params.selectHarnessForPreparedAttempts(
|
||||
params.getEffectiveModel(),
|
||||
preparedAuthAttempts,
|
||||
);
|
||||
if (finalizedHarness.id !== params.getAgentHarness().id) {
|
||||
params.setAgentHarness(finalizedHarness);
|
||||
resolvedAuthPreparation = createAuthPreparation();
|
||||
preparedAuthAttempts = resolvedAuthPreparation.attempts;
|
||||
activePreparedAuthPlan = resolvedAuthPreparation.plan;
|
||||
params.applyResolvedRuntimeModel(await materializeAuthPlan(activePreparedAuthPlan));
|
||||
const confirmedHarness = params.selectHarnessForPreparedAttempts(
|
||||
params.getEffectiveModel(),
|
||||
preparedAuthAttempts,
|
||||
);
|
||||
if (confirmedHarness.id !== params.getAgentHarness().id) {
|
||||
throw new Error(
|
||||
`Prepared auth route did not converge on one agent harness for ${params.provider}/${params.modelId}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
params.markStage?.("harness");
|
||||
|
||||
return {
|
||||
usesOpenAIAuthRouting,
|
||||
attemptAuthProfileStore,
|
||||
lockedProfileId,
|
||||
preferredProfileId,
|
||||
providerUsesProfileScopedModelMetadata,
|
||||
materializeAuthPlan,
|
||||
materializeAuthPlanUncached,
|
||||
preparedAuthAttempts,
|
||||
activePreparedAuthPlan,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import type { Model } from "../../../llm/types.js";
|
||||
import {
|
||||
selectAgentHarness,
|
||||
selectAgentHarnessForPreparedModelProviders,
|
||||
type AgentHarnessPreparedModelProvider,
|
||||
} from "../../harness/selection.js";
|
||||
import {
|
||||
resolveAgentHarnessPreparedAuthSupport,
|
||||
resolveAgentHarnessPreparedRouteSupport,
|
||||
} from "../../harness/support.js";
|
||||
import type { AgentHarness } from "../../harness/types.js";
|
||||
import { resolveContextConfigProviderForRuntime } from "../../openai-routing.js";
|
||||
import type { PreparedAgentRuntimeAuthAttempt } from "../../runtime-plan/prepare-auth.js";
|
||||
import type { AgentRuntimeAuthPlan } from "../../runtime-plan/types.js";
|
||||
import type { RunEmbeddedAgentParams } from "./params.js";
|
||||
import { resolveEmbeddedRuntimeModelPolicy } from "./setup.js";
|
||||
|
||||
type HarnessSelectionContext = {
|
||||
runParams: RunEmbeddedAgentParams;
|
||||
provider: string;
|
||||
modelId: string;
|
||||
requestStreamTransportOverrides?: "present";
|
||||
nativeModelOwnedHarnessId?: string;
|
||||
};
|
||||
|
||||
export function resolveEmbeddedRunEffectiveModel(
|
||||
params: HarnessSelectionContext & {
|
||||
modelConfigProvider: string;
|
||||
agentHarnessId: string;
|
||||
runtimeModel: Model;
|
||||
nativeModelOwned: boolean;
|
||||
},
|
||||
) {
|
||||
return resolveEmbeddedRuntimeModelPolicy({
|
||||
cfg: params.runParams.config,
|
||||
provider: params.provider,
|
||||
contextConfigProvider: resolveContextConfigProviderForRuntime({
|
||||
provider: params.modelConfigProvider,
|
||||
runtimeId: params.agentHarnessId,
|
||||
config: params.runParams.config,
|
||||
}),
|
||||
modelId: params.modelId,
|
||||
runtimeModel: params.runtimeModel,
|
||||
nativeModelOwned: params.nativeModelOwned,
|
||||
});
|
||||
}
|
||||
|
||||
function buildHarnessModelProvider(
|
||||
params: HarnessSelectionContext & {
|
||||
model: Model;
|
||||
plan?: AgentRuntimeAuthPlan;
|
||||
preparedAuthAttempt?: PreparedAgentRuntimeAuthAttempt;
|
||||
},
|
||||
): AgentHarnessPreparedModelProvider {
|
||||
const route = params.plan?.modelRoute;
|
||||
const routeSupport = resolveAgentHarnessPreparedRouteSupport(params.plan);
|
||||
const requestTransportOverrides =
|
||||
params.requestStreamTransportOverrides ?? routeSupport.requestTransportOverrides;
|
||||
return {
|
||||
api: route?.api ?? params.model.api,
|
||||
baseUrl: route?.baseUrl ?? params.model.baseUrl,
|
||||
...(requestTransportOverrides ? { requestTransportOverrides } : {}),
|
||||
...(routeSupport.runtimePolicy ? { runtimePolicy: routeSupport.runtimePolicy } : {}),
|
||||
...(params.plan
|
||||
? {
|
||||
preparedAuth: resolveAgentHarnessPreparedAuthSupport({
|
||||
plan: params.plan,
|
||||
...(params.preparedAuthAttempt?.kind === "profile" ||
|
||||
params.preparedAuthAttempt?.kind === "direct"
|
||||
? { source: params.preparedAuthAttempt.kind }
|
||||
: {}),
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
function assertPinnedHarness(
|
||||
nativeModelOwnedHarnessId: string | undefined,
|
||||
selected: AgentHarness,
|
||||
subject: string,
|
||||
): void {
|
||||
if (nativeModelOwnedHarnessId && selected.id !== nativeModelOwnedHarnessId) {
|
||||
throw new Error(
|
||||
`${subject} changed the session-pinned agent harness from "${nativeModelOwnedHarnessId}" to "${selected.id}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function selectEmbeddedRunHarness(
|
||||
params: HarnessSelectionContext & {
|
||||
model: Model;
|
||||
plan?: AgentRuntimeAuthPlan;
|
||||
preparedAuthAttempt?: PreparedAgentRuntimeAuthAttempt;
|
||||
},
|
||||
): AgentHarness {
|
||||
const selected = selectAgentHarness({
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
modelProvider: buildHarnessModelProvider(params),
|
||||
config: params.runParams.config,
|
||||
agentId: params.runParams.agentId,
|
||||
sessionKey: params.runParams.sessionKey,
|
||||
agentHarnessId: params.runParams.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: params.runParams.agentHarnessRuntimeOverride,
|
||||
});
|
||||
assertPinnedHarness(params.nativeModelOwnedHarnessId, selected, "Prepared model route");
|
||||
return selected;
|
||||
}
|
||||
|
||||
export function selectEmbeddedRunHarnessForPreparedAttempts(
|
||||
params: HarnessSelectionContext & {
|
||||
model: Model;
|
||||
attempts: readonly PreparedAgentRuntimeAuthAttempt[];
|
||||
},
|
||||
): AgentHarness {
|
||||
const selected = selectAgentHarnessForPreparedModelProviders({
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
modelProviders: params.attempts.map((attempt) => {
|
||||
const route = attempt.plan.modelRoute;
|
||||
const model = route
|
||||
? { ...params.model, api: route.api, baseUrl: route.baseUrl }
|
||||
: params.model;
|
||||
return buildHarnessModelProvider({
|
||||
...params,
|
||||
model,
|
||||
plan: attempt.plan,
|
||||
preparedAuthAttempt: attempt,
|
||||
});
|
||||
}),
|
||||
config: params.runParams.config,
|
||||
agentId: params.runParams.agentId,
|
||||
sessionKey: params.runParams.sessionKey,
|
||||
agentHarnessId: params.runParams.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: params.runParams.agentHarnessRuntimeOverride,
|
||||
});
|
||||
assertPinnedHarness(params.nativeModelOwnedHarnessId, selected, "Prepared auth routes");
|
||||
return selected;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
import { FailoverError } from "../../failover-error.js";
|
||||
import { ensureSelectedAgentHarnessPlugin } from "../../harness/runtime-plugin.js";
|
||||
import { selectAgentHarness } from "../../harness/selection.js";
|
||||
import { ensureOpenClawModelsJson } from "../../models-config.js";
|
||||
import { resolveSelectedOpenAIRuntimeProvider } from "../../openai-routing.js";
|
||||
import { createEmptyAgentDiscoveryStores, resolveModelAsync } from "../model.js";
|
||||
import type { RunEmbeddedAgentParams } from "./params.js";
|
||||
import { resolveRequestStreamTransportOverrides } from "./runtime-resolution.js";
|
||||
import {
|
||||
buildBeforeModelResolveAttachments,
|
||||
createNativeModelOwnedRuntimeModel,
|
||||
resolveHookModelSelection,
|
||||
resolveNativeModelOwnedHarnessId,
|
||||
} from "./setup.js";
|
||||
|
||||
export async function resolveEmbeddedRunModelSetup(params: {
|
||||
runParams: RunEmbeddedAgentParams;
|
||||
provider: string;
|
||||
modelId: string;
|
||||
agentDir: string;
|
||||
workspaceDir: string;
|
||||
globalLane: string;
|
||||
hookRunner: Parameters<typeof resolveHookModelSelection>[0]["hookRunner"];
|
||||
hookContext: Parameters<typeof resolveHookModelSelection>[0]["hookContext"];
|
||||
onHooksResolved: () => void;
|
||||
}) {
|
||||
const runParams = params.runParams;
|
||||
const hookSelection = await resolveHookModelSelection({
|
||||
prompt: runParams.prompt,
|
||||
attachments: buildBeforeModelResolveAttachments(runParams.images),
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
modelSelectionLocked: runParams.modelSelectionLocked,
|
||||
hookRunner: params.hookRunner,
|
||||
hookContext: params.hookContext,
|
||||
});
|
||||
const modelSelectionChangedByHook =
|
||||
hookSelection.provider !== params.provider || hookSelection.modelId !== params.modelId;
|
||||
let provider = hookSelection.provider;
|
||||
const modelId = hookSelection.modelId;
|
||||
const requestedModelId = modelId;
|
||||
const requestStreamTransportOverrides = resolveRequestStreamTransportOverrides(
|
||||
runParams.streamParams,
|
||||
);
|
||||
params.onHooksResolved();
|
||||
|
||||
await ensureSelectedAgentHarnessPlugin({
|
||||
provider,
|
||||
modelId,
|
||||
config: runParams.config,
|
||||
agentId: runParams.agentId,
|
||||
sessionKey: runParams.sessionKey,
|
||||
agentHarnessId: runParams.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: runParams.agentHarnessRuntimeOverride,
|
||||
requestTransportOverrides: requestStreamTransportOverrides,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
const agentHarness = selectAgentHarness({
|
||||
provider,
|
||||
modelId,
|
||||
...(requestStreamTransportOverrides
|
||||
? {
|
||||
modelProvider: {
|
||||
requestTransportOverrides: requestStreamTransportOverrides,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
config: runParams.config,
|
||||
agentId: runParams.agentId,
|
||||
sessionKey: runParams.sessionKey,
|
||||
agentHarnessId: runParams.agentHarnessId,
|
||||
agentHarnessRuntimeOverride: runParams.agentHarnessRuntimeOverride,
|
||||
});
|
||||
const pluginHarnessOwnsTransport = agentHarness.id !== "openclaw";
|
||||
const expectedHarnessArtifact = runParams.expectedAgentHarnessRuntimeArtifact;
|
||||
if (expectedHarnessArtifact && expectedHarnessArtifact.harnessId !== agentHarness.id) {
|
||||
throw new Error(
|
||||
`Verified inference requires agent harness ${expectedHarnessArtifact.harnessId}, but ${agentHarness.id} was selected.`,
|
||||
);
|
||||
}
|
||||
if (expectedHarnessArtifact && !agentHarness.runtimeArtifact) {
|
||||
throw new Error(
|
||||
`Agent harness ${agentHarness.id} cannot attest the verified inference runtime artifact.`,
|
||||
);
|
||||
}
|
||||
|
||||
const nativeModelOwnedHarnessId = resolveNativeModelOwnedHarnessId({
|
||||
agentHarnessId: runParams.agentHarnessId,
|
||||
modelSelectionLocked: runParams.modelSelectionLocked,
|
||||
selectedHarnessId: agentHarness.id,
|
||||
});
|
||||
const nativeModelOwned = nativeModelOwnedHarnessId !== undefined;
|
||||
const modelConfigProvider = provider;
|
||||
let resolvedModelProvider = provider;
|
||||
let firstModelResolution: Awaited<ReturnType<typeof resolveModelAsync>> | undefined;
|
||||
let modelResolution: Awaited<ReturnType<typeof resolveModelAsync>> | undefined;
|
||||
if (nativeModelOwned) {
|
||||
modelResolution = {
|
||||
model: createNativeModelOwnedRuntimeModel({ provider, modelId }),
|
||||
...createEmptyAgentDiscoveryStores(),
|
||||
};
|
||||
} else {
|
||||
const selectedRuntimeProvider = resolveSelectedOpenAIRuntimeProvider({
|
||||
provider,
|
||||
harnessRuntime: agentHarness.id,
|
||||
agentHarnessId: agentHarness.id,
|
||||
authProfileProvider: runParams.authProfileId?.split(":", 1)[0],
|
||||
authProfileId: runParams.authProfileId,
|
||||
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,
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: runParams.authProfileId,
|
||||
},
|
||||
);
|
||||
firstModelResolution ??= candidateResolution;
|
||||
if (candidateResolution.model) {
|
||||
resolvedModelProvider = candidateProvider;
|
||||
modelResolution = candidateResolution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!modelResolution && pluginHarnessOwnsTransport) {
|
||||
modelResolution = firstModelResolution;
|
||||
}
|
||||
if (!modelResolution) {
|
||||
await ensureOpenClawModelsJson(runParams.config, params.agentDir, {
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
for (const candidateProvider of modelResolutionProviders) {
|
||||
const candidateResolution = await resolveModelAsync(
|
||||
candidateProvider,
|
||||
modelId,
|
||||
params.agentDir,
|
||||
runParams.config,
|
||||
{
|
||||
workspaceDir: params.workspaceDir,
|
||||
authProfileId: runParams.authProfileId,
|
||||
allowBundledStaticCatalogFallback: true,
|
||||
},
|
||||
);
|
||||
firstModelResolution ??= candidateResolution;
|
||||
if (candidateResolution.model) {
|
||||
resolvedModelProvider = candidateProvider;
|
||||
modelResolution = candidateResolution;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
modelResolution ??= firstModelResolution;
|
||||
}
|
||||
if (!modelResolution) {
|
||||
throw new FailoverError(`Unknown model: ${provider}/${modelId}`, {
|
||||
reason: "model_not_found",
|
||||
provider,
|
||||
model: modelId,
|
||||
sessionId: runParams.sessionId,
|
||||
lane: params.globalLane,
|
||||
});
|
||||
}
|
||||
provider = resolvedModelProvider;
|
||||
const { model, error, authStorage, modelRegistry } = modelResolution;
|
||||
if (!model) {
|
||||
throw new FailoverError(error ?? `Unknown model: ${provider}/${modelId}`, {
|
||||
reason: "model_not_found",
|
||||
provider,
|
||||
model: modelId,
|
||||
sessionId: runParams.sessionId,
|
||||
lane: params.globalLane,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
provider,
|
||||
modelId,
|
||||
requestedModelId,
|
||||
modelSelectionChangedByHook,
|
||||
beforeAgentStartResult: hookSelection.beforeAgentStartResult,
|
||||
requestStreamTransportOverrides,
|
||||
expectedHarnessArtifact,
|
||||
agentHarness,
|
||||
pluginHarnessOwnsTransport,
|
||||
nativeModelOwnedHarnessId,
|
||||
nativeModelOwned,
|
||||
modelConfigProvider,
|
||||
model,
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user