mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
docs: document model selection reconciliation
This commit is contained in:
@@ -3,6 +3,7 @@ import { resolveRuntimeCliBackends } from "../plugins/cli-backends.runtime.js";
|
||||
import { resolvePluginSetupCliBackendDescriptor } from "../plugins/setup-registry.runtime.js";
|
||||
import { normalizeProviderId } from "./model-selection-normalize.js";
|
||||
|
||||
/** Return true when a provider id resolves to a configured or plugin CLI backend. */
|
||||
export function isCliProvider(provider: string, cfg?: OpenClawConfig): boolean {
|
||||
const normalized = normalizeProviderId(provider);
|
||||
const backends = cfg?.agents?.defaults?.cliBackends ?? {};
|
||||
|
||||
@@ -28,6 +28,8 @@ import {
|
||||
parseModelRef,
|
||||
} from "./model-selection-normalize.js";
|
||||
|
||||
// Shared model-selection helpers for config aliases, allowlists, provider
|
||||
// inference, and configured catalog rows used by CLI and runtime selectors.
|
||||
let log: ReturnType<typeof createSubsystemLogger> | null = null;
|
||||
|
||||
function getLog(): ReturnType<typeof createSubsystemLogger> {
|
||||
@@ -106,6 +108,8 @@ function createModelManifestPluginContext(params: {
|
||||
return {
|
||||
peek: () => manifestPlugins,
|
||||
get: () => {
|
||||
// Manifest metadata can touch plugin registries. Defer that work until a
|
||||
// path actually needs plugin/provider normalization.
|
||||
if (!resolved) {
|
||||
manifestPlugins = resolveManifestPluginsForModelIdNormalization(params);
|
||||
resolved = true;
|
||||
@@ -173,6 +177,7 @@ function mergeModelCatalogEntries(params: {
|
||||
return merged;
|
||||
}
|
||||
|
||||
/** Infer a unique provider for a bare model from configured model rows. */
|
||||
export function inferUniqueProviderFromConfiguredModels(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
@@ -252,6 +257,7 @@ export function inferUniqueProviderFromConfiguredModels(
|
||||
return providers.values().next().value;
|
||||
}
|
||||
|
||||
/** Infer a unique provider for a bare model from a provider catalog. */
|
||||
export function inferUniqueProviderFromCatalog(params: {
|
||||
catalog: readonly ModelCatalogEntry[];
|
||||
model: string;
|
||||
@@ -281,6 +287,7 @@ export function inferUniqueProviderFromCatalog(params: {
|
||||
return providers.size === 1 ? providers.values().next().value : undefined;
|
||||
}
|
||||
|
||||
/** Resolve the provider used when a model string omits provider/id syntax. */
|
||||
export function resolveBareModelDefaultProvider(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
@@ -346,6 +353,7 @@ function resolveConfiguredOpenRouterCompatFreeRef(
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Resolve OpenRouter compatibility aliases such as openrouter:auto/free. */
|
||||
export function resolveConfiguredOpenRouterCompatAlias(
|
||||
params: {
|
||||
cfg?: OpenClawConfig;
|
||||
@@ -475,6 +483,7 @@ function resolveExactConfiguredProviderRef(
|
||||
return normalizeExactConfiguredProviderRef(exactConfigured, params);
|
||||
}
|
||||
|
||||
/** Normalize a configured allowlist entry into the canonical provider/model key. */
|
||||
export function resolveAllowlistModelKey(
|
||||
params: {
|
||||
cfg?: OpenClawConfig;
|
||||
@@ -498,6 +507,7 @@ export function resolveAllowlistModelKey(
|
||||
return modelKey(parsed.provider, parsed.model);
|
||||
}
|
||||
|
||||
/** Build the exact configured model keys that constrain model visibility. */
|
||||
export function buildConfiguredAllowlistKeys(
|
||||
params: {
|
||||
cfg: OpenClawConfig | undefined;
|
||||
@@ -549,6 +559,8 @@ function buildModelAliasIndexWithManifestContext(
|
||||
const manifestPlugins = params.manifestPluginContext.get();
|
||||
|
||||
for (const { keyRaw, alias } of aliasCandidates) {
|
||||
// Aliases point at configured model keys, not arbitrary model ids, so profile
|
||||
// suffixes and configured-provider normalization happen on the target key.
|
||||
const parsed = parseModelRefWithCompatAlias({
|
||||
cfg: params.cfg,
|
||||
raw: keyRaw,
|
||||
@@ -571,6 +583,7 @@ function buildModelAliasIndexWithManifestContext(
|
||||
return { byAlias, byKey };
|
||||
}
|
||||
|
||||
/** Build lookup maps from user-facing aliases to normalized model refs. */
|
||||
export function buildModelAliasIndex(params: BuildModelAliasIndexParams): ModelAliasIndex {
|
||||
return buildModelAliasIndexWithManifestContext({
|
||||
cfg: params.cfg,
|
||||
@@ -719,6 +732,7 @@ export function resolveModelRefFromString(
|
||||
return { ref: parsed };
|
||||
}
|
||||
|
||||
/** Resolve the default configured model ref, including aliases and fallback provider rows. */
|
||||
export function resolveConfiguredModelRef(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
@@ -742,6 +756,8 @@ export function resolveConfiguredModelRef(
|
||||
? (exactAliasCandidate ?? strippedAliasCandidate)
|
||||
: undefined;
|
||||
if (profileAliasCandidate) {
|
||||
// Auth-profile suffixes are not part of alias matching; resolve the alias
|
||||
// target while preserving the provider/model semantics of the key.
|
||||
const aliasRef = parseModelRefWithCompatAlias({
|
||||
cfg: params.cfg,
|
||||
raw: profileAliasCandidate.keyRaw,
|
||||
@@ -828,6 +844,8 @@ export function resolveConfiguredModelRef(
|
||||
(!inferredProvider || inferredProvider !== "openai") &&
|
||||
hasConfiguredRowsNeedingManifestLookup(params.cfg, params.defaultProvider)
|
||||
) {
|
||||
// Non-default provider rows may normalize through plugin manifests. Avoid
|
||||
// that heavier lookup unless the cheap configured pass was ambiguous.
|
||||
inferredProviderManifestPlugins = manifestPluginContext.get();
|
||||
inferredProvider =
|
||||
inferUniqueProviderFromConfiguredModels({
|
||||
@@ -883,6 +901,7 @@ export function resolveConfiguredModelRef(
|
||||
return { provider: params.defaultProvider, model: params.defaultModel };
|
||||
}
|
||||
|
||||
/** Build allowed model keys/catalog entries after provider wildcards and fallbacks. */
|
||||
export function buildAllowedModelSetWithFallbacks(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
@@ -1006,6 +1025,8 @@ export function buildAllowedModelSetWithFallbacks(
|
||||
!findModelCatalogEntry(catalog, { provider: parsed.provider, modelId: parsed.model }) &&
|
||||
!syntheticCatalogEntries.has(key)
|
||||
) {
|
||||
// Config can allow a model before it appears in live provider catalogs.
|
||||
// Synthetic entries keep UI/model switchers aligned with that allowlist.
|
||||
syntheticCatalogEntries.set(key, buildSyntheticAllowedCatalogEntry({ parsed, metadata }));
|
||||
}
|
||||
};
|
||||
@@ -1059,6 +1080,7 @@ export function buildAllowedModelSetWithFallbacks(
|
||||
return { allowAny: false, allowedCatalog, allowedKeys };
|
||||
}
|
||||
|
||||
/** Status of a candidate model against catalog and configured allowlist state. */
|
||||
export type ModelRefStatus = {
|
||||
key: string;
|
||||
inCatalog: boolean;
|
||||
@@ -1119,6 +1141,7 @@ export function getModelRefStatusWithFallbackModels(
|
||||
});
|
||||
}
|
||||
|
||||
/** Resolve a requested model string only if it is allowed by the supplied status check. */
|
||||
export function resolveAllowedModelRefFromAliasIndex(
|
||||
params: {
|
||||
cfg: OpenClawConfig;
|
||||
@@ -1160,6 +1183,7 @@ export function resolveAllowedModelRefFromAliasIndex(
|
||||
return { ref: resolved.ref, key: status.key };
|
||||
}
|
||||
|
||||
/** True when config contains provider model rows that should seed catalogs. */
|
||||
export function hasConfiguredProviderModelRows(cfg: OpenClawConfig): boolean {
|
||||
const providers = cfg.models?.providers;
|
||||
if (!providers || typeof providers !== "object") {
|
||||
@@ -1239,6 +1263,7 @@ function resolveConfiguredModelManifestPlugins(params: {
|
||||
}).plugins;
|
||||
}
|
||||
|
||||
/** Build catalog entries from configured provider model rows. */
|
||||
export function buildConfiguredModelCatalog(params: {
|
||||
cfg: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
|
||||
@@ -15,8 +15,11 @@ import {
|
||||
type SubagentLifecycleEndedReason,
|
||||
} from "./subagent-lifecycle-events.js";
|
||||
|
||||
// Reconcile subagent lifecycle events with persisted session store state. This
|
||||
// lets parent sessions announce completion even if registry updates arrive late.
|
||||
export type SubagentSessionStoreCache = Map<string, Record<string, SessionEntry>>;
|
||||
|
||||
/** Completion inferred from the child session store. */
|
||||
export type SubagentSessionCompletion = {
|
||||
startedAt?: number;
|
||||
endedAt: number;
|
||||
@@ -68,6 +71,7 @@ function findSessionEntryByKey(store: Record<string, SessionEntry>, sessionKey:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Load a child session entry using the agent-specific session store path. */
|
||||
export function loadSubagentSessionEntry(params: {
|
||||
childSessionKey: string;
|
||||
storeCache?: SubagentSessionStoreCache;
|
||||
@@ -88,6 +92,7 @@ export function loadSubagentSessionEntry(params: {
|
||||
return findSessionEntryByKey(store, key);
|
||||
}
|
||||
|
||||
/** Convert persisted session status into a subagent completion outcome. */
|
||||
export function resolveCompletionFromSessionEntry(
|
||||
sessionEntry: SessionEntry | undefined,
|
||||
fallbackEndedAt: number,
|
||||
@@ -158,6 +163,7 @@ export function resolveCompletionFromSessionEntry(
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Resolve child completion by reading its persisted session entry. */
|
||||
export function resolveSubagentSessionCompletion(params: {
|
||||
childSessionKey: string;
|
||||
fallbackEndedAt: number;
|
||||
@@ -176,6 +182,7 @@ export function resolveSubagentSessionCompletion(params: {
|
||||
);
|
||||
}
|
||||
|
||||
/** Resolve a fresh child session start time for lifecycle reconciliation. */
|
||||
export function resolveSubagentSessionStartedAt(params: {
|
||||
childSessionKey: string;
|
||||
notBeforeMs?: number;
|
||||
|
||||
Reference in New Issue
Block a user