


























@@ -15,9 +15,7 @@ export type OllamaPluginConfig = {
1515type OllamaDiscoveryContext = {
1616config: {
1717models?: {
18-providers?: {
19-ollama?: ModelProviderConfig;
20-};
18+providers?: Record<string, ModelProviderConfig | undefined>;
2119ollamaDiscovery?: {
2220enabled?: boolean;
2321};
@@ -73,6 +71,17 @@ function shouldSkipAmbientOllamaDiscovery(env: NodeJS.ProcessEnv): boolean {
73717472const LOCAL_OLLAMA_HOSTNAMES = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "::"]);
757374+function isIpv4Loopback(host: string): boolean {
75+if (!/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
76+return false;
77+}
78+const octets = host.split(".").map((part) => Number.parseInt(part, 10));
79+if (octets.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
80+return false;
81+}
82+return octets[0] === 127;
83+}
84+7685function isIpv4PrivateRange(host: string): boolean {
7786if (!/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
7887return false;
@@ -113,6 +122,44 @@ export function isLocalOllamaBaseUrl(baseUrl: string | undefined | null): boolea
113122);
114123}
115124125+function isLoopbackOllamaBaseUrl(baseUrl: string | undefined | null): boolean {
126+if (!baseUrl) {
127+return true;
128+}
129+let parsed: URL;
130+try {
131+parsed = new URL(baseUrl);
132+} catch {
133+return false;
134+}
135+let host = parsed.hostname.toLowerCase();
136+if (host.startsWith("[") && host.endsWith("]")) {
137+host = host.slice(1, -1);
138+}
139+return LOCAL_OLLAMA_HOSTNAMES.has(host) || isIpv4Loopback(host);
140+}
141+142+function hasExplicitRemoteOllamaApiProvider(
143+providers: Record<string, ModelProviderConfig | undefined> | undefined,
144+): boolean {
145+if (!providers) {
146+return false;
147+}
148+for (const [providerId, provider] of Object.entries(providers)) {
149+if (providerId === OLLAMA_PROVIDER_ID || !provider) {
150+continue;
151+}
152+if (normalizeOptionalString(provider.api)?.toLowerCase() !== "ollama") {
153+continue;
154+}
155+const baseUrl = readProviderBaseUrl(provider);
156+if (baseUrl && !isLoopbackOllamaBaseUrl(baseUrl)) {
157+return true;
158+}
159+}
160+return false;
161+}
162+116163export function shouldUseSyntheticOllamaAuth(
117164providerConfig: ModelProviderConfig | undefined,
118165): boolean {
@@ -171,6 +218,9 @@ export async function resolveOllamaDiscoveryResult(params: {
171218const explicit = params.ctx.config.models?.providers?.ollama;
172219const hasExplicitModels = Array.isArray(explicit?.models) && explicit.models.length > 0;
173220const hasMeaningfulExplicitConfig = hasMeaningfulExplicitOllamaConfig(explicit);
221+const hasRemoteOllamaApiProvider = hasExplicitRemoteOllamaApiProvider(
222+params.ctx.config.models?.providers,
223+);
174224const discoveryEnabled =
175225params.pluginConfig.discovery?.enabled ?? params.ctx.config.models?.ollamaDiscovery?.enabled;
176226if (!hasExplicitModels && discoveryEnabled === false) {
@@ -202,6 +252,9 @@ export async function resolveOllamaDiscoveryResult(params: {
202252},
203253};
204254}
255+if (!hasMeaningfulExplicitConfig && hasRemoteOllamaApiProvider) {
256+return null;
257+}
205258if (!hasOllamaDiscoveryOptIn && !hasMeaningfulExplicitConfig) {
206259return null;
207260}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。