Skip to content

feat(web): Moonshot and Kimi native search (rescue of #5686, review findings fixed) - #5720

Merged
Hmbown merged 8 commits into
mainfrom
rescue/kimi-native-search-20260829
Aug 30, 2026
Merged

feat(web): Moonshot and Kimi native search (rescue of #5686, review findings fixed)#5720
Hmbown merged 8 commits into
mainfrom
rescue/kimi-native-search-20260829

Conversation

@Hmbown

@Hmbown Hmbown commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Rescue of #5686@h3c-hexin's fork branch can't be force-pushed (403), and it conflicts with current main. This PR carries their exact commits 2258bf3cf7 + abb997805 (authorship preserved verbatim, second parent of the merge 5f4dd7583), so #5686 auto-links as MERGED when this lands. This is the last slice of the #5681 native-search umbrella.

Review findings fixed on top (the P1 was already satisfied on main by the contributor's own d8a90177fd — KIMI_K3_FORMULA_MIN_TIMEOUT_MS = 180_000 with first == Some(Duration::from_millis(180_000)) asserted; the PR doesn't touch web_search.rs):

  • P2 domains fail-closed (7e35b0447): zero-maximum_domain_count() providers (Moonshot et al.) with domain-filtered queries now decline with NotAvailable so the chain falls back to configured/DuckDuckGo, instead of erroring the whole search; over-limit on providers that honor domains stays InvalidInput. Test: zero_domain_native_providers_decline_without_failing_the_chain.
  • P2 route matcher (3c525e636): Kimi Code dispatch now uses the exact route matcher (moonshot_base_url_is_exact_kimi_code: scheme/authority case-insensitive, path exact) — https://API.KIMI.COM/CODING/V1 no longer misroutes to the official /search; the capability gate is tightened the same way.
  • P2 citation parens (7ee65864d): citations_from_text tracks paren depth so wiki/Foo_(bar) and nested Baz_(qux_(nested)) extract intact; an unmatched closer still terminates.

Conflict resolutions (additive throughout): main's adapter roster + Moonshot/Kimi modules merged; capabilities/resolver/tests kept main's modelstudio/deepseek/zai cases and added the PR's moonshot gate + tests; changelogs carry the PR's bullet.

Verification on the final tree: focused native search 24/24 (incl. k3_formula_executes_tool_fiber_and_returns_citations); config 624 passed / 1 skipped; full TUI lib 11,530 passed / 0 failed / 13 skipped; fmt clean; CI-exact clippy exit 0.

Closes #5686
Closes #5681 (with this slice the umbrella's six providers — Openai/Anthropic/Xai/Zai/ModelStudio/Deepseek arms plus Moonshot/Kimi — are all landed)

h3c-hexin and others added 6 commits August 28, 2026 16:37
Signed-off-by: hexin <372726039@qq.com>
Signed-off-by: hexin <372726039@qq.com>
…e search

Rescue merge of abb9978 (Pinvou feat/moonshot-kimi-provider-native-search,
PR #5686) onto current main (142 ahead). Contributor head preserved verbatim
as the second parent; conflicts resolved following the landed native-search
adapter pattern (#5682/#5683/#5690/#5691/#5693): main's Mimo/Zai/ModelStudio/
DeepSeek adapters and the post-fallback domain-constraint/budget plumbing
stay, the Kimi module + Moonshot dispatch and capability wiring join them.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…losed

Moonshot/Kimi (and the other contracts without a domain filter field) report
maximum_domain_count() == Some(0), so a domain-filtered query hit the shared
InvalidInput branch and killed the whole backend chain instead of reaching
the configured provider or DuckDuckGo. Decline with NotAvailable for that
case so the chain falls back; keep InvalidInput for genuinely over-limit
counts on providers that do honor domains (#5686 review).

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
The local case-insensitive whole-URL comparison let differently-cased paths
(https://API.KIMI.COM/CODING/V1) inherit the official membership /search
service. Reuse config's exact matcher (scheme/authority case-insensitive,
path exact) for both the kimi.rs dispatch and the capability gate, matching
the is_exact_kimi_code_route discipline everywhere else (#5686 review).

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Wikipedia-style URLs such as .../wiki/Foo_(bar) lost their balanced closing
paren to the terminator scan. Track paren depth while scanning so balanced
pairs stay part of the URL and an unmatched closer still ends it (#5686
review).

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@claude

claude Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 6m 4s —— View job


Claude's review of #5720

Todo list

Summary

Reviewed the diff between the PR's recorded pre-merge base (8cd2006, the first parent of the 5f4dd75 merge commit) and HEAD — the resulting stat matched the PR's stated 12 files / +876/-14 exactly, so I'm confident it's the right diff (see environment note below on why I didn't use origin/main...HEAD directly).

One finding worth checking before merge (posted as an inline comment on kimi.rs):

  • search_builtin() may never fetch real search results (crates/tui/src/client/provider_native_search/kimi.rs:61-134, used for direct kimi-k2.6). When the model returns a $web_search tool call, this path echoes the model's own generated arguments (its query) back as the synthetic tool message content, then loops — it never calls out to actually execute a search. Compare with search_formula() just below it, which POSTs to a real .../fibers endpoint and injects the actual result before continuing. If Moonshot's builtin_function tool type requires the client to supply real results (rather than being fully server-executed within the same completion), this path would silently return ungrounded answers/citations while looking like it works. It's also the only one of the three new adapters (Kimi Code /search, K3 Formula, K2.6 builtin) with no mock-server round-trip test — k3_formula_executes_tool_fiber_and_returns_citations and the parse_kimi_code structured-response test both exist, but there's no equivalent for search_builtin. I can't verify Moonshot's actual builtin-function contract offline (no network access in this run), so flagging for the author's confirmation rather than as a confirmed bug.

  • Minor/lower-confidence: formula_fiber_result requires status == "succeeded" on the very first /fibers POST and bails otherwise. If fiber execution is actually asynchronous (create-then-poll) rather than synchronous, a "running"/"queued" status would fail immediately instead of being polled. Worth a quick check against the docs.

What looks solid

  • Domain fail-open fix (backend.rs): the Some(0) vs Some(n) vs None branching is correct — zero-domain-capable providers with a domain-filtered query now decline (NotAvailable, chain falls back) instead of the old fail-closed error; over-limit stays a typed InvalidInput. Covered by zero_domain_native_providers_decline_without_failing_the_chain, and I traced the logic by hand to confirm no double-fire for the domains.is_empty() / Some(0) case.
  • Kimi Code route matcher reuse: moonshot_base_url_is_exact_kimi_code (tui) correctly delegates to the pre-existing exact-route helper is_exact_kimi_code_route (config crate) rather than reintroducing a loose prefix match — good adherence to "grep for the existing thing and edit it."
  • Provider/model safety (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608): capability gating (documented_moonshot_web_search_for_route) is exact-route + exact-model, and the route resolver's gate must pass (Supported) before kimi.rs::search() is ever reached. So the model-name branch inside search() (K3 Formula vs K2.6 builtin) is safe — it's not inferring provider/model selection from a string prefix, it's dispatching wire dialect within an already-gated, already-selected Moonshot route.
  • Citation paren-balancing fix: correct, and tested for both the balanced (Foo_(bar), nested) and unbalanced-closer cases.
  • Bounded loops: MAX_NATIVE_SEARCH_ROUNDS / MAX_NATIVE_SEARCH_TOOL_CALLS with cumulative (not per-round) counting prevent a runaway tool-call loop, and the cumulative behavior is explicitly tested.
  • No secret-handling or injection issues found; API keys/headers flow through the existing DeepSeekClient transport shared by other adapters.

Environment note

This sandboxed run's origin/main git ref is shallow (only one unrelated commit), and git fetch, cargo, and gh all require approval that wasn't available non-interactively, so I could not run cargo check/cargo test myself to independently verify the PR's claimed 24/24 native-search and 624/1 config test results, or compile-check the diff. I instead reconstructed the correct diff from the merge commit's recorded parent and reviewed it statically line-by-line. If you want me to independently execute the build/test suite in a future run, cargo/git fetch would need to be added to --allowedTools.

Comment on lines +104 to +122
if tool_call.pointer("/function/name").and_then(Value::as_str) != Some("$web_search") {
bail!("Kimi native search requested an unexpected tool");
}
let id = tool_call
.get("id")
.and_then(Value::as_str)
.context("Kimi native web-search call omitted id")?;
let arguments = tool_call
.pointer("/function/arguments")
.and_then(Value::as_str)
.context("Kimi native web-search call omitted arguments")?;
let _: Value = serde_json::from_str(arguments)
.context("Kimi native web-search arguments were not valid JSON")?;
messages.push(json!({
"role": "tool",
"tool_call_id": id,
"name": "$web_search",
"content": arguments,
}));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search_builtin (used for direct kimi-k2.6) never calls out to fetch real search results for the $web_search tool call — it echoes the model's own generated arguments (the query it wants to search) back as the tool message content at line 121, then loops. Compare with search_formula just below, which POSTs to a .../fibers endpoint and injects the actual result (formula_fiber_result) before continuing.

If Moonshot's builtin_function tool type isn't fully server-executed within the same completion (i.e. the client is expected to supply real results, the way search_formula does), this path never performs a grounded search — the model just sees its own query parroted back, and the resulting "answer"/citations parsed by parse_final_message would be ungrounded rather than search-backed.

This is also the only one of the three new adapters with no mock-server round-trip test (k3_formula_executes_tool_fiber_and_returns_citations covers the Formula path, parse_kimi_code covers the Kimi Code path). Worth confirming against the actual $web_search builtin-function contract and adding an equivalent wiremock test for search_builtin.

Fix this →

@claude

claude Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 48s —— View job


Claude's review of #5720 (re-run after synchronize)

Todo list

  • Gather context (diff stat matches PR description exactly: 12 files, +876/-14)
  • Read full diff against origin/main
  • Check whether prior finding (search_builtin in kimi.rs) was addressed
  • Review correctness (logic, panics, unwrap/expect, error handling)
  • Review provider/model/route safety (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608 invariant)
  • Review reuse/simplification/dead code
  • Review test coverage
  • Review security (secrets, exec policy, input validation)
  • Post final review summary

Summary

git diff origin/main...HEAD on the current tree is byte-for-byte the same reviewed content as my prior pass — the extra commits since then (20e9574 sandbox/Windows-CI fix, 658292b merge of origin/main) don't touch any file in this PR's diff. So this is a re-confirmation, not a new review; the one open finding from before still applies unchanged.

Still open — search_builtin() may never fetch real search results (crates/tui/src/client/provider_native_search/kimi.rs:61-134, the path used for direct kimi-k2.6). When the model emits a $web_search tool call, this loop pushes the model's own arguments string straight back as the tool message content (line ~121) and continues — it never calls out anywhere to execute a search. Contrast with search_formula() immediately below, which POSTs to a real .../fibers endpoint and injects formula_fiber_result before continuing. If Moonshot's builtin_function tool type expects the client to supply real results (rather than being fully resolved server-side within the same completion), this path returns ungrounded answers/citations while looking like a working search. I don't have web access in this run to check Moonshot's docs and confirm the contract either way, so this remains a flag for the author to confirm, not a confirmed bug. It's also still the only one of the three adapters without a mock-server round-trip test (k3_formula_executes_tool_fiber_and_returns_citations covers Formula, kimi_code_request_and_structured_response_contract covers Kimi Code structured parsing; search_builtin has no equivalent).

Fix this →

Minor/lower-confidence (also unchanged): formula_fiber_result (kimi.rs) requires status == "succeeded" on the very first /fibers POST and bails otherwise; if fiber execution can be asynchronous (create-then-poll) rather than synchronous, a "running"/"queued" status would fail immediately instead of being polled.

Re-verified as correct

  • Domain fail-open (backend.rs:348-362): Some(0) (zero-domain-capable provider + domain-filtered query) now returns NotAvailable so the chain falls back; Some(n) over-limit stays InvalidInput. zero_domain_native_providers_decline_without_failing_the_chain exercises both branches against real Moonshot and xAI configs and asserts the exact error variant.
  • Kimi Code exact-route dispatch: kimi.rs::search() gates on moonshot_base_url_is_exact_kimi_code before falling through to model-based Formula/builtin dispatch; is_exact_moonshot_platform_route and documented_moonshot_web_search_for_route (capabilities.rs) independently gate capability exposure the same exact way (scheme/authority case-insensitive, path exact, now correctly extended to api.moonshot.cn too). route/tests.rs::moonshot_native_search_requires_exact_product_model_pair proves this end-to-end through RouteResolver, not just the isolated matcher — adjacent paths and unlisted models correctly stay Unknown.
  • Citation paren-balancing: citations_from_text's new depth-tracked match arms correctly keep Foo_(bar) and nested Baz_(qux_(nested)) intact while still terminating on an unmatched closer; both cases are tested.
  • Provider/model safety (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608): the search_formula vs search_builtin branch inside kimi.rs::search() only runs after documented_moonshot_web_search_for_route has already gated on exact route + exact model via the resolver — it's wire-dialect dispatch within an already-authorized route, not inference of provider selection from a model-name prefix.
  • Bounded loops: MAX_NATIVE_SEARCH_ROUNDS/MAX_NATIVE_SEARCH_TOOL_CALLS cumulative counting (reserve_native_search_tool_calls) is correct and covered by native_search_tool_call_limit_is_total_not_per_round.
  • No secret-handling or injection issues; auth flows through the existing shared DeepSeekClient/send_with_retry transport.

Environment note

No network access was available in this run (WebFetch permission not granted), so I could not check Moonshot's actual builtin_function/$web_search contract to resolve the one open finding, and did not re-run cargo test/cargo check — this review is based on static line-by-line reading of the diff, cross-checked against the route-resolver test that exercises the full path.

aboimpinto pushed a commit to aboimpinto/CodeWhale that referenced this pull request Aug 30, 2026
…, greening shared macOS/Windows CI

Hosted `Test (macos-latest)` and `Test (windows-latest)` have been red on
main since S1 landed, and every open PR riding main inherited the same
failures (Hmbown#5712 Hmbown#5719 Hmbown#5720 Hmbown#5721 Hmbown#5703 Hmbown#5722 — verified from each exact
head's own job logs).

macOS (6 failures in sandbox::read_guard::tests): the hosted runner's
$TMPDIR is /var/folders/... — a symlink into /private/var/... —
so `canonicalize` and `current_dir` hand back the resolved spelling while
the rule was only lexically normalized against the literal one. The
canonicalized candidate could therefore never match a rule, and none of
the symlink / denied-tree tests fired. Prior local verification passed
only because it ran with TMPDIR on a plain volume.

That is a product hole, not a test artifact: on macOS /etc, /var and /tmp
are symlinks into /private, so `read_file /private/etc/sudoers` walked
around the built-in /etc/sudoers rule (the Seatbelt setter already
canonicalized its own copy of the list; the in-process matcher did not).
A subtree rule now remembers its resolved spelling (`DenyRule::subtree`)
and `check` matches a candidate against either spelling. Exemptions still
compare the configured spelling only — unchanged, out of scope here.

Windows (1 failure): root_parent_traversal_does_not_escape_above_root
asserted a Unix `/etc` while `normalize_lexically("/../../etc")` correctly
resolves a rooted-but-driveless path against the cwd's drive (`D:\etc` on
the runner). The test now spells the traversal from the current drive root
and keeps the Unix literal under `cfg!(unix)`.

Defeat tests (fail on the unpatched matcher on every host, independent of
TMPDIR): rule_spelled_through_a_symlinked_root_matches_the_resolved_spelling
(unix) and macos_private_spelling_of_a_machine_wide_rule_is_refused (macOS).

Local, hosted-equivalent (RUSTFLAGS=-Dwarnings, RUST_MIN_STACK=16MiB,
cargo nextest run -p codewhale-tui --lib --all-features --locked
--profile ci, TMPDIR = macOS default /var/folders/...):
baseline at 20e9574: 22 run / 16 passed / 6 failed (the hosted six);
after: 24 run / 24 passed. Defeat probe with rule resolution disabled
(tests kept): plain TMPDIR 6 run / 2 failed = exactly the two new tests;
symlinked TMPDIR 6 run / 4 failed. fmt clean; changelog synced. Hosted CI
is the final gate.

Not touched (branch-specific, not shared-main): Hmbown#5712's Windows
runtime_api::tests::events_endpoint_respects_since_seq_cursor timeout
(8.91 s vs the 8 s ci_scaled budget; Hmbown#5712 does not touch runtime_api) and
Hmbown#5722's Ubuntu fleet::executor failure.

No-Issue: shared-main CI repair; no tracking issue exists for the
hosted-runner symlink shape.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 47s —— View job


Claude's review of #5720 (re-run after synchronize)

Todo list

  • Gather context (diff stat matches PR description exactly: 12 files, +876/-14)
  • Read full diff against origin/main
  • Check whether prior finding (search_builtin in kimi.rs) was addressed
  • Review correctness (logic, panics, unwrap/expect, error handling)
  • Review provider/model/route safety (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608 invariant)
  • Review reuse/simplification/dead code
  • Review test coverage
  • Review security (secrets, exec policy, input validation)
  • Post final review summary

Summary

git diff origin/main...HEAD is again byte-for-byte the same reviewed content as both prior passes — the new commits since the last review (8a3f14b merge of PR #5724's Windows-CI sandbox fix, a5e2c7a merge of origin/main) don't touch any file in this PR's diff. This is a third re-confirmation, not a new review; the one open finding still applies unchanged.

Still open — search_builtin() may never fetch real search results (crates/tui/src/client/provider_native_search/kimi.rs:61-134, the path used for direct kimi-k2.6). When the model emits a $web_search tool call, this loop pushes the model's own arguments string straight back as the tool message content (line ~121) and continues — it never calls out anywhere to execute a search. Contrast with search_formula() immediately below (line 129+), which POSTs to a real .../fibers endpoint and injects formula_fiber_result before continuing. If Moonshot's builtin_function tool type expects the client to supply real results (rather than being fully resolved server-side within the same completion), this path returns ungrounded answers/citations while looking like a working search. I still don't have web access in this run to check Moonshot's docs and resolve the contract question either way. It also remains the only one of the three adapters without a mock-server round-trip test — confirmed again by grepping kimi.rs's mod tests: k3_formula_executes_tool_fiber_and_returns_citations covers Formula, kimi_code_request_and_structured_response_contract covers Kimi Code, and there is still no equivalent for search_builtin.

Fix this →

Minor/lower-confidence (also unchanged): formula_fiber_result (kimi.rs) requires status == "succeeded" on the very first /fibers POST and bails otherwise; if fiber execution can be asynchronous (create-then-poll) rather than synchronous, a "running"/"queued" status would fail immediately instead of being polled.

Re-verified as correct (unchanged from prior passes)

  • Domain fail-open (backend.rs): Some(0) (zero-domain-capable provider + domain-filtered query) returns NotAvailable so the chain falls back; Some(n) over-limit stays InvalidInput. zero_domain_native_providers_decline_without_failing_the_chain exercises both branches.
  • Kimi Code exact-route dispatch: kimi.rs::search() gates on moonshot_base_url_is_exact_kimi_code before falling through to model-based Formula/builtin dispatch; route/tests.rs::moonshot_native_search_requires_exact_product_model_pair proves this end-to-end through RouteResolver.
  • Citation paren-balancing: citations_from_text's depth-tracked match arms keep Foo_(bar) and nested Baz_(qux_(nested)) intact while still terminating on an unmatched closer; both cases are tested.
  • Provider/model safety (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608): the search_formula vs search_builtin branch inside kimi.rs::search() only runs after documented_moonshot_web_search_for_route has already gated on exact route + exact model via the resolver — wire-dialect dispatch within an already-authorized route, not inference of provider selection from a model-name prefix.
  • Bounded loops: MAX_NATIVE_SEARCH_ROUNDS/MAX_NATIVE_SEARCH_TOOL_CALLS cumulative counting is correct and tested (native_search_tool_call_limit_is_total_not_per_round).
  • No secret-handling or injection issues; auth flows through the existing shared DeepSeekClient/send_with_retry transport.

Environment note

No network access was available in this run, so I could not check Moonshot's actual builtin_function/$web_search contract to resolve the one open finding, and did not re-run cargo test/cargo check — this review is based on static line-by-line reading of the diff (confirmed byte-identical to the two prior reviews), cross-checked against the route-resolver test that exercises the full path.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 potential issues.

Devin Review

Comment on lines +356 to +361
let domain_limit = client.maximum_domain_count();
if !query.domains.is_empty() && domain_limit == Some(0) {
return Err(ToolError::not_available(format!(
"{} native web search cannot honor domain filters",
client.provider().as_str()
)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Fallback changes keep stale results

When maximum_domain_count declines a domain-filtered search, its fallback result excludes the fallback configuration from the cache key. Backend changes can return old results.

Prompt for agents
ProviderNativeSearchBackend now deliberately returns NotAvailable for domain-filtered queries on providers such as Moonshot, making the configured search backend the normal execution path. However, execute_search in crates/tui/src/tools/web_search.rs keys every provider-native-first search only by ProviderNativeSearchClient::cache_identity. It omits the configured fallback provider and search_base_url. Include the complete effective backend-chain identity in provider-native cache scope, so changing the configured fallback provider or endpoint cannot reuse a result produced by the old chain. Add a focused cache test using the same state namespace, native provider, and query with two different fallback configurations.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +260 to +270
if provider_kind == ProviderKind::Moonshot {
let effective_base_url = req
.base_url_override
.as_deref()
.unwrap_or_else(|| descriptor.default_base_url());
selected.capabilities.server_side_web_search = documented_moonshot_web_search_for_route(
provider_kind,
selected.wire_model_id.as_str(),
effective_base_url,
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Capability overwrite stays route-scoped

documented_moonshot_web_search_for_route runs after custom facts are cleared, but only exact endpoint/model pairs regain native search. Custom gateways remain unknown.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +533 to +535
kind == ProviderKind::Moonshot
&& (is_exact_https_route(base_url, "api.moonshot.ai", "v1")
|| is_exact_https_route(base_url, "api.moonshot.cn", "v1"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: China K3 keeps Formula budget

The existing Formula timeout uses the shared direct-route matcher. Adding the China host therefore grants it the same 180-second budget.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +351 to 366
// Moonshot/Kimi, Z.AI, MiMo, and the Responses-dialect routes cannot
// express domain filters in their native wire contracts. Declining
// here must not fail the whole search: report this backend unavailable
// so the chain falls back to the configured provider or DuckDuckGo,
// which honor domains natively or through post-filtering.
let domain_limit = client.maximum_domain_count();
if !query.domains.is_empty() && domain_limit == Some(0) {
return Err(ToolError::not_available(format!(
"{} native web search cannot honor domain filters",
client.provider().as_str()
)));
}
if let Some(maximum) = domain_limit
&& query.domains.len() > maximum
{
return Err(ToolError::invalid_input(format!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Domain refusal preserves fallback

maximum_domain_count returns zero for contracts without filtering, so NotAvailable advances the chain. Positive limits still reject oversized domain lists.

(Refers to this code)

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +590 to +607
// Balanced parentheses belong to the URL (Wikipedia titles such as
// `Foo_(bar)` keep their closing paren); an unmatched closer ends it.
let mut open_parens = 0_usize;
let end = tail
.char_indices()
.find_map(|(index, ch)| {
(index > 0
.find_map(|(index, ch)| match ch {
'(' => {
open_parens += 1;
None
}
')' if open_parens > 0 => {
open_parens -= 1;
None
}
_ => (index > 0
&& (ch.is_whitespace()
|| matches!(ch, ')' | ']' | '}' | '>' | '"' | '\'' | '`')))
.then_some(index)
.then_some(index),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Parenthesis scanning remains bounded

open_parens preserves balanced URL segments, while unmatched closers still terminate. Unclosed segments cannot consume text beyond the next whitespace.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@Hmbown
Hmbown merged commit e1a8fb8 into main Aug 30, 2026
28 checks passed
@Hmbown
Hmbown deleted the rescue/kimi-native-search-20260829 branch August 30, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend provider-native web search to DeepSeek, Qwen, Kimi, Z.AI/BigModel, and MiMo

2 participants