



























@@ -43,18 +43,85 @@ function readStringValue(value: unknown): string | undefined {
43434444export function resolveOllamaDiscoveryApiKey(params: {
4545env: NodeJS.ProcessEnv;
46+baseUrl?: string;
4647explicitApiKey?: string;
48+hasDeclaredApiKey?: boolean;
4749resolvedApiKey?: unknown;
48-}): string {
49-const envApiKey = params.env.OLLAMA_API_KEY?.trim() ? "OLLAMA_API_KEY" : undefined;
50+}): string | undefined {
51+const envValue = normalizeOptionalString(params.env.OLLAMA_API_KEY);
52+const envApiKey = envValue ? "OLLAMA_API_KEY" : undefined;
5053const resolvedApiKey = normalizeOptionalString(params.resolvedApiKey);
51-return envApiKey ?? params.explicitApiKey ?? resolvedApiKey ?? OLLAMA_DEFAULT_API_KEY;
54+const explicitApiKey = normalizeOptionalString(params.explicitApiKey);
55+if (explicitApiKey) {
56+return explicitApiKey;
57+}
58+if (params.hasDeclaredApiKey && resolvedApiKey) {
59+return resolvedApiKey;
60+}
61+if (!isLocalOllamaBaseUrl(params.baseUrl)) {
62+return envApiKey ?? (resolvedApiKey !== OLLAMA_DEFAULT_API_KEY ? resolvedApiKey : undefined);
63+}
64+if (resolvedApiKey && resolvedApiKey !== envValue && resolvedApiKey !== OLLAMA_DEFAULT_API_KEY) {
65+return resolvedApiKey;
66+}
67+return OLLAMA_DEFAULT_API_KEY;
5268}
53695470function shouldSkipAmbientOllamaDiscovery(env: NodeJS.ProcessEnv): boolean {
5571return Boolean(env.VITEST) || env.NODE_ENV === "test";
5672}
577374+const LOCAL_OLLAMA_HOSTNAMES = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "::"]);
75+76+function isIpv4PrivateRange(host: string): boolean {
77+if (!/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
78+return false;
79+}
80+const octets = host.split(".").map((part) => Number.parseInt(part, 10));
81+if (octets.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
82+return false;
83+}
84+const [a, b] = octets;
85+return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
86+}
87+88+function isIpv6LocalRange(host: string): boolean {
89+const lower = host.toLowerCase();
90+return /^fe[89ab][0-9a-f]:/.test(lower) || /^f[cd][0-9a-f]{2}:/.test(lower);
91+}
92+93+export function isLocalOllamaBaseUrl(baseUrl: string | undefined | null): boolean {
94+if (!baseUrl) {
95+return true;
96+}
97+let parsed: URL;
98+try {
99+parsed = new URL(baseUrl);
100+} catch {
101+return false;
102+}
103+let host = parsed.hostname.toLowerCase();
104+if (host.startsWith("[") && host.endsWith("]")) {
105+host = host.slice(1, -1);
106+}
107+return (
108+LOCAL_OLLAMA_HOSTNAMES.has(host) ||
109+host.endsWith(".local") ||
110+isIpv4PrivateRange(host) ||
111+isIpv6LocalRange(host) ||
112+(!host.includes(".") && !host.includes(":"))
113+);
114+}
115+116+export function shouldUseSyntheticOllamaAuth(
117+providerConfig: ModelProviderConfig | undefined,
118+): boolean {
119+if (!hasMeaningfulExplicitOllamaConfig(providerConfig)) {
120+return false;
121+}
122+return isLocalOllamaBaseUrl(readProviderBaseUrl(providerConfig));
123+}
124+58125export function hasMeaningfulExplicitOllamaConfig(
59126providerConfig: ModelProviderConfig | undefined,
60127): boolean {
@@ -116,17 +183,22 @@ export async function resolveOllamaDiscoveryResult(params: {
116183ollamaKey.trim().length > 0 &&
117184ollamaKey.trim() !== OLLAMA_DEFAULT_API_KEY;
118185const explicitApiKey = readStringValue(explicit?.apiKey);
186+const hasDeclaredApiKey = explicit?.apiKey !== undefined;
119187if (hasExplicitModels && explicit) {
188+const baseUrl = resolveOllamaApiBase(readProviderBaseUrl(explicit) ?? OLLAMA_DEFAULT_BASE_URL);
189+const apiKey = resolveOllamaDiscoveryApiKey({
190+env: params.ctx.env,
191+ baseUrl,
192+ explicitApiKey,
193+ hasDeclaredApiKey,
194+resolvedApiKey: ollamaKey,
195+});
120196return {
121197provider: {
122198 ...explicit,
123-baseUrl: resolveOllamaApiBase(readProviderBaseUrl(explicit) ?? OLLAMA_DEFAULT_BASE_URL),
199+ baseUrl,
124200api: explicit.api ?? "ollama",
125-apiKey: resolveOllamaDiscoveryApiKey({
126-env: params.ctx.env,
127- explicitApiKey,
128-resolvedApiKey: ollamaKey,
129-}),
201+ ...(apiKey ? { apiKey } : {}),
130202},
131203};
132204}
@@ -141,20 +213,24 @@ export async function resolveOllamaDiscoveryResult(params: {
141213return null;
142214}
143215144-const provider = await params.buildProvider(readProviderBaseUrl(explicit), {
216+const configuredBaseUrl = readProviderBaseUrl(explicit);
217+const provider = await params.buildProvider(configuredBaseUrl, {
145218quiet: !hasRealOllamaKey && !hasMeaningfulExplicitConfig,
146219});
147220if (provider.models?.length === 0 && !ollamaKey && !explicit?.apiKey) {
148221return null;
149222}
223+const apiKey = resolveOllamaDiscoveryApiKey({
224+env: params.ctx.env,
225+baseUrl: provider.baseUrl ?? configuredBaseUrl,
226+ explicitApiKey,
227+ hasDeclaredApiKey,
228+resolvedApiKey: ollamaKey,
229+});
150230return {
151231provider: {
152232 ...provider,
153-apiKey: resolveOllamaDiscoveryApiKey({
154-env: params.ctx.env,
155- explicitApiKey,
156-resolvedApiKey: ollamaKey,
157-}),
233+ ...(apiKey ? { apiKey } : {}),
158234},
159235};
160236}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。