Skip to content

Commit ad076d0

Browse files
committed
add web search
1 parent 8a25f9e commit ad076d0

12 files changed

Lines changed: 1974 additions & 26 deletions

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,38 @@ project-local `.claude/settings.json` and `.claude/settings.local.json` and
128128
applied to Claude requests only when the model supports reasoning. If it is not
129129
configured, Claude requests do not infer or attach a reasoning effort.
130130

131+
## Web Search
132+
133+
Not every Copilot model can run web search. If web search is not configured or
134+
the selected backend cannot search, the bridge tells the client how to configure
135+
`COPILOT_WEB_SEARCH_BACKEND` and does not switch models automatically.
136+
137+
For Claude Code, configure web search in the user-level
138+
`~/.claude/settings.json`:
139+
140+
```json
141+
{
142+
"env": {
143+
"COPILOT_WEB_SEARCH_BACKEND": "gpt-5.5"
144+
}
145+
}
146+
```
147+
148+
For Codex CLI, configure the same backend as a top-level key in
149+
`~/.codex/config.toml`:
150+
151+
```toml
152+
COPILOT_WEB_SEARCH_BACKEND = "gpt-5.5"
153+
```
154+
155+
| Value | Search path | Requirement |
156+
| ----- | ----------- | ----------- |
157+
| Copilot model id, for example `gpt-5.5` | Copilot HTTP `/responses` + `web_search_preview` | The model must support Copilot Responses web search. |
158+
| `searxng` | Local SearXNG at `http://localhost:8080` | Start SearXNG yourself. Setup guide: https://github.com/betaHi/openclaw-searxng-search. |
159+
| `copilot-cli` or `copilot` | GitHub Copilot CLI `web_search` tool, using the current request model | Install and sign in to GitHub Copilot CLI yourself. |
160+
161+
The bridge never installs Docker, SearXNG, or Copilot CLI automatically.
162+
131163
## Start flags
132164

133165
Common:

src/bridges/claude/anthropic-types.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,35 @@ export interface AnthropicToolUseBlock {
5252
input: Record<string, unknown>
5353
}
5454

55+
export interface AnthropicServerToolUseBlock {
56+
type: "server_tool_use"
57+
id: string
58+
name: "web_search"
59+
input: Record<string, unknown>
60+
}
61+
62+
export interface AnthropicWebSearchResultBlock {
63+
type: "web_search_tool_result"
64+
tool_use_id: string
65+
content:
66+
| Array<{
67+
type: "web_search_result"
68+
title: string
69+
url: string
70+
encrypted_content?: string
71+
page_age?: string | null
72+
}>
73+
| {
74+
type: "web_search_tool_result_error"
75+
error_code:
76+
| "too_many_requests"
77+
| "invalid_input"
78+
| "max_uses_exceeded"
79+
| "query_too_long"
80+
| "unavailable"
81+
}
82+
}
83+
5584
export interface AnthropicThinkingBlock {
5685
type: "thinking"
5786
thinking: string
@@ -65,6 +94,8 @@ export type AnthropicUserContentBlock =
6594
export type AnthropicAssistantContentBlock =
6695
| AnthropicTextBlock
6796
| AnthropicToolUseBlock
97+
| AnthropicServerToolUseBlock
98+
| AnthropicWebSearchResultBlock
6899
| AnthropicThinkingBlock
69100

70101
export interface AnthropicUserMessage {
@@ -106,6 +137,9 @@ export interface AnthropicResponse {
106137
cache_creation_input_tokens?: number
107138
cache_read_input_tokens?: number
108139
service_tier?: "standard" | "priority" | "batch"
140+
server_tool_use?: {
141+
web_search_requests?: number
142+
}
109143
}
110144
}
111145

@@ -129,6 +163,10 @@ export interface AnthropicContentBlockStartEvent {
129163
| (Omit<AnthropicToolUseBlock, "input"> & {
130164
input: Record<string, unknown>
131165
})
166+
| (Omit<AnthropicServerToolUseBlock, "input"> & {
167+
input: Record<string, unknown>
168+
})
169+
| AnthropicWebSearchResultBlock
132170
| { type: "thinking"; thinking: string }
133171
}
134172

src/bridges/claude/non-stream-translation.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -567,14 +567,22 @@ function translateAnthropicToolsToOpenAI(
567567
return undefined
568568
}
569569

570-
return anthropicTools.map((tool) => ({
571-
type: "function",
572-
function: {
573-
name: toolNameMapper.toOpenAI(tool.name),
574-
description: tool.description,
575-
parameters: tool.input_schema,
576-
},
577-
}))
570+
const tools = anthropicTools.flatMap((tool) => {
571+
if (!tool.input_schema) {
572+
return []
573+
}
574+
575+
return [{
576+
type: "function" as const,
577+
function: {
578+
name: toolNameMapper.toOpenAI(tool.name),
579+
description: tool.description,
580+
parameters: tool.input_schema,
581+
},
582+
}]
583+
})
584+
585+
return tools.length > 0 ? tools : undefined
578586
}
579587

580588
function translateAnthropicToolChoiceToOpenAI(

0 commit comments

Comments
 (0)