Skip to content

feat: add MiMo V2 Pro reasoning support via OpenRouter#67

Open
thygrrr wants to merge 2 commits intomainfrom
feature/mimo-v2-pro-integration
Open

feat: add MiMo V2 Pro reasoning support via OpenRouter#67
thygrrr wants to merge 2 commits intomainfrom
feature/mimo-v2-pro-integration

Conversation

@thygrrr
Copy link
Copy Markdown
Collaborator

@thygrrr thygrrr commented Apr 14, 2026

Summary

  • Adds LLMResponse.reasoning_details and threads it through add_assistant_messageloop.py / tty.py so OpenRouter's MiMo V2 Pro reasoning segments round-trip verbatim in multi-turn history
  • Request-side reasoning is passed via extra_body={"reasoning": {"enabled": True}} through a new OpenRouter registry override — extra_body bypasses litellm.drop_params=True, which would otherwise silently strip the unknown kwarg
  • Fixes a latent bug in _apply_model_overrides: it only consulted find_by_model(), which skips gateways, so any model_overrides attached to OpenRouter or AiHubMix was dead code. It now also consults self._gateway

Files

  • pocketfox/providers/base.py — new reasoning_details field on LLMResponse
  • pocketfox/providers/litellm_provider.py_merge_overrides helper, gateway-aware override lookup, reasoning_details extraction with model_extra fallback, synthesizes reasoning_content from details when server populates details-only
  • pocketfox/providers/registry.py — OpenRouter spec gains ("xiaomi/mimo", {"extra_body": {"reasoning": {"enabled": True}}})
  • pocketfox/agent/context.pyadd_assistant_message gains reasoning_details param
  • pocketfox/agent/loop.py + pocketfox/cli/tty.py — forward response.reasoning_details
  • tests/test_litellm_reasoning.py — 9 new unit tests
  • scripts/test_mimo.py — manual end-to-end smoke test (requires `OPENROUTER_API_KEY`)

Test plan

  • `pytest tests/test_litellm_reasoning.py -v` — 9/9 green (parse paths + gateway override routing + extra_body merge)
  • `pytest tests/` — 228 passed, 1 pre-existing flake in `test_message_batching` (passes in isolation, unrelated to reasoning code)
  • `python scripts/test_mimo.py` — manual two-turn smoke test against real OpenRouter API; verifies `reasoning_details` is populated and round-trips through a follow-up turn without the server rejecting history

Notes

The gateway-override fix is a secondary benefit — it unblocks any future per-model override on OpenRouter/AiHubMix, not just MiMo.

Summary by CodeRabbit

  • New Features

    • Capture and propagate extended AI "reasoning" details from model responses.
    • Apply model-specific overrides to enable advanced reasoning for compatible models.
  • Tests

    • Added comprehensive tests for reasoning parsing, synthesis, provider override behavior, and merge logic.
  • Chores

    • Added a manual smoke-test script to exercise reasoning-enabled model flows.

Adds reasoning_details round-trip for OpenRouter's MiMo V2 Pro in the
same style as the existing reasoning_content path for DeepSeek-R1/Kimi.
Request-side reasoning is passed via extra_body so litellm.drop_params
can't silently strip it, and _apply_model_overrides now consults the
active gateway spec so registry overrides fire for gateway-routed models
(previously a latent dead-code bug for any override attached to
OpenRouter or AiHubMix).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 671d047b-7e62-4afe-8c75-c98baf9afc0e

📥 Commits

Reviewing files that changed from the base of the PR and between c05160e and 77327d6.

📒 Files selected for processing (1)
  • scripts/test_mimo.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/test_mimo.py

Walkthrough

Adds an optional reasoning_details field across the provider, parsing, agent loop, context, CLI, registry overrides, tests, and a manual smoke-test script, enabling propagation and synthesis of structured reasoning data alongside assistant messages.

Changes

Cohort / File(s) Summary
Context & Agent
pocketfox/agent/context.py, pocketfox/agent/loop.py, pocketfox/cli/tty.py
ContextBuilder.add_assistant_message() now accepts reasoning_details; callers in the agent loop and TTY pass response.reasoning_details into the conversation context.
Provider Base Types
pocketfox/providers/base.py
LLMResponse dataclass gained optional `reasoning_details: list[dict[str, Any]]
LiteLLM Provider
pocketfox/providers/litellm_provider.py
Added _merge_overrides() for deep-merge of extra_body; _apply_model_overrides() consults registry and gateway spec; _parse_response() extracts reasoning_details (from message or model_extra), synthesizes reasoning_content when missing, and includes reasoning_details in returned LLMResponse.
Provider Registry
pocketfox/providers/registry.py
Added a model-specific override for openrouter/xiaomi/mimo injecting extra_body with {"reasoning": {"enabled": True}}.
Tests & Scripts
tests/test_litellm_reasoning.py, scripts/test_mimo.py
New unit tests for parsing/synthesis and override merging; added scripts/test_mimo.py manual smoke test demonstrating reasoning extraction and multi-turn behavior with the Mimo model.

Sequence Diagram

sequenceDiagram
    participant Client as Client/TTY
    participant AgentLoop as Agent Loop
    participant Provider as LiteLLM Provider
    participant LLM as LLM Service
    participant Context as Context Builder

    Client->>AgentLoop: send user input
    AgentLoop->>Provider: request completion (model, overrides)
    Provider->>Provider: apply overrides (_merge_overrides / gateway)
    Provider->>LLM: send request (includes extra_body.reasoning)
    LLM-->>Provider: response (content, reasoning_content?, reasoning_details?)
    Provider->>Provider: _parse_response -> extract/synthesize reasoning_content and reasoning_details
    Provider-->>AgentLoop: return LLMResponse (content + reasoning_details)
    AgentLoop->>Context: add_assistant_message(content, reasoning_details)
    Context->>Context: store assistant message with reasoning_details
    AgentLoop-->>Client: present assistant content
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I found a trail of reasoning in the grass,
Little details tucked behind each pass,
Merged and parsed, they hop along with care,
From model to context, now we all share,
A clever breadcrumb tale — hop, sniff, and pass! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main objective: adding MiMo V2 Pro reasoning support via OpenRouter, which aligns directly with the primary changes across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
scripts/test_mimo.py (1)

20-20: Remove unused import.

The ContextBuilder import is marked as unused (noqa: F401) with a comment claiming it "mirrors prod code path", but this script doesn't exercise any ContextBuilder functionality. The import can be safely removed.

🔧 Suggested fix
-from pocketfox.agent.context import ContextBuilder  # noqa: F401  (unused direct, but mirrors prod code path)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test_mimo.py` at line 20, Remove the unused import of ContextBuilder
in scripts/test_mimo.py: delete the line "from pocketfox.agent.context import
ContextBuilder  # noqa: F401" (and the trailing noqa comment) since the script
does not use ContextBuilder; verify there are no references to ContextBuilder
elsewhere in the file and run tests/lint to ensure no remaining unused-import
warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/test_mimo.py`:
- Line 20: Remove the unused import of ContextBuilder in scripts/test_mimo.py:
delete the line "from pocketfox.agent.context import ContextBuilder  # noqa:
F401" (and the trailing noqa comment) since the script does not use
ContextBuilder; verify there are no references to ContextBuilder elsewhere in
the file and run tests/lint to ensure no remaining unused-import warnings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: daaa7f8b-cfa3-45b0-bc94-23fd6b849b1e

📥 Commits

Reviewing files that changed from the base of the PR and between 833341a and c05160e.

📒 Files selected for processing (8)
  • pocketfox/agent/context.py
  • pocketfox/agent/loop.py
  • pocketfox/cli/tty.py
  • pocketfox/providers/base.py
  • pocketfox/providers/litellm_provider.py
  • pocketfox/providers/registry.py
  • scripts/test_mimo.py
  • tests/test_litellm_reasoning.py

def _merge_overrides(kwargs: dict[str, Any], overrides: dict[str, Any]) -> None:
"""Merge registry overrides into kwargs, deep-merging extra_body."""
for key, val in overrides.items():
if key == "extra_body" and isinstance(kwargs.get("extra_body"), dict) and isinstance(val, dict):
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This looks fishy, why can we have a dict or a non-dict here, and have to fall back on RTTI?

@thygrrr thygrrr self-assigned this Apr 14, 2026
@thygrrr thygrrr requested a review from blue-duval April 14, 2026 18:05
Copy link
Copy Markdown
Collaborator

@blue-duval blue-duval left a comment

Choose a reason for hiding this comment

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

Solid PR. The core approach is clean — reasoning_details verbatim round-trip plus synthesized reasoning_content for backwards compatibility. The model_extra fallback for LiteLLM's drop_params behavior is a nice detail.

What I like:

  • _merge_overrides with deep merge for extra_body — elegant solution to the drop_params problem
  • Gateway-based override fix (_apply_model_overrides now consults self._gateway) — good that this came along as a secondary benefit
  • Tests are thorough and mock cleanly
  • scripts/test_mimo.py is well-documented with clear exit codes

Minor things:

  1. _apply_model_overrides early return: The loop breaks after the first pattern match (return). If find_by_model and self._gateway matched the same pattern, the first wins. Currently harmless because find_by_model returns None for gateway models — but if that changes, it's a silent bug. Worth a comment at minimum.

  2. Synthesis with mixed segment types: reasoning_details could theoretically contain different type values (not just reasoning.text). The synthesis logic concatenates everything with .get("text", "") — works, but if OpenRouter ever sends non-text segments, empty strings end up in the synthesis. Probably not an issue today.

  3. Docstring coverage: CodeRabbit is right to flag this — _merge_overrides and _fake_response have docstrings, but the new test functions don't. I know test functions are self-explanatory, but if the coverage rule wants 80%...

  4. Minor in test_merge_overrides_non_extra_body_keys_overwrite: The test checks shallow overwrite, but _merge_overrides does kwargs[key] = val for non-extra_body keys — that's an override, not a merge. Test name is correct, but in the context of the "deep merge" tests it could be confusing. Very minor.

Overall: LGTM after gustos. The docstring thing is the only thing CodeRabbit blocks — whether you fix that or dismiss the check is your call. 👍

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.

2 participants