mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 03:30:25 +00:00
refactor: split models-config provider normalization helper
This commit is contained in:
133
src/agents/models-config.providers.normalize.ts
Normal file
133
src/agents/models-config.providers.normalize.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
||||
import {
|
||||
normalizeProviderSpecificConfig,
|
||||
resolveProviderConfigApiKeyResolver,
|
||||
} from "./models-config.providers.policy.js";
|
||||
import type { ProviderConfig, SecretDefaults } from "./models-config.providers.secrets.js";
|
||||
import {
|
||||
normalizeConfiguredProviderApiKey,
|
||||
normalizeHeaderValues,
|
||||
normalizeResolvedEnvApiKey,
|
||||
resolveApiKeyFromProfiles,
|
||||
resolveMissingProviderApiKey,
|
||||
} from "./models-config.providers.secrets.js";
|
||||
import { enforceSourceManagedProviderSecrets } from "./models-config.providers.source-managed.js";
|
||||
|
||||
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
|
||||
|
||||
export function normalizeProviders(params: {
|
||||
providers: ModelsConfig["providers"];
|
||||
agentDir: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
secretDefaults?: SecretDefaults;
|
||||
sourceProviders?: ModelsConfig["providers"];
|
||||
sourceSecretDefaults?: SecretDefaults;
|
||||
secretRefManagedProviders?: Set<string>;
|
||||
}): ModelsConfig["providers"] {
|
||||
const { providers } = params;
|
||||
if (!providers) {
|
||||
return providers;
|
||||
}
|
||||
const env = params.env ?? process.env;
|
||||
const authStore = ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
let mutated = false;
|
||||
const next: Record<string, ProviderConfig> = {};
|
||||
|
||||
for (const [key, provider] of Object.entries(providers)) {
|
||||
const normalizedKey = key.trim();
|
||||
if (!normalizedKey) {
|
||||
mutated = true;
|
||||
continue;
|
||||
}
|
||||
if (normalizedKey !== key) {
|
||||
mutated = true;
|
||||
}
|
||||
let normalizedProvider = provider;
|
||||
const normalizedHeaders = normalizeHeaderValues({
|
||||
headers: normalizedProvider.headers,
|
||||
secretDefaults: params.secretDefaults,
|
||||
});
|
||||
if (normalizedHeaders.mutated) {
|
||||
mutated = true;
|
||||
normalizedProvider = { ...normalizedProvider, headers: normalizedHeaders.headers };
|
||||
}
|
||||
const profileApiKey = resolveApiKeyFromProfiles({
|
||||
provider: normalizedKey,
|
||||
store: authStore,
|
||||
env,
|
||||
});
|
||||
const providerWithConfiguredApiKey = normalizeConfiguredProviderApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
secretDefaults: params.secretDefaults,
|
||||
profileApiKey,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
if (providerWithConfiguredApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerWithConfiguredApiKey;
|
||||
}
|
||||
|
||||
// Reverse-lookup: if apiKey looks like a resolved secret value (not an env
|
||||
// var name), check whether it matches the canonical env var for this provider.
|
||||
// This prevents resolveConfigEnvVars()-resolved secrets from being persisted
|
||||
// to models.json as plaintext. (Fixes #38757)
|
||||
const providerWithResolvedEnvApiKey = normalizeResolvedEnvApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
env,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
if (providerWithResolvedEnvApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerWithResolvedEnvApiKey;
|
||||
}
|
||||
|
||||
const providerWithApiKey = resolveMissingProviderApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
env,
|
||||
profileApiKey,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
providerApiKeyResolver: resolveProviderConfigApiKeyResolver(normalizedKey),
|
||||
});
|
||||
if (providerWithApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerWithApiKey;
|
||||
}
|
||||
|
||||
const providerSpecificNormalized = normalizeProviderSpecificConfig(
|
||||
normalizedKey,
|
||||
normalizedProvider,
|
||||
);
|
||||
if (providerSpecificNormalized !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerSpecificNormalized;
|
||||
}
|
||||
|
||||
const existing = next[normalizedKey];
|
||||
if (existing) {
|
||||
// Keep deterministic behavior if users accidentally define duplicate
|
||||
// provider keys that only differ by surrounding whitespace.
|
||||
mutated = true;
|
||||
next[normalizedKey] = {
|
||||
...existing,
|
||||
...normalizedProvider,
|
||||
models: normalizedProvider.models ?? existing.models,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
next[normalizedKey] = normalizedProvider;
|
||||
}
|
||||
|
||||
const normalizedProviders = mutated ? next : providers;
|
||||
return enforceSourceManagedProviderSecrets({
|
||||
providers: normalizedProviders,
|
||||
sourceProviders: params.sourceProviders,
|
||||
sourceSecretDefaults: params.sourceSecretDefaults,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
}
|
||||
@@ -1,20 +1,6 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
||||
export * from "./models-config.providers.static.js";
|
||||
export { resolveImplicitProviders } from "./models-config.providers.implicit.js";
|
||||
import {
|
||||
normalizeProviderSpecificConfig,
|
||||
resolveProviderConfigApiKeyResolver,
|
||||
} from "./models-config.providers.policy.js";
|
||||
import type { ProviderConfig, SecretDefaults } from "./models-config.providers.secrets.js";
|
||||
import {
|
||||
normalizeConfiguredProviderApiKey,
|
||||
normalizeHeaderValues,
|
||||
normalizeResolvedEnvApiKey,
|
||||
resolveApiKeyFromProfiles,
|
||||
resolveMissingProviderApiKey,
|
||||
} from "./models-config.providers.secrets.js";
|
||||
import { enforceSourceManagedProviderSecrets } from "./models-config.providers.source-managed.js";
|
||||
export { normalizeProviders } from "./models-config.providers.normalize.js";
|
||||
export type {
|
||||
ProfileApiKeyResolution,
|
||||
ProviderApiKeyResolver,
|
||||
@@ -22,126 +8,8 @@ export type {
|
||||
ProviderConfig,
|
||||
SecretDefaults,
|
||||
} from "./models-config.providers.secrets.js";
|
||||
export { enforceSourceManagedProviderSecrets };
|
||||
export { applyNativeStreamingUsageCompat } from "./models-config.providers.policy.js";
|
||||
export { enforceSourceManagedProviderSecrets } from "./models-config.providers.source-managed.js";
|
||||
export { resolveOllamaApiBase } from "../plugin-sdk/ollama-surface.js";
|
||||
export { normalizeGoogleModelId } from "../plugin-sdk/google.js";
|
||||
export { normalizeXaiModelId } from "../plugin-sdk/xai.js";
|
||||
|
||||
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
|
||||
|
||||
export function normalizeProviders(params: {
|
||||
providers: ModelsConfig["providers"];
|
||||
agentDir: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
secretDefaults?: SecretDefaults;
|
||||
sourceProviders?: ModelsConfig["providers"];
|
||||
sourceSecretDefaults?: SecretDefaults;
|
||||
secretRefManagedProviders?: Set<string>;
|
||||
}): ModelsConfig["providers"] {
|
||||
const { providers } = params;
|
||||
if (!providers) {
|
||||
return providers;
|
||||
}
|
||||
const env = params.env ?? process.env;
|
||||
const authStore = ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
let mutated = false;
|
||||
const next: Record<string, ProviderConfig> = {};
|
||||
|
||||
for (const [key, provider] of Object.entries(providers)) {
|
||||
const normalizedKey = key.trim();
|
||||
if (!normalizedKey) {
|
||||
mutated = true;
|
||||
continue;
|
||||
}
|
||||
if (normalizedKey !== key) {
|
||||
mutated = true;
|
||||
}
|
||||
let normalizedProvider = provider;
|
||||
const normalizedHeaders = normalizeHeaderValues({
|
||||
headers: normalizedProvider.headers,
|
||||
secretDefaults: params.secretDefaults,
|
||||
});
|
||||
if (normalizedHeaders.mutated) {
|
||||
mutated = true;
|
||||
normalizedProvider = { ...normalizedProvider, headers: normalizedHeaders.headers };
|
||||
}
|
||||
const profileApiKey = resolveApiKeyFromProfiles({
|
||||
provider: normalizedKey,
|
||||
store: authStore,
|
||||
env,
|
||||
});
|
||||
const providerWithConfiguredApiKey = normalizeConfiguredProviderApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
secretDefaults: params.secretDefaults,
|
||||
profileApiKey,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
if (providerWithConfiguredApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerWithConfiguredApiKey;
|
||||
}
|
||||
|
||||
// Reverse-lookup: if apiKey looks like a resolved secret value (not an env
|
||||
// var name), check whether it matches the canonical env var for this provider.
|
||||
// This prevents resolveConfigEnvVars()-resolved secrets from being persisted
|
||||
// to models.json as plaintext. (Fixes #38757)
|
||||
const providerWithResolvedEnvApiKey = normalizeResolvedEnvApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
env,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
if (providerWithResolvedEnvApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerWithResolvedEnvApiKey;
|
||||
}
|
||||
|
||||
const providerWithApiKey = resolveMissingProviderApiKey({
|
||||
providerKey: normalizedKey,
|
||||
provider: normalizedProvider,
|
||||
env,
|
||||
profileApiKey,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
providerApiKeyResolver: resolveProviderConfigApiKeyResolver(normalizedKey),
|
||||
});
|
||||
if (providerWithApiKey !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerWithApiKey;
|
||||
}
|
||||
|
||||
const providerSpecificNormalized = normalizeProviderSpecificConfig(
|
||||
normalizedKey,
|
||||
normalizedProvider,
|
||||
);
|
||||
if (providerSpecificNormalized !== normalizedProvider) {
|
||||
mutated = true;
|
||||
normalizedProvider = providerSpecificNormalized;
|
||||
}
|
||||
|
||||
const existing = next[normalizedKey];
|
||||
if (existing) {
|
||||
// Keep deterministic behavior if users accidentally define duplicate
|
||||
// provider keys that only differ by surrounding whitespace.
|
||||
mutated = true;
|
||||
next[normalizedKey] = {
|
||||
...existing,
|
||||
...normalizedProvider,
|
||||
models: normalizedProvider.models ?? existing.models,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
next[normalizedKey] = normalizedProvider;
|
||||
}
|
||||
|
||||
const normalizedProviders = mutated ? next : providers;
|
||||
return enforceSourceManagedProviderSecrets({
|
||||
providers: normalizedProviders,
|
||||
sourceProviders: params.sourceProviders,
|
||||
sourceSecretDefaults: params.sourceSecretDefaults,
|
||||
secretRefManagedProviders: params.secretRefManagedProviders,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user