refactor: split models-config source-managed helpers

This commit is contained in:
Peter Steinberger 2026-03-28 00:52:20 +00:00
parent 7db79b04c6
commit 5c52824d3e
2 changed files with 155 additions and 149 deletions

View File

@ -0,0 +1,153 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveSecretInputRef } from "../config/types.secrets.js";
import { isRecord } from "../utils.js";
import {
resolveNonEnvSecretRefApiKeyMarker,
resolveNonEnvSecretRefHeaderValueMarker,
resolveEnvSecretRefHeaderValueMarker,
} from "./model-auth-markers.js";
import type { ProviderConfig, SecretDefaults } from "./models-config.providers.secrets.js";
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
function normalizeSourceProviderLookup(
providers: ModelsConfig["providers"] | undefined,
): Record<string, ProviderConfig> {
if (!providers) {
return {};
}
const out: Record<string, ProviderConfig> = {};
for (const [key, provider] of Object.entries(providers)) {
const normalizedKey = key.trim();
if (!normalizedKey || !isRecord(provider)) {
continue;
}
out[normalizedKey] = provider;
}
return out;
}
function resolveSourceManagedApiKeyMarker(params: {
sourceProvider: ProviderConfig | undefined;
sourceSecretDefaults: SecretDefaults | undefined;
}): string | undefined {
const sourceApiKeyRef = resolveSecretInputRef({
value: params.sourceProvider?.apiKey,
defaults: params.sourceSecretDefaults,
}).ref;
if (!sourceApiKeyRef || !sourceApiKeyRef.id.trim()) {
return undefined;
}
return sourceApiKeyRef.source === "env"
? sourceApiKeyRef.id.trim()
: resolveNonEnvSecretRefApiKeyMarker(sourceApiKeyRef.source);
}
function resolveSourceManagedHeaderMarkers(params: {
sourceProvider: ProviderConfig | undefined;
sourceSecretDefaults: SecretDefaults | undefined;
}): Record<string, string> {
const sourceHeaders = isRecord(params.sourceProvider?.headers)
? (params.sourceProvider.headers as Record<string, unknown>)
: undefined;
if (!sourceHeaders) {
return {};
}
const markers: Record<string, string> = {};
for (const [headerName, headerValue] of Object.entries(sourceHeaders)) {
const sourceHeaderRef = resolveSecretInputRef({
value: headerValue,
defaults: params.sourceSecretDefaults,
}).ref;
if (!sourceHeaderRef || !sourceHeaderRef.id.trim()) {
continue;
}
markers[headerName] =
sourceHeaderRef.source === "env"
? resolveEnvSecretRefHeaderValueMarker(sourceHeaderRef.id)
: resolveNonEnvSecretRefHeaderValueMarker(sourceHeaderRef.source);
}
return markers;
}
export function enforceSourceManagedProviderSecrets(params: {
providers: ModelsConfig["providers"];
sourceProviders: ModelsConfig["providers"] | undefined;
sourceSecretDefaults?: SecretDefaults;
secretRefManagedProviders?: Set<string>;
}): ModelsConfig["providers"] {
const { providers } = params;
if (!providers) {
return providers;
}
const sourceProvidersByKey = normalizeSourceProviderLookup(params.sourceProviders);
if (Object.keys(sourceProvidersByKey).length === 0) {
return providers;
}
let nextProviders: Record<string, ProviderConfig> | null = null;
for (const [providerKey, provider] of Object.entries(providers)) {
if (!isRecord(provider)) {
continue;
}
const sourceProvider = sourceProvidersByKey[providerKey.trim()];
if (!sourceProvider) {
continue;
}
let nextProvider = provider;
let providerMutated = false;
const sourceApiKeyMarker = resolveSourceManagedApiKeyMarker({
sourceProvider,
sourceSecretDefaults: params.sourceSecretDefaults,
});
if (sourceApiKeyMarker) {
params.secretRefManagedProviders?.add(providerKey.trim());
if (nextProvider.apiKey !== sourceApiKeyMarker) {
providerMutated = true;
nextProvider = {
...nextProvider,
apiKey: sourceApiKeyMarker,
};
}
}
const sourceHeaderMarkers = resolveSourceManagedHeaderMarkers({
sourceProvider,
sourceSecretDefaults: params.sourceSecretDefaults,
});
if (Object.keys(sourceHeaderMarkers).length > 0) {
const currentHeaders = isRecord(nextProvider.headers)
? (nextProvider.headers as Record<string, unknown>)
: undefined;
const nextHeaders = {
...(currentHeaders as Record<string, NonNullable<ProviderConfig["headers"]>[string]>),
};
let headersMutated = !currentHeaders;
for (const [headerName, marker] of Object.entries(sourceHeaderMarkers)) {
if (nextHeaders[headerName] === marker) {
continue;
}
headersMutated = true;
nextHeaders[headerName] = marker;
}
if (headersMutated) {
providerMutated = true;
nextProvider = {
...nextProvider,
headers: nextHeaders,
};
}
}
if (!providerMutated) {
continue;
}
if (!nextProviders) {
nextProviders = { ...providers };
}
nextProviders[providerKey] = nextProvider;
}
return nextProviders ?? providers;
}

View File

@ -1,5 +1,4 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveSecretInputRef } from "../config/types.secrets.js";
import { resolveBedrockConfigApiKey } from "../plugin-sdk/amazon-bedrock.js";
import { resolveAnthropicVertexConfigApiKey } from "../plugin-sdk/anthropic-vertex.js";
import {
@ -8,14 +7,8 @@ import {
} from "../plugin-sdk/google.js";
import { applyModelStudioNativeStreamingUsageCompat } from "../plugin-sdk/modelstudio.js";
import { applyMoonshotNativeStreamingUsageCompat } from "../plugin-sdk/moonshot.js";
import { isRecord } from "../utils.js";
import { ensureAuthProfileStore } from "./auth-profiles.js";
export * from "./models-config.providers.static.js";
import {
resolveNonEnvSecretRefApiKeyMarker,
resolveNonEnvSecretRefHeaderValueMarker,
resolveEnvSecretRefHeaderValueMarker,
} from "./model-auth-markers.js";
export { resolveImplicitProviders } from "./models-config.providers.implicit.js";
import type { ProviderConfig, SecretDefaults } from "./models-config.providers.secrets.js";
import {
@ -25,6 +18,7 @@ import {
resolveApiKeyFromProfiles,
resolveMissingProviderApiKey,
} from "./models-config.providers.secrets.js";
import { enforceSourceManagedProviderSecrets } from "./models-config.providers.source-managed.js";
export type {
ProfileApiKeyResolution,
ProviderApiKeyResolver,
@ -32,6 +26,7 @@ export type {
ProviderConfig,
SecretDefaults,
} from "./models-config.providers.secrets.js";
export { enforceSourceManagedProviderSecrets };
export { resolveOllamaApiBase } from "../plugin-sdk/ollama-surface.js";
export { normalizeGoogleModelId } from "../plugin-sdk/google.js";
export { normalizeXaiModelId } from "../plugin-sdk/xai.js";
@ -76,148 +71,6 @@ function normalizeProviderSpecificConfig(
return provider;
}
function normalizeSourceProviderLookup(
providers: ModelsConfig["providers"] | undefined,
): Record<string, ProviderConfig> {
if (!providers) {
return {};
}
const out: Record<string, ProviderConfig> = {};
for (const [key, provider] of Object.entries(providers)) {
const normalizedKey = key.trim();
if (!normalizedKey || !isRecord(provider)) {
continue;
}
out[normalizedKey] = provider;
}
return out;
}
function resolveSourceManagedApiKeyMarker(params: {
sourceProvider: ProviderConfig | undefined;
sourceSecretDefaults: SecretDefaults | undefined;
}): string | undefined {
const sourceApiKeyRef = resolveSecretInputRef({
value: params.sourceProvider?.apiKey,
defaults: params.sourceSecretDefaults,
}).ref;
if (!sourceApiKeyRef || !sourceApiKeyRef.id.trim()) {
return undefined;
}
return sourceApiKeyRef.source === "env"
? sourceApiKeyRef.id.trim()
: resolveNonEnvSecretRefApiKeyMarker(sourceApiKeyRef.source);
}
function resolveSourceManagedHeaderMarkers(params: {
sourceProvider: ProviderConfig | undefined;
sourceSecretDefaults: SecretDefaults | undefined;
}): Record<string, string> {
const sourceHeaders = isRecord(params.sourceProvider?.headers)
? (params.sourceProvider.headers as Record<string, unknown>)
: undefined;
if (!sourceHeaders) {
return {};
}
const markers: Record<string, string> = {};
for (const [headerName, headerValue] of Object.entries(sourceHeaders)) {
const sourceHeaderRef = resolveSecretInputRef({
value: headerValue,
defaults: params.sourceSecretDefaults,
}).ref;
if (!sourceHeaderRef || !sourceHeaderRef.id.trim()) {
continue;
}
markers[headerName] =
sourceHeaderRef.source === "env"
? resolveEnvSecretRefHeaderValueMarker(sourceHeaderRef.id)
: resolveNonEnvSecretRefHeaderValueMarker(sourceHeaderRef.source);
}
return markers;
}
export function enforceSourceManagedProviderSecrets(params: {
providers: ModelsConfig["providers"];
sourceProviders: ModelsConfig["providers"] | undefined;
sourceSecretDefaults?: SecretDefaults;
secretRefManagedProviders?: Set<string>;
}): ModelsConfig["providers"] {
const { providers } = params;
if (!providers) {
return providers;
}
const sourceProvidersByKey = normalizeSourceProviderLookup(params.sourceProviders);
if (Object.keys(sourceProvidersByKey).length === 0) {
return providers;
}
let nextProviders: Record<string, ProviderConfig> | null = null;
for (const [providerKey, provider] of Object.entries(providers)) {
if (!isRecord(provider)) {
continue;
}
const sourceProvider = sourceProvidersByKey[providerKey.trim()];
if (!sourceProvider) {
continue;
}
let nextProvider = provider;
let providerMutated = false;
const sourceApiKeyMarker = resolveSourceManagedApiKeyMarker({
sourceProvider,
sourceSecretDefaults: params.sourceSecretDefaults,
});
if (sourceApiKeyMarker) {
params.secretRefManagedProviders?.add(providerKey.trim());
if (nextProvider.apiKey !== sourceApiKeyMarker) {
providerMutated = true;
nextProvider = {
...nextProvider,
apiKey: sourceApiKeyMarker,
};
}
}
const sourceHeaderMarkers = resolveSourceManagedHeaderMarkers({
sourceProvider,
sourceSecretDefaults: params.sourceSecretDefaults,
});
if (Object.keys(sourceHeaderMarkers).length > 0) {
const currentHeaders = isRecord(nextProvider.headers)
? (nextProvider.headers as Record<string, unknown>)
: undefined;
const nextHeaders = {
...(currentHeaders as Record<string, NonNullable<ProviderConfig["headers"]>[string]>),
};
let headersMutated = !currentHeaders;
for (const [headerName, marker] of Object.entries(sourceHeaderMarkers)) {
if (nextHeaders[headerName] === marker) {
continue;
}
headersMutated = true;
nextHeaders[headerName] = marker;
}
if (headersMutated) {
providerMutated = true;
nextProvider = {
...nextProvider,
headers: nextHeaders,
};
}
}
if (!providerMutated) {
continue;
}
if (!nextProviders) {
nextProviders = { ...providers };
}
nextProviders[providerKey] = nextProvider;
}
return nextProviders ?? providers;
}
export function normalizeProviders(params: {
providers: ModelsConfig["providers"];
agentDir: string;