Skip to content

Conversation

@JordiPosthumus
Copy link

  • feat(llm): add OpenAI-compatible (LM Studio) support and provider switch
    - Add provider to LLMConfig (default: anthropic; also supports openai-compatible)
    - Implement OpenAI chat/completions path with tool schema conversion
    - Parse tool_calls from OpenAI-style responses
    - Improve error reporting for non-2xx responses

    • feat(llm): reasoning support for LM Studio
      • Use message.reasoning_content when available (LM Studio setting)
      • Fallback: extract … from message.content into Thinking panel
    • docs: LM Studio usage and reasoning section
      • Explain LM Studio developer setting to separate reasoning_content
      • Document fallback extraction
      • Clarify MiniMax (Anthropic) interleaved thinking vs. LM Studio behavior
    • CLI: pass provider from config into LLMClient
    • config-example.yaml: document provider and LM Studio example

    Interleaved thinking behavior

    • MiniMax M2 (Anthropic-compatible): returns structured blocks (text, thinking, tool_use). Mini Agent preserves and resubmits these,
      enabling true interleaved thinking across steps.
    • LM Studio (OpenAI-compatible): returns standard Chat Completions. Reasoning is provided via message.reasoning_content (when enabled)
      or inline …. Mini Agent now captures both and renders them in the Thinking panel.

    Test plan

    • Start LM Studio local server at http://localhost:1234/v1 and load a model
    • User config (~/.mini-agent/config/config.yaml):
    • Run mini-agent and verify:
      • Basic chat works
      • Thinking panel shows reasoning via reasoning_content or extraction
      • Tool calls (optional) work with OpenAI tools schema

              - Add provider to config + CLI
              - Implement chat/completions path with tool schema conversion
              - Update README + config example for LM Studio
              - Preserve Anthropic (MiniMax) default
…hinking

      - Explain LM Studio setting for reasoning_content
      - Note <think>…</think> fallback extraction
      - Clarify Anthropic-style interleaved thinking for MiniMax M2
@adao-max
Copy link
Collaborator

Thanks for you PR, our engineer will review it soon

@AkairoDev
Copy link
Collaborator

Thanks for the contribution! This is a solid start for LM Studio support. I've identified a few critical issues that need to be addressed before merging.


Critical Issue 1: Architecture - Missing Provider Abstraction

The current implementation mixes both providers in a single LLMClient class with if-else branching. This will become unmaintainable as we add more providers.

Suggested refactoring:

llm/base.py

class BaseLLM(ABC):
@AbstractMethod
async def generate(self, messages: list[Message], tools: list[dict] | None) -> LLMResponse:
pass

@abstractmethod
def _convert_messages(self, messages: list[Message]) -> Any:
    pass

@abstractmethod
def _parse_response(self, result: dict) -> LLMResponse:
    pass

llm/anthropic.py

class AnthropicLLM(BaseLLM):
...

llm/openai.py

class OpenAILLM(BaseLLM):
...This follows the Strategy pattern and makes it easy to add new providers without modifying existing code.


Critical Issue 2: Missing Thinking/Reasoning Implementation

According to the MiniMax documentation, OpenAI-compatible responses include thinking in two ways:

  1. Via message.reasoning_content field (when reasoning_split=True)
  2. Via <think>...</think> tags in content (when reasoning_split=False)

Current implementation (lines 239-278):
def _parse_openai_response(self, result: dict[str, Any]) -> LLMResponse:
return LLMResponse(
content=text_content,
thinking=None, # ❌ Always None - doesn't extract thinking!
...
)Required implementation:
def _parse_openai_response(self, result: dict[str, Any]) -> LLMResponse:
msg = choice.get("message", {})
text_content = msg.get("content") or ""

# Method 1: Extract from reasoning_content field
thinking = msg.get("reasoning_content")

# Method 2: Extract from <think> tags if reasoning_content not present
if not thinking and text_content:
    import re
    think_pattern = r'<think>(.*?)</think>'
    think_matches = re.findall(think_pattern, text_content, re.DOTALL | re.IGNORECASE)
    if think_matches:
        thinking = '\n\n'.join(match.strip() for match in think_matches)
        text_content = re.sub(think_pattern, '', text_content, flags=re.DOTALL | re.IGNORECASE).strip()

return LLMResponse(content=text_content, thinking=thinking, ...)This is critical because your README promises this feature and interleaved thinking is a core capability of MiniMax M2.

Important Issue 3: Missing Test Coverage

The PR lacks tests for the new OpenAI provider functionality. Minimum required tests:

  • Message format conversion
  • Tool schema conversion
  • Thinking extraction from reasoning_content
  • Thinking extraction from <think> tags
  • Tool calls response parsing
  • Integration test with mocked LM Studio response

Let me know if you need any clarification or help with the implementation.

  - Extract BaseLLM and provider classes (AnthropicLLM, OpenAILLM)
  - LLMClient becomes a thin facade that selects provider
  - OpenAI path: parse reasoning_content and <think> fallback
  - Convert tools to OpenAI schema; parse tool_calls
  - docs: LM Studio reasoning + interleaved thinking notes
  - tests: add OpenAI provider unit tests (no network)
@JordiPosthumus
Copy link
Author

  • Summary
    • Implemented provider Strategy pattern (AnthropicLLM, OpenAILLM) with LLMClient as a thin facade.
    • LM Studio/OpenAI path now handles reasoning:
      • Uses message.reasoning_content when provided by LM Studio.
      • Falls back to parsing … from message.content.
    • Tools are converted from Anthropic-style to OpenAI tools schema; tool_calls are parsed back.
    • Docs updated for LM Studio usage and reasoning.
    • Added offline unit tests.
  • Validation
    • pytest -q tests/test_openai_provider.py → 5 passed.
    • Local LM Studio e2e: confirmed chat works and Thinking panel is populated via reasoning_content and extraction.

@JordiPosthumus
Copy link
Author

  • Thanks for the detailed review — addressed all points:
    • Critical 1 (Architecture): Introduced Strategy pattern providers
      • mini_agent/llm/providers/base.py
      • mini_agent/llm/providers/anthropic.py
      • mini_agent/llm/providers/openai_compat.py
      • LLMClient now dispatches to the provider from mini_agent/llm/init.py.
    • Critical 2 (Thinking): Implemented OpenAI-compatible reasoning handling
      • Use message.reasoning_content when available.
      • Fallback: extract … from content, move to Thinking; strip from visible output.
    • Important 3 (Tests): Added offline coverage in tests/test_openai_provider.py
      • Tool schema conversion
      • Reasoning extraction (reasoning_content + )
      • Tool calls parsing
      • Basic message conversion
  • Happy to add more test vectors or adjust structure if you prefer a slightly different provider layout.

@AkairoDev
Copy link
Collaborator

Hi @JordiPosthumus,

Thank you for taking the time to contribute this PR and for adding LM Studio support! We really appreciate your effort in implementing the OpenAI-compatible provider with reasoning handling.

However, after careful review, we found that this implementation doesn't fully meet our current integration requirements and code standards. We apologize for not being able to merge it at this time.

That said, we do recognize the value of OpenAI protocol support. Our team is planning to implement a native OpenAI-compatible layer in the near future that will align with our architecture and design principles.

We hope you understand, and we truly appreciate your interest in improving Mini-Agent. Feel free to continue engaging with the project, and we look forward to your future contributions!

Best regards,
The Mini-Agent Team

@AkairoDev AkairoDev closed this Nov 13, 2025
@JordiPosthumus
Copy link
Author

You are welcome ! I love Minimax M2 and I really really want a harness where I can use it at full power with proper thinking on LM Studio (or MLX) so much. This model is amazing!

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.

3 participants