feat(gateway): add web search emulation for Anthropic API Key accounts
Inject web search capability for Claude Console (API Key) accounts that don't natively support Anthropic's web_search tool. When a pure web_search request is detected, the gateway calls Brave Search or Tavily API directly and constructs an Anthropic-protocol-compliant SSE/JSON response without forwarding to upstream. Backend: - New `pkg/websearch/` SDK: Brave and Tavily provider implementations with io.LimitReader, proxy support, and Redis-based quota tracking (Lua atomic INCR + TTL, DECR rollback on failure) - Global config via `settings.web_search_emulation_config` (JSON) with in-process cache + singleflight, input validation, API key merge on save, and sanitized API responses - Channel-level toggle via `channels.features_config` JSONB column (DB migration 101) - Account-level toggle via `accounts.extra.web_search_emulation` - Request interception in `Forward()` with SSE streaming response construction using json.Marshal (no manual string concatenation) - Manager hot-reload: `RebuildWebSearchManager()` called on config save and startup via `SetWebSearchRedisClient()` - 70 unit tests covering providers, manager, config validation, sanitization, tool detection, query extraction, and response building Frontend: - Settings → Gateway tab: Web Search Emulation config card with global toggle, provider list (add/remove, API key, priority, quota, proxy) - Channels → Anthropic tab: web search emulation toggle with global state linkage (disabled when global off) - Account Create/Edit modals: web search emulation toggle for API Key type with Toggle component - Full i18n coverage (zh + en)
This commit is contained in:
@@ -41,6 +41,7 @@ export interface Channel {
|
||||
status: string
|
||||
billing_model_source: string // "requested" | "upstream"
|
||||
restrict_models: boolean
|
||||
features_config?: Record<string, unknown>
|
||||
group_ids: number[]
|
||||
model_pricing: ChannelModelPricing[]
|
||||
model_mapping: Record<string, Record<string, string>> // platform → {src→dst}
|
||||
@@ -56,6 +57,7 @@ export interface CreateChannelRequest {
|
||||
model_mapping?: Record<string, Record<string, string>>
|
||||
billing_model_source?: string
|
||||
restrict_models?: boolean
|
||||
features_config?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface UpdateChannelRequest {
|
||||
@@ -67,6 +69,7 @@ export interface UpdateChannelRequest {
|
||||
model_mapping?: Record<string, Record<string, string>>
|
||||
billing_model_source?: string
|
||||
restrict_models?: boolean
|
||||
features_config?: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface PaginatedResponse<T> {
|
||||
|
||||
@@ -482,6 +482,42 @@ export async function updateBetaPolicySettings(
|
||||
return data
|
||||
}
|
||||
|
||||
// --- Web Search Emulation Config ---
|
||||
|
||||
export interface WebSearchProviderConfig {
|
||||
type: 'brave' | 'tavily'
|
||||
api_key: string
|
||||
api_key_configured: boolean
|
||||
priority: number
|
||||
quota_limit: number
|
||||
quota_refresh_interval: 'daily' | 'weekly' | 'monthly'
|
||||
quota_used?: number
|
||||
proxy_id: number | null
|
||||
expires_at: number | null
|
||||
}
|
||||
|
||||
export interface WebSearchEmulationConfig {
|
||||
enabled: boolean
|
||||
providers: WebSearchProviderConfig[]
|
||||
}
|
||||
|
||||
export async function getWebSearchEmulationConfig(): Promise<WebSearchEmulationConfig> {
|
||||
const { data } = await apiClient.get<WebSearchEmulationConfig>(
|
||||
'/admin/settings/web-search-emulation'
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function updateWebSearchEmulationConfig(
|
||||
config: WebSearchEmulationConfig
|
||||
): Promise<WebSearchEmulationConfig> {
|
||||
const { data } = await apiClient.put<WebSearchEmulationConfig>(
|
||||
'/admin/settings/web-search-emulation',
|
||||
config
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
export const settingsAPI = {
|
||||
getSettings,
|
||||
updateSettings,
|
||||
@@ -497,7 +533,9 @@ export const settingsAPI = {
|
||||
getRectifierSettings,
|
||||
updateRectifierSettings,
|
||||
getBetaPolicySettings,
|
||||
updateBetaPolicySettings
|
||||
updateBetaPolicySettings,
|
||||
getWebSearchEmulationConfig,
|
||||
updateWebSearchEmulationConfig
|
||||
}
|
||||
|
||||
export default settingsAPI
|
||||
|
||||
Reference in New Issue
Block a user