This commit is contained in:
Suksham 2026-01-29 21:53:35 -05:00 committed by GitHub
commit c1fe067c8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 140 additions and 1 deletions

View File

@ -175,6 +175,7 @@ Search the web using your configured provider.
- `search_lang` (optional): ISO language code for search results (e.g., "de", "en", "fr")
- `ui_lang` (optional): ISO language code for UI elements
- `freshness` (optional, Brave only): filter by discovery time (`pd`, `pw`, `pm`, `py`, or `YYYY-MM-DDtoYYYY-MM-DD`)
- `model` (optional, Perplexity only): override the configured model per-query (e.g., "sonar", "sonar-pro")
**Examples:**
@ -200,6 +201,18 @@ await web_search({
query: "TMBG interview",
freshness: "pw"
});
// Perplexity: use faster model for simple lookups
await web_search({
query: "Nick Saban birthday",
model: "sonar"
});
// Perplexity: use deeper reasoning for complex analysis
await web_search({
query: "compare React vs Vue performance benchmarks 2026",
model: "sonar-pro"
});
```
## web_fetch

View File

@ -63,6 +63,12 @@ const WebSearchSchema = Type.Object({
"Filter results by discovery time (Brave only). Values: 'pd' (past 24h), 'pw' (past week), 'pm' (past month), 'py' (past year), or date range 'YYYY-MM-DDtoYYYY-MM-DD'.",
}),
),
model: Type.Optional(
Type.String({
description:
"Perplexity model to use (e.g., 'sonar', 'sonar-pro'). Overrides configured default. Perplexity provider only.",
}),
),
});
type WebSearchConfig = NonNullable<MoltbotConfig["tools"]>["web"] extends infer Web
@ -459,6 +465,14 @@ export function createWebSearchTool(options?: {
docs: "https://docs.molt.bot/tools/web",
});
}
const modelOverride = readStringParam(params, "model");
if (modelOverride && provider !== "perplexity") {
return jsonResult({
error: "unsupported_model",
message: "model parameter is only supported by the Perplexity web_search provider.",
docs: "https://docs.molt.bot/tools/web",
});
}
const result = await runWebSearch({
query,
count: resolveSearchCount(count, DEFAULT_SEARCH_COUNT),
@ -475,7 +489,7 @@ export function createWebSearchTool(options?: {
perplexityAuth?.source,
perplexityAuth?.apiKey,
),
perplexityModel: resolvePerplexityModel(perplexityConfig),
perplexityModel: modelOverride || resolvePerplexityModel(perplexityConfig),
});
return jsonResult(result);
},

View File

@ -307,3 +307,115 @@ describe("web_search perplexity baseUrl defaults", () => {
expect(mockFetch.mock.calls[0]?.[0]).toBe("https://openrouter.ai/api/v1/chat/completions");
});
});
describe("web_search model parameter", () => {
const priorFetch = global.fetch;
afterEach(() => {
vi.unstubAllEnvs();
// @ts-expect-error global fetch cleanup
global.fetch = priorFetch;
});
it("passes model parameter to Perplexity API", async () => {
vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test");
const mockFetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ choices: [{ message: { content: "ok" } }], citations: [] }),
} as Response),
);
// @ts-expect-error mock fetch
global.fetch = mockFetch;
const tool = createWebSearchTool({
config: { tools: { web: { search: { provider: "perplexity" } } } },
sandboxed: true,
});
await tool?.execute?.(1, { query: "test-model-param", model: "sonar" });
expect(mockFetch).toHaveBeenCalled();
const body = JSON.parse(mockFetch.mock.calls[0]?.[1]?.body as string);
expect(body.model).toBe("sonar");
});
it("uses configured model when no model parameter provided", async () => {
vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test");
const mockFetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ choices: [{ message: { content: "ok" } }], citations: [] }),
} as Response),
);
// @ts-expect-error mock fetch
global.fetch = mockFetch;
const tool = createWebSearchTool({
config: {
tools: {
web: {
search: {
provider: "perplexity",
perplexity: { model: "sonar-reasoning-pro" },
},
},
},
},
sandboxed: true,
});
await tool?.execute?.(1, { query: "test-configured-model" });
expect(mockFetch).toHaveBeenCalled();
const body = JSON.parse(mockFetch.mock.calls[0]?.[1]?.body as string);
expect(body.model).toBe("sonar-reasoning-pro");
});
it("model parameter overrides configured model", async () => {
vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test");
const mockFetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ choices: [{ message: { content: "ok" } }], citations: [] }),
} as Response),
);
// @ts-expect-error mock fetch
global.fetch = mockFetch;
const tool = createWebSearchTool({
config: {
tools: {
web: {
search: {
provider: "perplexity",
perplexity: { model: "sonar-pro" },
},
},
},
},
sandboxed: true,
});
await tool?.execute?.(1, { query: "test-model-override", model: "sonar" });
expect(mockFetch).toHaveBeenCalled();
const body = JSON.parse(mockFetch.mock.calls[0]?.[1]?.body as string);
expect(body.model).toBe("sonar");
});
it("rejects model parameter for Brave provider", async () => {
vi.stubEnv("BRAVE_API_KEY", "test-key");
const mockFetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ web: { results: [] } }),
} as Response),
);
// @ts-expect-error mock fetch
global.fetch = mockFetch;
const tool = createWebSearchTool({ config: undefined, sandboxed: true });
const result = await tool?.execute?.(1, { query: "test-brave-model", model: "sonar" });
expect(mockFetch).not.toHaveBeenCalled();
expect(result?.details).toMatchObject({ error: "unsupported_model" });
});
});