Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.2.12 gunicorn markdown
RUN pip install flask praisonai==2.2.13 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
2 changes: 1 addition & 1 deletion docker/Dockerfile.chat
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.2.12" \
"praisonai==2.2.13" \
"praisonai[chat]" \
"embedchain[github,youtube]"

Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.2.12" \
"praisonai==2.2.13" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile.ui
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.2.12" \
"praisonai==2.2.13" \
"praisonai[ui]" \
"praisonai[crewai]"

Expand Down
2 changes: 1 addition & 1 deletion docs/api/praisonai/deploy.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ <h2 id="raises">Raises</h2>
file.write(&#34;FROM python:3.11-slim\n&#34;)
file.write(&#34;WORKDIR /app\n&#34;)
file.write(&#34;COPY . .\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.2.12 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.2.13 gunicorn markdown\n&#34;)
file.write(&#34;EXPOSE 8080\n&#34;)
file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)

Expand Down
2 changes: 1 addition & 1 deletion docs/developers/local-development.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ WORKDIR /app

COPY . .

RUN pip install flask praisonai==2.2.12 watchdog
RUN pip install flask praisonai==2.2.13 watchdog

EXPOSE 5555

Expand Down
2 changes: 1 addition & 1 deletion docs/ui/chat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ To facilitate local development with live reload, you can use Docker. Follow the

COPY . .

RUN pip install flask praisonai==2.2.12 watchdog
RUN pip install flask praisonai==2.2.13 watchdog

EXPOSE 5555

Expand Down
2 changes: 1 addition & 1 deletion docs/ui/code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ To facilitate local development with live reload, you can use Docker. Follow the

COPY . .

RUN pip install flask praisonai==2.2.12 watchdog
RUN pip install flask praisonai==2.2.13 watchdog

EXPOSE 5555

Expand Down
6 changes: 5 additions & 1 deletion praisonai/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,15 @@ def __init__(self, agent_file="agents.yaml", framework="", auto=False, init=Fals
Initialize the PraisonAI object with default parameters.
"""
self.agent_yaml = agent_yaml
# Create config_list with AutoGen compatibility
api_key = os.environ.get("OPENAI_API_KEY")
self.config_list = [
{
'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-4o"),
'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
'api_key': os.environ.get("OPENAI_API_KEY")
'api_key': api_key,
'openai_api_key': api_key, # AutoGen sometimes expects this field name
'api_type': 'openai' # AutoGen expects this field
}
]
self.agent_file = agent_file
Expand Down
2 changes: 1 addition & 1 deletion praisonai/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def create_dockerfile(self):
file.write("FROM python:3.11-slim\n")
file.write("WORKDIR /app\n")
file.write("COPY . .\n")
file.write("RUN pip install flask praisonai==2.2.12 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.13 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.2.12"
version = "2.2.13"
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
readme = "README.md"
license = ""
Expand Down Expand Up @@ -89,7 +89,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]

[tool.poetry]
name = "PraisonAI"
version = "2.2.12"
version = "2.2.13"
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
authors = ["Mervin Praison"]
license = ""
Expand Down
114 changes: 82 additions & 32 deletions tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ def test_main_with_agents_advanced(self):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
Comment on lines +21 to 30
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for skipping tests based on the API key status (lines 22-30) is a great improvement for test reliability. However, this exact block of code is repeated in several test methods within this file (e.g., test_main_with_autogen_framework, test_main_with_custom_framework, test_main_with_internet_search_tool, test_main_with_built_in_tool, and similar logic in TestExamples and TestCommandLine).

Could we refactor this duplicated logic into a helper method within the test classes (or a standalone utility function)? This would improve maintainability, as any future changes to this skip logic would only need to be made in one place.

For example, you could introduce a helper method like _handle_api_exception_skip(self, e):

# In TestMainFunctionality class (and similar for other classes)
def _handle_api_exception_skip(self, e: Exception):
    """Checks if the test should be skipped due to API key status and the nature of the exception."""
    api_key = os.environ.get('OPENAI_API_KEY', '')
    is_fallback_or_missing_key = (
        not api_key or 
        api_key.startswith('sk-test-') or 
        api_key == 'nokey' or
        'fallback' in api_key
    )

    if is_fallback_or_missing_key:
        # Current logic skips for any exception if key is fallback/missing.
        # You might want to add checks for specific auth-related error strings from 'e' here
        # if you want to be more precise about *why* it's skipping with a fallback key.
        self.skipTest(f"Skipping due to no valid API key provided or fallback key in use: {e}")
        return True # Indicates test was skipped
    return False # Indicates test should not be skipped, and exception should be raised

# Then, in your test methods:
try:
    # ... test code ...
except Exception as e:
    if self._handle_api_exception_skip(e):
        return # Test was skipped
    # Real API key provided or not an API-related skip - test should fail
    raise

This would make the test methods cleaner and the skip logic centralized. A similar helper could be adapted for the command-line tests that check result strings instead of exceptions.

        except Exception as e:
            if self._handle_api_exception_skip(e):
                return # Test was skipped by the helper
            # Real API key provided - test should fail, not skip
            raise


def test_main_with_autogen_framework(self):
Expand All @@ -30,10 +35,15 @@ def test_main_with_autogen_framework(self):
result = praisonai.run()
self.assertTrue('Task Output' in result or '### Output ###' in result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise

def test_main_with_custom_framework(self):
Expand All @@ -42,10 +52,15 @@ def test_main_with_custom_framework(self):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise

def test_main_with_internet_search_tool(self):
Expand All @@ -54,10 +69,15 @@ def test_main_with_internet_search_tool(self):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise

def test_main_with_built_in_tool(self):
Expand All @@ -66,10 +86,15 @@ def test_main_with_built_in_tool(self):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise


Expand All @@ -88,19 +113,29 @@ def run_command(self, command):
def test_praisonai_command(self):
command = "praisonai --framework autogen --auto create movie script about cat in mars"
result = self.run_command(command)
# Handle API authentication errors in command line output
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to API authentication in command output")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to no valid API key provided")
self.assertIn('TERMINATE', result)

def test_praisonai_init_command(self):
command = "praisonai --framework autogen --init create movie script about cat in mars"
result = self.run_command(command)
# Handle API authentication errors in command line output
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to API authentication in command output")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to no valid API key provided")
self.assertIn('created successfully', result)

class TestExamples(unittest.TestCase):
Expand All @@ -119,10 +154,15 @@ def test_basic_example(self):
f"Expected meaningful result, got: {result}"
)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise

def test_advanced_example(self):
Expand All @@ -140,10 +180,15 @@ def test_advanced_example(self):
f"Expected meaningful result, got: {result}"
)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise

def test_auto_example(self):
Expand All @@ -161,10 +206,15 @@ def test_auto_example(self):
f"Expected meaningful result, got: {result}"
)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise

if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading