mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
refactor: fold implicit provider injection into resolver
This commit is contained in:
@@ -271,11 +271,14 @@ export async function resolveApiKeyForProvider(params: {
|
||||
export type EnvApiKeyResult = { apiKey: string; source: string };
|
||||
export type ModelAuthMode = "api-key" | "oauth" | "token" | "mixed" | "aws-sdk" | "unknown";
|
||||
|
||||
export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
|
||||
export function resolveEnvApiKey(
|
||||
provider: string,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): EnvApiKeyResult | null {
|
||||
const normalized = normalizeProviderId(provider);
|
||||
const applied = new Set(getShellEnvAppliedKeys());
|
||||
const pick = (envVar: string): EnvApiKeyResult | null => {
|
||||
const value = normalizeOptionalSecretInput(process.env[envVar]);
|
||||
const value = normalizeOptionalSecretInput(env[envVar]);
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("provider discovery auth marker guardrails", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
const providers = await resolveImplicitProviders({ agentDir, env: {} });
|
||||
expect(providers?.vllm?.apiKey).toBe(NON_ENV_SECRETREF_MARKER);
|
||||
const request = fetchMock.mock.calls[0]?.[1] as
|
||||
| { headers?: Record<string, string> }
|
||||
@@ -96,7 +96,7 @@ describe("provider discovery auth marker guardrails", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
const providers = await resolveImplicitProviders({ agentDir, env: {} });
|
||||
expect(providers?.huggingface?.apiKey).toBe(NON_ENV_SECRETREF_MARKER);
|
||||
const huggingfaceCalls = fetchMock.mock.calls.filter(([url]) =>
|
||||
String(url).includes("router.huggingface.co"),
|
||||
@@ -132,7 +132,7 @@ describe("provider discovery auth marker guardrails", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
await resolveImplicitProviders({ agentDir });
|
||||
await resolveImplicitProviders({ agentDir, env: {} });
|
||||
const vllmCall = fetchMock.mock.calls.find(([url]) => String(url).includes(":8000"));
|
||||
const request = vllmCall?.[1] as { headers?: Record<string, string> } | undefined;
|
||||
expect(request?.headers?.Authorization).toBe("Bearer ALLCAPS_SAMPLE");
|
||||
|
||||
@@ -403,8 +403,11 @@ function normalizeApiKeyConfig(value: string): string {
|
||||
return match?.[1] ?? trimmed;
|
||||
}
|
||||
|
||||
function resolveEnvApiKeyVarName(provider: string): string | undefined {
|
||||
const resolved = resolveEnvApiKey(provider);
|
||||
function resolveEnvApiKeyVarName(
|
||||
provider: string,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): string | undefined {
|
||||
const resolved = resolveEnvApiKey(provider, env);
|
||||
if (!resolved) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -470,6 +473,7 @@ function toDiscoveryApiKey(value: string | undefined): string | undefined {
|
||||
|
||||
function resolveApiKeyFromCredential(
|
||||
cred: ReturnType<typeof ensureAuthProfileStore>["profiles"][string] | undefined,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): ProfileApiKeyResolution | undefined {
|
||||
if (!cred) {
|
||||
return undefined;
|
||||
@@ -482,7 +486,7 @@ function resolveApiKeyFromCredential(
|
||||
return {
|
||||
apiKey: envVar,
|
||||
source: "env-ref",
|
||||
discoveryApiKey: toDiscoveryApiKey(process.env[envVar]),
|
||||
discoveryApiKey: toDiscoveryApiKey(env[envVar]),
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -507,7 +511,7 @@ function resolveApiKeyFromCredential(
|
||||
return {
|
||||
apiKey: envVar,
|
||||
source: "env-ref",
|
||||
discoveryApiKey: toDiscoveryApiKey(process.env[envVar]),
|
||||
discoveryApiKey: toDiscoveryApiKey(env[envVar]),
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -529,10 +533,11 @@ function resolveApiKeyFromCredential(
|
||||
function resolveApiKeyFromProfiles(params: {
|
||||
provider: string;
|
||||
store: ReturnType<typeof ensureAuthProfileStore>;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): ProfileApiKeyResolution | undefined {
|
||||
const ids = listProfilesForProvider(params.store, params.provider);
|
||||
for (const id of ids) {
|
||||
const resolved = resolveApiKeyFromCredential(params.store.profiles[id]);
|
||||
const resolved = resolveApiKeyFromCredential(params.store.profiles[id], params.env);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
@@ -1117,23 +1122,26 @@ async function buildKilocodeProviderWithDiscovery(): Promise<ProviderConfig> {
|
||||
|
||||
export async function resolveImplicitProviders(params: {
|
||||
agentDir: string;
|
||||
config?: OpenClawConfig;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
explicitProviders?: Record<string, ProviderConfig> | null;
|
||||
}): Promise<ModelsConfig["providers"]> {
|
||||
const providers: Record<string, ProviderConfig> = {};
|
||||
const env = params.env ?? process.env;
|
||||
const authStore = ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
const resolveProviderApiKey = (
|
||||
provider: string,
|
||||
): { apiKey: string | undefined; discoveryApiKey?: string } => {
|
||||
const envVar = resolveEnvApiKeyVarName(provider);
|
||||
const envVar = resolveEnvApiKeyVarName(provider, env);
|
||||
if (envVar) {
|
||||
return {
|
||||
apiKey: envVar,
|
||||
discoveryApiKey: toDiscoveryApiKey(process.env[envVar]),
|
||||
discoveryApiKey: toDiscoveryApiKey(env[envVar]),
|
||||
};
|
||||
}
|
||||
const fromProfiles = resolveApiKeyFromProfiles({ provider, store: authStore });
|
||||
const fromProfiles = resolveApiKeyFromProfiles({ provider, store: authStore, env });
|
||||
return {
|
||||
apiKey: fromProfiles?.apiKey,
|
||||
discoveryApiKey: fromProfiles?.discoveryApiKey,
|
||||
@@ -1145,7 +1153,7 @@ export async function resolveImplicitProviders(params: {
|
||||
providers.minimax = { ...buildMinimaxProvider(), apiKey: minimaxKey };
|
||||
}
|
||||
|
||||
const minimaxPortalEnvKey = resolveEnvApiKeyVarName("minimax-portal");
|
||||
const minimaxPortalEnvKey = resolveEnvApiKeyVarName("minimax-portal", env);
|
||||
const minimaxOauthProfile = listProfilesForProvider(authStore, "minimax-portal");
|
||||
if (minimaxPortalEnvKey || minimaxOauthProfile.length > 0) {
|
||||
providers["minimax-portal"] = {
|
||||
@@ -1220,8 +1228,8 @@ export async function resolveImplicitProviders(params: {
|
||||
if (!baseUrl) {
|
||||
continue;
|
||||
}
|
||||
const envVarApiKey = resolveEnvApiKeyVarName("cloudflare-ai-gateway");
|
||||
const profileApiKey = resolveApiKeyFromCredential(cred)?.apiKey;
|
||||
const envVarApiKey = resolveEnvApiKeyVarName("cloudflare-ai-gateway", env);
|
||||
const profileApiKey = resolveApiKeyFromCredential(cred, env)?.apiKey;
|
||||
const apiKey = envVarApiKey ?? profileApiKey ?? "";
|
||||
if (!apiKey) {
|
||||
continue;
|
||||
@@ -1329,6 +1337,35 @@ export async function resolveImplicitProviders(params: {
|
||||
providers.kilocode = { ...(await buildKilocodeProviderWithDiscovery()), apiKey: kilocodeKey };
|
||||
}
|
||||
|
||||
if (!providers["github-copilot"]) {
|
||||
const implicitCopilot = await resolveImplicitCopilotProvider({
|
||||
agentDir: params.agentDir,
|
||||
env,
|
||||
});
|
||||
if (implicitCopilot) {
|
||||
providers["github-copilot"] = implicitCopilot;
|
||||
}
|
||||
}
|
||||
|
||||
const implicitBedrock = await resolveImplicitBedrockProvider({
|
||||
agentDir: params.agentDir,
|
||||
config: params.config,
|
||||
env,
|
||||
});
|
||||
if (implicitBedrock) {
|
||||
const existing = providers["amazon-bedrock"];
|
||||
providers["amazon-bedrock"] = existing
|
||||
? {
|
||||
...implicitBedrock,
|
||||
...existing,
|
||||
models:
|
||||
Array.isArray(existing.models) && existing.models.length > 0
|
||||
? existing.models
|
||||
: implicitBedrock.models,
|
||||
}
|
||||
: implicitBedrock;
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,15 +11,12 @@ import { isRecord } from "../utils.js";
|
||||
import { resolveOpenClawAgentDir } from "./agent-paths.js";
|
||||
import {
|
||||
mergeProviders,
|
||||
mergeProviderModels,
|
||||
mergeWithExistingProviderSecrets,
|
||||
type ExistingProviderConfig,
|
||||
} from "./models-config.merge.js";
|
||||
import {
|
||||
normalizeProviders,
|
||||
type ProviderConfig,
|
||||
resolveImplicitBedrockProvider,
|
||||
resolveImplicitCopilotProvider,
|
||||
resolveImplicitProviders,
|
||||
} from "./models-config.providers.js";
|
||||
|
||||
@@ -52,24 +49,15 @@ async function resolveProvidersForModelsJson(params: {
|
||||
}): Promise<Record<string, ProviderConfig>> {
|
||||
const { cfg, agentDir } = params;
|
||||
const explicitProviders = cfg.models?.providers ?? {};
|
||||
const implicitProviders = await resolveImplicitProviders({ agentDir, explicitProviders });
|
||||
const implicitProviders = await resolveImplicitProviders({
|
||||
agentDir,
|
||||
config: cfg,
|
||||
explicitProviders,
|
||||
});
|
||||
const providers: Record<string, ProviderConfig> = mergeProviders({
|
||||
implicit: implicitProviders,
|
||||
explicit: explicitProviders,
|
||||
});
|
||||
|
||||
const implicitBedrock = await resolveImplicitBedrockProvider({ agentDir, config: cfg });
|
||||
if (implicitBedrock) {
|
||||
const existing = providers["amazon-bedrock"];
|
||||
providers["amazon-bedrock"] = existing
|
||||
? mergeProviderModels(implicitBedrock, existing)
|
||||
: implicitBedrock;
|
||||
}
|
||||
|
||||
const implicitCopilot = await resolveImplicitCopilotProvider({ agentDir });
|
||||
if (implicitCopilot && !providers["github-copilot"]) {
|
||||
providers["github-copilot"] = implicitCopilot;
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user