mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Desktop: add reasoning_effort to DEFAULT_SETTINGS, the settings registry (Models section) and _llm_overrides(), forwarded through start() only when a real level is chosen -- "off" stays a no-op so a provider default is never overridden. Reuses the exact path temperature/top_p already use, no new surface. CLI: delete InteractiveConfig.variant, a documented "reasoning effort" knob with zero consumers and no flag to set it. Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com> Co-authored-by: MervinPraison <[email protected]>
This commit is contained in:
1 parent
3c688fcb74
commit
ba7019534e
6 files changed
+100
-4
No files matched your search
@@ -64,9 +64,6 @@ class InteractiveConfig:
|
||||
# Sharing
|
||||
share: bool = False
|
||||
|
||||
# Variant (reasoning effort)
|
||||
variant: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "InteractiveConfig":
|
||||
"""Create config from environment variables."""
|
||||
@@ -97,7 +94,6 @@ class InteractiveConfig:
|
||||
memory=getattr(args, "memory", False),
|
||||
files=getattr(args, "file", []) or [],
|
||||
share=getattr(args, "share", False),
|
||||
variant=getattr(args, "variant", None),
|
||||
autonomy=getattr(args, "autonomy", True),
|
||||
no_context=getattr(args, "no_context", False),
|
||||
)
|
||||
|
||||
@@ -713,6 +713,7 @@ DEFAULT_SETTINGS = {
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 0,
|
||||
"top_p": 1,
|
||||
"reasoning_effort": "off",
|
||||
"base_url": "",
|
||||
"api_key": "",
|
||||
"system_prompt": "",
|
||||
@@ -1100,6 +1101,11 @@ def _llm_overrides(cfg: dict) -> dict:
|
||||
out["max_tokens"] = int(cfg["max_tokens"])
|
||||
if cfg.get("top_p") != DEFAULT_SETTINGS["top_p"]:
|
||||
out["top_p"] = float(cfg["top_p"])
|
||||
# "off" is the default no-op; anything else is a provider-portable effort
|
||||
# level (minimal|low|medium|high) that Agent maps to each backend's native
|
||||
# knob, exactly as the CLI's --thinking does.
|
||||
if cfg.get("reasoning_effort") not in (None, "", DEFAULT_SETTINGS["reasoning_effort"]):
|
||||
out["reasoning_effort"] = str(cfg["reasoning_effort"])
|
||||
_apply_env(cfg)
|
||||
return out
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ class StubAgent:
|
||||
self.started = 0
|
||||
self.chat_history = []
|
||||
self.seen_history = None
|
||||
self.seen_kwargs = None
|
||||
|
||||
def _append_to_chat_history(self, message):
|
||||
self.chat_history.append(message)
|
||||
@@ -51,6 +52,7 @@ class StubAgent:
|
||||
def start(self, prompt, stream=True, **kwargs):
|
||||
self.started += 1
|
||||
self.seen_history = list(self.chat_history)
|
||||
self.seen_kwargs = dict(kwargs)
|
||||
# Tools "run" before the model's answer would arrive, exactly as the
|
||||
# display callback records them during a real turn.
|
||||
for tool in self.tools:
|
||||
@@ -312,6 +314,71 @@ class HistoryAcrossSessions(unittest.TestCase):
|
||||
f"a blank turn was replayed: {agent.seen_history}")
|
||||
|
||||
|
||||
class ReasoningEffortReachesTheModel(unittest.TestCase):
|
||||
"""The GUI setting must actually arrive at the turn.
|
||||
|
||||
reasoning_effort is a first-class Agent kwarg -- the engine forwards it
|
||||
through start() alongside temperature/top_p, and only when the user chose a
|
||||
real level. "off" is the default no-op and must not be sent, or it would
|
||||
override a provider default the user never touched.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.server = ThreadingHTTPServer(("127.0.0.1", 0), server.Handler)
|
||||
cls.port = cls.server.server_address[1]
|
||||
cls.thread = threading.Thread(target=cls.server.serve_forever, daemon=True)
|
||||
cls.thread.start()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.server.shutdown()
|
||||
cls.server.server_close()
|
||||
|
||||
def setUp(self):
|
||||
server._agents.clear()
|
||||
server._tool_queue().clear()
|
||||
self._orig = server.load_settings
|
||||
|
||||
def tearDown(self):
|
||||
server.load_settings = self._orig
|
||||
|
||||
def _install(self):
|
||||
agent = StubAgent(chunks=["ok"])
|
||||
server._agents["t"] = agent
|
||||
server._agents["t\x00notools"] = agent
|
||||
return agent
|
||||
|
||||
def _chat(self):
|
||||
body = json.dumps({"prompt": "hi", "chat_id": "t", "session": "t"}).encode()
|
||||
req = urllib.request.Request(
|
||||
f"http://127.0.0.1:{self.port}/chat", data=body, method="POST",
|
||||
headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(req, timeout=30) as response:
|
||||
response.read()
|
||||
|
||||
def test_a_chosen_level_is_forwarded_to_start(self):
|
||||
server.load_settings = lambda: dict(self._orig(), reasoning_effort="high")
|
||||
agent = self._install()
|
||||
self._chat()
|
||||
self.assertEqual(agent.seen_kwargs.get("reasoning_effort"), "high",
|
||||
"the GUI reasoning_effort never reached the turn")
|
||||
|
||||
def test_off_is_the_default_and_is_not_sent(self):
|
||||
# Forwarding "off" unasked would override a provider default the user
|
||||
# never touched, exactly as sending temperature=0.7 unasked would.
|
||||
agent = self._install()
|
||||
self._chat()
|
||||
self.assertNotIn("reasoning_effort", agent.seen_kwargs,
|
||||
"the no-op default was forwarded and overrode the provider")
|
||||
|
||||
def test_the_setting_round_trips_through_save(self):
|
||||
# It is a real storage key, not a value dropped by load_settings.
|
||||
server.save_settings({"reasoning_effort": "low"})
|
||||
self.assertEqual(server.load_settings()["reasoning_effort"], "low")
|
||||
server.save_settings({"reasoning_effort": "off"})
|
||||
|
||||
|
||||
class FetchUrlDoesNotFollowRedirects(unittest.TestCase):
|
||||
"""The approval card names one URL; the fetch must not reach another.
|
||||
|
||||
|
||||
@@ -47,6 +47,15 @@ const SETTINGS = [
|
||||
control: { kind: "slider", min: 0, max: 1, step: 0.01 },
|
||||
default: 1, validate: clampNum(0, 1) },
|
||||
|
||||
{ key: "reasoning_effort", section: "model", label: "Reasoning effort",
|
||||
description: "How hard the model thinks before answering. Off leaves the provider default. Maps to each backend's native knob.",
|
||||
keywords: ["thinking", "reasoning", "effort", "budget", "o1", "o3"],
|
||||
control: { kind: "select", options: [
|
||||
{ value: "off", label: "Off" }, { value: "minimal", label: "Minimal" },
|
||||
{ value: "low", label: "Low" }, { value: "medium", label: "Medium" },
|
||||
{ value: "high", label: "High" }]},
|
||||
default: "off" },
|
||||
|
||||
{ key: "base_url", section: "model", label: "Base URL override",
|
||||
description: "Point at a proxy, Azure, or a local server. Blank uses the provider default.",
|
||||
keywords: ["endpoint", "proxy", "azure", "ollama", "lm studio"],
|
||||
|
||||
@@ -676,6 +676,15 @@ const SETTINGS = [
|
||||
control: { kind: "slider", min: 0, max: 1, step: 0.01 },
|
||||
default: 1, validate: clampNum(0, 1) },
|
||||
|
||||
{ key: "reasoning_effort", section: "model", label: "Reasoning effort",
|
||||
description: "How hard the model thinks before answering. Off leaves the provider default. Maps to each backend's native knob.",
|
||||
keywords: ["thinking", "reasoning", "effort", "budget", "o1", "o3"],
|
||||
control: { kind: "select", options: [
|
||||
{ value: "off", label: "Off" }, { value: "minimal", label: "Minimal" },
|
||||
{ value: "low", label: "Low" }, { value: "medium", label: "Medium" },
|
||||
{ value: "high", label: "High" }]},
|
||||
default: "off" },
|
||||
|
||||
{ key: "base_url", section: "model", label: "Base URL override",
|
||||
description: "Point at a proxy, Azure, or a local server. Blank uses the provider default.",
|
||||
keywords: ["endpoint", "proxy", "azure", "ollama", "lm studio"],
|
||||
|
||||
@@ -47,6 +47,15 @@ const SETTINGS = [
|
||||
control: { kind: "slider", min: 0, max: 1, step: 0.01 },
|
||||
default: 1, validate: clampNum(0, 1) },
|
||||
|
||||
{ key: "reasoning_effort", section: "model", label: "Reasoning effort",
|
||||
description: "How hard the model thinks before answering. Off leaves the provider default. Maps to each backend's native knob.",
|
||||
keywords: ["thinking", "reasoning", "effort", "budget", "o1", "o3"],
|
||||
control: { kind: "select", options: [
|
||||
{ value: "off", label: "Off" }, { value: "minimal", label: "Minimal" },
|
||||
{ value: "low", label: "Low" }, { value: "medium", label: "Medium" },
|
||||
{ value: "high", label: "High" }]},
|
||||
default: "off" },
|
||||
|
||||
{ key: "base_url", section: "model", label: "Base URL override",
|
||||
description: "Point at a proxy, Azure, or a local server. Blank uses the provider default.",
|
||||
keywords: ["endpoint", "proxy", "azure", "ollama", "lm studio"],
|
||||
|
||||
Reference in new issue
Block a user