Skip to content

Commit ef6b1a8

Browse files
committed
Release v4.5.31
1 parent dc9c250 commit ef6b1a8

File tree

15 files changed

+95
-43
lines changed

15 files changed

+95
-43
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.30" \
19+
"praisonai>=4.5.31" \
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.30" \
23+
"praisonai>=4.5.31" \
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.30" \
19+
"praisonai>=4.5.31" \
2020
"praisonai[ui]" \
2121
"praisonai[crewai]"
2222

src/praisonai-agents/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "praisonaiagents"
7-
version = "1.5.30"
7+
version = "1.5.31"
88
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
99
readme = "README.md"
1010
requires-python = ">=3.10"

src/praisonai-agents/tests/unit/knowledge/test_directory_ingestion.py

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616
)
1717

1818

19+
def _make_knowledge_or_skip():
20+
"""Create a Knowledge() instance, skipping if OpenAI API key is unavailable."""
21+
from praisonaiagents import Knowledge
22+
try:
23+
return Knowledge()
24+
except Exception as e:
25+
if "api_key" in str(e).lower() or "openai" in str(e).lower():
26+
pytest.skip(f"Skipping: OpenAI API key required for embeddings: {e}")
27+
raise
28+
29+
1930
@requires_knowledge
2031
class TestDirectoryIngestion:
2132
"""Test that directories are properly processed and file contents stored."""
@@ -42,10 +53,14 @@ def temp_knowledge_dir(self):
4253

4354
def test_directory_ingestion_stores_text_not_path(self, temp_knowledge_dir):
4455
"""Test that ingesting a directory stores file contents, not the directory path."""
45-
from praisonaiagents import Knowledge
56+
knowledge = _make_knowledge_or_skip()
4657

47-
knowledge = Knowledge()
48-
result = knowledge.add(temp_knowledge_dir, user_id='test_user')
58+
try:
59+
result = knowledge.add(temp_knowledge_dir, user_id='test_user')
60+
except Exception as e:
61+
if "api_key" in str(e).lower() or "openai" in str(e).lower():
62+
pytest.skip(f"Skipping: OpenAI API key required: {e}")
63+
raise
4964

5065
# Should have results from files
5166
assert 'results' in result
@@ -65,10 +80,14 @@ def test_directory_ingestion_stores_text_not_path(self, temp_knowledge_dir):
6580

6681
def test_directory_ingestion_processes_multiple_files(self, temp_knowledge_dir):
6782
"""Test that all files in a directory are processed."""
68-
from praisonaiagents import Knowledge
83+
knowledge = _make_knowledge_or_skip()
6984

70-
knowledge = Knowledge()
71-
result = knowledge.add(temp_knowledge_dir, user_id='test_user')
85+
try:
86+
knowledge.add(temp_knowledge_dir, user_id='test_user')
87+
except Exception as e:
88+
if "api_key" in str(e).lower() or "openai" in str(e).lower():
89+
pytest.skip(f"Skipping: OpenAI API key required: {e}")
90+
raise
7291

7392
# Search for content from both files
7493
search1 = knowledge.search('ZEBRA-71', user_id='test_user')
@@ -80,10 +99,14 @@ def test_directory_ingestion_processes_multiple_files(self, temp_knowledge_dir):
8099

81100
def test_directory_ingestion_sets_metadata(self, temp_knowledge_dir):
82101
"""Test that file metadata is properly set."""
83-
from praisonaiagents import Knowledge
102+
knowledge = _make_knowledge_or_skip()
84103

85-
knowledge = Knowledge()
86-
knowledge.add(temp_knowledge_dir, user_id='test_user')
104+
try:
105+
knowledge.add(temp_knowledge_dir, user_id='test_user')
106+
except Exception as e:
107+
if "api_key" in str(e).lower() or "openai" in str(e).lower():
108+
pytest.skip(f"Skipping: OpenAI API key required: {e}")
109+
raise
87110

88111
search_result = knowledge.search('ZEBRA-71', user_id='test_user')
89112

@@ -114,15 +137,20 @@ def test_agent_context_contains_text_not_path(self, temp_knowledge_dir):
114137
"""Test that Agent._get_knowledge_context returns text, not path."""
115138
from praisonaiagents import Agent
116139

117-
agent = Agent(
118-
name='TestAgent',
119-
instructions='Answer based on knowledge.',
120-
knowledge=[temp_knowledge_dir],
121-
output='silent',
122-
)
123-
124-
# Ensure knowledge is processed
125-
agent._ensure_knowledge_processed()
140+
try:
141+
agent = Agent(
142+
name='TestAgent',
143+
instructions='Answer based on knowledge.',
144+
knowledge=[temp_knowledge_dir],
145+
output='silent',
146+
)
147+
148+
# Ensure knowledge is processed
149+
agent._ensure_knowledge_processed()
150+
except Exception as e:
151+
if "api_key" in str(e).lower() or "openai" in str(e).lower():
152+
pytest.skip(f"Skipping: OpenAI API key required: {e}")
153+
raise
126154

127155
# Get context
128156
context, _ = agent._get_knowledge_context('What is the password?', use_rag=True)

src/praisonai-agents/tests/unit/test_autonomy_dry_refactor.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@
2323
class TestDoomLoopFiring:
2424
"""G1/G2: Doom loop detection must actually fire in run_autonomous()."""
2525

26-
def test_doom_loop_fires_when_agent_repeats_same_response(self):
26+
@patch("praisonaiagents.agent.agent.Agent.get_recommended_stage", return_value="heuristic")
27+
def test_doom_loop_fires_when_agent_repeats_same_response(self, mock_stage):
2728
"""run_autonomous() must call _record_action() so DoomLoopTracker gets data."""
2829
from praisonaiagents import Agent
2930

3031
agent = Agent(
3132
name="test_doom",
3233
instructions="You are a test agent",
33-
autonomy={"max_iterations": 10, "doom_loop_threshold": 3},
34+
# Need enough iterations for graduated recovery to exhaust all 4 stages:
35+
# threshold=3 means 3 identical actions per doom-loop cycle, 4 recovery
36+
# stages, plus check iterations = ~16 iterations minimum.
37+
autonomy={"max_iterations": 25, "doom_loop_threshold": 3},
3438
)
3539

3640
# Mock chat to return the same response every time
@@ -43,16 +47,16 @@ def mock_chat(prompt, **kwargs):
4347

4448
agent.chat = mock_chat
4549

46-
# Use a prompt that triggers autonomous stage (multi-step)
47-
# so the loop runs multiple iterations instead of exiting after 1 (direct)
4850
result = agent.run_autonomous("First refactor the auth module, then test it step by step")
4951

50-
# Doom loop should have been detected before max_iterations
51-
assert result.completion_reason == "doom_loop", (
52-
f"Expected doom_loop but got {result.completion_reason} after {result.iterations} iterations"
52+
# Graduated recovery produces needs_help (stage 3) or doom_loop (stage 4).
53+
# Both are valid doom-loop-family abort outcomes.
54+
assert result.completion_reason in ("doom_loop", "needs_help"), (
55+
f"Expected doom_loop or needs_help but got {result.completion_reason} "
56+
f"after {result.iterations} iterations"
5357
)
5458
assert result.success is False
55-
assert result.iterations < 10 # Should stop well before max
59+
assert result.iterations < 25 # Should stop well before max
5660

5761
def test_doom_loop_does_not_fire_with_varied_responses(self):
5862
"""Varied responses should NOT trigger doom loop."""

src/praisonai-agents/tests/unit/test_autonomy_gaps.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ def test_doom_loop_eventually_aborts(self, mock_stage, mock_chat):
157157
agent = self._make_agent(threshold=2)
158158
result = agent.run_autonomous("Do something")
159159
assert result.success is False
160-
assert result.completion_reason == "doom_loop"
160+
# Graduated recovery produces needs_help (stage 3) or doom_loop (stage 4).
161+
# Both are valid doom-loop-family abort outcomes.
162+
assert result.completion_reason in ("doom_loop", "needs_help"), (
163+
f"Expected doom_loop or needs_help but got {result.completion_reason}"
164+
)
161165

162166
@patch("praisonaiagents.agent.agent.Agent.chat")
163167
@patch("praisonaiagents.agent.agent.Agent.get_recommended_stage", return_value="heuristic")

src/praisonai-agents/tests/unit/test_gateway_config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,10 @@ def test_creation_defaults(self):
232232
"""Test MultiChannelGatewayConfig with defaults."""
233233
from praisonaiagents.gateway import MultiChannelGatewayConfig
234234
config = MultiChannelGatewayConfig()
235-
assert isinstance(config.gateway, GatewayConfig)
235+
# Use qualname check to be robust against module reimport identity issues
236+
assert type(config.gateway).__qualname__ == "GatewayConfig", (
237+
f"Expected GatewayConfig, got {type(config.gateway).__qualname__}"
238+
)
236239
assert config.agents == {}
237240
assert config.channels == {}
238241

src/praisonai-agents/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/praisonai/praisonai.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ class Praisonai < Formula
33

44
desc "AI tools for various AI applications"
55
homepage "https://github.com/MervinPraison/PraisonAI"
6-
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v4.5.30.tar.gz"
7-
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v4.5.30.tar.gz | shasum -a 256`.split.first
6+
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v4.5.31.tar.gz"
7+
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v4.5.31.tar.gz | shasum -a 256`.split.first
88
license "MIT"
99

1010
depends_on "python@3.11"

0 commit comments

Comments
 (0)