fa45d8aa5f
- health_checklist.json: 192.168.1.122→node122
- ocr_client.py: docstring IP→node122
- docs/market-data-requirements.md: IP→node122
- 所有API调用通过ProxyHandler({})绕过系统代理
Privoxy对node122:18003返回500,直连正常
224 lines
8.1 KiB
Python
224 lines
8.1 KiB
Python
"""
|
|
LiteLLM Web Search Tool Definition
|
|
|
|
This module defines the standard web search tool used across LiteLLM.
|
|
Native provider tools (like Anthropic's web_search_20250305) are converted
|
|
to this format for consistent interception and execution.
|
|
"""
|
|
|
|
from typing import Any, Dict
|
|
|
|
from litellm.constants import LITELLM_WEB_SEARCH_TOOL_NAME
|
|
|
|
|
|
def get_litellm_web_search_tool() -> Dict[str, Any]:
|
|
"""
|
|
Get the standard LiteLLM web search tool definition.
|
|
|
|
This is the canonical tool definition that all native web search tools
|
|
(like Anthropic's web_search_20250305, Claude Code's web_search, etc.)
|
|
are converted to for interception.
|
|
|
|
Returns:
|
|
Dict containing the Anthropic-style tool definition with:
|
|
- name: Tool name
|
|
- description: What the tool does
|
|
- input_schema: JSON schema for tool parameters
|
|
|
|
Example:
|
|
>>> tool = get_litellm_web_search_tool()
|
|
>>> tool['name']
|
|
'litellm_web_search'
|
|
"""
|
|
return {
|
|
"name": LITELLM_WEB_SEARCH_TOOL_NAME,
|
|
"description": (
|
|
"Search the web for information. Use this when you need current "
|
|
"information or answers to questions that require up-to-date data."
|
|
),
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {
|
|
"type": "string",
|
|
"description": "The search query to execute",
|
|
}
|
|
},
|
|
"required": ["query"],
|
|
},
|
|
}
|
|
|
|
|
|
def get_litellm_web_search_tool_openai() -> Dict[str, Any]:
|
|
"""
|
|
Get the standard LiteLLM web search tool definition in OpenAI format.
|
|
|
|
Used by async_pre_call_deployment_hook which runs in the chat completions
|
|
path where tools must be in OpenAI format (type: "function" with
|
|
function.parameters).
|
|
|
|
Returns:
|
|
Dict containing the OpenAI-style tool definition.
|
|
"""
|
|
return {
|
|
"type": "function",
|
|
"function": {
|
|
"name": LITELLM_WEB_SEARCH_TOOL_NAME,
|
|
"description": (
|
|
"Search the web for information. Use this when you need current "
|
|
"information or answers to questions that require up-to-date data."
|
|
),
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {
|
|
"type": "string",
|
|
"description": "The search query to execute",
|
|
}
|
|
},
|
|
"required": ["query"],
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def is_web_search_tool_chat_completion(tool: Dict[str, Any]) -> bool:
|
|
"""
|
|
Check if a tool is a web search tool for Chat Completions API (strict check).
|
|
|
|
This is a stricter version that ONLY checks for the exact LiteLLM web search tool name.
|
|
Use this for Chat Completions API to avoid false positives with user-defined tools.
|
|
|
|
Detects ONLY:
|
|
- LiteLLM standard: name == "litellm_web_search" (Anthropic format)
|
|
- OpenAI format: type == "function" with function.name == "litellm_web_search"
|
|
|
|
Args:
|
|
tool: Tool dictionary to check
|
|
|
|
Returns:
|
|
True if tool is exactly the LiteLLM web search tool
|
|
|
|
Example:
|
|
>>> is_web_search_tool_chat_completion({"name": "litellm_web_search"})
|
|
True
|
|
>>> is_web_search_tool_chat_completion({"type": "function", "function": {"name": "litellm_web_search"}})
|
|
True
|
|
>>> is_web_search_tool_chat_completion({"name": "web_search"})
|
|
False
|
|
>>> is_web_search_tool_chat_completion({"name": "WebSearch"})
|
|
False
|
|
"""
|
|
tool_name = tool.get("name", "")
|
|
tool_type = tool.get("type", "")
|
|
|
|
# Check for OpenAI format: {"type": "function", "function": {"name": "litellm_web_search"}}
|
|
if tool_type == "function" and "function" in tool:
|
|
function_def = tool.get("function", {})
|
|
function_name = function_def.get("name", "")
|
|
if function_name == LITELLM_WEB_SEARCH_TOOL_NAME:
|
|
return True
|
|
|
|
# Check for LiteLLM standard tool (Anthropic format)
|
|
if tool_name == LITELLM_WEB_SEARCH_TOOL_NAME:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def is_anthropic_native_web_search_tool(tool: Dict[str, Any]) -> bool:
|
|
"""
|
|
Check if a tool is an Anthropic-native ``web_search_*`` tool.
|
|
|
|
Native clients (Anthropic SDK, Claude Desktop, Anthropic Console) send
|
|
tools like ``{"type": "web_search_20250305", "name": "web_search"}`` and
|
|
expect the response to contain ``web_search_tool_result`` content blocks
|
|
so that citations can be rendered. This helper identifies that contract
|
|
so the agentic loop can emit native-format blocks for those clients
|
|
without affecting clients that send the LiteLLM standard tool.
|
|
|
|
Returns False for the LiteLLM standard tool (``litellm_web_search``),
|
|
the OpenAI-shaped variant, the bare ``WebSearch`` legacy name, and the
|
|
bare ``web_search`` name (Claude Code style).
|
|
"""
|
|
tool_type = tool.get("type", "")
|
|
if not isinstance(tool_type, str):
|
|
return False
|
|
return tool_type.startswith("web_search_") and tool_type != "function"
|
|
|
|
|
|
def is_web_search_tool(tool: Dict[str, Any]) -> bool:
|
|
"""
|
|
Check if a tool is a web search tool (native or LiteLLM standard).
|
|
|
|
Detects:
|
|
- LiteLLM standard: name == "litellm_web_search"
|
|
- OpenAI format: type == "function" with function.name == "litellm_web_search"
|
|
- Anthropic native: type starts with "web_search_" (e.g., "web_search_20250305")
|
|
- Claude Code: name == "web_search" with a type field
|
|
- Custom: name == "WebSearch" (legacy interception marker — only matched
|
|
when input_schema is absent; see note below)
|
|
|
|
Note on the legacy ``WebSearch`` name:
|
|
Clients like Claude Desktop / Cowork ship a *client-side* tool called
|
|
``WebSearch`` (a fully-formed Anthropic client tool with its own
|
|
``input_schema``) that they handle themselves. Treating that as our
|
|
interception marker hijacks it server-side and the client's own tool
|
|
handler never fires — which means Cowork's separate native
|
|
``web_search_20250305`` sub-request (where citation data actually
|
|
flows) never gets made.
|
|
|
|
Real Anthropic client tools always carry an ``input_schema`` (the API
|
|
rejects them otherwise), so a bare ``{name: "WebSearch"}`` with no
|
|
schema is the only thing that could be a legacy interception marker.
|
|
Gate the match on schema absence to keep both groups working.
|
|
|
|
Args:
|
|
tool: Tool dictionary to check
|
|
|
|
Returns:
|
|
True if tool is a web search tool
|
|
|
|
Example:
|
|
>>> is_web_search_tool({"name": "litellm_web_search"})
|
|
True
|
|
>>> is_web_search_tool({"type": "function", "function": {"name": "litellm_web_search"}})
|
|
True
|
|
>>> is_web_search_tool({"type": "web_search_20250305", "name": "web_search"})
|
|
True
|
|
>>> is_web_search_tool({"name": "calculator"})
|
|
False
|
|
>>> is_web_search_tool({"name": "WebSearch"}) # legacy interception marker
|
|
True
|
|
>>> is_web_search_tool({"name": "WebSearch", "input_schema": {"type": "object"}}) # Cowork client tool
|
|
False
|
|
"""
|
|
tool_name = tool.get("name", "")
|
|
tool_type = tool.get("type", "")
|
|
|
|
# Check for OpenAI format: {"type": "function", "function": {"name": "..."}}
|
|
if tool_type == "function" and "function" in tool:
|
|
function_def = tool.get("function", {})
|
|
function_name = function_def.get("name", "")
|
|
if function_name == LITELLM_WEB_SEARCH_TOOL_NAME:
|
|
return True
|
|
|
|
# Check for LiteLLM standard tool (Anthropic format)
|
|
if tool_name == LITELLM_WEB_SEARCH_TOOL_NAME:
|
|
return True
|
|
|
|
# Check for native Anthropic web_search_* types
|
|
if tool_type.startswith("web_search_"):
|
|
return True
|
|
|
|
# Check for Claude Code's web_search with a type field
|
|
if tool_name == "web_search" and tool_type:
|
|
return True
|
|
|
|
# Legacy "WebSearch" interception marker — only when no schema is
|
|
# present, so real client-side WebSearch tools (Cowork) pass through.
|
|
if tool_name == "WebSearch" and "input_schema" not in tool:
|
|
return True
|
|
|
|
return False
|