198 lines
6.1 KiB
Python
198 lines
6.1 KiB
Python
"""
|
|
Thinking-control parameter registry.
|
|
|
|
This module only decides which request parameter should be sent to suppress
|
|
model reasoning. Transport, retry, and final output filtering live elsewhere.
|
|
Keep rules conservative: add a request parameter only when the provider/model
|
|
family has a known control surface. Unsupported parameters are removed by the
|
|
OpenAI-compatible degradation path.
|
|
"""
|
|
|
|
import re
|
|
from copy import deepcopy
|
|
from typing import Any, Dict, List
|
|
|
|
|
|
THINKING_CONTROL_RULES: List[Dict[str, Any]] = [
|
|
{
|
|
"name": "zai_glm_thinking",
|
|
"description": "GLM/Z.AI models with thinking.type control",
|
|
"patterns": [
|
|
r"glm[-_/.]?(4\.5|4\.6|4\.7|5)",
|
|
r"glm[-_/.]?4\.5v",
|
|
],
|
|
"params": {"thinking": {"type": "disabled"}},
|
|
"sources": ["Z.AI Thinking Mode"],
|
|
},
|
|
{
|
|
"name": "qwen_enable_thinking",
|
|
"description": "Qwen3/Qwen-VL endpoints that accept enable_thinking",
|
|
"patterns": [
|
|
r"qwen[-_/.]?3(?!.*r1)",
|
|
r"qwen.*[-_/.]?vl",
|
|
],
|
|
"params": {"enable_thinking": False},
|
|
"sources": ["Qwen/DashScope OpenAI-compatible API"],
|
|
},
|
|
{
|
|
"name": "deepseek_thinking",
|
|
"description": "DeepSeek endpoints that accept thinking.type",
|
|
"patterns": [
|
|
r"deepseek[-_/.]?(chat|v3)(?!.*r1)",
|
|
],
|
|
"params": {"thinking": {"type": "disabled"}},
|
|
"sources": ["DeepSeek thinking mode API"],
|
|
},
|
|
{
|
|
"name": "gemini_flash_reasoning",
|
|
"description": "Gemini Flash/Lite via OpenAI-compatible proxies",
|
|
"patterns": [
|
|
r"gemini[-_/.]?2\.(0|5)[-_/.]?(flash|lite)",
|
|
],
|
|
"params": {"reasoning_effort": "none"},
|
|
"sources": ["OpenAI-compatible Gemini proxies"],
|
|
},
|
|
]
|
|
|
|
|
|
OLLAMA_NATIVE_RULES = {
|
|
"parameter_name": "think",
|
|
"disable_value": False,
|
|
"enable_value": True,
|
|
"supported_patterns": [
|
|
r"deepseek.*(r1|v3)",
|
|
r"qwen.*(3|r1|thinking|vl)",
|
|
r".*thinking",
|
|
],
|
|
"sources": ["Ollama /api/chat think parameter"],
|
|
}
|
|
|
|
|
|
OLLAMA_NATIVE_EXCLUDE_PATTERNS = [
|
|
# Community abliterated/uncensored vision variants often expose Qwen names
|
|
# but do not consistently follow Ollama's native thinking contract.
|
|
r"abliterated",
|
|
r"uncensored",
|
|
]
|
|
|
|
|
|
EXCLUDE_PATTERNS = [
|
|
# Google states these cannot be fully disabled; final filtering is the fallback.
|
|
r"gemini[-_/.]?2\.5[-_/.]?pro",
|
|
r"gemini[-_/.]?3[-_/.]?pro",
|
|
# xAI reasoning models currently do not expose a reliable off switch.
|
|
r"grok.*reason",
|
|
r"grok[-_/.]?4",
|
|
# Ollama GPT-OSS accepts only low/medium/high and cannot be fully disabled.
|
|
r"gpt[-_/.]?oss",
|
|
# Reasoning-only families should not be forced with guessed parameters.
|
|
r".*speciale",
|
|
]
|
|
|
|
|
|
def _matches(patterns: List[str], model_lower: str) -> bool:
|
|
return any(re.search(pattern, model_lower) for pattern in patterns)
|
|
|
|
|
|
def build_thinking_suppression(
|
|
provider: str,
|
|
model: str,
|
|
disable_thinking: bool = True,
|
|
) -> Dict[str, Any]:
|
|
"""Return request parameters for thinking control, or an empty dict."""
|
|
if not model:
|
|
return {}
|
|
|
|
model_lower = model.strip().lower()
|
|
provider_lower = provider.strip().lower() if provider else ""
|
|
|
|
if _matches(EXCLUDE_PATTERNS, model_lower):
|
|
return {}
|
|
|
|
if provider_lower == "ollama":
|
|
if _matches(OLLAMA_NATIVE_EXCLUDE_PATTERNS, model_lower):
|
|
return {}
|
|
if _matches(OLLAMA_NATIVE_RULES["supported_patterns"], model_lower):
|
|
value = (
|
|
OLLAMA_NATIVE_RULES["disable_value"]
|
|
if disable_thinking
|
|
else OLLAMA_NATIVE_RULES["enable_value"]
|
|
)
|
|
return {OLLAMA_NATIVE_RULES["parameter_name"]: value}
|
|
return {}
|
|
|
|
if not disable_thinking:
|
|
return {}
|
|
|
|
for rule in THINKING_CONTROL_RULES:
|
|
if _matches(rule["patterns"], model_lower):
|
|
return deepcopy(rule["params"])
|
|
|
|
return {}
|
|
|
|
|
|
def should_append_no_thinking_instruction(
|
|
provider: str,
|
|
model: str,
|
|
disable_thinking: bool = True,
|
|
) -> bool:
|
|
if not disable_thinking or not model:
|
|
return False
|
|
|
|
provider_lower = provider.strip().lower() if provider else ""
|
|
model_lower = model.strip().lower()
|
|
|
|
if provider_lower == "ollama" and _matches(OLLAMA_NATIVE_EXCLUDE_PATTERNS, model_lower):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def get_rule_info(provider: str, model: str) -> Dict[str, Any]:
|
|
"""Return matching rule details for diagnostics."""
|
|
if not model:
|
|
return {"matched": False}
|
|
|
|
model_lower = model.strip().lower()
|
|
provider_lower = provider.strip().lower() if provider else ""
|
|
|
|
if _matches(EXCLUDE_PATTERNS, model_lower):
|
|
return {
|
|
"matched": True,
|
|
"rule_name": "excluded",
|
|
"description": "Known model family without a reliable disable parameter",
|
|
"params": {},
|
|
"sources": [],
|
|
}
|
|
|
|
if provider_lower == "ollama":
|
|
if _matches(OLLAMA_NATIVE_EXCLUDE_PATTERNS, model_lower):
|
|
return {
|
|
"matched": True,
|
|
"rule_name": "ollama_native_excluded",
|
|
"description": "Community model variant without reliable native thinking control",
|
|
"params": {},
|
|
"sources": [],
|
|
}
|
|
if _matches(OLLAMA_NATIVE_RULES["supported_patterns"], model_lower):
|
|
return {
|
|
"matched": True,
|
|
"rule_name": "ollama_native",
|
|
"description": "Ollama native /api/chat thinking control",
|
|
"params": {OLLAMA_NATIVE_RULES["parameter_name"]: OLLAMA_NATIVE_RULES["disable_value"]},
|
|
"sources": OLLAMA_NATIVE_RULES["sources"],
|
|
}
|
|
return {"matched": False}
|
|
|
|
for rule in THINKING_CONTROL_RULES:
|
|
if _matches(rule["patterns"], model_lower):
|
|
return {
|
|
"matched": True,
|
|
"rule_name": rule["name"],
|
|
"description": rule["description"],
|
|
"params": deepcopy(rule["params"]),
|
|
"sources": rule.get("sources", []),
|
|
}
|
|
|
|
return {"matched": False}
|