Skip to content

feat: add enable_thinking support for OpenAI-compatible providers - #2181

Open
RerankerGuo wants to merge 4 commits into
MemTensor:mainfrom
RerankerGuo:feat/issue-2149-enable-thinking
Open

feat: add enable_thinking support for OpenAI-compatible providers#2181
RerankerGuo wants to merge 4 commits into
MemTensor:mainfrom
RerankerGuo:feat/issue-2149-enable-thinking

Conversation

@RerankerGuo

Copy link
Copy Markdown
Contributor

Description

Fixes #2149

Adds enable_thinking configuration parameter for OpenAI-compatible providers (Qwen, DeepSeek, MiniMax, etc.) to control whether the model produces <think> reasoning blocks before the actual response.

Models like Qwen3 and DeepSeek-R1 support an enable_thinking parameter in the chat completion body. When thinking is enabled, output contains <think>...</think> tags before the actual response, which can break JSON parsing in structured-output tasks (capture summarization, L3 abstraction, skill crystallization).

Changes

  1. Added enable_thinking field to OpenAILLMConfigbool | None defaulting to None:
    • None (default): provider's default behavior preserved (backward compatible)
    • True: explicitly pass enable_thinking=true
    • False: explicitly pass enable_thinking=false (protects JSON output tasks)
  2. OpenAILLM.generate() — extracted _build_request_body() helper; passes enable_thinking to request body when configured
  3. OpenAILLM.generate_stream() — same enable_thinking injection
  4. AzureLLM.generate() / generate_stream() — added getattr-gated support for enable_thinking (future-proof)
  5. Per-call overridekwargs["enable_thinking"] takes precedence over config-level setting
  6. Comprehensive tests in tests/llms/test_enable_thinking.py: default, config-level, kwarg-level, False-value, param-preservation

Before (provider default)

After (configurable)

Type of change

  • Bug fix (non-breaking — prevents JSON output breakage when thinking is unwanted)
  • New feature (non-breaking — adds configurable parameter)

How Has This Been Tested?

  • python3 -m py_compile src/memos/configs/llm.py
  • python3 -m py_compile src/memos/llms/openai.py
  • python3 -m py_compile tests/llms/test_enable_thinking.py
  • Logic verification: enable_thinking=None → param omitted; True/False → param present; kwarg override wins
  • Zero behavior change when enable_thinking is not set (backward compatible)

Checklist

@Memtensor-AI Memtensor-AI added area:core MOS 编排层 / 框架底座 / 跨模块问题 area:model llm + embedder + reranker status:in-progress Someone or AI is working on it | 人工或 AI 正在处理 labels Jul 29, 2026
@Memtensor-AI

Memtensor-AI commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

🤖 Open Code Review

Target: PR #2181
Task: 03457e832d3ea1fa
Base: main
Head: feat/issue-2149-enable-thinking

🔍 OpenCodeReview found 2 issue(s) in this PR.


1. src/memos/llms/openai.py (L207-L213)

AzureLLMConfig does not declare an enable_thinking field (it extends BaseLLMConfig which also lacks it). The getattr(..., None) fallback silently swallows any misconfiguration — if a user sets enable_thinking in their YAML/dict config for the Azure backend, Pydantic will likely reject it or it will be lost, but there's no loud failure.

Either add enable_thinking: bool | None = Field(default=None, ...) to AzureLLMConfig (or BaseLLMConfig) so it can be configured, or remove the config-based lookup and only allow it via kwargs. The current asymmetry between OpenAILLM (self.config.enable_thinking direct access) and AzureLLM (getattr fallback) makes the intent unclear.

💡 Suggested Change

Before:

        enable_thinking = kwargs.get(
            "enable_thinking", getattr(self.config, "enable_thinking", None)
        )
        if enable_thinking is not None:
            request_body["enable_thinking"] = enable_thinking
        response = self.client.chat.completions.create(**request_body)
        logger.info(f"Response from Azure OpenAI: {response.model_dump_json()}")

After:

        enable_thinking = kwargs.get("enable_thinking", self.config.enable_thinking)
        if enable_thinking is not None:
            request_body["enable_thinking"] = enable_thinking
        response = self.client.chat.completions.create(**request_body)
        logger.info(f"Response from Azure OpenAI: {response.model_dump_json()}")

2. src/memos/llms/openai.py (L131-L144)

generate_stream() does not reuse _build_request_body(), so the non-streaming and streaming paths diverge. Specifically, the streaming path hardcodes self.config.model_name_or_path instead of honouring a model_name_or_path kwarg override the way _build_request_body() does (kwargs.get("model_name_or_path", self.config.model_name_or_path)). Any future addition to _build_request_body() will also need to be manually mirrored here, increasing maintenance risk.

Consider calling _build_request_body(messages, **kwargs) and then adding "stream": True to the result, similar to how generate() works.

💡 Suggested Change

Before:

        request_body = {
            "model": self.config.model_name_or_path,
            "messages": messages,
            "stream": True,
            "temperature": kwargs.get("temperature", self.config.temperature),
            "max_tokens": kwargs.get("max_tokens", self.config.max_tokens),
            "top_p": kwargs.get("top_p", self.config.top_p),
            "extra_body": kwargs.get("extra_body", self.config.extra_body),
            "tools": kwargs.get("tools", NOT_GIVEN),
        }

        enable_thinking = kwargs.get("enable_thinking", self.config.enable_thinking)
        if enable_thinking is not None:
            request_body["enable_thinking"] = enable_thinking

After:

        request_body = self._build_request_body(messages, **kwargs)
        request_body["stream"] = True
        request_body.pop("tools", None)  # stream API does not support tools, already guarded above

Generated by cloud-assistant via Open Code Review.

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: INCONCLUSIVE

Automated tests inconclusive (auto-generated test defect); treated as non-blocking. Manual review recommended. Details: The test helper _make_config() passes a provider='openai' field to OpenAILLMConfig, but that config class forbids extra inputs, causing all 8 tests to fail at construction time. [advisory, non-gating] AI-generated tests on branch test/auto-gen-afe461593e5cdcda-20260729091129: 13/58 passed, 45 failed — these do NOT affect the PR verdict; review the branch manually.

Branch: feat/issue-2149-enable-thinking

@RerankerGuo
RerankerGuo force-pushed the feat/issue-2149-enable-thinking branch from 07ba039 to 59e98be Compare July 30, 2026 00:54
@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: INCONCLUSIVE

Automated tests inconclusive (auto-generated test defect); treated as non-blocking. Manual review recommended. Details: The new test file passes a provider='openai' field to OpenAILLMConfig, but that field is not permitted by the Pydantic model, causing all 8 tests to fail at construction time before exercising the actual enable_thinking logic. [advisory, non-gating] AI-generated tests on branch test/auto-gen-58710bc92f74b814-20260730090707: 98/98 passed — these do NOT affect the PR verdict; review the branch manually.

Branch: feat/issue-2149-enable-thinking

@Memtensor-AI

Copy link
Copy Markdown
Collaborator

⚠️ Automated Test Results: INCONCLUSIVE

Automated tests inconclusive (auto-generated test defect); treated as non-blocking. Manual review recommended. Details: The AI-generated test passes an unsupported provider='openai' field into OpenAILLMConfig, which rejects extra inputs via Pydantic's extra_forbidden validation. [advisory, non-gating] AI-generated tests on branch test/auto-gen-89dcf9c3ff4eea6b-20260731100701: 158/158 passed — these do NOT affect the PR verdict; review the branch manually.

Branch: feat/issue-2149-enable-thinking

Closes MemTensor#2149

Adds enable_thinking configuration parameter for OpenAI-compatible
providers (qwen, deepseek, minimax) to control whether the model
produces <think> reasoning blocks before the actual response.

Changes:
- Added enable_thinking field (bool | None) to OpenAILLMConfig
  - None (default): preserve provider's default behavior
  - True: explicitly enable thinking mode
  - False: explicitly disable thinking mode (prevents JSON breaking)
- OpenAILLM.generate / generate_stream now pass enable_thinking
  to API calls when configured, via enable_thinking body param
- AzureLLM.generate / generate_stream also support the parameter
  (gated by getattr for backward compatibility with older configs)
- Per-call override supported via kwargs (enable_thinking=True/False)
- Added tests covering default, config-level, and kwarg-level
  enable_thinking behavior in test_enable_thinking.py

This is a non-breaking change: when enable_thinking is unset,
request bodies are identical to previous versions.

Test: python3 -m py_compile src/memos/configs/llm.py
Test: python3 -m py_compile src/memos/llms/openai.py
Test: python3 -m py_compile tests/llms/test_enable_thinking.py
@RerankerGuo
RerankerGuo force-pushed the feat/issue-2149-enable-thinking branch from 08760c2 to 333852d Compare August 5, 2026 05:24
@Memtensor-AI

Copy link
Copy Markdown
Collaborator

✅ Automated Test Results: PASSED

All tests passed (14/14 executed). memos_python_core/changed-repo-python: 14/14. Duration: 5s [advisory, non-gating] AI-generated tests on branch test/auto-gen-03457e832d3ea1fa-20260805133404: 87/129 passed, 42 failed — these do NOT affect the PR verdict; review the branch manually.

Branch: feat/issue-2149-enable-thinking

@Memtensor-AI Memtensor-AI added status:ready Ready for implementation; waiting for assignee or AI dispatch | 可进入实现,等待认领或派发 and removed status:in-progress Someone or AI is working on it | 人工或 AI 正在处理 labels Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core MOS 编排层 / 框架底座 / 跨模块问题 area:model llm + embedder + reranker status:ready Ready for implementation; waiting for assignee or AI dispatch | 可进入实现,等待认领或派发

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] openai_compatible provider: support enableThinking parameter for thinking-capable models (Qwen3, DeepSeek-R1)

5 participants