feat(websearch): add parallel provider rollout (#26227)

This commit is contained in:
Shoubhit Dash
2026-05-08 14:19:36 +05:30
committed by GitHub
parent ae25278eda
commit a43d3e0e1e
13 changed files with 276 additions and 44 deletions
+87 -16
View File
@@ -1,8 +1,11 @@
import { Effect, Schema } from "effect"
import { HttpClient } from "effect/unstable/http"
import * as Tool from "./tool"
import * as McpExa from "./mcp-exa"
import * as McpWebSearch from "./mcp-websearch"
import DESCRIPTION from "./websearch.txt"
import { Flag } from "@opencode-ai/core/flag/flag"
import { checksum } from "@opencode-ai/core/util/encode"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
export const Parameters = Schema.Struct({
query: Schema.String.annotate({ description: "Websearch query" }),
@@ -21,6 +24,81 @@ export const Parameters = Schema.Struct({
}),
})
const WebSearchProviderSchema = Schema.Literals(["exa", "parallel"])
export type WebSearchProvider = Schema.Schema.Type<typeof WebSearchProviderSchema>
export function selectWebSearchProvider(
sessionID: string,
flags = { exa: Flag.OPENCODE_ENABLE_EXA, parallel: Flag.OPENCODE_ENABLE_PARALLEL },
): WebSearchProvider {
const override = process.env.OPENCODE_WEBSEARCH_PROVIDER
if (override === "exa" || override === "parallel") return override
if (flags.parallel) return "parallel"
if (flags.exa) return "exa"
return Number.parseInt(checksum(sessionID) ?? "0", 36) % 2 === 0 ? "exa" : "parallel"
}
export function webSearchProviderLabel(provider: unknown) {
if (provider === "parallel") return "Parallel Web Search"
if (provider === "exa") return "Exa Web Search"
return "Web Search"
}
export function webSearchModelName(extra: Tool.Context["extra"]) {
const model = extra?.model
if (!model || typeof model !== "object") return undefined
const api = "api" in model && model.api && typeof model.api === "object" ? model.api : undefined
const apiID = api && "id" in api && typeof api.id === "string" ? api.id : undefined
const id = "id" in model && typeof model.id === "string" ? model.id : undefined
return (apiID ?? id)?.slice(0, 100)
}
function parallelAuthHeaders() {
const headers = { "User-Agent": `opencode/${InstallationVersion}` }
if (!process.env.PARALLEL_API_KEY) return headers
return { ...headers, Authorization: `Bearer ${process.env.PARALLEL_API_KEY}` }
}
function callProvider(
http: HttpClient.HttpClient,
provider: WebSearchProvider,
params: Schema.Schema.Type<typeof Parameters>,
ctx: Tool.Context,
) {
if (provider === "parallel") {
return McpWebSearch.call(
http,
McpWebSearch.PARALLEL_URL,
"web_search",
McpWebSearch.ParallelSearchArgs,
{
objective: params.query,
search_queries: [params.query],
session_id: ctx.sessionID,
model_name: webSearchModelName(ctx.extra),
},
"25 seconds",
parallelAuthHeaders(),
)
}
return McpWebSearch.call(
http,
McpWebSearch.EXA_URL,
"web_search_exa",
McpWebSearch.SearchArgs,
{
query: params.query,
type: params.type || "auto",
numResults: params.numResults || 8,
livecrawl: params.livecrawl || "fallback",
contextMaxCharacters: params.contextMaxCharacters,
},
"25 seconds",
)
}
export const WebSearchTool = Tool.define(
"websearch",
Effect.gen(function* () {
@@ -33,6 +111,10 @@ export const WebSearchTool = Tool.define(
parameters: Parameters,
execute: (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) =>
Effect.gen(function* () {
const provider = selectWebSearchProvider(ctx.sessionID)
const title = webSearchProviderLabel(provider)
yield* ctx.metadata({ title: `${title} "${params.query}"`, metadata: { provider } })
yield* ctx.ask({
permission: "websearch",
patterns: [params.query],
@@ -43,27 +125,16 @@ export const WebSearchTool = Tool.define(
livecrawl: params.livecrawl,
type: params.type,
contextMaxCharacters: params.contextMaxCharacters,
provider,
},
})
const result = yield* McpExa.call(
http,
"web_search_exa",
McpExa.SearchArgs,
{
query: params.query,
type: params.type || "auto",
numResults: params.numResults || 8,
livecrawl: params.livecrawl || "fallback",
contextMaxCharacters: params.contextMaxCharacters,
},
"25 seconds",
)
const result = yield* callProvider(http, provider, params, ctx)
return {
output: result ?? "No search results found. Please try a different query.",
title: `Web search: ${params.query}`,
metadata: {},
title: `${title}: ${params.query}`,
metadata: { provider },
}
}).pipe(Effect.orDie),
}