Skip to content

Commit 841c51d

Browse files
committed
Release v4.5.5
1 parent 6709ae1 commit 841c51d

File tree

28 files changed

+1356
-459
lines changed

28 files changed

+1356
-459
lines changed

docker/Dockerfile.chat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
1616
# Install Python packages (using latest versions)
1717
RUN pip install --no-cache-dir \
1818
praisonai_tools \
19-
"praisonai>=4.5.3" \
19+
"praisonai>=4.5.5" \
2020
"praisonai[chat]" \
2121
"embedchain[github,youtube]"
2222

docker/Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
2020
# Install Python packages (using latest versions)
2121
RUN pip install --no-cache-dir \
2222
praisonai_tools \
23-
"praisonai>=4.5.3" \
23+
"praisonai>=4.5.5" \
2424
"praisonai[ui]" \
2525
"praisonai[chat]" \
2626
"praisonai[realtime]" \

docker/Dockerfile.ui

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
1616
# Install Python packages (using latest versions)
1717
RUN pip install --no-cache-dir \
1818
praisonai_tools \
19-
"praisonai>=4.5.3" \
19+
"praisonai>=4.5.5" \
2020
"praisonai[ui]" \
2121
"praisonai[crewai]"
2222

src/praisonai-agents/praisonaiagents/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def _get_lazy_cache():
337337
'CachingConfig': ('praisonaiagents.config.feature_configs', 'CachingConfig'),
338338
'HooksConfig': ('praisonaiagents.config.feature_configs', 'HooksConfig'),
339339
'SkillsConfig': ('praisonaiagents.config.feature_configs', 'SkillsConfig'),
340-
'AutonomyConfig': ('praisonaiagents.config.feature_configs', 'AutonomyConfig'),
340+
'AutonomyConfig': ('praisonaiagents.agent.autonomy', 'AutonomyConfig'),
341341
'MemoryBackend': ('praisonaiagents.config.feature_configs', 'MemoryBackend'),
342342
'ChunkingStrategy': ('praisonaiagents.config.feature_configs', 'ChunkingStrategy'),
343343
'GuardrailAction': ('praisonaiagents.config.feature_configs', 'GuardrailAction'),
@@ -664,6 +664,10 @@ def warmup(include_litellm: bool = False, include_openai: bool = True) -> dict:
664664
'EmbeddingResult',
665665
'get_dimensions',
666666

667+
# Autonomy
668+
'AutonomyConfig',
669+
'AutonomyLevel',
670+
667671
# Sub-packages for organized imports
668672
# Usage: import praisonaiagents as pa; pa.config.MemoryConfig
669673
'config',

src/praisonai-agents/praisonaiagents/agent/agent.py

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,20 +1902,13 @@ def _init_autonomy(self, autonomy: Any, verification_hooks: Optional[List[Any]]
19021902
from .autonomy import AutonomyConfig, AutonomyTrigger, DoomLoopTracker
19031903

19041904
if autonomy is True:
1905-
self.autonomy_config = {}
19061905
config = AutonomyConfig()
19071906
elif isinstance(autonomy, dict):
1908-
self.autonomy_config = autonomy.copy()
19091907
config = AutonomyConfig.from_dict(autonomy)
19101908
# Extract verification_hooks from dict if provided
19111909
if "verification_hooks" in autonomy and not verification_hooks:
19121910
self._verification_hooks = autonomy.get("verification_hooks", [])
19131911
elif isinstance(autonomy, AutonomyConfig):
1914-
self.autonomy_config = {
1915-
"max_iterations": autonomy.max_iterations,
1916-
"doom_loop_threshold": autonomy.doom_loop_threshold,
1917-
"auto_escalate": autonomy.auto_escalate,
1918-
}
19191912
config = autonomy
19201913
# Extract verification_hooks from AutonomyConfig if provided
19211914
if autonomy.verification_hooks and not verification_hooks:
@@ -1927,8 +1920,31 @@ def _init_autonomy(self, autonomy: Any, verification_hooks: Optional[List[Any]]
19271920
self._doom_loop_tracker = None
19281921
return
19291922

1923+
# Preserve ALL AutonomyConfig fields in the dict (G14 fix: no lossy extraction)
1924+
self.autonomy_config = {
1925+
"enabled": config.enabled,
1926+
"level": config.level,
1927+
"max_iterations": config.max_iterations,
1928+
"doom_loop_threshold": config.doom_loop_threshold,
1929+
"auto_escalate": config.auto_escalate,
1930+
"observe": config.observe,
1931+
"completion_promise": config.completion_promise,
1932+
"clear_context": config.clear_context,
1933+
}
1934+
# Also preserve any extra user-provided keys from dict input
1935+
if isinstance(autonomy, dict):
1936+
for k, v in autonomy.items():
1937+
if k not in self.autonomy_config:
1938+
self.autonomy_config[k] = v
1939+
1940+
# Store the AutonomyConfig object for typed access
1941+
self._autonomy_config_obj = config
1942+
19301943
self._autonomy_trigger = AutonomyTrigger()
19311944
self._doom_loop_tracker = DoomLoopTracker(threshold=config.doom_loop_threshold)
1945+
1946+
# Wire level → approval bridge (G3 fix)
1947+
self._bridge_autonomy_level(config.level)
19321948

19331949
def analyze_prompt(self, prompt: str) -> set:
19341950
"""Analyze prompt for autonomy signals.
@@ -1986,6 +2002,22 @@ def _reset_doom_loop(self) -> None:
19862002
if self._doom_loop_tracker is not None:
19872003
self._doom_loop_tracker.reset()
19882004

2005+
def _bridge_autonomy_level(self, level: str) -> None:
2006+
"""Bridge autonomy level to approval system (G3 fix).
2007+
2008+
When level is 'full_auto', sets PRAISONAI_AUTO_APPROVE env var
2009+
so the SDK approval registry auto-approves tool calls.
2010+
2011+
Args:
2012+
level: Autonomy level string (suggest, auto_edit, full_auto)
2013+
"""
2014+
import os
2015+
if level == "full_auto":
2016+
os.environ["PRAISONAI_AUTO_APPROVE"] = "true"
2017+
else:
2018+
# Don't remove if already set by CLI or user
2019+
pass
2020+
19892021
def run_autonomous(
19902022
self,
19912023
prompt: str,
@@ -2112,13 +2144,39 @@ def run_autonomous(
21122144
started_at=started_at,
21132145
)
21142146

2115-
# Record the action
2147+
response_str = str(response)
2148+
2149+
# Record the action for doom loop tracking (G1 fix: was missing)
2150+
self._record_action(
2151+
"chat", {"prompt": prompt}, response_str[:200], True
2152+
)
2153+
2154+
# Record for action history
21162155
actions_taken.append({
21172156
"iteration": iterations,
2118-
"response": str(response)[:500],
2157+
"response": response_str[:500],
21192158
})
21202159

2121-
response_str = str(response)
2160+
# Observability logging (G10 fix: wire observe field)
2161+
if self.autonomy_config.get("observe"):
2162+
logging.getLogger(__name__).info(
2163+
f"[autonomy] iteration={iterations} stage={stage} "
2164+
f"response_len={len(response_str)}"
2165+
)
2166+
2167+
# Auto-save session after each iteration (memory integration)
2168+
self._auto_save_session()
2169+
2170+
# Auto-escalate stage if stuck (G11 fix: wire auto_escalate)
2171+
if (self.autonomy_config.get("auto_escalate")
2172+
and iterations > 1
2173+
and stage in ("direct", "heuristic")):
2174+
stage_order = ["direct", "heuristic", "planned", "autonomous"]
2175+
idx = stage_order.index(stage) if stage in stage_order else 0
2176+
if idx < len(stage_order) - 1:
2177+
stage = stage_order[idx + 1]
2178+
if self.autonomy_config.get("observe"):
2179+
logging.getLogger(__name__).info(f"[autonomy] auto-escalated to stage={stage}")
21222180

21232181
# Check for completion promise FIRST (structured signal)
21242182
if effective_promise:
@@ -2328,13 +2386,37 @@ async def main():
23282386
started_at=started_at,
23292387
)
23302388

2331-
# Record the action
2389+
response_str = str(response)
2390+
2391+
# Record the action for doom loop tracking (G2 fix: was missing)
2392+
self._record_action(
2393+
"chat", {"prompt": prompt}, response_str[:200], True
2394+
)
2395+
2396+
# Record for action history
23322397
actions_taken.append({
23332398
"iteration": iterations,
2334-
"response": str(response)[:500],
2399+
"response": response_str[:500],
23352400
})
23362401

2337-
response_str = str(response)
2402+
# Observability logging (G10 fix: wire observe field)
2403+
if self.autonomy_config.get("observe"):
2404+
logging.getLogger(__name__).info(
2405+
f"[autonomy-async] iteration={iterations} stage={stage} "
2406+
f"response_len={len(response_str)}"
2407+
)
2408+
2409+
# Auto-save session after each async iteration (memory integration)
2410+
self._auto_save_session()
2411+
2412+
# Auto-escalate stage if stuck (G11 fix: wire auto_escalate)
2413+
if (self.autonomy_config.get("auto_escalate")
2414+
and iterations > 1
2415+
and stage in ("direct", "heuristic")):
2416+
stage_order = ["direct", "heuristic", "planned", "autonomous"]
2417+
idx = stage_order.index(stage) if stage in stage_order else 0
2418+
if idx < len(stage_order) - 1:
2419+
stage = stage_order[idx + 1]
23382420

23392421
# Check for completion promise FIRST (structured signal)
23402422
if effective_promise:
@@ -2390,6 +2472,9 @@ async def main():
23902472
# Yield control to allow other async tasks to run
23912473
await asyncio.sleep(0)
23922474

2475+
# Final auto-save before returning
2476+
self._auto_save_session()
2477+
23932478
# Max iterations reached
23942479
return AutonomyResult(
23952480
success=False,

0 commit comments

Comments
 (0)