fix(cli): applied search provider settings
Initialized standalone search commands with configured web-search provider globals before resolving the implicit provider chain. Fixes #3793
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `omp search` applying `providers.webSearch` and `providers.webSearchExclude` before resolving its implicit provider chain. ([#3793](https://github.com/can1357/oh-my-pi/issues/3793))
|
||||
- Fixed the bash interceptor blocking `echo` / `printf` redirects to `/dev/null`, `/dev/tty`, `/dev/stdout`, and `/dev/stderr` device sinks while still directing real file writes to the write tool. ([#3763](https://github.com/can1357/oh-my-pi/issues/3763))
|
||||
|
||||
## [16.2.5] - 2026-06-28
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
* Handles `omp q`/`omp web-search` subcommands for testing web search providers.
|
||||
*/
|
||||
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
||||
import { APP_NAME, getProjectDir } from "@oh-my-pi/pi-utils";
|
||||
import chalk from "chalk";
|
||||
import { applyProviderGlobalsFromSettings } from "../config/provider-globals";
|
||||
import { Settings } from "../config/settings";
|
||||
import { initTheme, theme } from "../modes/theme/theme";
|
||||
import { runSearchQuery, type SearchQueryParams } from "../web/search/index";
|
||||
import { SEARCH_PROVIDER_ORDER } from "../web/search/provider";
|
||||
@@ -85,6 +87,9 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const settings = await Settings.init({ cwd: getProjectDir() });
|
||||
applyProviderGlobalsFromSettings(settings);
|
||||
|
||||
await initTheme();
|
||||
|
||||
const params: SearchQueryParams = {
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
|
||||
import * as path from "node:path";
|
||||
import { stripVTControlCharacters } from "node:util";
|
||||
import { resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { setExcludedSearchProviders, setPreferredSearchProvider } from "@oh-my-pi/pi-coding-agent/web/search/provider";
|
||||
import { getConfigRootDir, setAgentDir, TempDir } from "@oh-my-pi/pi-utils";
|
||||
import { runSearchCommand } from "../../../src/cli/web-search-cli";
|
||||
|
||||
const WEB_SEARCH_ENV_KEYS = [
|
||||
"ANTHROPIC_API_KEY",
|
||||
"BRAVE_API_KEY",
|
||||
"EXA_API_KEY",
|
||||
"FIRECRAWL_API_KEY",
|
||||
"JINA_API_KEY",
|
||||
"KAGI_API_KEY",
|
||||
"MOONSHOT_API_KEY",
|
||||
"MOONSHOT_SEARCH_API_KEY",
|
||||
"PARALLEL_API_KEY",
|
||||
"PERPLEXITY_API_KEY",
|
||||
"SEARXNG_ENDPOINT",
|
||||
"SYNTHETIC_API_KEY",
|
||||
"TAVILY_API_KEY",
|
||||
"TINYFISH_API_KEY",
|
||||
"XAI_API_KEY",
|
||||
] as const;
|
||||
|
||||
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
const fallbackAgentDir = path.join(getConfigRootDir(), "agent");
|
||||
|
||||
let tempAgentDir: TempDir | undefined;
|
||||
let originalEnv: Partial<Record<(typeof WEB_SEARCH_ENV_KEYS)[number], string | undefined>> = {};
|
||||
let originalExitCode: typeof process.exitCode;
|
||||
|
||||
function responseUrl(input: string | Request | URL): string {
|
||||
if (typeof input === "string") return input;
|
||||
if (input instanceof URL) return input.toString();
|
||||
return input.url;
|
||||
}
|
||||
|
||||
function makeFetchMock(): typeof fetch {
|
||||
return Object.assign(
|
||||
async (input: string | Request | URL, _init?: RequestInit): Promise<Response> => {
|
||||
const url = responseUrl(input);
|
||||
if (url.startsWith("https://s.jina.ai/")) {
|
||||
return new Response(
|
||||
JSON.stringify({ data: [{ title: "Jina result", url: "https://jina.example", content: "jina" }] }),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
if (url === "https://api.tavily.com/search") {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
answer: "Tavily answer",
|
||||
results: [{ title: "Tavily result", url: "https://tavily.example", content: "tavily" }],
|
||||
request_id: "req-test",
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return new Response(`unexpected URL: ${url}`, { status: 500 });
|
||||
},
|
||||
{ preconnect: fetch.preconnect },
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
originalEnv = Object.fromEntries(WEB_SEARCH_ENV_KEYS.map(key => [key, process.env[key]]));
|
||||
for (const key of WEB_SEARCH_ENV_KEYS) delete process.env[key];
|
||||
process.env.JINA_API_KEY = "test-jina-key";
|
||||
process.env.TAVILY_API_KEY = "test-tavily-key";
|
||||
originalExitCode = process.exitCode;
|
||||
process.exitCode = undefined;
|
||||
|
||||
resetSettingsForTest();
|
||||
setPreferredSearchProvider("auto");
|
||||
setExcludedSearchProviders([]);
|
||||
tempAgentDir = TempDir.createSync("@omp-search-cli-");
|
||||
setAgentDir(tempAgentDir.path());
|
||||
await Settings.init({
|
||||
inMemory: true,
|
||||
cwd: tempAgentDir.path(),
|
||||
overrides: {
|
||||
"providers.webSearch": "tavily",
|
||||
"providers.webSearchExclude": ["jina"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
resetSettingsForTest();
|
||||
setPreferredSearchProvider("auto");
|
||||
setExcludedSearchProviders([]);
|
||||
process.exitCode = originalExitCode;
|
||||
for (const key of WEB_SEARCH_ENV_KEYS) {
|
||||
const value = originalEnv[key];
|
||||
if (value === undefined) delete process.env[key];
|
||||
else process.env[key] = value;
|
||||
}
|
||||
if (originalAgentDir) {
|
||||
setAgentDir(originalAgentDir);
|
||||
} else {
|
||||
setAgentDir(fallbackAgentDir);
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
}
|
||||
if (tempAgentDir) {
|
||||
await tempAgentDir.remove();
|
||||
tempAgentDir = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
describe("runSearchCommand provider settings", () => {
|
||||
it("applies configured web-search preference and exclusions before resolving the implicit chain", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockImplementation(makeFetchMock());
|
||||
|
||||
let stdout = "";
|
||||
vi.spyOn(process.stdout, "write").mockImplementation(chunk => {
|
||||
stdout += typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
||||
return true;
|
||||
});
|
||||
|
||||
await runSearchCommand({ query: "provider selection smoke test", limit: 1, expanded: false });
|
||||
|
||||
const plain = stripVTControlCharacters(stdout);
|
||||
expect(plain).toContain("Provider: Tavily (API)");
|
||||
expect(plain).not.toContain("Provider: Jina");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user