mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:00:54 +00:00
fix(ollama): align web search endpoint routing
This commit is contained in:
@@ -125,7 +125,7 @@ describe("ollama web search provider", () => {
|
||||
).toBe("https://ollama.com");
|
||||
});
|
||||
|
||||
it("maps generic search args into the Ollama search endpoint", async () => {
|
||||
it("maps generic search args into the local Ollama proxy endpoint", async () => {
|
||||
const release = vi.fn(async () => {});
|
||||
fetchWithSsrFGuardMock.mockResolvedValue({
|
||||
response: new Response(
|
||||
@@ -157,7 +157,7 @@ describe("ollama web search provider", () => {
|
||||
|
||||
expect(fetchWithSsrFGuardMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "http://ollama.local:11434/api/web_search",
|
||||
url: "http://ollama.local:11434/api/experimental/web_search",
|
||||
auditContext: "ollama-web-search.search",
|
||||
}),
|
||||
);
|
||||
@@ -184,7 +184,7 @@ describe("ollama web search provider", () => {
|
||||
expect(release).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("falls back to the legacy Ollama web search endpoint when /api/web_search is missing", async () => {
|
||||
it("tries the future local direct endpoint when the local proxy endpoint is missing", async () => {
|
||||
fetchWithSsrFGuardMock
|
||||
.mockResolvedValueOnce({
|
||||
response: new Response("not found", { status: 404 }),
|
||||
@@ -211,11 +211,42 @@ describe("ollama web search provider", () => {
|
||||
});
|
||||
|
||||
expect(fetchWithSsrFGuardMock.mock.calls.map((call) => call[0].url)).toEqual([
|
||||
"http://ollama.local:11434/api/web_search",
|
||||
"http://ollama.local:11434/api/experimental/web_search",
|
||||
"http://ollama.local:11434/api/web_search",
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses only the hosted endpoint for Ollama Cloud base URLs", async () => {
|
||||
fetchWithSsrFGuardMock.mockResolvedValueOnce({
|
||||
response: new Response(
|
||||
JSON.stringify({
|
||||
results: [{ title: "Cloud", url: "https://example.com", content: "result" }],
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
),
|
||||
release: vi.fn(async () => {}),
|
||||
});
|
||||
|
||||
await expect(
|
||||
runOllamaWebSearch({
|
||||
config: createOllamaConfig({
|
||||
baseUrl: "https://ollama.com",
|
||||
apiKey: "cloud-config-secret",
|
||||
}),
|
||||
query: "openclaw",
|
||||
}),
|
||||
).resolves.toMatchObject({ count: 1 });
|
||||
|
||||
expect(fetchWithSsrFGuardMock.mock.calls).toHaveLength(1);
|
||||
expect(fetchWithSsrFGuardMock.mock.calls[0]?.[0].url).toBe("https://ollama.com/api/web_search");
|
||||
expect(fetchWithSsrFGuardMock.mock.calls[0]?.[0].init?.headers).toMatchObject({
|
||||
Authorization: "Bearer cloud-config-secret",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses an env Ollama key only for the cloud fallback from a local host", async () => {
|
||||
const original = process.env.OLLAMA_API_KEY;
|
||||
try {
|
||||
@@ -256,6 +287,11 @@ describe("ollama web search provider", () => {
|
||||
| undefined;
|
||||
expect(firstHeaders?.Authorization).toBeUndefined();
|
||||
expect(cloudHeaders?.Authorization).toBe("Bearer cloud-secret");
|
||||
expect(fetchWithSsrFGuardMock.mock.calls.map((call) => call[0].url)).toEqual([
|
||||
"http://ollama.local:11434/api/experimental/web_search",
|
||||
"http://ollama.local:11434/api/web_search",
|
||||
"https://ollama.com/api/web_search",
|
||||
]);
|
||||
expect(fetchWithSsrFGuardMock.mock.calls[2]?.[0].url).toBe(
|
||||
"https://ollama.com/api/web_search",
|
||||
);
|
||||
|
||||
@@ -41,8 +41,8 @@ const OLLAMA_WEB_SEARCH_SCHEMA = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
const OLLAMA_WEB_SEARCH_PATH = "/api/web_search";
|
||||
const OLLAMA_LEGACY_WEB_SEARCH_PATH = "/api/experimental/web_search";
|
||||
const OLLAMA_HOSTED_WEB_SEARCH_PATH = "/api/web_search";
|
||||
const OLLAMA_LOCAL_WEB_SEARCH_PROXY_PATH = "/api/experimental/web_search";
|
||||
const OLLAMA_CLOUD_BASE_URL = "https://ollama.com";
|
||||
const DEFAULT_OLLAMA_WEB_SEARCH_COUNT = 5;
|
||||
const DEFAULT_OLLAMA_WEB_SEARCH_TIMEOUT_MS = 15_000;
|
||||
@@ -58,6 +58,12 @@ type OllamaWebSearchResponse = {
|
||||
results?: OllamaWebSearchResult[];
|
||||
};
|
||||
|
||||
type OllamaWebSearchAttempt = {
|
||||
baseUrl: string;
|
||||
path: string;
|
||||
apiKey?: string;
|
||||
};
|
||||
|
||||
function isOllamaCloudBaseUrl(baseUrl: string): boolean {
|
||||
try {
|
||||
const parsed = new URL(baseUrl);
|
||||
@@ -111,6 +117,43 @@ function normalizeOllamaWebSearchResult(
|
||||
};
|
||||
}
|
||||
|
||||
function buildOllamaWebSearchAttempts(params: {
|
||||
baseUrl: string;
|
||||
configuredApiKey?: string;
|
||||
envApiKey?: string;
|
||||
}): OllamaWebSearchAttempt[] {
|
||||
if (isOllamaCloudBaseUrl(params.baseUrl)) {
|
||||
return [
|
||||
{
|
||||
baseUrl: params.baseUrl,
|
||||
path: OLLAMA_HOSTED_WEB_SEARCH_PATH,
|
||||
apiKey: params.configuredApiKey ?? params.envApiKey,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const attempts: OllamaWebSearchAttempt[] = [
|
||||
{
|
||||
baseUrl: params.baseUrl,
|
||||
path: OLLAMA_LOCAL_WEB_SEARCH_PROXY_PATH,
|
||||
apiKey: params.configuredApiKey,
|
||||
},
|
||||
{
|
||||
baseUrl: params.baseUrl,
|
||||
path: OLLAMA_HOSTED_WEB_SEARCH_PATH,
|
||||
apiKey: params.configuredApiKey,
|
||||
},
|
||||
];
|
||||
if (params.envApiKey) {
|
||||
attempts.push({
|
||||
baseUrl: OLLAMA_CLOUD_BASE_URL,
|
||||
path: OLLAMA_HOSTED_WEB_SEARCH_PATH,
|
||||
apiKey: params.envApiKey,
|
||||
});
|
||||
}
|
||||
return attempts;
|
||||
}
|
||||
|
||||
export async function runOllamaWebSearch(params: {
|
||||
config?: OpenClawConfig;
|
||||
query: string;
|
||||
@@ -127,27 +170,7 @@ export async function runOllamaWebSearch(params: {
|
||||
const count = resolveSearchCount(params.count, DEFAULT_OLLAMA_WEB_SEARCH_COUNT);
|
||||
const startedAt = Date.now();
|
||||
const body = JSON.stringify({ query, max_results: count });
|
||||
const attempts = [
|
||||
{
|
||||
baseUrl,
|
||||
path: OLLAMA_WEB_SEARCH_PATH,
|
||||
apiKey: isOllamaCloudBaseUrl(baseUrl) ? (configuredApiKey ?? envApiKey) : configuredApiKey,
|
||||
},
|
||||
{
|
||||
baseUrl,
|
||||
path: OLLAMA_LEGACY_WEB_SEARCH_PATH,
|
||||
apiKey: isOllamaCloudBaseUrl(baseUrl) ? (configuredApiKey ?? envApiKey) : configuredApiKey,
|
||||
},
|
||||
...(!isOllamaCloudBaseUrl(baseUrl) && envApiKey
|
||||
? [
|
||||
{
|
||||
baseUrl: OLLAMA_CLOUD_BASE_URL,
|
||||
path: OLLAMA_WEB_SEARCH_PATH,
|
||||
apiKey: envApiKey,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
const attempts = buildOllamaWebSearchAttempts({ baseUrl, configuredApiKey, envApiKey });
|
||||
|
||||
let payload: OllamaWebSearchResponse | undefined;
|
||||
let lastError: Error | undefined;
|
||||
@@ -305,6 +328,7 @@ export function createOllamaWebSearchProvider(): WebSearchProviderPlugin {
|
||||
}
|
||||
|
||||
export const __testing = {
|
||||
buildOllamaWebSearchAttempts,
|
||||
normalizeOllamaWebSearchResult,
|
||||
resolveConfiguredOllamaWebSearchApiKey,
|
||||
resolveEnvOllamaWebSearchApiKey,
|
||||
|
||||
Reference in New Issue
Block a user