F5: web search/fetch on the Kimi services (kimi-cli parity)

web_search now speaks the Kimi search service (kimi-cli tools/web/search.py,
wire-verified against api.kimi.com):
- POST {coding_base}/search with {text_query, limit 1-20 (default 5),
  enable_page_crawling, timeout_seconds: 30}, OAuth bearer +
  X-Msh-Tool-Call-Id; results render in kimi-cli's Title/Date/URL/Summary
  schema with result URLs as citations.
- The old implementation called the xAI Responses API with a search model;
  that client is fully replaced and the entire model-based config surface
  is excised root-and-branch: web_search_model config keys/env/CLI plumbing,
  resolve_web_search_sampling_config, toolset web_search SamplerConfig,
  RemoteSettings.web_search_model, default_web_search_model.
- Enablement is now purely structural: the service exists only on the Kimi
  Code subscription channel, so OAuth sessions get Enabled and API-key-only
  sessions get Disabled (tool absent) — per PRD F5.

web_fetch gains the Kimi fetch service as its primary path (kimi-cli
tools/web/fetch.py): POST {coding_base}/fetch with {url}, Accept:
text/markdown, OAuth bearer + X-Msh-Tool-Call-Id; the 200 body is the
extracted markdown (still overflow-budgeted). Any service failure falls
back to the existing local pipeline (SSRF guards, cache, extraction).
The tool gate defaults ON now (kimi-cli always offers FetchURL) and the
egress User-Agent no longer claims grok-agent/x.ai.

Verified end-to-end against the scripted mock service: a headless session
drove web_search (limit/crawling/call-id observed on the wire) then
web_fetch (Accept + call-id observed) to completion.
This commit is contained in:
2026-07-17 19:33:19 -04:00
parent 78cd94a751
commit 74b210535e
34 changed files with 659 additions and 1209 deletions
-8
View File
@@ -328,8 +328,6 @@ pub const DEFAULT_MODELS_JSON: &str = include_str!("../default_models.json");
struct DefaultModels {
default: String,
/// Falls back to `default` if not specified in JSON.
web_search: Option<String>,
/// Falls back to `default` if not specified in JSON.
image_description: Option<String>,
/// Falls back to `default` if not specified in JSON.
session_summary: Option<String>,
@@ -361,11 +359,6 @@ pub fn default_model() -> &'static str {
&DEFAULTS.default
}
/// Model for web search tool synthesis. Falls back to default model.
pub fn default_web_search_model() -> &'static str {
DEFAULTS.web_search.as_deref().unwrap_or(&DEFAULTS.default)
}
/// Model for image describe. Falls back to default model.
pub fn default_image_description_model() -> &'static str {
DEFAULTS
@@ -613,7 +606,6 @@ mod tests {
fn bundled_fallback_is_kimi_catalog() {
assert_eq!(default_model(), "kimi-for-coding");
// Aux models fall back to the default (no dedicated entries).
assert_eq!(default_web_search_model(), "kimi-for-coding");
assert_eq!(default_image_description_model(), "kimi-for-coding");
assert_eq!(default_session_summary_model(), "kimi-for-coding");
// No grok remnants in the embedded fallback.