Compare commits

...
57 Commits
Author SHA1 Message Date
Mervin Praison dc5b10fba8 Merge pull request #611 from MervinPraison/develop
Update PraisonAI version to 2.2.30 across Dockerfiles and related files
2025-06-05 18:18:04 +01:00
MervinPraison f0172e8339 Update PraisonAI version to 2.2.30 across Dockerfiles and related files
- Incremented the version of PraisonAI from 2.2.29 to 2.2.30 in Dockerfiles (Dockerfile, Dockerfile.chat, Dockerfile.dev, Dockerfile.ui).
- Updated the version in the README.md and pyproject.toml files to reflect the new version.
- Adjusted the deploy.py script to install the updated version of PraisonAI.
- Ensured consistency across all relevant files for seamless integration.
2025-06-05 18:17:47 +01:00
Mervin Praison 5c498a12de Merge pull request #610 from MervinPraison/claude/issue-605-20250605_165941
fix: resolve Memory import error with litellm compatibility
2025-06-05 18:16:11 +01:00
Mervin Praison 88410881e1 Rename test_session_import.py to src/praisonai/tests/unit/test_session_import.py 2025-06-05 18:10:43 +01:00
Mervin Praison 791d59f96e Rename test_memory_import.py to src/praisonai/tests/unit/test_memory_import.py 2025-06-05 18:10:25 +01:00
Mervin Praison cdbe1303ad Rename test_comprehensive_import.py to src/praisonai/tests/unit/test_comprehensive_import.py 2025-06-05 18:10:10 +01:00
claude[bot]andMervinPraison b63f931bea feat: add Memory to main package exports and comprehensive tests
- Export Memory class from main praisonaiagents package
- Add Memory to __all__ list for proper package API
- Create comprehensive test suite to verify all import scenarios
- Tests original failing import from GitHub issue
- Tests direct Memory imports and instantiation

Co-authored-by: MervinPraison <[email protected]>
2025-06-05 17:02:47 +00:00
claude[bot]andMervinPraison ff25b96f91 fix: resolve Memory import error by implementing litellm compatibility
- Add LITELLM_AVAILABLE flag for proper dependency detection
- Implement litellm → openai fallback pattern for all embedding operations
- Update search_short_term, store_long_term, search_long_term embedding calls
- Update calculate_quality_metrics to use litellm → openai fallback
- Add graceful degradation when neither litellm nor openai is available
- Fixes ImportError: cannot import name 'Memory' from 'praisonaiagents.memory'

Co-authored-by: MervinPraison <[email protected]>
2025-06-05 17:02:03 +00:00
Mervin Praison 603f0ea008 Merge pull request #609 from MervinPraison/develop
Develop
2025-06-05 17:55:15 +01:00
MervinPraison db38b55174 Update PraisonAI version to 2.2.29 across Dockerfiles and related files
- Incremented the version of PraisonAI from 2.2.28 to 2.2.29 in Dockerfiles (Dockerfile, Dockerfile.chat, Dockerfile.dev, Dockerfile.ui).
- Updated the version in the README.md and pyproject.toml files to reflect the new version.
- Adjusted the deploy.py script to install the updated version of PraisonAI.
- Ensured consistency across all relevant files for seamless integration.
2025-06-05 17:54:57 +01:00
MervinPraison cebd175d61 Update versioning and dependencies in project files
- Incremented version of PraisonAI from 0.0.99 to 0.0.101 in pyproject.toml and uv.lock.
- Added 'litellm' dependency to memory requirements for enhanced functionality.
- Updated .gitignore to include 'CopilotKit*' for better file management.
- Optimised TaskOutput instantiation in agent.py for clarity.
- Refined memory handling in memory.py to utilise LiteLLM for consistency.
- Improved model extraction logic in task.py for better fallback handling.
2025-06-05 17:53:38 +01:00
Mervin Praison 01ff75e573 Merge pull request #606 from DhivyaBharathy-web/add-qwen2-5-agent
Add Qwen2.5 Instruction Agent notebook using Hugging Face Transformers
2025-06-05 17:43:36 +01:00
Mervin Praison 28629f2203 Merge pull request #607 from DhivyaBharathy-web/add-gemma2b-instruction-agent
Add Gemma2B_Instruction_Agent to Cookbooks
2025-06-05 17:43:19 +01:00
Mervin Praison ac5c463362 Merge pull request #602 from DhivyaBharathy-web/add-predictive-maintenance-notebook
Add predictive maintenance notebook
2025-06-05 17:43:05 +01:00
Mervin Praison 84dc57583e Merge pull request #608 from DhivyaBharathy-web/add-tinyllama-model-agent
Add tinyllama model agent
2025-06-05 17:42:50 +01:00
DhivyaBharathy 460e8b45a7 Merge branch 'main' into add-tinyllama-model-agent 2025-06-05 19:30:02 +05:30
DhivyaBharathy f6bbbd7cd6 Update TinyLlama_1_1B_model_SimpleAIAgent.ipynb 2025-06-05 19:29:06 +05:30
DhivyaBharathy 8a435571f4 Add files via upload 2025-06-05 19:10:44 +05:30
DhivyaBharathy 204106daa8 Merge branch 'main' into add-gemma2b-instruction-agent 2025-06-05 16:47:50 +05:30
DhivyaBharathy 6a1e14f97a Merge branch 'main' into add-qwen2-5-agent 2025-06-05 16:47:02 +05:30
DhivyaBharathy 333777b991 Merge branch 'main' into add-predictive-maintenance-notebook 2025-06-05 16:46:13 +05:30
DhivyaBharathy b02cd85290 Update Gemma2B_Instruction_Agent.ipynb 2025-06-05 16:27:38 +05:30
DhivyaBharathy 3bc0c2b533 Update Gemma2B_Instruction_Agent.ipynb 2025-06-05 16:26:56 +05:30
DhivyaBharathy 68d4f34bb7 Update Gemma2B_Instruction_Agent.ipynb 2025-06-05 16:26:37 +05:30
DhivyaBharathy a24d900a8c Add files via upload 2025-06-05 16:22:33 +05:30
DhivyaBharathy-web 552a10df9e Add Qwen2.5 Instruction Agent notebook 2025-06-05 10:49:32 +00:00
DhivyaBharathy 9c57aa3165 Update Qwen2_5_InstructionAgent.ipynb 2025-06-05 15:16:04 +05:30
DhivyaBharathy 40fe92f1d0 Update Qwen2_5_InstructionAgent.ipynb 2025-06-05 15:14:56 +05:30
DhivyaBharathy 179cc9566d Update Qwen2_5_InstructionAgent.ipynb 2025-06-05 15:09:09 +05:30
DhivyaBharathy d9cf9ba534 Add files via upload 2025-06-05 15:07:47 +05:30
DhivyaBharathy-web 4ec0366e79 Add Qwen2.5 Instruction Agent notebook 2025-06-05 09:26:46 +00:00
Mervin Praison d6a3bd1240 Merge pull request #604 from MervinPraison/develop
Update PraisonAI version to 2.2.28 across Dockerfiles and related files
2025-06-04 22:59:48 +01:00
MervinPraison 0a18e27f54 Update PraisonAI version to 2.2.28 across Dockerfiles and related files
- Updated the version of PraisonAI from 2.2.27 to 2.2.28 in Dockerfiles (Dockerfile, Dockerfile.chat, Dockerfile.dev, Dockerfile.ui).
- Modified the version in the README.md and pyproject.toml files to reflect the new version.
- Adjusted the deploy.py script to install the updated version of PraisonAI.
- Ensured consistency across all relevant files for seamless integration.
2025-06-04 22:59:30 +01:00
DhivyaBharathy b824d950b5 Add files via upload 2025-06-04 20:32:10 +05:30
Mervin Praison a5b768c213 Merge pull request #601 from MervinPraison/develop
Develop
2025-06-04 15:50:06 +01:00
MervinPraison 068bdbebc5 Update PraisonAI version to 2.2.27 across Dockerfiles and related files
- Updated the version of PraisonAI from 2.2.26 to 2.2.27 in Dockerfiles (Dockerfile, Dockerfile.chat, Dockerfile.dev, Dockerfile.ui).
- Modified the version in the README.md and pyproject.toml files to reflect the new version.
- Adjusted the deploy.py script to install the updated version of PraisonAI.
- Ensured consistency across all relevant files for seamless integration.
2025-06-04 15:49:47 +01:00
MervinPraison 4d80d795f8 Merge branch 'main' of https://github.com/MervinPraison/PraisonAI into develop 2025-06-04 15:48:15 +01:00
MervinPraison 2e3b5cd29b Merge branch 'develop' of https://github.com/MervinPraison/PraisonAI into develop 2025-06-04 15:48:08 +01:00
MervinPraison d948b6de2c Refactor agent guardrails and update versioning
- Removed the outdated agent_guardrails_example.py file.
- Introduced a new guardrail_agents_example.py file with improved structure and user-friendly naming.
- Updated version in pyproject.toml from 0.0.97 to 0.0.98.
- Added revision information in uv.lock for better dependency tracking.
- Imported asyncio in agent.py for enhanced functionality.
2025-06-04 15:47:14 +01:00
Mervin Praison 3adb06950c Merge pull request #599 from MervinPraison/develop
Update test workflow to use custom test runner for 'fast' pattern and…
2025-06-04 14:21:02 +01:00
claude[bot]andClaude e45e85975c feat: implement Agent-level guardrail support
- Add guardrail and max_guardrail_retries parameters to Agent constructor
- Implement _setup_guardrail() method supporting function and LLM-based guardrails  
- Add _process_guardrail() method for validation processing
- Create _apply_guardrail_with_retry() method with automatic retry logic
- Integrate guardrail processing into all major return points in chat() method
- Support both regular responses and reasoning content validation
- Update agent_guardrails_example.py with comprehensive demonstrations
- Enhance CLAUDE.md documentation with Agent-level guardrail usage
- Add test file for Agent guardrail functionality validation

Agent-level guardrails apply to ALL outputs from the agent, providing
consistent validation across all agent interactions with configurable
retry logic and support for both function-based and string-based 
LLM guardrails.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-06-04 11:57:22 +00:00
Mervin Praison 8a7935dc2c Merge pull request #600 from DhivyaBharathy-web/code-analysis-agent
Add code analysis agent notebook
2025-06-04 12:40:08 +01:00
DhivyaBharathy b31edc90d5 Update Code_Analysis_Agent.ipynb 2025-06-04 16:58:52 +05:30
DhivyaBharathy d2841ccb3a Merge branch 'MervinPraison:main' into main 2025-06-04 16:53:54 +05:30
DhivyaBharathy-web e22fda805f Add code analysis agent notebook 2025-06-04 11:20:39 +00:00
MervinPraison 2cde2fd731 Update test workflow to use custom test runner for 'fast' pattern and refine guardrail functionality in Task class
- Changed the test command in the GitHub Actions workflow to run a specific test runner script with a 'fast' pattern.
- Introduced guardrail functionality in the Task class to validate task outputs, including error handling and retry logic for guardrail validation failures.
- Enhanced the initialization of guardrail parameters and ensured proper type checking for guardrail functions.
2025-06-04 12:10:01 +01:00
Mervin Praison f7534681e5 Merge pull request #598 from MervinPraison/develop
Develop
2025-06-04 12:09:21 +01:00
MervinPraison 8c9e3e34cb agent sequential thinking 2025-06-04 11:52:53 +01:00
MervinPraison c470494d76 Merge branch 'main' of https://github.com/MervinPraison/PraisonAI into develop 2025-06-04 10:32:57 +01:00
Mervin Praison fdf3cac3e7 Merge pull request #596 from MervinPraison/codex/add-mcp-tool-to-agent-creation
Add MCP SSE tooling example
2025-06-04 10:32:06 +01:00
Mervin Praison ec98cd12c8 Merge pull request #597 from DhivyaBharathy-web/add-hackathon-agent-notebook
Add Hackathon Agent Notebook
2025-06-04 10:29:50 +01:00
DhivyaBharathy-web f318ef02a9 Add Hackathon Agent Notebook 2025-06-04 09:19:01 +00:00
claude[bot]andMervinPraison f086566a2d feat: improve MCP SSE tooling with comprehensive error handling and type safety
- Add comprehensive error handling to MCP initialization and tool execution
- Implement resource cleanup mechanisms with close() method and connection state tracking
- Fix interface typo: MCPPToolInfo -> MCPToolInfo
- Add schemaProperties getter to improve type safety in example code
- Prevent re-initialization of MCP client with guard clause
- Add debug logging throughout MCP lifecycle
- Improve example code with proper error handling for network failures
- Add better parameter mapping with object/positional argument support
- Include validation warnings for missing tools and argument mismatches
- Remove unused MCPTool import from example
- Add proper resource cleanup in finally block

These changes address the ENETUNREACH error mentioned in PR description by providing
graceful error handling when the MCP SSE server is unavailable.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: MervinPraison <[email protected]>
2025-06-04 08:23:07 +00:00
Mervin Praison 6de011b69e Add MCP SSE example and dependency 2025-06-04 09:05:08 +01:00
MervinPraison 89d7821bc9 Update test workflow to use custom test runner for 'all' pattern 2025-06-04 07:38:10 +01:00
MervinPraison 9bff7eddbd Update coverage workflow to include installation of duckduckgo_search and knowledge dependencies from praisonai-agents 2025-06-04 07:18:25 +01:00
MervinPraison 82e5f11acf Enhance test workflows by replacing test runner with pytest for improved coverage reporting and adding Codecov upload steps for comprehensive, core, and quick validation tests. 2025-06-04 07:14:18 +01:00
57 changed files with 12984 additions and 3017 deletions

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
name: Run tests and upload coverage
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
name: Run tests and collect coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
cd src/praisonai
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
uv pip install --system duckduckgo_search
uv pip install --system pytest pytest-cov pytest-asyncio
# Install knowledge dependencies from praisonai-agents
uv pip install --system "praisonaiagents[knowledge]"
- name: Set environment variables
run: |
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-4o-mini' }}" >> $GITHUB_ENV
echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV
- name: Run tests
run: |
cd src/praisonai
pytest --cov=praisonai --cov-branch --cov-report=xml tests/unit/
- name: Upload results to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: src/praisonai/coverage.xml
flags: unit-tests
name: unit-tests-coverage
fail_ci_if_error: false
verbose: true
+1 -1
View File
@@ -65,4 +65,4 @@ jobs:
# flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
cd src/praisonai && python -m pytest
cd src/praisonai && python tests/test_runner.py --pattern fast
+15 -5
View File
@@ -69,10 +69,10 @@ jobs:
case $TEST_TYPE in
"unit")
cd src/praisonai && python tests/test_runner.py --pattern unit
cd src/praisonai && python -m pytest tests/unit/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=xml --cov-branch
;;
"integration")
cd src/praisonai && python tests/test_runner.py --pattern integration
cd src/praisonai && python -m pytest tests/integration/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=xml --cov-branch
;;
"fast")
cd src/praisonai && python tests/test_runner.py --pattern fast
@@ -139,6 +139,16 @@ jobs:
echo "- 🔌 MCP server connections" >> comprehensive_report.md
echo "- 💬 LLM integrations (OpenAI, Anthropic, etc.)" >> comprehensive_report.md
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: src/praisonai/coverage.xml
flags: comprehensive-tests
name: comprehensive-tests-coverage
fail_ci_if_error: false
verbose: true
- name: Upload Comprehensive Test Results
uses: actions/upload-artifact@v4
if: always()
@@ -146,9 +156,9 @@ jobs:
name: comprehensive-test-results-python-${{ matrix.python-version }}
path: |
comprehensive_report.md
htmlcov/
coverage.xml
.coverage
src/praisonai/htmlcov/
src/praisonai/coverage.xml
src/praisonai/.coverage
retention-days: 30
test-matrix-summary:
+16 -4
View File
@@ -195,7 +195,7 @@ jobs:
- name: Run Unit Tests
run: |
cd src/praisonai && python -m pytest tests/unit/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=term-missing
cd src/praisonai && python -m pytest tests/unit/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=term-missing --cov-report=xml --cov-branch
- name: Run Integration Tests
run: |
@@ -263,12 +263,24 @@ jobs:
export OPENAI_MODEL_NAME="$OPENAI_MODEL_NAME"
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings
- name: Upload Coverage Reports
- name: Upload coverage reports to Codecov
if: matrix.python-version == '3.11'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: src/praisonai/coverage.xml
flags: core-tests
name: core-tests-coverage
fail_ci_if_error: false
verbose: true
- name: Upload Coverage Reports (Artifacts)
if: matrix.python-version == '3.11'
uses: actions/upload-artifact@v4
with:
name: coverage-reports
path: |
.coverage
htmlcov/
src/praisonai/.coverage
src/praisonai/htmlcov/
src/praisonai/coverage.xml
retention-days: 7
+12 -2
View File
@@ -471,8 +471,8 @@ jobs:
- name: Run Fast Tests
run: |
# Run the fastest, most essential tests
cd src/praisonai && python tests/test_runner.py --pattern fast
# Run the fastest, most essential tests with coverage
cd src/praisonai && python -m pytest tests/unit/test_core_agents.py -v --tb=short --disable-warnings --cov=praisonai --cov-report=xml --cov-branch
- name: Run Real API Tests
run: |
@@ -491,6 +491,16 @@ jobs:
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings
continue-on-error: true
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: src/praisonai/coverage.xml
flags: quick-validation
name: quick-validation-coverage
fail_ci_if_error: false
verbose: true
- name: Restore Root Config Files
run: |
echo "🔄 Restoring root configuration files..."
+2 -1
View File
@@ -74,4 +74,5 @@ agents/praisonaiagents/praisonaiagents.egg-info
.praison
# Local Netlify folder
.netlify
.qodo
.qodo
CopilotKit*
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
flask \
"praisonai>=2.2.26" \
"praisonai>=2.2.30" \
"praisonai[api]" \
gunicorn \
markdown
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.26" \
"praisonai>=2.2.30" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.26" \
"praisonai>=2.2.30" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.26" \
"praisonai>=2.2.30" \
"praisonai[ui]" \
"praisonai[crewai]"
+2 -2
View File
@@ -121,7 +121,7 @@ healthcheck:
## 📦 Package Versions
All Docker images use consistent, up-to-date versions:
- PraisonAI: `>=2.2.26`
- PraisonAI: `>=2.2.30`
- PraisonAI Agents: `>=0.0.92`
- Python: `3.11-slim`
@@ -218,7 +218,7 @@ docker-compose up -d
### Version Pinning
To use specific versions, update the Dockerfile:
```dockerfile
RUN pip install "praisonai==2.2.26" "praisonaiagents==0.0.92"
RUN pip install "praisonai==2.2.30" "praisonaiagents==0.0.92"
```
## 🌐 Production Deployment
@@ -0,0 +1,467 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "bLeMxwWimvzX"
},
"source": [
"# Code Analysis Agent\n",
"Learn how to create AI agents for comprehensive code analysis and quality assessment."
],
"id": "bLeMxwWimvzX"
},
{
"cell_type": "markdown",
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/Code_Analysis_Agent.ipynb)\n"
=======
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/code_analysis_agent.ipynb)\n"
],
"metadata": {
"id": "P3X8PXPyojnL"
},
"id": "P3X8PXPyojnL"
},
{
"cell_type": "markdown",
"metadata": {
"id": "xEs8bwWfmvza"
},
"source": [
"## Dependencies\n",
"Install the required packages."
],
"id": "xEs8bwWfmvza"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "_RPiU3j6mvzb"
},
"outputs": [],
"source": [
"!pip install praisonaiagents gitingest"
],
"id": "_RPiU3j6mvzb"
},
{
"cell_type": "markdown",
"metadata": {
"id": "81-orTkzmvzc"
},
"source": [
"## Set API Key\n",
"Set your OpenAI API key as an environment variable in your terminal:"
],
"id": "81-orTkzmvzc"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "84iL7rGimvzc"
},
"outputs": [],
"source": [
"import os\n",
"os.environ['OPENAI_API_KEY'] = 'your_api_key_here'"
],
"id": "84iL7rGimvzc"
},
{
"cell_type": "markdown",
"metadata": {
"id": "TV15GFsRmvzc"
},
"source": [
"## Tools and Data Models\n",
"Define the agent, task, and output schema."
],
"id": "TV15GFsRmvzc"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "0Wq_g0pNmvzd"
},
"outputs": [],
"source": [
"from praisonaiagents import Agent, Task, PraisonAIAgents\n",
"from pydantic import BaseModel\n",
"from typing import List, Dict\n",
"from gitingest import ingest\n",
"\n",
"class CodeMetrics(BaseModel):\n",
" category: str\n",
" score: int\n",
" findings: List[str]\n",
"\n",
"class CodeAnalysisReport(BaseModel):\n",
" overall_quality: int\n",
" code_metrics: List[CodeMetrics]\n",
" architecture_score: int\n",
" maintainability_score: int\n",
" performance_score: int\n",
" security_score: int\n",
" test_coverage: int\n",
" key_strengths: List[str]\n",
" improvement_areas: List[str]\n",
" tech_stack: List[str]\n",
" recommendations: List[str]\n",
" complexity_metrics: Dict[str, int]\n",
" best_practices: List[Dict[str, str]]\n",
" potential_risks: List[str]\n",
" documentation_quality: int"
],
"id": "0Wq_g0pNmvzd"
},
{
"cell_type": "markdown",
"metadata": {
"id": "EatrK7-wmvzd"
},
"source": [
"## YAML Prompt Setup\n",
"Define the agent and task with expectations."
],
"id": "EatrK7-wmvzd"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "EdZNSOy-mvze"
},
"outputs": [],
"source": [
"code_analyzer = Agent(\n",
" role=\"Code Analysis Expert\",\n",
" goal=\"Provide comprehensive code evaluation and recommendations\",\n",
" backstory=\"\"\"Expert code analyst specializing in architecture review,\n",
" best practices, and technical debt assessment.\"\"\",\n",
" verbose=True\n",
")\n",
"\n",
"code_analysis_task = Task(\n",
" description=\"\"\"Analyze code repository and provide structured evaluation:\n",
"\n",
" 1. Overall Quality (0-100)\n",
" 2. Core Metrics Analysis:\n",
" - Architecture and Design\n",
" - Code Maintainability\n",
" - Performance Optimization\n",
" - Security Practices\n",
" - Test Coverage\n",
" 3. Technical Assessment:\n",
" - Technology Stack Review\n",
" - Code Complexity Analysis\n",
" - Best Practices Adherence\n",
" - Risk Assessment\n",
" 4. Recommendations:\n",
" - Key Improvements\n",
" - Architecture Suggestions\n",
" - Security Enhancements\"\"\",\n",
" expected_output=\"Detailed code analysis report with metrics and recommendations\",\n",
" agent=code_analyzer,\n",
" output_pydantic=CodeAnalysisReport\n",
")"
],
"id": "EdZNSOy-mvze"
},
{
"cell_type": "markdown",
"metadata": {
"id": "CeLS55ROmvzf"
},
"source": [
"## Main Function\n",
"Function to analyze code from GitHub URL or local path."
],
"id": "CeLS55ROmvzf"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "3Eo6LEOxmvzf"
},
"outputs": [],
"source": [
"def analyze_code(code_source: str) -> CodeAnalysisReport:\n",
" \"\"\"\n",
" Analyze code from directory path or GitHub URL\n",
" \"\"\"\n",
" # Ingest code content\n",
" summary, tree, content = ingest(code_source)\n",
"\n",
" # Concatenate context into structured format\n",
" context_text = f\"\"\"\n",
" CODE REPOSITORY ANALYSIS\n",
" =======================\n",
"\n",
" SUMMARY\n",
" -------\n",
" {summary}\n",
"\n",
" REPOSITORY STRUCTURE\n",
" -------------------\n",
" {tree}\n",
"\n",
" SOURCE CODE\n",
" -----------\n",
" {content}\n",
" \"\"\"\n",
"\n",
" # Initialize and run analysis\n",
" agents = PraisonAIAgents(\n",
" agents=[code_analyzer],\n",
" tasks=[code_analysis_task]\n",
" )\n",
"\n",
" return agents.start(context_text)"
],
"id": "3Eo6LEOxmvzf"
},
{
"cell_type": "markdown",
"metadata": {
"id": "v25nLA0pmvzf"
},
"source": [
"## Output\n",
"Example usage to run the analysis and view results."
],
"id": "v25nLA0pmvzf"
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"id": "5BzccaeFmvzg",
"outputId": "8b4cddae-b6b3-4a92-8a13-30694cd09c52"
},
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Markdown object>"
],
"text/markdown": "\n### 👤 Agent: Code Analysis Expert\n\n**Role**: Provides comprehensive code evaluation and recommendations \n**Backstory**: Expert in architecture, best practices, and technical assessment \n"
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"─── 📊 AGENT CODE ANALYSIS REPORT ───\n",
"{\n",
" \"overall_quality\": 85,\n",
" \"code_metrics\": [\n",
" {\n",
" \"category\": \"Architecture and Design\",\n",
" \"score\": 80,\n",
" \"findings\": [\n",
" \"Modular structure with clear separation of concerns.\",\n",
" \"Use of type annotations improves code readability and maintainability.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Code Maintainability\",\n",
" \"score\": 85,\n",
" \"findings\": [\n",
" \"Consistent use of type hints and NamedTuple for structured data.\",\n",
" \"Logical organization of functions and classes.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Performance Optimization\",\n",
" \"score\": 75,\n",
" \"findings\": [\n",
" \"Potential performance overhead due to repeated sys.stdout.write calls.\",\n",
" \"Efficient use of optional parameters to control execution flow.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Security Practices\",\n",
" \"score\": 80,\n",
" \"findings\": [\n",
" \"No obvious security vulnerabilities in the code.\",\n",
" \"Proper encapsulation of functionality.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Test Coverage\",\n",
" \"score\": 70,\n",
" \"findings\": [\n",
" \"Lack of explicit test cases in the provided code.\",\n",
" \"Use of type checking suggests some level of validation.\"\n",
" ]\n",
" }\n",
" ],\n",
" \"architecture_score\": 80,\n",
" \"maintainability_score\": 85,\n",
" \"performance_score\": 75,\n",
" \"security_score\": 80,\n",
" \"test_coverage\": 70,\n",
" \"key_strengths\": [\n",
" \"Strong use of type annotations and typing extensions.\",\n",
" \"Clear separation of CLI argument parsing and business logic.\"\n",
" ],\n",
" \"improvement_areas\": [\n",
" \"Increase test coverage to ensure robustness.\",\n",
" \"Optimize I/O operations to improve performance.\"\n",
" ],\n",
" \"tech_stack\": [\n",
" \"Python\",\n",
" \"argparse\",\n",
" \"typing_extensions\"\n",
" ],\n",
" \"recommendations\": [\n",
" \"Add unit tests to improve reliability.\",\n",
" \"Consider async I/O for improved performance in CLI tools.\"\n",
" ]\n",
"}\n"
]
}
],
"source": [
"import json\n",
"from IPython.display import display, Markdown\n",
"\n",
"# Optional: Define agent info\n",
"agent_info = \"\"\"\n",
"### 👤 Agent: Code Analysis Expert\n",
"\n",
"**Role**: Provides comprehensive code evaluation and recommendations\n",
"**Backstory**: Expert in architecture, best practices, and technical assessment\n",
"\"\"\"\n",
"\n",
"# Analysis Result Data\n",
"analysis_result = {\n",
" \"overall_quality\": 85,\n",
" \"code_metrics\": [\n",
" {\n",
" \"category\": \"Architecture and Design\",\n",
" \"score\": 80,\n",
" \"findings\": [\n",
" \"Modular structure with clear separation of concerns.\",\n",
" \"Use of type annotations improves code readability and maintainability.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Code Maintainability\",\n",
" \"score\": 85,\n",
" \"findings\": [\n",
" \"Consistent use of type hints and NamedTuple for structured data.\",\n",
" \"Logical organization of functions and classes.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Performance Optimization\",\n",
" \"score\": 75,\n",
" \"findings\": [\n",
" \"Potential performance overhead due to repeated sys.stdout.write calls.\",\n",
" \"Efficient use of optional parameters to control execution flow.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Security Practices\",\n",
" \"score\": 80,\n",
" \"findings\": [\n",
" \"No obvious security vulnerabilities in the code.\",\n",
" \"Proper encapsulation of functionality.\"\n",
" ]\n",
" },\n",
" {\n",
" \"category\": \"Test Coverage\",\n",
" \"score\": 70,\n",
" \"findings\": [\n",
" \"Lack of explicit test cases in the provided code.\",\n",
" \"Use of type checking suggests some level of validation.\"\n",
" ]\n",
" }\n",
" ],\n",
" \"architecture_score\": 80,\n",
" \"maintainability_score\": 85,\n",
" \"performance_score\": 75,\n",
" \"security_score\": 80,\n",
" \"test_coverage\": 70,\n",
" \"key_strengths\": [\n",
" \"Strong use of type annotations and typing extensions.\",\n",
" \"Clear separation of CLI argument parsing and business logic.\"\n",
" ],\n",
" \"improvement_areas\": [\n",
" \"Increase test coverage to ensure robustness.\",\n",
" \"Optimize I/O operations to improve performance.\"\n",
" ],\n",
" \"tech_stack\": [\"Python\", \"argparse\", \"typing_extensions\"],\n",
" \"recommendations\": [\n",
" \"Add unit tests to improve reliability.\",\n",
" \"Consider async I/O for improved performance in CLI tools.\"\n",
" ]\n",
"}\n",
"\n",
"# Display Agent Info and Analysis Report\n",
"display(Markdown(agent_info))\n",
"print(\"─── 📊 AGENT CODE ANALYSIS REPORT ───\")\n",
"print(json.dumps(analysis_result, indent=4))\n"
],
"id": "5BzccaeFmvzg"
},
{
"cell_type": "code",
"source": [
"%cd PraisonAI"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "mNZjZLedorlu",
"outputId": "82c96cdf-e2e5-4a9c-e633-f0fc225ad973"
},
"id": "mNZjZLedorlu",
"execution_count": 5,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content/PraisonAI\n"
]
}
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10"
},
"colab": {
"provenance": []
}
},
"nbformat": 4,
"nbformat_minor": 5
}
=======
}
@@ -0,0 +1,605 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "PnWDOzZelHlE",
"metadata": {
"id": "PnWDOzZelHlE"
},
"source": [
"# 🌱 Gemma 2B Instruction Agent\n",
"\n",
"**Goal:** You will learn how to do data prep, how to train, how to run the model, and how to save it using Google’s `gemma-2b-it` open-source model.\n",
"\n",
"---\n"
]
},
{
"cell_type": "markdown",
"id": "4qumJb5qs9CF",
"metadata": {
"id": "4qumJb5qs9CF"
},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/Gemma2B_Instruction_Agent.ipynb)\n"
]
},
{
"cell_type": "markdown",
"id": "qdsqiQDosXmA",
"metadata": {
"id": "qdsqiQDosXmA"
},
"source": [
"# Dependencies"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6G4RPDe3lHlH",
"metadata": {
"id": "6G4RPDe3lHlH"
},
"outputs": [],
"source": [
"!pip install transformers accelerate datasets bitsandbytes -q"
]
},
{
"cell_type": "markdown",
"id": "mop0KNO7sdEl",
"metadata": {
"id": "mop0KNO7sdEl"
},
"source": [
"# Tools & Model Setup"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "spOeRX2UlHlI",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 369,
"referenced_widgets": [
"a2db67e368d94f579c59b36c6ce7af33",
"5816fa4288cd42d6b15094e2c79f760f",
"5d6fe9b464f944cdbb00b9936669e737",
"19920e5b8157423ba5d173dd376e52e6",
"bd7157974bc5485ab5b6db54facb9721",
"0063e7320c98440d91ae475596c5cd90",
"a0f045bc54754d5a85ed6bf7b6b33c1a",
"9174c5aff19b4caab8a38fbf3acb2449",
"3de0f0b7ecb34c488145b24e30418e4b",
"d4457543819d468eb916f126382184b0",
"08f90c04f0de4b1db57624db4b43e17d",
"269373e971704b9ea2e10e261b1c56d1",
"d54d35c945ce4f75af6f83f37dd6af6c",
"b828857fecdc40fa91f6a8633f0f20b4",
"b5e70949aea24ae9a75e45aa291cc3aa",
"9c556838b24246a8b39b1f3f9babfe4a",
"062b64d17b7043c1bccc419280c9db0a",
"138ae14327184f65896c29a424bad18d",
"cb65fa660df54218ad804815dfba4071",
"93d2454f1b564896bef50906e859c660",
"64e7861e6afc4b57b28063096533aac8",
"13fe0b8a8fd14a3684ae5d9beefb3f76",
"d1ec61e51d4c4b07b301959c4919f2a2",
"a45e9ed3d46645a18ca1f6b06f875ec9",
"a364770555cf40fdab5ff7bb85997127",
"12b3225aeb8c4f61b23b588738d48683",
"dca4952bae4c49ddb2fec2c36726a34d",
"163df8921f04425caed240465d8bd8b7",
"1a09718a80cd4bb5a9e985a3f599c614",
"71c719d57e4f4e23bd520f80e2e07e33",
"5e8c8eb7eba948fc8a39b601e9ac658e",
"db4fa2c97b7247c4826a1a9f69c1bed3",
"42a6f9c136584cc98fc2be973d9edaec",
"c1bc8cf2ed6542c49c1940673a662b1d",
"365b69634e2846a09b91de6b61d0719b",
"13eb69bc1c3c432db6c504856f3a0f7a",
"56444404d97549848ed729ee8bc14ec8",
"f0303454544d42a1ada2213a085a4ac0",
"c717969ed1b145a9af67d74482d49eab",
"9e9a826dfb264e44aa69c553d978729d",
"f2b4ff56a96c4d7e9f56668f6b7b5213",
"f71f0a3ccc654df288d7489abca0ffc0",
"5ab0574ed1db47e9b6f43e27acd6c2b2",
"426fae423dbb43ef9252e6fcfb099c6f",
"2e1c3ca06b2948f0bcf53a975dd67aec",
"3685a74a466a4ca0b5f05e7aee631d74",
"7bda5a4e803c4cbc8da67da8a565725b",
"5450fde9a54d450d90d14b009047b522",
"e827839bf4d34d2193776d1ee90d27ad",
"3d9854af016a4c3c8a582fc7f0f0b0bf",
"7045a11d5f0f4b2d8ff224bb76a9c943",
"32e568b25c2d447a8fa63055cb44c75c",
"ebd9d55db82542e5ae6bb9e715e36a6a",
"798a57883792465ab024dc19a58a54be",
"c9d1133770b44b22bda89f3d0eecd4f8",
"c040fa1bbd9f442884776fdf33efb4a2",
"c57e497d4fd546ff950eb2c898b94c88",
"5ae22935b7374219a16272cef31d6d9e",
"aae2686ee64740218d046ec37e50b584",
"87b4c953163a4cff8a88b76f40da9de8",
"30ee0e766fae44bfb570ae8adfba1a54",
"1c2db67dfc904e30a4013d9fe0253a4c",
"f6c5380bba0f40a4bea9b78d205eb726",
"0de9f765500b45428f828ad173dd279e",
"0663e5cfc20c496993b7c0054ce3d1a1",
"7ce5d369129b461681f424e52415e534",
"dfa90d76c670436a80f1a4fd0971b171",
"0694216654ac41909d567a8c4af247de",
"658048f25b4a4ad6a3ee66e97a542cc9",
"0edccfd243f940cfad9723b1d3f5a97c",
"32d473040a6744718b1b35543f2b3a0f",
"eadbfe846763427e98a40597560d8519",
"3cec8231bccb4bcf8d657ee8d2120f79",
"8bd43207fa8f4b309590f605ad81354f",
"8821912860f04105959ceb42e53de948",
"3666b044c757439f948f6942d7682c2f",
"d47e7c7504374fee82eef9f8cbe24eff",
"9c6d577f113f4f4baaa1f0e001549862",
"b0c88d34b0df46b7a3da8db42ff3274e",
"e4e4a2d0422b45c09c8691ce14ba9b9a",
"d9b1de1b37f14d70bc56a5f6cb468c9b",
"cb95b11368984236937fcec7cb689b04",
"69e70bfdafd4459491bad75651028626",
"a3b9f03c1f014691b5959fb9012c0bab",
"088da12bcff64297ad82dc7b2d01709e",
"8a69dc8aa84e49b1aabb58eddca1eee3",
"263e458b8a5f405bb1ee365fe561309d",
"aa3039e03447492da11f86185384470b",
"3c22264e11c0402fb07f56a0763dc055",
"db2c5746b8cc48d9a059bcef3b55217d",
"ad3ab2ab81f6469485a3072bd1ba6a4a",
"74ab1c79c43041c7ac314d62a6da04bf",
"ad1a3719b1814f6b88202e131ae923c7",
"d1cb72251682425fa6ca68341a084f95",
"895914fca4204593b1a4314e50b8e5a1",
"a8374dc19b294b6d8f5f92bee5196a1a",
"b7bef910950949fbac65fd996ad058e2",
"ce6b7aadbd904b3caa93d927ced6beb3",
"ba6a9e31ddf7419db317a5d27a1567bf",
"f271e9c78dcf4ab686caac37f18bd6f7",
"9ed57fe81783476986d6cb98d07e9aa1",
"8da75a35ab184675b0164682d8217406",
"f411c4485b844ceb9b3116f209a6fc86",
"60ac9cd6505141348323b39db7314554",
"59b2d494f11344879463a3471716bc3f",
"46abda4b52224874a93b729f5d4d195b",
"37bc81b97d48474aa0fd7e7739654d86",
"73da5101d99440abac30dc8c084439d4",
"ef790742325743fdb8f7cc6822388185",
"5eccfbe5715f4df9a96cffb2573f916f",
"3bf293367470430f84f341ef8711460a",
"317281ca2af54a148fa4cfd8f6e0f382",
"d655ef37643448aa95253143f8552f58",
"158e61376dee400e820f47c87d59bac5",
"d1fbbe8010e340119bf556c75284e1c2",
"eea1731926554e56a85867de15153f82",
"766fb34d23b941b2a9e0de153f4cb031",
"beb52b95db564105bbd8bc2b2d21c1ce",
"2effd1c45bee4278bd9172310f58fed4",
"91d6488876284748932b664953c02146",
"9157d82cca3f45c5b5a666a3d35d6abe"
]
},
"id": "spOeRX2UlHlI",
"outputId": "093b74d1-9ece-4db5-d177-3aff6e2d6e0b"
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "a2db67e368d94f579c59b36c6ce7af33",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer_config.json: 0%| | 0.00/47.0k [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "269373e971704b9ea2e10e261b1c56d1",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer.model: 0%| | 0.00/4.24M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d1ec61e51d4c4b07b301959c4919f2a2",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer.json: 0%| | 0.00/17.5M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "c1bc8cf2ed6542c49c1940673a662b1d",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"special_tokens_map.json: 0%| | 0.00/636 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "2e1c3ca06b2948f0bcf53a975dd67aec",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"config.json: 0%| | 0.00/838 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "c040fa1bbd9f442884776fdf33efb4a2",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"model.safetensors.index.json: 0%| | 0.00/24.2k [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "dfa90d76c670436a80f1a4fd0971b171",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Fetching 2 files: 0%| | 0/2 [00:00<?, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "9c6d577f113f4f4baaa1f0e001549862",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"model-00001-of-00002.safetensors: 0%| | 0.00/4.99G [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "3c22264e11c0402fb07f56a0763dc055",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"model-00002-of-00002.safetensors: 0%| | 0.00/241M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "f271e9c78dcf4ab686caac37f18bd6f7",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Loading checkpoint shards: 0%| | 0/2 [00:00<?, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "3bf293367470430f84f341ef8711460a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"generation_config.json: 0%| | 0.00/187 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from huggingface_hub import login\n",
"from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer\n",
"from datasets import load_dataset\n",
"import torch\n",
"\n",
"login(\"Enter your token here\")\n",
"\n",
"model_id = \"google/gemma-2-2b-it\"\n",
"\n",
"tokenizer = AutoTokenizer.from_pretrained(model_id)\n",
"model = AutoModelForCausalLM.from_pretrained(\n",
" model_id,\n",
" device_map=\"auto\", # Automatically selects GPU if available\n",
" torch_dtype=torch.float16 # Optimized for performance\n",
")\n"
]
},
{
"cell_type": "markdown",
"id": "GaykKFKYspMd",
"metadata": {
"id": "GaykKFKYspMd"
},
"source": [
"# Yaml Prompt Configuration"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "iLBZ7WPAlHlJ",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "iLBZ7WPAlHlJ",
"outputId": "a99fc3f2-68e0-4af8-cfbd-3c48a865538f"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"You are Qwen, a helpful assistant.\n",
"User: What is the capital of France?\n",
"Assistant: The capital of France is **Paris**. \n",
"\n"
]
}
],
"source": [
"prompt = \"You are Qwen, a helpful assistant.\\nUser: What is the capital of France?\\nAssistant:\"\n",
"inputs = tokenizer(prompt, return_tensors=\"pt\").to(model.device)\n",
"outputs = model.generate(**inputs, max_new_tokens=100)\n",
"response = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
"print(response)\n"
]
},
{
"cell_type": "markdown",
"id": "XPqtCExSsvhn",
"metadata": {
"id": "XPqtCExSsvhn"
},
"source": [
"# Use a small sample dataset"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "ActfjFx0lHlK",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 49,
"referenced_widgets": [
"f1028b5dcab4460c81b09802ca7ec89c",
"ff33dcccaad945e9bf136d0f72e38d74",
"eec3a11b0605435d8d1e55092f5e5b85",
"d997b7bc993b4aa58cfafa7c59829ad3",
"62e2dc508746419ba3c0405a7199c6dc",
"2a7fc59019c749aa98d0e970a9b6b168",
"78067f66c8544020aa262c0012aa019b",
"f5e511b4279747c0a33b10f499a4f718",
"10e248a5730c4c09834b4065b6dd7843",
"bbd5a106c1de43e497987648899569e0",
"118faf3d1ed142038af4783fb3ba86cc"
]
},
"id": "ActfjFx0lHlK",
"outputId": "f257e553-7baa-4f1e-b531-a6b7dc358665"
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "f1028b5dcab4460c81b09802ca7ec89c",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Map: 0%| | 0/4 [00:00<?, ? examples/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from datasets import Dataset\n",
"\n",
"sample_data = {\n",
" 'text': [\n",
" 'The sun is a star at the center of our solar system.',\n",
" 'Photosynthesis is the process by which green plants make food.',\n",
" 'Water freezes at 0 degrees Celsius.',\n",
" 'The Earth revolves around the sun in 365 days.'\n",
" ]\n",
"}\n",
"\n",
"dataset = Dataset.from_dict(sample_data)\n",
"\n",
"def tokenize_function(example):\n",
" return tokenizer(example['text'], padding='max_length', truncation=True, max_length=64)\n",
"\n",
"tokenized_dataset = dataset.map(tokenize_function)\n",
"tokenized_dataset.set_format(type='torch', columns=['input_ids', 'attention_mask'])"
]
},
{
"cell_type": "markdown",
"id": "x7icWpOnszjj",
"metadata": {
"id": "x7icWpOnszjj"
},
"source": [
"# Inference"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "8mM4PAhRlHlL",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "8mM4PAhRlHlL",
"outputId": "1ba90639-b847-4cb1-df42-3fdd644e4cb8"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"user\n",
"Explain photosynthesis to a child.\n",
"model\n",
"Imagine plants are like tiny chefs, and they cook their own food! \n",
"\n",
"They use sunlight, air, and water to make yummy food called sugar. \n",
"\n",
"Here's how it works:\n",
"\n",
"1. **Sunlight:** Plants have special green stuff called chlorophyll that acts like a solar panel, soaking up the sun's energy.\n",
"2. **Air:** Plants take in air through tiny holes in their leaves called stomata. The air has a gas called carbon dioxide.\n",
"3\n"
]
}
],
"source": [
"input_text = \"Explain photosynthesis to a child.\"\n",
"chat = tokenizer.apply_chat_template(\n",
" [{\"role\": \"user\", \"content\": input_text}],\n",
" tokenize=False,\n",
" add_generation_prompt=True\n",
")\n",
"inputs = tokenizer(chat, return_tensors=\"pt\").to(model.device)\n",
"output = model.generate(**inputs, max_new_tokens=100)\n",
"print(tokenizer.decode(output[0], skip_special_tokens=True))"
]
},
{
"cell_type": "markdown",
"id": "Fhkfhbjns2oq",
"metadata": {
"id": "Fhkfhbjns2oq"
},
"source": [
"# Save Model"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "OuJEt2-jlHlL",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "OuJEt2-jlHlL",
"outputId": "f076e950-cce7-4850-edd9-ff1d84a096d6"
},
"outputs": [
{
"data": {
"text/plain": [
"('gemma-finetuned-demo/tokenizer_config.json',\n",
" 'gemma-finetuned-demo/special_tokens_map.json',\n",
" 'gemma-finetuned-demo/chat_template.jinja',\n",
" 'gemma-finetuned-demo/tokenizer.model',\n",
" 'gemma-finetuned-demo/added_tokens.json',\n",
" 'gemma-finetuned-demo/tokenizer.json')"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.save_pretrained(\"gemma-finetuned-demo\")\n",
"tokenizer.save_pretrained(\"gemma-finetuned-demo\")"
]
},
{
"cell_type": "markdown",
"id": "uFK1sCzblHlM",
"metadata": {
"id": "uFK1sCzblHlM"
},
"source": [
"# Output\n",
"`Photosynthesis is how plants eat sunlight! 🌞 They use air, water, and sunlight to make food and grow.`"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,436 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "dcpGQid2Jcud"
},
"source": [
"# 📘 Hackathon Project Video Evaluator using PraisonAIAgents"
],
"id": "dcpGQid2Jcud"
},
{
"cell_type": "markdown",
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/Hackathon_Agent_Notebook.ipynb)\n"
],
"metadata": {
"id": "9A8PW-_1NyK4"
},
"id": "9A8PW-_1NyK4"
},
{
"cell_type": "markdown",
"source": [
"# Dependencies"
],
"metadata": {
"id": "B-RlKMC3NWZL"
},
"id": "B-RlKMC3NWZL"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ItoyCaVhJcug"
},
"outputs": [],
"source": [
"!pip install praisonaiagents opencv-python moviepy"
],
"id": "ItoyCaVhJcug"
},
{
"cell_type": "markdown",
"source": [
"# Set OpenAI API Key for Authentication"
],
"metadata": {
"id": "x4dBCoN-Nf4h"
},
"id": "x4dBCoN-Nf4h"
},
{
"cell_type": "code",
"source": [
"import os\n",
"os.environ[\"OPENAI_API_KEY\"] = \"Enter your api key\"\n",
"print(\"OpenAI API key has been set successfully!\")"
],
"metadata": {
"id": "wz9hEZf1Ncv2"
},
"id": "wz9hEZf1Ncv2",
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Setup: Imports and Agent Frameworks"
],
"metadata": {
"id": "L8UiiaAiNtRQ"
},
"id": "L8UiiaAiNtRQ"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "eU6iiDynJcuh"
},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"import re\n",
"import cv2\n",
"import numpy as np\n",
"from typing import List\n",
"from pydantic import BaseModel\n",
"from praisonaiagents import Agent, Task, PraisonAIAgents"
],
"id": "eU6iiDynJcuh"
},
{
"cell_type": "markdown",
"metadata": {
"id": "jIBUGHAbJcui"
},
"source": [
"## 🛠️ Tools and Data Models"
],
"id": "jIBUGHAbJcui"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "3wGOzkZcJcuj"
},
"outputs": [],
"source": [
"class ProjectEvaluation(BaseModel):\n",
" innovation_score: int\n",
" technical_complexity: int\n",
" presentation_quality: int\n",
" user_experience: int\n",
" completeness: int\n",
" overall_score: int\n",
" key_strengths: List[str]\n",
" areas_for_improvement: List[str]\n",
" notable_features: List[str]\n",
" technical_highlights: List[str]\n",
" recommendations: List[str]\n",
" market_potential: str\n",
" scalability_assessment: str"
],
"id": "3wGOzkZcJcuj"
},
{
"cell_type": "markdown",
"metadata": {
"id": "idRWbOQNJcuj"
},
"source": [
"## 🎞️ Video Frame Extraction"
],
"id": "idRWbOQNJcuj"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Rlhm_jzfJcuj"
},
"outputs": [],
"source": [
"def extract_frames(video_path: str, num_frames: int = 5) -> List[str]:\n",
" if not os.path.exists(video_path):\n",
" raise FileNotFoundError(f\"Video not found: {video_path}\")\n",
" frames_dir = os.path.join(os.path.dirname(video_path), \"frames\")\n",
" os.makedirs(frames_dir, exist_ok=True)\n",
" cap = cv2.VideoCapture(video_path)\n",
" total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n",
" interval = total_frames // (num_frames + 1)\n",
" paths = []\n",
" for i in range(1, num_frames + 1):\n",
" cap.set(cv2.CAP_PROP_POS_FRAMES, i * interval)\n",
" ret, frame = cap.read()\n",
" if not ret: break\n",
" path = os.path.join(frames_dir, f\"frame_{i}.jpg\")\n",
" cv2.imwrite(path, frame)\n",
" paths.append(path)\n",
" cap.release()\n",
" return paths"
],
"id": "Rlhm_jzfJcuj"
},
{
"cell_type": "markdown",
"metadata": {
"id": "97Bap7zKJcuk"
},
"source": [
"## 🤖 Define the AI Agent"
],
"id": "97Bap7zKJcuk"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "KLME0GcsJcuk"
},
"outputs": [],
"source": [
"hackathon_judge = Agent(\n",
" name=\"HackathonJudge\",\n",
" role=\"Technical Project Evaluator\",\n",
" goal=\"Evaluate hackathon projects through video demonstrations\",\n",
" backstory=\"\"\"You are an expert hackathon judge. You give fair scores and detailed feedback\n",
" on technical complexity, innovation, presentation, and UX.\"\"\",\n",
" llm=\"gpt-4o-mini\",\n",
" self_reflect=False\n",
")"
],
"id": "KLME0GcsJcuk"
},
{
"cell_type": "markdown",
"metadata": {
"id": "HG5dmlzhJcul"
},
"source": [
"## 🚀 Main Evaluation Function"
],
"id": "HG5dmlzhJcul"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "6ioAIkG4Jcul"
},
"outputs": [],
"source": [
"def evaluate_project(video_path: str) -> ProjectEvaluation:\n",
" frame_paths = extract_frames(video_path)\n",
" task = Task(\n",
" name=\"project_evaluation\",\n",
" description=\"\"\"Analyze hackathon video frames and score: innovation, complexity, UX,\n",
" presentation, completeness. Provide strengths, improvements, highlights, market potential,\n",
" and recommendations.\"\"\",\n",
" expected_output=\"JSON feedback with scores and suggestions\",\n",
" agent=hackathon_judge,\n",
" output_pydantic=ProjectEvaluation,\n",
" images=frame_paths\n",
" )\n",
" agents = PraisonAIAgents(\n",
" agents=[hackathon_judge],\n",
" tasks=[task],\n",
" process=\"sequential\",\n",
" verbose=True\n",
" )\n",
"\n",
" try:\n",
" response = agents.start()\n",
" if isinstance(response, dict) and 'task_results' in response:\n",
" output = response['task_results'][0]\n",
" if hasattr(output, 'pydantic'):\n",
" return output.pydantic\n",
" elif hasattr(output, 'raw'):\n",
" raw = output.raw.strip()\n",
" json_text = re.search(r'```(?:json)?\\s*([\\s\\S]+?)\\s*```', raw)\n",
" raw = json_text.group(1).strip() if json_text else raw\n",
" return ProjectEvaluation(**json.loads(raw))\n",
" elif isinstance(response, str):\n",
" return ProjectEvaluation(**json.loads(response))\n",
" return ProjectEvaluation(**response)\n",
" finally:\n",
" for path in frame_paths:\n",
" os.remove(path)\n",
" try:\n",
" os.rmdir(os.path.join(os.path.dirname(video_path), \"frames\"))\n",
" except: pass"
],
"id": "6ioAIkG4Jcul"
},
{
"cell_type": "markdown",
"metadata": {
"id": "VAwiZiJ1Jcum"
},
"source": [
"## ▶️ Run the Evaluation"
],
"id": "VAwiZiJ1Jcum"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Jf9_efNPJcum"
},
"outputs": [],
"source": [
"os.environ[\"OPENAI_API_KEY\"] = \"sk-proj-...YOUR_API_KEY...\"\n",
"\n",
"video_file = \"presentation.mp4\"\n",
"try:\n",
" result = evaluate_project(video_file)\n",
" print(\"\\n🔍 Hackathon Project Evaluation\")\n",
" print(\"=\" * 40)\n",
" print(f\"Overall Score: {result.overall_score}/100\")\n",
" print(f\"Innovation: {result.innovation_score}/100\")\n",
" print(f\"Technical Complexity: {result.technical_complexity}/100\")\n",
" print(f\"Presentation Quality: {result.presentation_quality}/100\")\n",
" print(f\"User Experience: {result.user_experience}/100\")\n",
" print(f\"Completeness: {result.completeness}/100\")\n",
" print(\"\\n✅ Key Strengths:\")\n",
" for s in result.key_strengths: print(f\"- {s}\")\n",
" print(\"\\n⚠️ Areas for Improvement:\")\n",
" for a in result.areas_for_improvement: print(f\"- {a}\")\n",
" print(\"\\n🛠️ Technical Highlights:\")\n",
" for t in result.technical_highlights: print(f\"- {t}\")\n",
" print(\"\\n📌 Recommendations:\")\n",
" for r in result.recommendations: print(f\"- {r}\")\n",
" print(f\"\\n📈 Market Potential: {result.market_potential}\")\n",
" print(f\"\\n📊 Scalability Assessment: {result.scalability_assessment}\")\n",
"except FileNotFoundError:\n",
" print(\"❗ 'presentation.mp4' not found. Please add it to the current directory.\")\n",
"except Exception as e:\n",
" print(f\"❌ Evaluation Error: {e}\")"
],
"id": "Jf9_efNPJcum"
},
{
"cell_type": "code",
"source": [
"output = \"\"\"\n",
"╭─ Agent Info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ 👤 Agent: HackathonJudge │\n",
"│ Role: Technical Project Evaluator │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n",
"╭───────────────────────────────────────────────────── Generating... 6.6s ─────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ { │\n",
"│ \"innovation_score\": 30, │\n",
"│ \"technical_complexity\": 20, │\n",
"│ \"presentation_quality\": 25, │\n",
"│ \"user_experience\": 35, │\n",
"│ \"completeness\": 20, │\n",
"│ \"overall_score\": 24, │\n",
"│ \"key_strengths\": [ │\n",
"│ \"Simple concept\", │\n",
"│ \"Clear visual representation\" │\n",
"│ ], │\n",
"│ \"areas_for_improvement\": [ │\n",
"│ \"Enhance technical features\", │\n",
"│ \"Improve presentation clarity\", │\n",
"│ \"Add user interaction elements\" │\n",
"│ ], │\n",
"│ \"notable_features\": [ │\n",
"│ \"Basic visual interface\" │\n",
"│ ], │\n",
"│ \"technical_highlights\": [ │\n",
"│ \"Use of basic graphics\" │\n",
"│ ], │\n",
"│ \"recommendations\": [ │\n",
"│ \"Incorporate user feedback mechanisms\", │\n",
"│ \"Develop a more robust backend\", │\n",
"│ \"Enhance visual aesthetics\" │\n",
"│ ], │\n",
"│ \"market_potential\": \"Limited due to simplicity\", │\n",
"│ \"scalability_assessment\": \"Low, requires significant development\" │\n",
"│ } │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\"\"\"\n",
"print(output)\n"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "T-wYaKzkKGNK",
"outputId": "ebeaf7e3-6362-4d1c-f9d1-cc3504d733a2"
},
"id": "T-wYaKzkKGNK",
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"╭─ Agent Info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ 👤 Agent: HackathonJudge │\n",
"│ Role: Technical Project Evaluator │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n",
"╭───────────────────────────────────────────────────── Generating... 6.6s ─────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ { │\n",
"│ \"innovation_score\": 30, │\n",
"│ \"technical_complexity\": 20, │\n",
"│ \"presentation_quality\": 25, │\n",
"│ \"user_experience\": 35, │\n",
"│ \"completeness\": 20, │\n",
"│ \"overall_score\": 24, │\n",
"│ \"key_strengths\": [ │\n",
"│ \"Simple concept\", │\n",
"│ \"Clear visual representation\" │\n",
"│ ], │\n",
"│ \"areas_for_improvement\": [ │\n",
"│ \"Enhance technical features\", │\n",
"│ \"Improve presentation clarity\", │\n",
"│ \"Add user interaction elements\" │\n",
"│ ], │\n",
"│ \"notable_features\": [ │\n",
"│ \"Basic visual interface\" │\n",
"│ ], │\n",
"│ \"technical_highlights\": [ │\n",
"│ \"Use of basic graphics\" │\n",
"│ ], │\n",
"│ \"recommendations\": [ │\n",
"│ \"Incorporate user feedback mechanisms\", │\n",
"│ \"Develop a more robust backend\", │\n",
"│ \"Enhance visual aesthetics\" │\n",
"│ ], │\n",
"│ \"market_potential\": \"Limited due to simplicity\", │\n",
"│ \"scalability_assessment\": \"Low, requires significant development\" │\n",
"│ } │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n"
]
}
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.11"
},
"colab": {
"provenance": []
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,401 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "l7oSysosTfJj"
},
"source": [
"# Predictive Maintenance Workflow using PraisonAIAgents\n",
"\n",
"This notebook demonstrates how to build a predictive maintenance workflow using multiple AI agents."
],
"id": "l7oSysosTfJj"
},
{
"cell_type": "markdown",
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/Predictive_Maintenance_Multi-Agent_Workflow.ipynb)"
],
"metadata": {
"id": "LH0_JjO7Y1G2"
},
"id": "LH0_JjO7Y1G2"
},
{
"cell_type": "markdown",
"source": [
"# Dependencies"
],
"metadata": {
"id": "ei-yNZSKXgtJ"
},
"id": "ei-yNZSKXgtJ"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "fnkuN8sTTfJq"
},
"outputs": [],
"source": [
"!pip install praisonaiagents"
],
"id": "fnkuN8sTTfJq"
},
{
"cell_type": "markdown",
"source": [
"# Set your OpenAI API key"
],
"metadata": {
"id": "7PUSTQHwX5ia"
},
"id": "7PUSTQHwX5ia"
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"id": "33HivD-LTfJu"
},
"outputs": [],
"source": [
"import os\n",
"os.environ['OPENAI_API_KEY'] = 'enter your api key'"
],
"id": "33HivD-LTfJu"
},
{
"cell_type": "markdown",
"source": [
"# Import required modules"
],
"metadata": {
"id": "D6we7t4nYESB"
},
"id": "D6we7t4nYESB"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "03z7Pi22TfJw"
},
"outputs": [],
"source": [
"from praisonaiagents import Agent, Task, PraisonAIAgents\n",
"import time\n",
"from typing import Dict, List\n",
"import asyncio"
],
"id": "03z7Pi22TfJw"
},
{
"cell_type": "markdown",
"source": [
"# Define helper functions"
],
"metadata": {
"id": "TzZt0jbjYGgw"
},
"id": "TzZt0jbjYGgw"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "2oMzDsp3TfJw"
},
"outputs": [],
"source": [
"\n",
"def collect_sensor_data():\n",
" return {\n",
" \"temperature\": 75 + (int(time.time()) % 20),\n",
" \"vibration\": 0.5 + (int(time.time()) % 10) / 10,\n",
" \"pressure\": 100 + (int(time.time()) % 50),\n",
" \"noise_level\": 60 + (int(time.time()) % 30)\n",
" }\n",
"\n",
"def analyze_performance():\n",
" return {\n",
" \"efficiency\": 0.8 + (int(time.time()) % 20) / 100,\n",
" \"uptime\": 0.95 + (int(time.time()) % 5) / 100,\n",
" \"output_quality\": 0.9 + (int(time.time()) % 10) / 100\n",
" }\n",
"\n",
"def detect_anomalies(sensor_data: Dict, performance: Dict):\n",
" anomalies = []\n",
" if sensor_data[\"temperature\"] > 90:\n",
" anomalies.append({\"type\": \"temperature_high\", \"severity\": \"critical\"})\n",
" if sensor_data[\"vibration\"] > 1.2:\n",
" anomalies.append({\"type\": \"vibration_excess\", \"severity\": \"warning\"})\n",
" if performance[\"efficiency\"] < 0.85:\n",
" anomalies.append({\"type\": \"efficiency_low\", \"severity\": \"warning\"})\n",
" return anomalies\n",
"\n",
"def predict_failures(anomalies: List[Dict]):\n",
" predictions = []\n",
" severity_scores = {\"critical\": 0.9, \"warning\": 0.6}\n",
" for anomaly in anomalies:\n",
" predictions.append({\n",
" \"component\": anomaly[\"type\"].split(\"_\")[0],\n",
" \"probability\": severity_scores[anomaly[\"severity\"]],\n",
" \"timeframe\": \"24_hours\" if anomaly[\"severity\"] == \"critical\" else \"7_days\"\n",
" })\n",
" return predictions\n",
"\n",
"def schedule_maintenance(predictions: List[Dict]):\n",
" schedule = []\n",
" for pred in predictions:\n",
" schedule.append({\n",
" \"component\": pred[\"component\"],\n",
" \"priority\": \"immediate\" if pred[\"timeframe\"] == \"24_hours\" else \"planned\",\n",
" \"estimated_duration\": \"2_hours\",\n",
" \"required_parts\": [\"replacement_\" + pred[\"component\"]]\n",
" })\n",
" return schedule"
],
"id": "2oMzDsp3TfJw"
},
{
"cell_type": "markdown",
"source": [
"# Define agents and tasks"
],
"metadata": {
"id": "-7qMHyY5YIph"
},
"id": "-7qMHyY5YIph"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "4ZYbYzjNTfJy"
},
"outputs": [],
"source": [
"\n",
"sensor_monitor = Agent(\"Sensor Monitor\", \"Data Collection\", \"Collect sensor data\", \"Monitor and collect sensor readings\", [collect_sensor_data])\n",
"performance_analyzer = Agent(\"Performance Analyzer\", \"Performance Analysis\", \"Analyze equipment performance\", \"Analyze operational metrics\", [analyze_performance])\n",
"anomaly_detector = Agent(\"Anomaly Detector\", \"Anomaly Detection\", \"Detect operational anomalies\", \"Identify abnormal patterns\", [detect_anomalies])\n",
"failure_predictor = Agent(\"Failure Predictor\", \"Failure Prediction\", \"Predict potential failures\", \"Predict equipment failures\", [predict_failures])\n",
"maintenance_scheduler = Agent(\"Maintenance Scheduler\", \"Maintenance Planning\", \"Schedule maintenance activities\", \"Plan and schedule maintenance\", [schedule_maintenance])\n",
"\n",
"sensor_task = Task(\"collect_data\", \"Collect sensor data\", \"Sensor readings\", sensor_monitor, is_start=True, next_tasks=[\"analyze_performance\"], async_execution=True)\n",
"performance_task = Task(\"analyze_performance\", \"Analyze performance metrics\", \"Performance analysis\", performance_analyzer, next_tasks=[\"detect_anomalies\"], async_execution=True)\n",
"anomaly_task = Task(\"detect_anomalies\", \"Detect operational anomalies\", \"Detected anomalies\", anomaly_detector, next_tasks=[\"predict_failures\"], context=[sensor_task, performance_task])\n",
"prediction_task = Task(\"predict_failures\", \"Predict potential failures\", \"Failure predictions\", failure_predictor, next_tasks=[\"schedule_maintenance\"], task_type=\"decision\", condition={\"critical\": [\"schedule_maintenance\"], \"warning\": [\"schedule_maintenance\"], \"normal\": \"\"})\n",
"scheduling_task = Task(\"schedule_maintenance\", \"Schedule maintenance activities\", \"Maintenance schedule\", maintenance_scheduler, context=[prediction_task])"
],
"id": "4ZYbYzjNTfJy"
},
{
"cell_type": "markdown",
"source": [
"# Create and run the workflow"
],
"metadata": {
"id": "XOdIxJKiYKuD"
},
"id": "XOdIxJKiYKuD"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "KYu_YTVDTfJz"
},
"outputs": [],
"source": [
"\n",
"workflow = PraisonAIAgents(\n",
" agents=[sensor_monitor, performance_analyzer, anomaly_detector, failure_predictor, maintenance_scheduler],\n",
" tasks=[sensor_task, performance_task, anomaly_task, prediction_task, scheduling_task],\n",
" process=\"workflow\",\n",
" verbose=True\n",
")\n",
"\n",
"async def main():\n",
" print(\"\\nStarting Predictive Maintenance Workflow...\")\n",
" print(\"=\" * 50)\n",
" results = await workflow.astart()\n",
" print(\"\\nMaintenance Planning Results:\")\n",
" print(\"=\" * 50)\n",
" for task_id, result in results[\"task_results\"].items():\n",
" if result:\n",
" print(f\"\\nTask: {task_id}\\nResult: {result.raw}\\n{'-'*50}\")\n",
"\n",
"await main()"
],
"id": "KYu_YTVDTfJz"
},
{
"cell_type": "code",
"source": [
"print(\"\"\"\n",
"[Starting Predictive Maintenance Workflow...\n",
"==================================================\n",
"╭─ Agent Info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ 👤 Agent: Sensor Monitor │\n",
"│ Role: Data Collection │\n",
"│ Tools: collect_sensor_data │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n",
"╭─ Agent Info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ 👤 Agent: Performance Analyzer │\n",
"│ Role: Performance Analysis │\n",
"│ Tools: analyze_performance │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n",
"[20:01:26] INFO [20:01:26] process.py:429 INFO Task schedule_maintenance has no next tasks, ending workflow process.py:429\n",
"\n",
"Maintenance Planning Results:\n",
"==================================================\n",
"\n",
"Task: 0\n",
"Result: The sensor readings you have collected are as follows:\n",
"\n",
"- **Temperature**: 86°F\n",
"- **Vibration**: 0.6 (units not specified, but typically measured in g-forces or mm/s)\n",
"- **Pressure**: 101 (units not specified, but typically measured in kPa or psi)\n",
"- **Noise Level**: 81 dB\n",
"\n",
"Here's a brief analysis of these readings:\n",
"\n",
"1. **Temperature**: At 86°F, the temperature is relatively warm. Depending on the context (e.g., industrial equipment, environmental monitoring), this could be within normal operating conditions or might require cooling measures if it's above the optimal range.\n",
"\n",
"2. **Vibration**: A vibration level of 0.6 is generally low, but the significance depends on the type of equipment being monitored. For precision machinery, even small vibrations can be critical, whereas for more robust equipment, this might be negligible.\n",
"\n",
"3. **Pressure**: A pressure reading of 101 is often within normal ranges for many systems, but without specific units or context, it's hard to determine if this is optimal or requires adjustment.\n",
"\n",
"4. **Noise Level**: At 81 dB, the noise level is relatively high. Prolonged exposure to noise levels above 85 dB can be harmful to hearing, so if this is a workplace environment, it might be necessary to implement noise reduction measures or provide hearing protection.\n",
"\n",
"Overall, these readings should be compared against the specific operational thresholds and safety standards relevant to the equipment or environment being monitored. If any values are outside of acceptable ranges, further investigation or corrective actions may be needed.\n",
"--------------------------------------------------\n",
"\n",
"Task: 1\n",
"Result: Based on the provided operational metrics, here's an analysis of the equipment performance:\n",
"\n",
"1. **Efficiency (94%)**:\n",
" - The equipment is operating at a high efficiency level, with 94% of the input being effectively converted into useful output. This suggests\n",
"that the equipment is well-maintained and optimized for performance. However, there is still a 6% margin for improvement, which could be addressed by identifying and minimizing any inefficiencies in the process.\n",
"\n",
"2. **Uptime (99%)**:\n",
" - The equipment has an excellent uptime rate of 99%, indicating that it is available and operational almost all the time. This is a strong indicator of reliability and suggests that downtime due to maintenance or unexpected failures is minimal. Maintaining this level of uptime should\n",
"be a priority, as it directly impacts productivity and operational continuity.\n",
"\n",
"3. **Output Quality (94%)**:\n",
" - The output quality is also at 94%, which is a positive sign that the equipment is producing high-quality products or results. However, similar to efficiency, there is room for improvement. Efforts could be made to identify any factors that might be affecting quality, such as calibration issues, material inconsistencies, or process deviations.\n",
"\n",
"**Overall Assessment**:\n",
"The equipment is performing well across all key metrics, with high efficiency, uptime, and output quality. To further enhance performance, focus should be placed on fine-tuning processes to close the small gaps in efficiency and quality. Regular maintenance, monitoring, and process optimization can help sustain and potentially improve these metrics.\n",
"--------------------------------------------------]\n",
"\"\"\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "4hrEJ5S6XpJ7",
"outputId": "899e677d-19d5-4e0a-d9ab-ebc945aeee1b"
},
"id": "4hrEJ5S6XpJ7",
"execution_count": 4,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"[Starting Predictive Maintenance Workflow...\n",
"==================================================\n",
"╭─ Agent Info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ 👤 Agent: Sensor Monitor │\n",
"│ Role: Data Collection │\n",
"│ Tools: collect_sensor_data │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n",
"╭─ Agent Info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
"│ │\n",
"│ 👤 Agent: Performance Analyzer │\n",
"│ Role: Performance Analysis │\n",
"│ Tools: analyze_performance │\n",
"│ │\n",
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
"\n",
"[20:01:26] INFO [20:01:26] process.py:429 INFO Task schedule_maintenance has no next tasks, ending workflow process.py:429\n",
"\n",
"Maintenance Planning Results:\n",
"==================================================\n",
"\n",
"Task: 0\n",
"Result: The sensor readings you have collected are as follows:\n",
"\n",
"- **Temperature**: 86°F\n",
"- **Vibration**: 0.6 (units not specified, but typically measured in g-forces or mm/s)\n",
"- **Pressure**: 101 (units not specified, but typically measured in kPa or psi)\n",
"- **Noise Level**: 81 dB\n",
"\n",
"Here's a brief analysis of these readings:\n",
"\n",
"1. **Temperature**: At 86°F, the temperature is relatively warm. Depending on the context (e.g., industrial equipment, environmental monitoring), this could be within normal operating conditions or might require cooling measures if it's above the optimal range.\n",
"\n",
"2. **Vibration**: A vibration level of 0.6 is generally low, but the significance depends on the type of equipment being monitored. For precision machinery, even small vibrations can be critical, whereas for more robust equipment, this might be negligible.\n",
"\n",
"3. **Pressure**: A pressure reading of 101 is often within normal ranges for many systems, but without specific units or context, it's hard to determine if this is optimal or requires adjustment.\n",
"\n",
"4. **Noise Level**: At 81 dB, the noise level is relatively high. Prolonged exposure to noise levels above 85 dB can be harmful to hearing, so if this is a workplace environment, it might be necessary to implement noise reduction measures or provide hearing protection.\n",
"\n",
"Overall, these readings should be compared against the specific operational thresholds and safety standards relevant to the equipment or environment being monitored. If any values are outside of acceptable ranges, further investigation or corrective actions may be needed.\n",
"--------------------------------------------------\n",
"\n",
"Task: 1\n",
"Result: Based on the provided operational metrics, here's an analysis of the equipment performance:\n",
"\n",
"1. **Efficiency (94%)**:\n",
" - The equipment is operating at a high efficiency level, with 94% of the input being effectively converted into useful output. This suggests \n",
"that the equipment is well-maintained and optimized for performance. However, there is still a 6% margin for improvement, which could be addressed by identifying and minimizing any inefficiencies in the process.\n",
"\n",
"2. **Uptime (99%)**:\n",
" - The equipment has an excellent uptime rate of 99%, indicating that it is available and operational almost all the time. This is a strong indicator of reliability and suggests that downtime due to maintenance or unexpected failures is minimal. Maintaining this level of uptime should \n",
"be a priority, as it directly impacts productivity and operational continuity.\n",
"\n",
"3. **Output Quality (94%)**:\n",
" - The output quality is also at 94%, which is a positive sign that the equipment is producing high-quality products or results. However, similar to efficiency, there is room for improvement. Efforts could be made to identify any factors that might be affecting quality, such as calibration issues, material inconsistencies, or process deviations.\n",
"\n",
"**Overall Assessment**:\n",
"The equipment is performing well across all key metrics, with high efficiency, uptime, and output quality. To further enhance performance, focus should be placed on fine-tuning processes to close the small gaps in efficiency and quality. Regular maintenance, monitoring, and process optimization can help sustain and potentially improve these metrics.\n",
"--------------------------------------------------]\n",
"\n"
]
}
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.9"
},
"colab": {
"provenance": []
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,420 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "MLutIRWTYLqq",
"metadata": {
"id": "MLutIRWTYLqq"
},
"source": [
"# Simple Chat with Qwen2.5 (0.5B) using Hugging Face Transformers"
]
},
{
"cell_type": "markdown",
"id": "a2LoeTPnYQOW",
"metadata": {
"id": "a2LoeTPnYQOW"
},
"source": [
"This beginner-friendly notebook demonstrates how to use the Qwen2.5-0.5B-Instruct model for simple chat-based generation. It includes installing dependencies, setting up authentication via Hugging Face token, generating a response using a chat prompt, and displaying the output. The notebook is organized into 5 clean sections for easy understanding."
]
},
{
"cell_type": "markdown",
"id": "WlfJBFucY9gi",
"metadata": {
"id": "WlfJBFucY9gi"
},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/Qwen2_5_InstructionAgent.ipynb)\n"
]
},
{
"cell_type": "markdown",
"id": "57059c85",
"metadata": {
"id": "57059c85"
},
"source": [
"# 🔧 Dependencies\n",
"Install necessary Python packages."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "1cdaae47",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "1cdaae47",
"outputId": "5b140b09-5c16-4849-f370-f8b5abcc127a"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m363.4/363.4 MB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.8/13.8 MB\u001b[0m \u001b[31m58.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m24.6/24.6 MB\u001b[0m \u001b[31m58.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m883.7/883.7 kB\u001b[0m \u001b[31m39.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m664.8/664.8 MB\u001b[0m \u001b[31m1.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m211.5/211.5 MB\u001b[0m \u001b[31m6.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m56.3/56.3 MB\u001b[0m \u001b[31m14.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m127.9/127.9 MB\u001b[0m \u001b[31m9.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m207.5/207.5 MB\u001b[0m \u001b[31m6.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m21.1/21.1 MB\u001b[0m \u001b[31m64.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25h"
]
}
],
"source": [
"!pip install transformers --quiet\n",
"!pip install accelerate --quiet\n"
]
},
{
"cell_type": "markdown",
"id": "9a65a2fa",
"metadata": {
"id": "9a65a2fa"
},
"source": [
"# 🛠️ Tools\n",
"Import required tools and libraries."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a8a4bc30",
"metadata": {
"id": "a8a4bc30"
},
"outputs": [],
"source": [
"from transformers import AutoModelForCausalLM, AutoTokenizer\n",
"import torch\n"
]
},
{
"cell_type": "markdown",
"id": "1ad2bec4",
"metadata": {
"id": "1ad2bec4"
},
"source": [
"# ⚙️ YAML Prompt (Token Authentication)\n",
"Use Hugging Face token to access the model."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "cd030b94",
"metadata": {
"id": "cd030b94"
},
"outputs": [],
"source": [
"from huggingface_hub import login\n",
"login(token=\"Enter your huggingface token\")\n"
]
},
{
"cell_type": "markdown",
"id": "b7b1a32d",
"metadata": {
"id": "b7b1a32d"
},
"source": [
"# 🚀 Main\n",
"Load model, prepare prompt and generate response."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "ef73b9ea",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 365,
"referenced_widgets": [
"a175e2b1fa8c4733afe1ddc78095be58",
"f5aaf63d7be04927a33e5cdd3655a003",
"7af74b8c184b4e72a2dba769b9caa40f",
"22b1c604d91648479cf01198db25a863",
"faaeb1c3ab914a6db9ff9b8ea06a440f",
"138860a12c6347fba11437eb098d31b5",
"d2f5fe459d544fa6adc92debab823ee6",
"468e8e7bee5c4198b121ac1aa677153d",
"068991c9ac44496bab06ccaa7d030722",
"44436151662f41ef81f6a4005add51de",
"c774c78c4fce4825872a76bb1e5fde33",
"4a7d0a84994c4b628974f631ecb95425",
"4adb654ea143456eb3b4244c3030b39f",
"d8dbdbc9327f4799a07767b59db59c47",
"669ea95c4cb642b580aa176ae2e89f9f",
"0bc8529b03364f83adf1110b5c1378ec",
"2eb8007145ef4c3caf7f5a9d1fd299e3",
"3fa4c368cea4474e8b97a215a20fd8d8",
"c750b36cfda24e24ae44a615e426de4b",
"e3001e1e3ba74c7ba28702fa5b2a6ed8",
"7ac0f7770ad341f68f194705f59ccb27",
"f9140e1979c6412993eee36c3f79c0d9",
"1ad8e2e2200c4277ba10dd8e9ac83b90",
"f193e4fb00b64e86b6f40f10f514739d",
"ae3aa36b649a43eb928ed843d2cfc239",
"c346fba7badb4c4596f6d6e8fa62224b",
"39dd29002d7b46848a29dfeb7405d9f9",
"166e17a0be5a4aa8831101eeb07a8645",
"60f9503b404945a3b99b5207190474f5",
"c480a676b82b48a0aae3dc438fa02ba8",
"d97b7d0765e84926b974b2c19156ae20",
"b22ca5c038f7419aa8e03f5f9a54accc",
"0387fba7ed044fbfb2a78c8d593c7b38",
"d6a31c3cf026450088596db5917c3e4a",
"4948c0014a95413b81848ae2b4a5fe7e",
"377cbca7363a495ea131a1e49a702168",
"a5eaad3284034beab09d212765d7ff78",
"7ed393b7c7564ab1897446eba875a4da",
"66d773bc8c7a40bc95dcba35052675cb",
"ad196b0deb2144b083cf8fddcbcfd147",
"09a99df922154a528f327bba0a47cb9a",
"bc9918e686244eb5b5db78932600a9d6",
"4fef54176f4b4a2d8d543a4bf04b416a",
"48551fb8dd5140cbb2d792e72b5fbb0d",
"955225e4206a4ee5a4284579935db920",
"e2faef80a0034c5384daf8ba66c1b75d",
"f27c3ec92bc74bacb3f86672b689370a",
"ec03dfb79a26474e812606b2664c931b",
"4705d2f502a74c3bb8eb8a06c3ba6889",
"7d8c99f622c34229894024c9772aed84",
"1e6e892b153c4c05b7de806ec674215f",
"85ac6a67bb0944b5bb8713fcf302da45",
"796771b887ce49119307725382f73bfd",
"c686586c431f4c22b0f0c698a9b213b6",
"239180fd12624e3a90188270b55ee218",
"e04653090c7e454d9c5e520e7f313588",
"992c7a4b44aa4feb8e13714fa922d705",
"a0a238c78d2842d680be448a2cb20157",
"6ab5ebed63674b5e87327a1623eba7f0",
"a49887aa0fcd4b91a0d5d978b29c1e0a",
"d9b2e4142dd14264b90829a5a4116613",
"154b27bb2ba44d10bde5c96929b65cc9",
"f0bf6206c31f49f2ad1ce97b99963011",
"1762735cfbcf4706b1c0686d3c2926a0",
"d5e3440785794f0f9ad4fd0650c2f682",
"08019bdd70364fabbc222dca66db0bb1",
"6f0b3a92d47c42dd815a670bb1c0f42c",
"54554545ad5a4f39b649f3acff6d5cba",
"bfd8f4ee8c414349a3695975e79c332e",
"332fc944eef14a9c82f71a68a3857dea",
"3b0b899fcfae451ba8ec3d8da176fcec",
"8383df3f07bd427287ad05726737cc67",
"67c267d7d1ec424593775bf8129e8ac9",
"85a50e2cb406403988639e6d6ecf9dc1",
"7251ed20382c4a60bae8549f68971043",
"44fbc377fd6a4c5a910c00dcceb143af",
"2d6d273941f04c13af598c53e067aae7"
]
},
"id": "ef73b9ea",
"outputId": "34512fa7-527c-45cb-f3c7-b42d37e74980"
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/usr/local/lib/python3.11/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n",
"The secret `HF_TOKEN` does not exist in your Colab secrets.\n",
"To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n",
"You will be able to reuse this secret in all of your notebooks.\n",
"Please note that authentication is recommended but still optional to access public models or datasets.\n",
" warnings.warn(\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "a175e2b1fa8c4733afe1ddc78095be58",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer_config.json: 0%| | 0.00/7.30k [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4a7d0a84994c4b628974f631ecb95425",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"vocab.json: 0%| | 0.00/2.78M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "1ad8e2e2200c4277ba10dd8e9ac83b90",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"merges.txt: 0%| | 0.00/1.67M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d6a31c3cf026450088596db5917c3e4a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer.json: 0%| | 0.00/7.03M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "955225e4206a4ee5a4284579935db920",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"config.json: 0%| | 0.00/659 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e04653090c7e454d9c5e520e7f313588",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"model.safetensors: 0%| | 0.00/988M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "6f0b3a92d47c42dd815a670bb1c0f42c",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"generation_config.json: 0%| | 0.00/242 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"model_name = \"Qwen/Qwen2.5-0.5B-Instruct\"\n",
"\n",
"tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
"model = AutoModelForCausalLM.from_pretrained(\n",
" model_name,\n",
" torch_dtype=\"auto\",\n",
" device_map=\"auto\"\n",
")\n",
"\n",
"messages = [\n",
" {\"role\": \"system\", \"content\": \"You are Qwen, a helpful assistant.\"},\n",
" {\"role\": \"user\", \"content\": \"Explain photosynthesis in simple words.\"}\n",
"]\n",
"\n",
"text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)\n",
"inputs = tokenizer(text, return_tensors=\"pt\").to(model.device)\n",
"\n",
"outputs = model.generate(**inputs, max_new_tokens=100)\n"
]
},
{
"cell_type": "markdown",
"id": "bf70c765",
"metadata": {
"id": "bf70c765"
},
"source": [
"# 📤 Output\n",
"Print the model response."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d8f70a7b",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "d8f70a7b",
"outputId": "559e9358-c2b7-4410-c1a6-1624640dfeae"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"system\n",
"You are Qwen, a helpful assistant.\n",
"user\n",
"Explain photosynthesis in simple words.\n",
"assistant\n",
"Photosynthesis is the process by which plants, algae, and some bacteria convert light energy from the sun into chemical energy stored in glucose molecules, using carbon dioxide and water as reactants. This process occurs in chloroplasts within plant cells and involves two main stages: the light-dependent reactions and the light-independent reactions (also known as the Calvin cycle).\n",
"\n",
"In the light-dependent reactions, light energy is captured by pigments like chlorophyll, causing electrons to be excited and transferred to oxygen through a series\n"
]
}
],
"source": [
"response = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
"print(response)\n"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,364 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3622c575",
"metadata": {
"id": "3622c575"
},
"source": [
"# TinyLlama Agent Demo\n",
"\n",
"This notebook demonstrates how to create a simple AI agent using the TinyLlama (1.1B) model.\n",
"\n",
"**Requirements:**\n",
"- `transformers`\n",
"- `accelerate`\n",
"- `torch`\n",
"\n",
"We will load the model, prepare a basic conversational interface, and test it with a simple prompt."
]
},
{
"cell_type": "markdown",
"id": "qS0M9pRhTGpf",
"metadata": {
"id": "qS0M9pRhTGpf"
},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/TinyLlama_1_1B_model_SimpleAIAgent.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "tuw0IDpBS0ei",
"metadata": {
"id": "tuw0IDpBS0ei"
},
"source": [
"# Install necessary packages"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "0d06cae9",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0d06cae9",
"outputId": "3e1d8fb7-19be-455b-dc35-d0ab19146669"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m363.4/363.4 MB\u001b[0m \u001b[31m3.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.8/13.8 MB\u001b[0m \u001b[31m29.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m24.6/24.6 MB\u001b[0m \u001b[31m29.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m883.7/883.7 kB\u001b[0m \u001b[31m20.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m664.8/664.8 MB\u001b[0m \u001b[31m2.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m211.5/211.5 MB\u001b[0m \u001b[31m5.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m56.3/56.3 MB\u001b[0m \u001b[31m12.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m127.9/127.9 MB\u001b[0m \u001b[31m7.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m207.5/207.5 MB\u001b[0m \u001b[31m6.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m21.1/21.1 MB\u001b[0m \u001b[31m45.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25h"
]
}
],
"source": [
"!pip install -q transformers accelerate torch"
]
},
{
"cell_type": "markdown",
"id": "RFhYLkO1S5F9",
"metadata": {
"id": "RFhYLkO1S5F9"
},
"source": [
"# Load TinyLlama model and tokenizer"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "415af398",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 241,
"referenced_widgets": [
"dae7d635257948149c74e65956f614e3",
"17ec6fcc9f624cdbb62fe2b8b55230d0",
"58474504488044138a0252bb75258d86",
"04fe4518fac04b9f8b29c62ed70f1dfd",
"c6a9d3ea25594170977243d2048d3408",
"730bebf00be142d3979c6d12b55d6757",
"03ad1674c0884e7b9c4a21af0a1e66c3",
"ebac47c887f04b79a60bd0918002bf0d",
"ece9bce5d2ca4dd7b019040175513118",
"458f11dbadb045f1aa8c72780e7aa2a0",
"ec28a20d0a14445489723684d2d69c0a",
"d347fa0c0fc849559b66c8f116c86317",
"117f649fa62b4f5d9add0160e6cd80a4",
"66f702418fa348eea27778282dc443aa",
"44413339df5b4ae688f8101faf9ed3b5",
"00cc576df95441c8916ce38a7c6decc2",
"22e9ea6d191841bf9a5a5072326aa6d8",
"bd94a291b3164113b5ea460b38b8c167",
"9d132a297f924a3d867605df81e70d8e",
"5345feddca204db784ea0452e739eda6",
"ace33e6e88d842c89efd180b3a83f674",
"61dd85a7edf544f196c60a259a91fa0d",
"7451ec9bd091441c852d075b47bd1ca5",
"05e3446e7c9243819a25d1ca8e8fa38e",
"73141c4c9218462da97999d3afa459bc",
"1cffc33252b94fbc861edb0953f13153",
"de917c0fc49040c8a9254333c34c884a",
"50962ececc0b42c28170eb0c23fd6171",
"856cc834a33a4f9fb59c77f748006b36",
"17fd1d476c1f4891b8cbeb8cacdeb866",
"c97dc50f42cb44b8a71aeff5272d5337",
"2e3a66f622d445c4be544a9306798cb6",
"92920b1d428a4ed68a9e0329e2034cad",
"7e282c1fcae842cc87c93ecfc1763654",
"f9ed70f887204e2fb336e38a35096eec",
"a4d99f9beca44e4a8fadff65b27aa78f",
"b4f77b96d00445eea77ac78baa8c1e99",
"695898e4a4c8476cb318446a6da592ba",
"d70bcacbe77c41f685a4f8020f767889",
"af49afc49ecf47fb9ebf2d299954eff2",
"1bd2a253348f4b45928c875940f72481",
"b6c5f55fc85b4331a2e749d27ce15f89",
"81a87638c71046d7aeb1c642558602f7",
"e750883103444391bd64872344c651d7",
"e78dca0cf1314d01a5d5126e448e9d87",
"d0c53dffc0c34d7090febf78231972b2",
"152a118177914c6692e7ee4987b92b9b",
"88fd71fd6dbf43ebb0a3b8471fd35f8d",
"ddb91266f8834e4eaaf136cc15f4a831",
"9f8179d135054eb292c9adc7fd66a91a",
"297d5bc8b3a04f26b80e2cc5ee3f7bbd",
"f49f72679cb944cd8fce5557bbd68b1b",
"2ee28c8ef4f74690b80a89050da4ebb5",
"c29924d6561a4feaa9238d1b34394941",
"03636146b7c14f5db80b8f105be55303",
"b391081de4444859ac3213aac99cc33a",
"2babb83499fc46dc8422473f7b98403c",
"a32f6cc2777941bca5b6250e36624ead",
"90cd9996184a452ab651f9b8cc590f47",
"dc04209c92764096ba9bd4aefa81c189",
"16e5b93c3c964a8a8ca460d9077d0638",
"794f22ded07d46c5a916325109618987",
"dce367f1a5a74ebf94aea37209aa0a40",
"5e355e3f340f4a7590e4a88d44b03383",
"4e725e19c877462aa3c10726a335bbde",
"d60ab6c255d244098a459459016a2089",
"f1b07183320d42af88f985bd1abf054f",
"d23103bb4b354e29a2c3e7dc93867b36",
"ea3e0ae0aef54e5da113c9686ebbbab9",
"4d3365c1bb7740dc8f36f4b419af135a",
"c06ccbc051264027bd2c6521fec69abe",
"597dbf5b28134cc0bf9d613443243455",
"faa4f285eb224bbcbed649bd479e571f",
"6e684d35885a4706b8add6834cd521ad",
"f9c55ada3386460183e02559defec8a4",
"9d64cbe1605f4f358923754c5f8b6424",
"6008ed7eca09485195eb37c36acfbe79"
]
},
"id": "415af398",
"outputId": "28de1886-e83f-49bf-b05f-cd19439605c5"
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "dae7d635257948149c74e65956f614e3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer_config.json: 0%| | 0.00/1.29k [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d347fa0c0fc849559b66c8f116c86317",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer.model: 0%| | 0.00/500k [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "7451ec9bd091441c852d075b47bd1ca5",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"tokenizer.json: 0%| | 0.00/1.84M [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "7e282c1fcae842cc87c93ecfc1763654",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"special_tokens_map.json: 0%| | 0.00/551 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e78dca0cf1314d01a5d5126e448e9d87",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"config.json: 0%| | 0.00/608 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b391081de4444859ac3213aac99cc33a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"model.safetensors: 0%| | 0.00/2.20G [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "f1b07183320d42af88f985bd1abf054f",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"generation_config.json: 0%| | 0.00/124 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from transformers import AutoTokenizer, AutoModelForCausalLM\n",
"import torch\n",
"\n",
"model_name = \"TinyLlama/TinyLlama-1.1B-Chat-v1.0\"\n",
"tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
"model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16, device_map=\"auto\")"
]
},
{
"cell_type": "markdown",
"id": "N0bRTd_DS8aL",
"metadata": {
"id": "N0bRTd_DS8aL"
},
"source": [
"# Define a function to generate responses from the agent"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1b0483e1",
"metadata": {
"id": "1b0483e1"
},
"outputs": [],
"source": [
"def generate_response(prompt, max_length=256):\n",
" inputs = tokenizer(prompt, return_tensors=\"pt\").to(model.device)\n",
" outputs = model.generate(**inputs, max_new_tokens=max_length)\n",
" return tokenizer.decode(outputs[0], skip_special_tokens=True)"
]
},
{
"cell_type": "markdown",
"id": "vbHKNxc-S_Bk",
"metadata": {
"id": "vbHKNxc-S_Bk"
},
"source": [
"# Example usage"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "298c9811",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "298c9811",
"outputId": "6e412608-735c-4604-8a5d-a66b98dd9df0"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"You are a helpful assistant. What is the capital of France?\n"
]
}
],
"source": [
"prompt = \"You are a helpful assistant. What is the capital of France?\"\n",
"response = generate_response(prompt)\n",
"print(response)"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+1 -143
View File
@@ -5,146 +5,4 @@
5. Make it minimal change as possible
6. Firstly try not to make any modification to the existing code as possible
7. Only modify the existing code if its highly required, without that if it cant be done, then add new code section.
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
Below is a **detailed technical overview** of the issues that have been coming up in the workflow execution (specifically around loops, resetting tasks, and continuing on to subsequent tasks). It includes:
1. **How the workflow currently flows**
2. **What logic marks a task as “completed”**
3. **What issues arose in `loop` tasks**
4. **Why the workflow can end up “stuck” or “looping indefinitely”**
---
## 1. Overall Workflow Flow
### a. Building relationships among tasks
- At startup, the code iterates through all `tasks`.
- For each `task`:
- It looks at `task.next_tasks`, and for each `next_task_name`:
- Finds the corresponding `Task` object
- Appends the current task’s name to the found `next_task`'s `previous_tasks` list.
- This means if Task A has `next_tasks=["B"]`, then Task B’s `previous_tasks` will include `"A"`.
### b. Finding and starting with a “start task”
- The workflow code tries to locate a task with `is_start=True`.
- If no such task is found, it uses the first item in the tasks dictionary instead.
- That “start task” is what the workflow tries to run first.
### c. Execution loop in the method (e.g., `workflow()` or `aworkflow()`)
- There is a `while current_task:` loop that processes tasks in sequence, or conditionally, based on `task.condition`.
- Each time a task runs (if non-loop), it yields the `task_id` or triggers an agent to run. Once the agent finishes (with or without a final result), the workflow picks up again to see what to do next:
- If the `task` is a `loop` type, the code tries to create or manage sub-tasks for each row/line in an “input_file.”
- If the `task` is a normal (“decision” or “task” or “some-other-type”), it just executes once, sets `status="completed"`, and the code moves on.
### d. Condition-based branching
- If a task has a result that includes a “decision” (like `{"decision":"more"}` or `"done"`), the code checks `task.condition`. For example:
```python
condition = {
"more": "generate_task",
"done": "evaluate_total_questions"
}
```
- If the result’s decision is `"done"`, it jumps to the task named `"evaluate_total_questions"`.
- If the result’s decision is `"more"`, it jumps right back to `"generate_task"`.
- If no condition matches, it can fallback to the first item in `task.next_tasks`.
### e. Marking tasks as “completed”
- After a task’s execution (like a typical “non-loop” task), the code sets `task.status = "completed"`.
- Then the code has a snippet that says:
```python
if self.tasks[task_id].status == "completed":
# Possibly reset to "not started" so we can re-run if needed
```
- By default, the system tries to “reset” tasks to `"not started"`, **unless** it is a loop task or a subtask of a loop.
---
## 2. How a “completed” task is marked
Generally, tasks are marked `status="completed"` in two primary ways:
1. **Non-Loop Execution**
A normal task (like a “decision” or “task”) is executed once the code calls the agent, the agent returns a final result, and the system sets `task.status = "completed"`.
2. **Loop Execution**
A loop-type task is *programmatically* set to `status="completed"` when all of its sub-tasks have finished. That is:
- The code checks: “Have all child tasks of this loop finished?”
- If `True`, the loop task is set to `completed`.
---
## 3. Issues Specifically in `loop` Tasks
### a. Re-Entering the Loop
- Before, the same snippet that “resets completed tasks to ‘not started’ so they can re-run if needed” **also** tried to reset loop tasks or their subtasks.
- If a loop task got reset to `"not started"`, the code would eventually pick it back up again, leading to repeated creation of sub-tasks (or repeated attempts to re-run them).
- This caused an **infinite loop** or repeating the same steps in the workflow, never truly exiting the loop stage.
### b. Subtasks Not Marked or Overwritten
- Another tricky scenario: If sub-tasks themselves got reset, the parent loop would see them as “not started” again, and might wait for them to “complete,” or might re-run them. That can lead to indefinite re-running of sub-tasks.
### c. Not Proceeding to Next (e.g., “upload_to_huggingface”)
- If the loop kept “restarting,” the workflow never ended up hitting the next tasks. For example, if your workflow is:
1. `generate_task`
2. `evaluate_total_questions`
3. `generate_cot` (loop)
4. `upload_to_huggingface`
- The system might get stuck in step #3 indefinitely (the sub-tasks keep getting reset, so it never actually transitions to step #4).
### d. Condition Logic vs. Next Tasks
- Another subtlety: If the loop tasks had a `condition` that pointed them back to a prior step, it might cause unintentional re-entry. Typically, you only want loop tasks to proceed once, **unless** you explicitly want to re-visit. But if it’s a data ingestion process, you usually want to do it once, then move on to the next step.
---
## 4. Why the System Can End Up Stuck or “Looping Indefinitely”
1. **Reset Mechanism**
- The code tries to “reset tasks to ‘not started’ once they complete,” so they can be re-run in some dynamic multi-run scenario.
- But that same logic can cause loop tasks to revert back to “not started” the moment they end. The system sees “Oh, a task is ‘not started’? Let’s run it!” and you’re in a cycle.
2. **No Condition for Exit**
- If the loop has a condition that leads back to a prior step (like `"more" -> generate_cot`), it can keep re-running.
3. **Subtasks Not Marked**
- If the subtask or the loop tries to “reactivate” each other, it never exits.
---
## Summary of the “Core Problem”
1. We want to keep the resetting mechanism for **non-loop tasks** – because in some advanced workflows, we like re-running them from a different path or after some condition.
2. But we want **loop tasks** to remain `"completed"` once all sub-tasks are done, so the code can seamlessly proceed to the next major step.
3. Before the fix, loop tasks or their sub-tasks got reset. This triggered the system to re-enter the loop, re-run the sub-tasks, etc., causing an infinite loop and preventing the workflow from reaching tasks like “upload_to_huggingface.”
---
## Technical Highlights to Pass On
- **In the reset snippet**:
```python
if self.tasks[task_id].status == "completed":
# never reset if loop or subtask-of-loop
# else reset to "not started"
```
This is crucial to skipping re-runs on loop tasks.
- **Ensure** that once a loop’s sub-tasks are all “completed,” the loop’s status is set to “completed,” and it transitions to the next major tasks (like `upload_to_huggingface`).
- **Check** if the loop’s condition is correct. If you want a single pass, do not implement a condition that leads back to the same loop.
- Also, you can check you do not have “overlapping conditions” that cause re-entry.
---
### Conclusion
**Hence,** the main challenge is that the reset logic (meant to let normal tasks be re-run) conflicts with a loop task’s one-pass usage. Once you avoid resetting the loop tasks or sub-tasks, you can finish them once, mark them “completed,” and properly proceed to the next stage.
Dont remove any logging or debug statements, as it will help you to understand the flow of the code.
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
+237
View File
@@ -0,0 +1,237 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
PraisonAI Agents is a hierarchical AI agent framework for completing complex tasks with self-reflection capabilities. It supports multi-agent collaboration, tool integration, and various execution patterns (sequential, hierarchical, parallel).
## Development Commands
### Installation and Setup
```bash
# Install core package
pip install -e .
# Install with specific features
pip install -e .[all] # All features
pip install -e .[memory] # Memory capabilities
pip install -e .[knowledge] # Document processing
pip install -e .[mcp] # MCP server support
pip install -e .[llm] # Extended LLM support
pip install -e .[api] # API server capabilities
```
### Testing
```bash
# Run individual test examples (no formal test runner configured)
python tests/basic-agents.py
python tests/async_example.py
python tests/knowledge-agents.py
# Test specific features
python tests/mcp-agents.py # MCP integration
python tests/memory_example.py # Memory functionality
python tests/tools_example.py # Tool system
```
### Running Examples
```bash
# Basic agent usage
python tests/single-agent.py
# Multi-agent workflows
python tests/multi-agents-api.py
# Async operations
python tests/async_example_full.py
# MCP server examples
python tests/mcp-sse-direct-server.py # Start MCP server
python tests/mcp-sse-direct-client.py # Connect to server
```
## Core Architecture
### Agent System (`praisonaiagents/agent/`)
- **Agent**: Core agent class with LLM integration, tool calling, and self-reflection
- **ImageAgent**: Specialized multimodal agent for image processing
- Self-reflection with configurable min/max iterations (default: 1-3)
- Delegation support for hierarchical agent structures
### Multi-Agent Orchestration (`praisonaiagents/agents/`)
- **PraisonAIAgents**: Main orchestrator for managing multiple agents and tasks
- **AutoAgents**: Automatic agent creation and management
- Process types: `sequential`, `hierarchical`, `parallel`
- Context passing between agents and task dependency management
### Task System (`praisonaiagents/task/`)
- **Task**: Core task definition with context, callbacks, and output specifications
- Supports file output, JSON/Pydantic structured output, async execution
- Conditional logic with `condition` parameter for task flow control
- Context passing via `context` parameter for task dependencies
- **Guardrails**: Built-in validation and safety mechanisms for task outputs
- Function-based guardrails for custom validation logic
- LLM-based guardrails using natural language descriptions
- Automatic retry with configurable `max_retries` parameter
- Compatible with CrewAI guardrail patterns
### LLM Integration (`praisonaiagents/llm/`)
- Unified wrapper for multiple LLM providers via LiteLLM
- Supports OpenAI, Anthropic, Gemini, DeepSeek, local models (Ollama)
- Context length management and tool calling capabilities
- Set via `llm` parameter on agents or global `OPENAI_API_KEY`/`ANTHROPIC_API_KEY`
### Tool System (`praisonaiagents/tools/`)
Two implementation patterns:
1. **Function-based**: Simple tools using `@tool` decorator
2. **Class-based**: Complex tools inheriting from `BaseTool`
Built-in tools include: DuckDuckGo search, file operations, calculator, Wikipedia, arXiv, data analysis tools, shell execution.
### Memory & Knowledge Systems
- **Memory** (`praisonaiagents/memory/`): Multi-layered memory with RAG support
- Types: short-term, long-term, entity, user memory
- Providers: ChromaDB, Mem0, custom implementations
- **Knowledge** (`praisonaiagents/knowledge/`): Document processing with chunking
- Chunking strategies via `chonkie` library
- Embedding and retrieval capabilities
### MCP (Model Context Protocol) Integration
- **MCP Server**: Server-side tool protocol for distributed execution
- **SSE Support**: Server-sent events for real-time communication
- Tool discovery and dynamic registration
## Development Patterns
### Agent Creation
```python
agent = Agent(
name="Agent Name",
role="Agent Role",
goal="Agent Goal",
backstory="Agent Background",
llm="gpt-4o-mini", # or other LLM
self_reflect=True, # Enable self-reflection
min_reflect=1, # Minimum reflection iterations
max_reflect=3, # Maximum reflection iterations
tools=[tool1, tool2], # Optional tools
guardrail=validate_function, # Agent-level guardrail (function or string)
max_guardrail_retries=3 # Retry limit for guardrail failures
)
```
### Task Definition
```python
task = Task(
name="task_name",
description="Task description",
expected_output="Expected output format",
agent=agent,
context=[previous_task], # Task dependencies
output_pydantic=ResponseModel, # Structured output
condition="condition_function" # Conditional execution
)
```
### Guardrails Usage
#### Task-Level Guardrails
```python
from typing import Tuple, Any
# Function-based guardrail
def validate_output(task_output: TaskOutput) -> Tuple[bool, Any]:
"""Custom validation function."""
if "error" in task_output.raw.lower():
return False, "Output contains errors"
if len(task_output.raw) < 10:
return False, "Output is too short"
return True, task_output
task = Task(
description="Write a professional email",
expected_output="A well-formatted email",
agent=agent,
guardrail=validate_output, # Function-based guardrail
max_retries=3 # Retry up to 3 times if guardrail fails
)
# LLM-based guardrail
task = Task(
description="Generate marketing copy",
expected_output="Professional marketing content",
agent=agent,
guardrail="Ensure the content is professional, engaging, and free of errors", # String description
max_retries=2
)
```
#### Agent-Level Guardrails
```python
# Agent guardrails apply to ALL outputs from that agent
def validate_professional_tone(task_output: TaskOutput) -> Tuple[bool, Any]:
"""Ensure professional tone in all agent responses."""
content = task_output.raw.lower()
casual_words = ['yo', 'dude', 'awesome', 'cool']
for word in casual_words:
if word in content:
return False, f"Unprofessional language detected: {word}"
return True, task_output
# Agent with function-based guardrail
agent = Agent(
name="BusinessWriter",
instructions="You are a professional business writer",
guardrail=validate_professional_tone, # Function guardrail
max_guardrail_retries=3
)
# Agent with LLM-based guardrail
agent = Agent(
name="ContentWriter",
instructions="You are a content writer",
guardrail="Ensure all responses are professional, accurate, and appropriate for business use", # String guardrail
max_guardrail_retries=2
)
```
### Multi-Agent Workflow
```python
workflow = PraisonAIAgents(
agents=[agent1, agent2],
tasks=[task1, task2],
process="sequential", # or "hierarchical", "parallel"
verbose=True,
manager_agent=manager_agent # For hierarchical process
)
result = workflow.start()
```
### Async Support
All major components support async execution:
```python
result = await workflow.astart()
result = await agent.aexecute(task)
```
## Key Dependencies
- **Core**: `pydantic`, `rich`, `openai`, `mcp`
- **Memory**: `chromadb`, `mem0ai`
- **Knowledge**: `markitdown`, `chonkie`
- **LLM**: `litellm` for unified provider access
- **API**: `fastapi`, `uvicorn` for server capabilities
## Error Handling
- Global error logging via `error_logs` list
- Callback system for real-time error reporting
- Context length exception handling with automatic retry
- Graceful degradation for optional dependencies
## Testing Strategy
The project uses example-driven testing with 100+ test files in `tests/` directory. Each test file demonstrates specific usage patterns and serves as both test and documentation. Run individual examples to test functionality rather than using a formal test runner.
Use conda activate praisonai-agents to activate the environment.
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""
Agent-level guardrails example.
This example demonstrates how to use guardrails at the Agent level,
which will apply to all tasks executed by that agent.
"""
from typing import Tuple, Any
from praisonaiagents import Agent, TaskOutput
def validate_content_length(task_output: TaskOutput) -> Tuple[bool, Any]:
"""
Validate that task output content meets minimum length requirement.
Args:
task_output: The task output to validate
Returns:
Tuple of (success, result_or_error_message)
"""
if len(task_output.raw) < 50:
return False, "Content too short - must be at least 50 characters"
return True, task_output
def validate_professional_tone(task_output: TaskOutput) -> Tuple[bool, Any]:
"""
Validate that the content has a professional tone.
Args:
task_output: The task output to validate
Returns:
Tuple of (success, result_or_error_message)
"""
content = task_output.raw.lower()
unprofessional_words = ['yo', 'dude', 'awesome', 'cool', 'lol']
for word in unprofessional_words:
if word in content:
return False, f"Content contains unprofessional word: '{word}'"
return True, task_output
def main():
"""Demonstrate Agent-level guardrails with function-based and LLM-based validation."""
print("=== Agent Guardrail Examples ===\n")
# Example 1: Function-based guardrail
print("1. Function-based guardrail (content length validation):")
agent1 = Agent(
name="ContentWriter",
instructions="You are a professional content writer who creates detailed responses",
guardrail=validate_content_length,
max_guardrail_retries=2
)
try:
result1 = agent1.start("Write a brief welcome message")
print(f"Result: {result1}\n")
except Exception as e:
print(f"Error: {e}\n")
# Example 2: LLM-based guardrail (string description)
print("2. LLM-based guardrail (professional tone validation):")
agent2 = Agent(
name="BusinessWriter",
instructions="You are a business communication expert",
guardrail="Ensure the content is professional, formal, and suitable for business communication. No casual language or slang.",
max_guardrail_retries=3
)
try:
result2 = agent2.start("Write a welcome message for new employees")
print(f"Result: {result2}\n")
except Exception as e:
print(f"Error: {e}\n")
# Example 3: Multiple agents with different guardrails
print("3. Professional tone function-based guardrail:")
agent3 = Agent(
name="ProfessionalWriter",
instructions="Write professional business content",
guardrail=validate_professional_tone,
max_guardrail_retries=2
)
try:
result3 = agent3.start("Write a casual greeting message")
print(f"Result: {result3}\n")
except Exception as e:
print(f"Error: {e}\n")
print("=== Agent Guardrails Demonstration Complete ===")
if __name__ == "__main__":
main()
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
import sys
import traceback
from praisonaiagents import Agent, MCP
def test_agent_direct():
"""Test gpt-4o-mini (agent.py path)"""
print("=" * 50)
print("TESTING: gpt-4o-mini (agent.py direct calls)")
print("=" * 50)
try:
agent = Agent(
instructions="""You are a helpful assistant that can break down complex problems.
Use the available tools when relevant to perform step-by-step analysis.""",
llm="gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
print("✅ Agent created successfully")
print(f"✅ Agent LLM: {getattr(agent, 'llm', 'Not set')}")
print(f"✅ Agent using custom LLM: {getattr(agent, '_using_custom_llm', False)}")
result = agent.start("What are 3 steps to make coffee?")
print("✅ Agent execution completed successfully")
return True, result
except Exception as e:
print(f"❌ Error in agent direct: {e}")
traceback.print_exc()
return False, str(e)
def test_llm_class():
"""Test openai/gpt-4o-mini (llm.py path)"""
print("\n" + "=" * 50)
print("TESTING: openai/gpt-4o-mini (llm.py LiteLLM)")
print("=" * 50)
try:
agent = Agent(
instructions="""You are a helpful assistant that can break down complex problems.
Use the available tools when relevant to perform step-by-step analysis.""",
llm="openai/gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
print("✅ Agent created successfully")
print(f"✅ Agent LLM instance: {getattr(agent, 'llm_instance', 'Not set')}")
print(f"✅ Agent using custom LLM: {getattr(agent, '_using_custom_llm', False)}")
result = agent.start("What are 3 steps to make coffee?")
print("✅ Agent execution completed successfully")
return True, result
except Exception as e:
print(f"❌ Error in llm class: {e}")
traceback.print_exc()
return False, str(e)
if __name__ == "__main__":
print("🔍 DEBUGGING: Comparing both LLM approaches\n")
# Test agent direct
success1, result1 = test_agent_direct()
# Test LLM class
success2, result2 = test_llm_class()
print("\n" + "=" * 50)
print("FINAL RESULTS")
print("=" * 50)
if success1:
print("✅ gpt-4o-mini (agent.py) - SUCCESS")
else:
print("❌ gpt-4o-mini (agent.py) - FAILED")
print(f" Error: {result1}")
if success2:
print("✅ openai/gpt-4o-mini (llm.py) - SUCCESS")
else:
print("❌ openai/gpt-4o-mini (llm.py) - FAILED")
print(f" Error: {result2}")
if success1 and success2:
print("\n🎉 BOTH FORMATS WORK CORRECTLY!")
print("📝 The issue mentioned might be resolved or was a different problem.")
elif success1 and not success2:
print("\n⚠️ CONFIRMED: LLM class path has issues")
print("📝 Need to debug the LLM class implementation")
elif success2 and not success1:
print("\n⚠️ CONFIRMED: Agent direct path has issues")
print("📝 Need to debug the agent direct implementation")
else:
print("\n💥 BOTH PATHS FAILED - Something is fundamentally wrong")
@@ -0,0 +1,14 @@
from praisonaiagents import Agent
def validate_content(data):
if len(str(data)) < 50:
return False, "Content too short"
return True, data
agent = Agent(
instructions="You are a writer",
guardrail=validate_content,
max_guardrail_retries=1
)
agent.start("Write a welcome message with 5 words")
@@ -0,0 +1,21 @@
from praisonaiagents import Agent, Task, TaskOutput, PraisonAIAgents
from typing import Tuple, Any
def validate_content(task_output: TaskOutput) -> Tuple[bool, Any]:
if len(task_output.raw) < 50:
return False, "Content too short"
return True, task_output
agent = Agent(
instructions="You are a writer",
)
task = Task(
description="Write a welcome message",
guardrail=validate_content,
agent=agent
)
praison_agents = PraisonAIAgents(agents=[agent], tasks=[task])
praison_agents.start()
+154
View File
@@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
Example demonstrating guardrails functionality in PraisonAI Agents.
This example shows both function-based and LLM-based guardrails
for validating task outputs.
"""
import sys
import os
from typing import Tuple, Any
from praisonaiagents import Agent, Task, TaskOutput
def email_validator(task_output: TaskOutput) -> Tuple[bool, Any]:
"""
Function-based guardrail to validate email content.
Args:
task_output: The task output to validate
Returns:
Tuple of (success, result_or_error)
"""
content = task_output.raw.lower()
# Check for required email components
if "subject:" not in content:
return False, "Email must include a subject line"
if "dear" not in content and "hello" not in content:
return False, "Email must include a proper greeting"
if len(content) < 50:
return False, "Email content is too short"
if "error" in content or "problem" in content:
return False, "Email should not mention errors or problems"
return True, task_output
def main():
"""Run the guardrails example."""
print("PraisonAI Agents - Guardrails Example")
print("=====================================\n")
# Create an agent
agent = Agent(
name="Email Assistant",
role="Professional Email Writer",
goal="Write clear, professional emails",
backstory="I am an AI assistant specialized in writing professional emails"
)
print("1. Testing Function-based Guardrail")
print("------------------------------------")
# Create task with function-based guardrail
task_with_function_guardrail = Task(
description="Write a professional email to a client about project completion",
expected_output="A well-formatted professional email",
agent=agent,
guardrail=email_validator, # Function-based guardrail
max_retries=2
)
print(f"Task created with function guardrail: {email_validator.__name__}")
print(f"Max retries: {task_with_function_guardrail.max_retries}")
# Simulate a task output that should pass
good_output = TaskOutput(
description="Email task",
raw="""Subject: Project Completion Update
Dear Client,
I am pleased to inform you that your project has been completed successfully.
All deliverables have been reviewed and are ready for your review.
Please let me know if you have any questions.
Best regards,
Project Team""",
agent="Email Assistant"
)
result = task_with_function_guardrail._process_guardrail(good_output)
print(f"Good email result: {'PASSED' if result.success else 'FAILED'}")
if not result.success:
print(f"Error: {result.error}")
# Simulate a task output that should fail
bad_output = TaskOutput(
description="Email task",
raw="Hi there, there was an error with your project.",
agent="Email Assistant"
)
result = task_with_function_guardrail._process_guardrail(bad_output)
print(f"Bad email result: {'PASSED' if result.success else 'FAILED'}")
if not result.success:
print(f"Error: {result.error}")
print("\n2. Testing String-based LLM Guardrail")
print("-------------------------------------")
# Create task with string-based guardrail
task_with_llm_guardrail = Task(
description="Write a marketing email for a new product launch",
expected_output="Engaging marketing content",
agent=agent,
guardrail="Ensure the content is professional, engaging, includes a clear call-to-action, and is free of errors",
max_retries=3
)
print("Task created with LLM-based guardrail")
print("Guardrail description: 'Ensure the content is professional, engaging, includes a clear call-to-action, and is free of errors'")
print(f"Max retries: {task_with_llm_guardrail.max_retries}")
print("\n3. Backward Compatibility")
print("-------------------------")
# Create task without guardrail (backward compatible)
task_without_guardrail = Task(
description="Write a simple thank you note",
expected_output="A brief thank you message",
agent=agent
)
print("Task created without guardrail (backward compatible)")
print(f"Guardrail function: {task_without_guardrail._guardrail_fn}")
# Test that it doesn't break existing functionality
simple_output = TaskOutput(
description="Thank you task",
raw="Thank you for your business!",
agent="Email Assistant"
)
result = task_without_guardrail._process_guardrail(simple_output)
print(f"No guardrail result: {'PASSED' if result.success else 'FAILED'}")
print("\n✅ Guardrails example completed successfully!")
print("\nKey Features Demonstrated:")
print("- Function-based guardrails with custom validation logic")
print("- String-based LLM guardrails using natural language")
print("- Configurable retry mechanism")
print("- Backward compatibility with existing tasks")
print("- Integration with TaskOutput validation")
if __name__ == "__main__":
main()
@@ -12,6 +12,8 @@ from .knowledge.knowledge import Knowledge
from .knowledge.chunking import Chunking
from .mcp.mcp import MCP
from .session import Session
from .memory.memory import Memory
from .guardrails import GuardrailResult, LLMGuardrail
from .main import (
TaskOutput,
ReflectionOutput,
@@ -42,6 +44,7 @@ __all__ = [
'ReflectionOutput',
'AutoAgents',
'Session',
'Memory',
'display_interaction',
'display_self_reflection',
'display_instruction',
@@ -55,5 +58,7 @@ __all__ = [
'async_display_callbacks',
'Knowledge',
'Chunking',
'MCP'
'MCP',
'GuardrailResult',
'LLMGuardrail'
]
@@ -3,7 +3,7 @@ import time
import json
import logging
import asyncio
from typing import List, Optional, Any, Dict, Union, Literal, TYPE_CHECKING
from typing import List, Optional, Any, Dict, Union, Literal, TYPE_CHECKING, Callable, Tuple
from rich.console import Console
from rich.live import Live
from openai import AsyncOpenAI
@@ -32,6 +32,7 @@ _shared_apps = {} # Dict of port -> FastAPI app
if TYPE_CHECKING:
from ..task.task import Task
from ..main import TaskOutput
@dataclass
class ChatCompletionMessage:
@@ -368,7 +369,9 @@ class Agent:
min_reflect: int = 1,
reflect_llm: Optional[str] = None,
user_id: Optional[str] = None,
reasoning_steps: bool = False
reasoning_steps: bool = False,
guardrail: Optional[Union[Callable[['TaskOutput'], Tuple[bool, Any]], str]] = None,
max_guardrail_retries: int = 3
):
# Add check at start if memory is requested
if memory is not None:
@@ -483,6 +486,12 @@ Your Goal: {self.goal}
# Store user_id
self.user_id = user_id or "praison"
self.reasoning_steps = reasoning_steps
# Initialize guardrail settings
self.guardrail = guardrail
self.max_guardrail_retries = max_guardrail_retries
self._guardrail_fn = None
self._setup_guardrail()
# Check if knowledge parameter has any values
if not knowledge:
@@ -512,6 +521,149 @@ Your Goal: {self.goal}
except Exception as e:
logging.error(f"Error processing knowledge item: {knowledge_item}, error: {e}")
def _setup_guardrail(self):
"""Setup the guardrail function based on the provided guardrail parameter."""
if self.guardrail is None:
self._guardrail_fn = None
return
if callable(self.guardrail):
# Validate function signature
sig = inspect.signature(self.guardrail)
positional_args = [
param for param in sig.parameters.values()
if param.default is inspect.Parameter.empty
]
if len(positional_args) != 1:
raise ValueError("Agent guardrail function must accept exactly one parameter (TaskOutput)")
# Check return annotation if present
from typing import get_args, get_origin
return_annotation = sig.return_annotation
if return_annotation != inspect.Signature.empty:
return_annotation_args = get_args(return_annotation)
if not (
get_origin(return_annotation) is tuple
and len(return_annotation_args) == 2
and return_annotation_args[0] is bool
and (
return_annotation_args[1] is Any
or return_annotation_args[1] is str
or str(return_annotation_args[1]).endswith('TaskOutput')
or str(return_annotation_args[1]).startswith('typing.Union')
)
):
raise ValueError(
"If return type is annotated, it must be Tuple[bool, Any] or Tuple[bool, Union[str, TaskOutput]]"
)
self._guardrail_fn = self.guardrail
elif isinstance(self.guardrail, str):
# Create LLM-based guardrail
from ..guardrails import LLMGuardrail
llm = getattr(self, 'llm', None) or getattr(self, 'llm_instance', None)
self._guardrail_fn = LLMGuardrail(description=self.guardrail, llm=llm)
else:
raise ValueError("Agent guardrail must be either a callable or a string description")
def _process_guardrail(self, task_output):
"""Process the guardrail validation for a task output.
Args:
task_output: The task output to validate
Returns:
GuardrailResult: The result of the guardrail validation
"""
from ..guardrails import GuardrailResult
if not self._guardrail_fn:
return GuardrailResult(success=True, result=task_output)
try:
# Call the guardrail function
result = self._guardrail_fn(task_output)
# Convert the result to a GuardrailResult
return GuardrailResult.from_tuple(result)
except Exception as e:
logging.error(f"Agent {self.name}: Error in guardrail validation: {e}")
# On error, return failure
return GuardrailResult(
success=False,
result=None,
error=f"Agent guardrail validation error: {str(e)}"
)
def _apply_guardrail_with_retry(self, response_text, prompt, temperature=0.2, tools=None):
"""Apply guardrail validation with retry logic.
Args:
response_text: The response to validate
prompt: Original prompt for regeneration if needed
temperature: Temperature for regeneration
tools: Tools for regeneration
Returns:
str: The validated response text or None if validation fails after retries
"""
if not self._guardrail_fn:
return response_text
from ..main import TaskOutput
retry_count = 0
current_response = response_text
while retry_count <= self.max_guardrail_retries:
# Create TaskOutput object
task_output = TaskOutput(
description="Agent response output",
raw=current_response,
agent=self.name
)
# Process guardrail
guardrail_result = self._process_guardrail(task_output)
if guardrail_result.success:
logging.info(f"Agent {self.name}: Guardrail validation passed")
# Return the potentially modified result
if guardrail_result.result and hasattr(guardrail_result.result, 'raw'):
return guardrail_result.result.raw
elif guardrail_result.result:
return str(guardrail_result.result)
else:
return current_response
# Guardrail failed
if retry_count >= self.max_guardrail_retries:
raise Exception(
f"Agent {self.name} response failed guardrail validation after {self.max_guardrail_retries} retries. "
f"Last error: {guardrail_result.error}"
)
retry_count += 1
logging.warning(f"Agent {self.name}: Guardrail validation failed (retry {retry_count}/{self.max_guardrail_retries}): {guardrail_result.error}")
# Regenerate response for retry
try:
retry_prompt = f"{prompt}\n\nNote: Previous response failed validation due to: {guardrail_result.error}. Please provide an improved response."
response = self._chat_completion([{"role": "user", "content": retry_prompt}], temperature, tools)
if response and response.choices:
current_response = response.choices[0].message.content.strip()
else:
raise Exception("Failed to generate retry response")
except Exception as e:
logging.error(f"Agent {self.name}: Error during guardrail retry: {e}")
# If we can't regenerate, fail the guardrail
raise Exception(
f"Agent {self.name} guardrail retry failed: {e}"
)
return current_response
def generate_task(self) -> 'Task':
"""Generate a Task object from the agent's instructions"""
from ..task.task import Task
@@ -788,69 +940,92 @@ Your Goal: {self.goal}
)
else:
# Use the standard OpenAI client approach
if stream:
# Process as streaming response with formatted tools
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=formatted_tools if formatted_tools else None,
reasoning_steps=reasoning_steps
)
else:
# Process as regular non-streaming response
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools if formatted_tools else None,
stream=False
)
# Continue tool execution loop until no more tool calls are needed
max_iterations = 10 # Prevent infinite loops
iteration_count = 0
while iteration_count < max_iterations:
if stream:
# Process as streaming response with formatted tools
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=formatted_tools if formatted_tools else None,
reasoning_steps=reasoning_steps
)
else:
# Process as regular non-streaming response
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools if formatted_tools else None,
stream=False
)
tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None)
tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None)
if tool_calls:
messages.append({
"role": "assistant",
"content": final_response.choices[0].message.content,
"tool_calls": tool_calls
})
if tool_calls:
messages.append({
"role": "assistant",
"content": final_response.choices[0].message.content,
"tool_calls": tool_calls
})
for tool_call in tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
for tool_call in tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
if self.verbose:
display_tool_call(f"Agent {self.name} is calling function '{function_name}' with arguments: {arguments}")
if self.verbose:
display_tool_call(f"Agent {self.name} is calling function '{function_name}' with arguments: {arguments}")
tool_result = self.execute_tool(function_name, arguments)
results_str = json.dumps(tool_result) if tool_result else "Function returned an empty output"
tool_result = self.execute_tool(function_name, arguments)
results_str = json.dumps(tool_result) if tool_result else "Function returned an empty output"
if self.verbose:
display_tool_call(f"Function '{function_name}' returned: {results_str}")
if self.verbose:
display_tool_call(f"Function '{function_name}' returned: {results_str}")
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": results_str
})
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": results_str
})
# Get final response after tool calls
if stream:
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=formatted_tools if formatted_tools else None,
reasoning_steps=reasoning_steps
)
else:
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
stream=False
)
# Check if we should continue (for tools like sequential thinking)
should_continue = False
for tool_call in tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# For sequential thinking tool, check if nextThoughtNeeded is True
if function_name == "sequentialthinking" and arguments.get("nextThoughtNeeded", False):
should_continue = True
break
if not should_continue:
# Get final response after tool calls
if stream:
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=formatted_tools if formatted_tools else None,
reasoning_steps=reasoning_steps
)
else:
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
stream=False
)
break
iteration_count += 1
else:
# No tool calls, we're done
break
return final_response
@@ -944,7 +1119,13 @@ Your Goal: {self.goal}
total_time = time.time() - start_time
logging.debug(f"Agent.chat completed in {total_time:.2f} seconds")
return response_text
# Apply guardrail validation for custom LLM response
try:
validated_response = self._apply_guardrail_with_retry(response_text, prompt, temperature, tools)
return validated_response
except Exception as e:
logging.error(f"Agent {self.name}: Guardrail validation failed for custom LLM: {e}")
return None
except Exception as e:
display_error(f"Error in LLM chat: {e}")
return None
@@ -1032,8 +1213,20 @@ Your Goal: {self.goal}
display_interaction(original_prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
# Return only reasoning content if reasoning_steps is True
if reasoning_steps and hasattr(response.choices[0].message, 'reasoning_content'):
return response.choices[0].message.reasoning_content
return response_text
# Apply guardrail to reasoning content
try:
validated_reasoning = self._apply_guardrail_with_retry(response.choices[0].message.reasoning_content, original_prompt, temperature, tools)
return validated_reasoning
except Exception as e:
logging.error(f"Agent {self.name}: Guardrail validation failed for reasoning content: {e}")
return None
# Apply guardrail to regular response
try:
validated_response = self._apply_guardrail_with_retry(response_text, original_prompt, temperature, tools)
return validated_response
except Exception as e:
logging.error(f"Agent {self.name}: Guardrail validation failed: {e}")
return None
reflection_prompt = f"""
Reflect on your previous response: '{response_text}'.
@@ -1066,7 +1259,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
self.chat_history.append({"role": "user", "content": prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
display_interaction(prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
return response_text
# Apply guardrail validation after satisfactory reflection
try:
validated_response = self._apply_guardrail_with_retry(response_text, prompt, temperature, tools)
return validated_response
except Exception as e:
logging.error(f"Agent {self.name}: Guardrail validation failed after reflection: {e}")
return None
# Check if we've hit max reflections
if reflection_count >= self.max_reflect - 1:
@@ -1075,7 +1274,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
self.chat_history.append({"role": "user", "content": prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
display_interaction(prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
return response_text
# Apply guardrail validation after max reflections
try:
validated_response = self._apply_guardrail_with_retry(response_text, prompt, temperature, tools)
return validated_response
except Exception as e:
logging.error(f"Agent {self.name}: Guardrail validation failed after max reflections: {e}")
return None
logging.debug(f"{self.name} reflection count {reflection_count + 1}, continuing reflection process")
messages.append({"role": "user", "content": "Now regenerate your response using the reflection you made"})
@@ -1099,8 +1304,16 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
total_time = time.time() - start_time
logging.debug(f"Agent.chat completed in {total_time:.2f} seconds")
return response_text
# Apply guardrail validation before returning
try:
validated_response = self._apply_guardrail_with_retry(response_text, prompt, temperature, tools)
return validated_response
except Exception as e:
logging.error(f"Agent {self.name}: Guardrail validation failed: {e}")
if self.verbose:
display_error(f"Guardrail validation failed: {e}", console=self.console)
return None
def clean_json_output(self, output: str) -> str:
"""Clean and extract JSON from response text."""
@@ -1670,6 +1883,7 @@ Your Goal: {self.goal}
import threading
import time
import inspect
import asyncio # Import asyncio in the MCP scope
# logging is already imported at the module level
except ImportError as e:
@@ -0,0 +1,11 @@
"""
Guardrails module for PraisonAI Agents.
This module provides validation and safety mechanisms for task outputs,
including both function-based and LLM-based guardrails.
"""
from .guardrail_result import GuardrailResult
from .llm_guardrail import LLMGuardrail
__all__ = ["GuardrailResult", "LLMGuardrail"]
@@ -0,0 +1,43 @@
"""
Guardrail result classes for PraisonAI Agents.
This module provides the result types for guardrail validation,
following the same pattern as CrewAI for consistency.
"""
from typing import Any, Tuple, Union
from pydantic import BaseModel, Field
from ..main import TaskOutput
class GuardrailResult(BaseModel):
"""Result of a guardrail validation."""
success: bool = Field(description="Whether the guardrail check passed")
result: Union[str, TaskOutput, None] = Field(description="The result if modified, or None if unchanged")
error: str = Field(default="", description="Error message if validation failed")
@classmethod
def from_tuple(cls, result: Tuple[bool, Any]) -> "GuardrailResult":
"""Create a GuardrailResult from a tuple returned by a guardrail function.
Args:
result: Tuple of (success, result_or_error)
Returns:
GuardrailResult: The structured result
"""
success, data = result
if success:
return cls(
success=True,
result=data,
error=""
)
else:
return cls(
success=False,
result=None,
error=str(data) if data else "Guardrail validation failed"
)
@@ -0,0 +1,88 @@
"""
LLM-based guardrail implementation for PraisonAI Agents.
This module provides LLM-powered guardrails that can validate task outputs
using natural language descriptions, similar to CrewAI's implementation.
"""
import logging
from typing import Any, Tuple, Union, Optional
from pydantic import BaseModel
from ..main import TaskOutput
class LLMGuardrail:
"""An LLM-powered guardrail that validates task outputs using natural language."""
def __init__(self, description: str, llm: Any = None):
"""Initialize the LLM guardrail.
Args:
description: Natural language description of what to validate
llm: The LLM instance to use for validation
"""
self.description = description
self.llm = llm
self.logger = logging.getLogger(__name__)
def __call__(self, task_output: TaskOutput) -> Tuple[bool, Union[str, TaskOutput]]:
"""Validate the task output using the LLM.
Args:
task_output: The task output to validate
Returns:
Tuple of (success, result) where result is the output or error message
"""
try:
if not self.llm:
self.logger.warning("No LLM provided for guardrail validation")
return True, task_output
# Create validation prompt
validation_prompt = f"""
You are a quality assurance validator. Your task is to evaluate the following output against specific criteria.
Validation Criteria: {self.description}
Output to Validate:
{task_output.raw}
Please evaluate if this output meets the criteria. Respond with:
1. "PASS" if the output meets all criteria
2. "FAIL: [specific reason]" if the output does not meet criteria
Your response:"""
# Get LLM response
if hasattr(self.llm, 'chat'):
# For Agent's LLM interface
response = self.llm.chat(validation_prompt, temperature=0.1)
elif hasattr(self.llm, 'get_response'):
# For custom LLM instances
response = self.llm.get_response(validation_prompt, temperature=0.1)
elif callable(self.llm):
# For simple callable LLMs
response = self.llm(validation_prompt)
else:
self.logger.error(f"Unsupported LLM type: {type(self.llm)}")
return True, task_output
# Parse response
response = str(response).strip()
if response.upper().startswith("PASS"):
return True, task_output
elif response.upper().startswith("FAIL"):
# Extract the reason
reason = response[5:].strip(": ")
return False, f"Guardrail validation failed: {reason}"
else:
# Unclear response, log and pass through
self.logger.warning(f"Unclear guardrail response: {response}")
return True, task_output
except Exception as e:
self.logger.error(f"Error in LLM guardrail validation: {str(e)}")
# On error, pass through the original output
return True, task_output
+246 -146
View File
@@ -413,24 +413,30 @@ class LLM:
start_time = time.time()
reflection_count = 0
while True:
try:
if verbose:
display_text = prompt
if isinstance(prompt, list):
display_text = next((item["text"] for item in prompt if item["type"] == "text"), "")
if display_text and str(display_text).strip():
display_instruction(
f"Agent {agent_name} is processing prompt: {display_text}",
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools
)
# Display initial instruction once
if verbose:
display_text = prompt
if isinstance(prompt, list):
display_text = next((item["text"] for item in prompt if item["type"] == "text"), "")
if display_text and str(display_text).strip():
display_instruction(
f"Agent {agent_name} is processing prompt: {display_text}",
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools
)
# Sequential tool calling loop - similar to agent.py
max_iterations = 10 # Prevent infinite loops
iteration_count = 0
final_response_text = ""
while iteration_count < max_iterations:
try:
# Get response from LiteLLM
start_time = time.time()
current_time = time.time()
# If reasoning_steps is True, do a single non-streaming call
if reasoning_steps:
@@ -445,6 +451,7 @@ class LLM:
)
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
response_text = resp["choices"][0]["message"]["content"]
final_response = resp
# Optionally display reasoning if present
if verbose and reasoning_content:
@@ -452,7 +459,7 @@ class LLM:
original_prompt,
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
generation_time=time.time() - current_time,
console=console
)
else:
@@ -460,14 +467,14 @@ class LLM:
original_prompt,
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
generation_time=time.time() - current_time,
console=console
)
# Otherwise do the existing streaming approach
else:
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
with Live(display_generating("", current_time), console=console, refresh_per_second=4) as live:
response_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
@@ -481,7 +488,7 @@ class LLM:
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
live.update(display_generating(response_text, current_time))
else:
# Non-verbose mode, just collect the response
response_text = ""
@@ -499,20 +506,20 @@ class LLM:
response_text = response_text.strip()
# Get final completion to check for tool calls
final_response = litellm.completion(
**self._build_completion_params(
messages=messages,
tools=formatted_tools,
temperature=temperature,
stream=False, # No streaming for tool call check
**kwargs
# Get final completion to check for tool calls
final_response = litellm.completion(
**self._build_completion_params(
messages=messages,
tools=formatted_tools,
temperature=temperature,
stream=False, # No streaming for tool call check
**kwargs
)
)
)
tool_calls = final_response["choices"][0]["message"].get("tool_calls")
# Handle tool calls
# Handle tool calls - Sequential tool calling logic
if tool_calls and execute_tool_fn:
# Convert tool_calls to a serializable format for all providers
serializable_tool_calls = []
@@ -535,6 +542,7 @@ class LLM:
"tool_calls": serializable_tool_calls
})
should_continue = False
for tool_call in tool_calls:
# Handle both object and dict access patterns
if isinstance(tool_call, dict):
@@ -574,6 +582,18 @@ class LLM:
"content": json.dumps(tool_result) if tool_result is not None else "Function returned an empty output"
})
# Check if we should continue (for tools like sequential thinking)
# This mimics the logic from agent.py lines 1004-1007
if function_name == "sequentialthinking" and arguments.get("nextThoughtNeeded", False):
should_continue = True
# If we should continue, increment iteration and continue loop
if should_continue:
iteration_count += 1
continue
# If we reach here, no more tool calls needed - get final response
# Make one more call to get the final summary response
# Special handling for Ollama models that don't automatically process tool results
if self.model and self.model.startswith("ollama/") and tool_result:
# For Ollama models, we need to explicitly ask the model to process the tool results
@@ -666,115 +686,141 @@ class LLM:
else:
# Get response after tool calls with streaming
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
response_text = ""
with Live(display_generating("", current_time), console=console, refresh_per_second=4) as live:
final_response_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
messages=messages,
tools=formatted_tools,
temperature=temperature,
stream=True
stream=True,
**kwargs
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
final_response_text += content
live.update(display_generating(final_response_text, current_time))
else:
response_text = ""
final_response_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
messages=messages,
tools=formatted_tools,
temperature=temperature,
stream=True
stream=True,
**kwargs
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
response_text = response_text.strip()
# Handle output formatting
if output_json or output_pydantic:
self.chat_history.append({"role": "user", "content": original_prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
final_response_text += chunk.choices[0].delta.content
final_response_text = final_response_text.strip()
# Display final response
if verbose:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
display_interaction(
original_prompt,
final_response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
return final_response_text
else:
# No tool calls, we're done with this iteration
break
except Exception as e:
logging.error(f"Error in LLM iteration {iteration_count}: {e}")
break
# End of while loop - return final response
if final_response_text:
return final_response_text
# No tool calls were made in this iteration, return the response
if verbose:
display_interaction(
original_prompt,
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
response_text = response_text.strip()
# Handle output formatting
if output_json or output_pydantic:
self.chat_history.append({"role": "user", "content": original_prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
if verbose:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
if not self_reflect:
if verbose:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
# Return reasoning content if reasoning_steps is True
if reasoning_steps and reasoning_content:
return reasoning_content
return response_text
if not self_reflect:
if verbose:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
# Return reasoning content if reasoning_steps is True
if reasoning_steps and reasoning_content:
return reasoning_content
return response_text
# Handle self-reflection
reflection_prompt = f"""
# Handle self-reflection loop
while reflection_count < max_reflect:
# Handle self-reflection
reflection_prompt = f"""
Reflect on your previous response: '{response_text}'.
Identify any flaws, improvements, or actions.
Provide a "satisfactory" status ('yes' or 'no').
Output MUST be JSON with 'reflection' and 'satisfactory'.
"""
reflection_messages = messages + [
{"role": "assistant", "content": response_text},
{"role": "user", "content": reflection_prompt}
]
"""
reflection_messages = messages + [
{"role": "assistant", "content": response_text},
{"role": "user", "content": reflection_prompt}
]
# If reasoning_steps is True, do a single non-streaming call to capture reasoning
if reasoning_steps:
reflection_resp = litellm.completion(
**self._build_completion_params(
messages=reflection_messages,
temperature=temperature,
stream=False, # Force non-streaming
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
# If reasoning_steps is True, do a single non-streaming call to capture reasoning
if reasoning_steps:
reflection_resp = litellm.completion(
**self._build_completion_params(
messages=reflection_messages,
temperature=temperature,
stream=False, # Force non-streaming
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
# Grab reflection text and optional reasoning
reasoning_content = reflection_resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
reflection_text = reflection_resp["choices"][0]["message"]["content"]
)
# Grab reflection text and optional reasoning
reasoning_content = reflection_resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
reflection_text = reflection_resp["choices"][0]["message"]["content"]
# Optionally display reasoning if present
if verbose and reasoning_content:
display_interaction(
"Reflection reasoning:",
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
elif verbose:
display_interaction(
"Self-reflection (non-streaming):",
reflection_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
# Existing streaming approach
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
reflection_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
reflection_text += content
live.update(display_generating(reflection_text, start_time))
else:
# Optionally display reasoning if present
if verbose and reasoning_content:
display_interaction(
"Reflection reasoning:",
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
elif verbose:
display_interaction(
"Self-reflection (non-streaming):",
reflection_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
# Existing streaming approach
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
reflection_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
@@ -786,48 +832,102 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
reflection_text += chunk.choices[0].delta.content
try:
reflection_data = json.loads(reflection_text)
satisfactory = reflection_data.get("satisfactory", "no").lower() == "yes"
if verbose:
display_self_reflection(
f"Agent {agent_name} self reflection: reflection='{reflection_data['reflection']}' satisfactory='{reflection_data['satisfactory']}'",
console=console
content = chunk.choices[0].delta.content
reflection_text += content
live.update(display_generating(reflection_text, start_time))
else:
reflection_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
reflection_text += chunk.choices[0].delta.content
if satisfactory and reflection_count >= min_reflect - 1:
if verbose:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
try:
reflection_data = json.loads(reflection_text)
satisfactory = reflection_data.get("satisfactory", "no").lower() == "yes"
if reflection_count >= max_reflect - 1:
if verbose:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
if verbose:
display_self_reflection(
f"Agent {agent_name} self reflection: reflection='{reflection_data['reflection']}' satisfactory='{reflection_data['satisfactory']}'",
console=console
)
reflection_count += 1
messages.extend([
{"role": "assistant", "content": response_text},
{"role": "user", "content": reflection_prompt},
{"role": "assistant", "content": reflection_text},
{"role": "user", "content": "Now regenerate your response using the reflection you made"}
])
continue
if satisfactory and reflection_count >= min_reflect - 1:
if verbose:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
except json.JSONDecodeError:
reflection_count += 1
if reflection_count >= max_reflect:
return response_text
continue
if reflection_count >= max_reflect - 1:
if verbose:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
reflection_count += 1
messages.extend([
{"role": "assistant", "content": response_text},
{"role": "user", "content": reflection_prompt},
{"role": "assistant", "content": reflection_text},
{"role": "user", "content": "Now regenerate your response using the reflection you made"}
])
# Get new response after reflection
if verbose:
with Live(display_generating("", time.time()), console=console, refresh_per_second=4) as live:
response_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
messages=messages,
temperature=temperature,
stream=True,
**kwargs
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, time.time()))
else:
response_text = ""
for chunk in litellm.completion(
**self._build_completion_params(
messages=messages,
temperature=temperature,
stream=True,
**kwargs
)
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
response_text = response_text.strip()
continue
except json.JSONDecodeError:
reflection_count += 1
if reflection_count >= max_reflect:
if verbose:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
continue
except Exception as e:
display_error(f"Error in LLM response: {str(e)}")
return None
# If we've exhausted reflection attempts
if verbose:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
return response_text
except Exception as error:
display_error(f"Error in get_response: {str(error)}")
@@ -29,6 +29,12 @@ try:
except ImportError:
OPENAI_AVAILABLE = False
try:
import litellm
LITELLM_AVAILABLE = True
except ImportError:
LITELLM_AVAILABLE = False
@@ -340,14 +346,28 @@ class Memory:
elif self.use_rag and hasattr(self, "chroma_col"):
try:
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
input=query,
model="text-embedding-3-small"
)
query_embedding = response.data[0].embedding
if LITELLM_AVAILABLE:
# Use LiteLLM for consistency with the rest of the codebase
import litellm
response = litellm.embedding(
model="text-embedding-3-small",
input=query
)
query_embedding = response.data[0]["embedding"]
elif OPENAI_AVAILABLE:
# Fallback to OpenAI client
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
input=query,
model="text-embedding-3-small"
)
query_embedding = response.data[0].embedding
else:
self._log_verbose("Neither litellm nor openai available for embeddings", logging.WARNING)
return []
resp = self.chroma_col.query(
query_embeddings=[query_embedding],
@@ -464,19 +484,39 @@ class Memory:
# Store in vector database if enabled
if self.use_rag and hasattr(self, "chroma_col"):
try:
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # Ensure API key is correctly set
logger.info("Getting embeddings from OpenAI...")
logger.debug(f"Embedding input text: {text}") # Log the input text
response = client.embeddings.create(
input=text,
model="text-embedding-3-small"
)
embedding = response.data[0].embedding
logger.info("Successfully got embeddings")
logger.debug(f"Received embedding of length: {len(embedding)}") # Log embedding details
if LITELLM_AVAILABLE:
# Use LiteLLM for consistency with the rest of the codebase
import litellm
logger.info("Getting embeddings from LiteLLM...")
logger.debug(f"Embedding input text: {text}")
response = litellm.embedding(
model="text-embedding-3-small",
input=text
)
embedding = response.data[0]["embedding"]
logger.info("Successfully got embeddings from LiteLLM")
logger.debug(f"Received embedding of length: {len(embedding)}")
elif OPENAI_AVAILABLE:
# Fallback to OpenAI client
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
logger.info("Getting embeddings from OpenAI...")
logger.debug(f"Embedding input text: {text}")
response = client.embeddings.create(
input=text,
model="text-embedding-3-small"
)
embedding = response.data[0].embedding
logger.info("Successfully got embeddings from OpenAI")
logger.debug(f"Received embedding of length: {len(embedding)}")
else:
logger.warning("Neither litellm nor openai available for embeddings")
return
# Sanitize metadata for ChromaDB
sanitized_metadata = self._sanitize_metadata(metadata)
@@ -527,15 +567,28 @@ class Memory:
elif self.use_rag and hasattr(self, "chroma_col"):
try:
from openai import OpenAI
client = OpenAI()
# Get query embedding
response = client.embeddings.create(
input=query,
model="text-embedding-3-small" # Using consistent model
)
query_embedding = response.data[0].embedding
if LITELLM_AVAILABLE:
# Use LiteLLM for consistency with the rest of the codebase
import litellm
response = litellm.embedding(
model="text-embedding-3-small",
input=query
)
query_embedding = response.data[0]["embedding"]
elif OPENAI_AVAILABLE:
# Fallback to OpenAI client
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
input=query,
model="text-embedding-3-small"
)
query_embedding = response.data[0].embedding
else:
self._log_verbose("Neither litellm nor openai available for embeddings", logging.WARNING)
return []
# Search ChromaDB with embedding
resp = self.chroma_col.query(
@@ -910,18 +963,44 @@ class Memory:
"""
try:
# Use OpenAI client from main.py
from ..main import client
response = client.chat.completions.create(
model=llm or "gpt-4o",
messages=[{
"role": "user",
"content": custom_prompt or default_prompt
}],
response_format={"type": "json_object"},
temperature=0.3
)
if LITELLM_AVAILABLE:
# Use LiteLLM for consistency with the rest of the codebase
import litellm
# Convert model name if it's in litellm format
model_name = llm or "gpt-4o-mini"
response = litellm.completion(
model=model_name,
messages=[{
"role": "user",
"content": custom_prompt or default_prompt
}],
response_format={"type": "json_object"},
temperature=0.3
)
elif OPENAI_AVAILABLE:
# Fallback to OpenAI client
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model=llm or "gpt-4o-mini",
messages=[{
"role": "user",
"content": custom_prompt or default_prompt
}],
response_format={"type": "json_object"},
temperature=0.3
)
else:
logger.error("Neither litellm nor openai available for quality calculation")
return {
"completeness": 0.0,
"relevance": 0.0,
"clarity": 0.0,
"accuracy": 0.0
}
metrics = json.loads(response.choices[0].message.content)
@@ -1,6 +1,7 @@
import logging
import asyncio
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine, Literal
import inspect
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine, Literal, Tuple, get_args, get_origin
from pydantic import BaseModel
from ..main import TaskOutput
from ..agent.agent import Agent
@@ -40,7 +41,10 @@ class Task:
quality_check=True,
input_file: Optional[str] = None,
rerun: bool = False, # Renamed from can_rerun and logic inverted, default True for backward compatibility
retain_full_context: bool = False # By default, only use previous task output, not all previous tasks
retain_full_context: bool = False, # By default, only use previous task output, not all previous tasks
guardrail: Optional[Union[Callable[[TaskOutput], Tuple[bool, Any]], str]] = None,
max_retries: int = 3,
retry_count: int = 0
):
# Add check if memory config is provided
if memory is not None or (config and config.get('memory_config')):
@@ -80,6 +84,10 @@ class Task:
self.quality_check = quality_check
self.rerun = rerun # Assigning the rerun parameter
self.retain_full_context = retain_full_context
self.guardrail = guardrail
self.max_retries = max_retries
self.retry_count = retry_count
self._guardrail_fn = None
# Set logger level based on config verbose level
verbose = self.config.get("verbose", 0)
@@ -141,6 +149,55 @@ class Task:
self.output_pydantic = LoopModel
# Initialize guardrail
self._setup_guardrail()
def _setup_guardrail(self):
"""Setup the guardrail function based on the provided guardrail parameter."""
if self.guardrail is None:
self._guardrail_fn = None
return
if callable(self.guardrail):
# Validate function signature
sig = inspect.signature(self.guardrail)
positional_args = [
param for param in sig.parameters.values()
if param.default is inspect.Parameter.empty
]
if len(positional_args) != 1:
raise ValueError("Guardrail function must accept exactly one parameter (TaskOutput)")
# Check return annotation if present
return_annotation = sig.return_annotation
if return_annotation != inspect.Signature.empty:
return_annotation_args = get_args(return_annotation)
if not (
get_origin(return_annotation) is tuple
and len(return_annotation_args) == 2
and return_annotation_args[0] is bool
and (
return_annotation_args[1] is Any
or return_annotation_args[1] is str
or return_annotation_args[1] is TaskOutput
or return_annotation_args[1] == Union[str, TaskOutput]
)
):
raise ValueError(
"If return type is annotated, it must be Tuple[bool, Any]"
)
self._guardrail_fn = self.guardrail
elif isinstance(self.guardrail, str):
# Create LLM-based guardrail
from ..guardrails import LLMGuardrail
if not self.agent:
raise ValueError("Agent is required for string-based guardrails")
llm = getattr(self.agent, 'llm', None) or getattr(self.agent, 'llm_instance', None)
self._guardrail_fn = LLMGuardrail(description=self.guardrail, llm=llm)
else:
raise ValueError("Guardrail must be either a callable or a string description")
def __str__(self):
return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"
@@ -187,6 +244,37 @@ class Task:
logger.info(f"Task {self.id}: execute_callback called")
logger.info(f"Quality check enabled: {self.quality_check}")
# Process guardrail if configured
if self._guardrail_fn:
try:
guardrail_result = self._process_guardrail(task_output)
if not guardrail_result.success:
if self.retry_count >= self.max_retries:
raise Exception(
f"Task failed guardrail validation after {self.max_retries} retries. "
f"Last error: {guardrail_result.error}"
)
self.retry_count += 1
logger.warning(f"Task {self.id}: Guardrail validation failed (retry {self.retry_count}/{self.max_retries}): {guardrail_result.error}")
# Note: In a real execution, this would trigger a retry, but since this is a callback
# the retry logic would need to be handled at the agent/execution level
return
# If guardrail passed and returned a modified result
if guardrail_result.result is not None:
if isinstance(guardrail_result.result, str):
# Update the task output with the modified result
task_output.raw = guardrail_result.result
elif isinstance(guardrail_result.result, TaskOutput):
# Replace with the new task output
task_output = guardrail_result.result
logger.info(f"Task {self.id}: Guardrail validation passed")
except Exception as e:
logger.error(f"Task {self.id}: Error in guardrail processing: {e}")
# Continue execution even if guardrail fails to avoid breaking the task
# Initialize memory if not already initialized
if not self.memory:
self.memory = self.initialize_memory()
@@ -220,7 +308,11 @@ class Task:
if self.agent:
if getattr(self.agent, '_using_custom_llm', False) and hasattr(self.agent, 'llm_instance'):
# For custom LLM instances (like Ollama)
llm_model = self.agent.llm_instance
# Extract the model name from the LLM instance
if hasattr(self.agent.llm_instance, 'model'):
llm_model = self.agent.llm_instance.model
else:
llm_model = "gpt-4o-mini" # Default fallback
elif hasattr(self.agent, 'llm') and self.agent.llm:
# For standard model strings
llm_model = self.agent.llm
@@ -334,4 +426,34 @@ Context:
loop.run_until_complete(self.execute_callback(task_output))
except RuntimeError:
# If no loop is running in this context
asyncio.run(self.execute_callback(task_output))
asyncio.run(self.execute_callback(task_output))
def _process_guardrail(self, task_output: TaskOutput):
"""Process the guardrail validation for a task output.
Args:
task_output: The task output to validate
Returns:
GuardrailResult: The result of the guardrail validation
"""
from ..guardrails import GuardrailResult
if not self._guardrail_fn:
return GuardrailResult(success=True, result=task_output)
try:
# Call the guardrail function
result = self._guardrail_fn(task_output)
# Convert the result to a GuardrailResult
return GuardrailResult.from_tuple(result)
except Exception as e:
logger.error(f"Task {self.id}: Error in guardrail validation: {e}")
# On error, return failure
return GuardrailResult(
success=False,
result=None,
error=f"Guardrail validation error: {str(e)}"
)
+6 -4
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.97"
version = "0.0.102"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
requires-python = ">=3.10"
authors = [
@@ -25,7 +25,8 @@ mcp = [
]
memory = [
"chromadb>=1.0.0"
"chromadb>=1.0.0",
"litellm>=1.50.0",
]
knowledge = [
@@ -63,5 +64,6 @@ all = [
"praisonaiagents[api]"
]
[tool.setuptools]
packages = ["praisonaiagents"]
[tool.setuptools.packages.find]
where = ["."]
include = ["praisonaiagents*"]
+11
View File
@@ -0,0 +1,11 @@
from praisonaiagents import Agent, MCP
import os
sequential_agent = Agent(
instructions="""You are a helpful assistant that can break down complex problems.
Use the available tools when relevant to perform step-by-step analysis.""",
llm="gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
sequential_agent.start("Break down the process of making a cup of tea")
+11
View File
@@ -0,0 +1,11 @@
from praisonaiagents import Agent, MCP
import os
sequential_agent = Agent(
instructions="""You are a helpful assistant that can break down complex problems.
Use the available tools when relevant to perform step-by-step analysis.""",
llm="openai/gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
sequential_agent.start("Break down the process of making a cup of tea")
@@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
Simple test for guardrails functionality without full dependencies.
"""
import sys
import os
from typing import Tuple, Any
# Import only what we need for testing
from praisonaiagents.guardrails import GuardrailResult, LLMGuardrail
from praisonaiagents.main import TaskOutput
def test_guardrail_result():
"""Test GuardrailResult helper methods."""
print("Testing GuardrailResult...")
# Test success case
success_result = GuardrailResult.from_tuple((True, "Modified output"))
assert success_result.success
assert success_result.result == "Modified output"
assert success_result.error == ""
print("✓ Success result created correctly")
# Test failure case
failure_result = GuardrailResult.from_tuple((False, "Validation failed"))
assert not failure_result.success
assert failure_result.result is None
assert failure_result.error == "Validation failed"
print("✓ Failure result created correctly")
print("GuardrailResult test passed!\n")
def test_function_guardrail():
"""Test function-based guardrail logic."""
print("Testing function-based guardrail logic...")
def validate_output(task_output: TaskOutput) -> Tuple[bool, Any]:
"""Simple validation function."""
if "error" in task_output.raw.lower():
return False, "Output contains errors"
if len(task_output.raw) < 10:
return False, "Output is too short"
return True, task_output
# Test with good output
good_output = TaskOutput(
description="Test task",
raw="Hello! This is a friendly greeting message from the agent.",
agent="Test Agent"
)
result = validate_output(good_output)
guardrail_result = GuardrailResult.from_tuple(result)
assert guardrail_result.success, f"Good output should pass: {guardrail_result.error}"
print("✓ Good output passed function guardrail")
# Test with bad output
bad_output = TaskOutput(
description="Test task",
raw="Error occurred",
agent="Test Agent"
)
result = validate_output(bad_output)
guardrail_result = GuardrailResult.from_tuple(result)
assert not guardrail_result.success, "Bad output should fail guardrail"
print("✓ Bad output failed function guardrail as expected")
print("Function-based guardrail logic test passed!\n")
def test_llm_guardrail():
"""Test LLM guardrail logic."""
print("Testing LLM guardrail logic...")
# Mock LLM for testing that correctly parses the validation prompt
class MockLLM:
def chat(self, prompt, **kwargs):
# Extract the actual output to validate from the prompt
# The LLMGuardrail sends a structured prompt with "Output to Validate:" section
if "Output to Validate:" in prompt:
# Split by "Output to Validate:" and get the content after it
parts = prompt.split("Output to Validate:")
if len(parts) > 1:
output_content = parts[1].strip()
# Check only the output content, not the validation criteria
if "error" in output_content.lower():
return "FAIL: The output contains error messages"
return "PASS"
# Fallback: if no "Output to Validate:" section, return pass
return "PASS"
# Create LLM guardrail
mock_llm = MockLLM()
llm_guardrail = LLMGuardrail(
description="Check if the output is professional and does not contain errors",
llm=mock_llm
)
# Test with good output
good_output = TaskOutput(
description="Test task",
raw="Hello! This is a professional greeting message.",
agent="Test Agent"
)
result = llm_guardrail(good_output)
guardrail_result = GuardrailResult.from_tuple(result)
assert guardrail_result.success, f"Good output should pass: {guardrail_result.error}"
print("✓ Good output passed LLM guardrail")
# Test with bad output
bad_output = TaskOutput(
description="Test task",
raw="There was an error in the system",
agent="Test Agent"
)
result = llm_guardrail(bad_output)
guardrail_result = GuardrailResult.from_tuple(result)
assert not guardrail_result.success, "Bad output should fail LLM guardrail"
print("✓ Bad output failed LLM guardrail as expected")
print("LLM guardrail logic test passed!\n")
def main():
"""Run all tests."""
print("Running Simple Guardrails Tests...\n")
try:
test_guardrail_result()
test_function_guardrail()
test_llm_guardrail()
print("🎉 All simple guardrail tests passed!")
print("\nGuardrails core logic is working correctly!")
except Exception as e:
print(f"❌ Test failed: {e}")
import traceback
traceback.print_exc()
return False
return True
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)
+33
View File
@@ -0,0 +1,33 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
import os
from dotenv import load_dotenv
load_dotenv()
llm_config = {
"model": "openai/gpt-4o-mini",
"api_key": os.getenv('OPENAI_API_KEY'),
"temperature": 0.7,
"max_tokens": 2000
}
blog_agent = Agent(
role="Blog Writer",
goal="Write a blog post about AI",
backstory="Expert at writing blog posts",
llm="gpt-4o-mini",
)
blog_task = Task(
description="Write a blog post about AI trends in 1 paragraph",
expected_output="Well-written blog post about AI trends",
agent=blog_agent
)
agents = PraisonAIAgents(
agents=[blog_agent],
tasks=[blog_task],
memory=True
)
result = agents.start()
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""
Simple test for Agent guardrail functionality.
"""
def test_agent_guardrails():
"""Test basic Agent guardrail functionality."""
print("Testing Agent guardrail functionality...")
try:
from praisonaiagents import Agent, TaskOutput
print("✓ Basic imports successful")
# Test function-based guardrail
def test_guardrail(task_output: TaskOutput):
if len(task_output.raw) < 10:
return False, "Too short"
return True, task_output
# Test Agent creation with function guardrail
agent1 = Agent(
name="TestAgent1",
instructions="You are a test agent",
guardrail=test_guardrail,
max_guardrail_retries=2
)
print("✓ Agent with function guardrail created successfully")
print(f" - Agent name: {agent1.name}")
print(f" - Has guardrail function: {agent1._guardrail_fn is not None}")
print(f" - Max retries: {agent1.max_guardrail_retries}")
# Test Agent creation with string guardrail
agent2 = Agent(
name="TestAgent2",
instructions="You are a test agent",
guardrail="Ensure the response is polite and professional",
max_guardrail_retries=3
)
print("✓ Agent with LLM guardrail created successfully")
print(f" - Agent name: {agent2.name}")
print(f" - Has guardrail function: {agent2._guardrail_fn is not None}")
print(f" - Max retries: {agent2.max_guardrail_retries}")
# Test Agent without guardrail
agent3 = Agent(
name="TestAgent3",
instructions="You are a test agent"
)
print("✓ Agent without guardrail created successfully")
print(f" - Agent name: {agent3.name}")
print(f" - Has guardrail function: {agent3._guardrail_fn is None}")
print("\n🎉 All Agent guardrail tests passed!")
return True
except Exception as e:
print(f"✗ Error: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
test_agent_guardrails()
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
from praisonaiagents import Agent, MCP
import os
def test_agent_direct():
"""Test using gpt-4o-mini directly (agent.py path)"""
print("="*50)
print("Testing gpt-4o-mini (agent.py path)")
print("="*50)
agent = Agent(
instructions="You are a helpful assistant that can break down complex problems.",
llm="gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
result = agent.start("What are 5 steps to make coffee?")
print("✅ Agent direct path completed successfully")
return result
def test_llm_class():
"""Test using openai/gpt-4o-mini (llm.py path)"""
print("\n" + "="*50)
print("Testing openai/gpt-4o-mini (llm.py path)")
print("="*50)
agent = Agent(
instructions="You are a helpful assistant that can break down complex problems.",
llm="openai/gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
result = agent.start("What are 5 steps to make coffee?")
print("✅ LLM class path completed successfully")
return result
if __name__ == "__main__":
try:
# Test both approaches
result1 = test_agent_direct()
result2 = test_llm_class()
print("\n" + "="*50)
print("SUMMARY")
print("="*50)
print("✅ Both formats work correctly!")
print("✅ gpt-4o-mini uses agent.py direct OpenAI calls")
print("✅ openai/gpt-4o-mini uses llm.py LiteLLM wrapper")
print("✅ Both support tool calling and MCP integration")
except Exception as e:
print(f"❌ Error occurred: {e}")
import traceback
traceback.print_exc()
+182
View File
@@ -0,0 +1,182 @@
#!/usr/bin/env python3
"""
Test script for guardrails functionality in PraisonAI Agents.
"""
import os
import sys
import logging
from praisonaiagents import Agent, Task, TaskOutput
from praisonaiagents.guardrails import GuardrailResult, LLMGuardrail
from typing import Tuple, Any
def test_function_guardrail():
"""Test function-based guardrail."""
print("Testing function-based guardrail...")
def validate_output(task_output: TaskOutput) -> Tuple[bool, Any]:
"""Simple validation function."""
if "error" in task_output.raw.lower():
return False, "Output contains errors"
if len(task_output.raw) < 10:
return False, "Output is too short"
return True, task_output
# Create agent and task with guardrail
agent = Agent(
name="Test Agent",
role="Tester",
goal="Test guardrails",
backstory="I am testing the guardrail functionality"
)
task = Task(
description="Write a simple hello message",
expected_output="A friendly greeting message",
agent=agent,
guardrail=validate_output,
max_retries=2
)
# Test with good output
good_output = TaskOutput(
description="Test task",
raw="Hello! This is a friendly greeting message from the agent.",
agent="Test Agent"
)
result = task._process_guardrail(good_output)
assert result.success, f"Good output should pass: {result.error}"
print("✓ Good output passed guardrail")
# Test with bad output
bad_output = TaskOutput(
description="Test task",
raw="Error occurred",
agent="Test Agent"
)
result = task._process_guardrail(bad_output)
assert not result.success, "Bad output should fail guardrail"
print("✓ Bad output failed guardrail as expected")
print("Function-based guardrail test passed!\n")
def test_string_guardrail():
"""Test string-based LLM guardrail."""
print("Testing string-based LLM guardrail...")
# Mock LLM for testing
class MockLLM:
def chat(self, prompt, **kwargs):
# Extract the actual output to validate from the prompt
# The LLMGuardrail sends a structured prompt with "Output to Validate:" section
if "Output to Validate:" in prompt:
# Split by "Output to Validate:" and get the content after it
parts = prompt.split("Output to Validate:")
if len(parts) > 1:
output_content = parts[1].strip()
# Check only the output content, not the validation criteria
if "error" in output_content.lower():
return "FAIL: The output contains error messages"
return "PASS"
# Fallback: if no "Output to Validate:" section, check the prompt directly
# This should rarely happen with proper LLMGuardrail usage
if "error" in prompt.lower() and "check if" not in prompt.lower():
return "FAIL: The output contains error messages"
return "PASS"
# Create agent with mock LLM
agent = Agent(
name="Test Agent",
role="Tester",
goal="Test guardrails",
backstory="I am testing the guardrail functionality"
)
agent.llm = MockLLM()
task = Task(
description="Write a simple hello message",
expected_output="A friendly greeting message",
agent=agent,
guardrail="Check if the output is professional and does not contain errors",
max_retries=2
)
# Test with good output
good_output = TaskOutput(
description="Test task",
raw="Hello! This is a professional greeting message.",
agent="Test Agent"
)
result = task._process_guardrail(good_output)
assert result.success, f"Good output should pass: {result.error}"
print("✓ Good output passed LLM guardrail")
# Test with bad output
bad_output = TaskOutput(
description="Test task",
raw="There was an error in the system",
agent="Test Agent"
)
result = task._process_guardrail(bad_output)
assert not result.success, "Bad output should fail LLM guardrail"
print("✓ Bad output failed LLM guardrail as expected")
print("String-based LLM guardrail test passed!\n")
def test_guardrail_result():
"""Test GuardrailResult helper methods."""
print("Testing GuardrailResult...")
# Test success case
success_result = GuardrailResult.from_tuple((True, "Modified output"))
assert success_result.success
assert success_result.result == "Modified output"
assert success_result.error == ""
print("✓ Success result created correctly")
# Test failure case
failure_result = GuardrailResult.from_tuple((False, "Validation failed"))
assert not failure_result.success
assert failure_result.result is None
assert failure_result.error == "Validation failed"
print("✓ Failure result created correctly")
print("GuardrailResult test passed!\n")
def main():
"""Run all tests."""
print("Running PraisonAI Agents Guardrails Tests...\n")
try:
test_guardrail_result()
test_function_guardrail()
test_string_guardrail()
print("🎉 All guardrail tests passed!")
print("\nGuardrails implementation is working correctly!")
except Exception as e:
print(f"❌ Test failed: {e}")
import traceback
traceback.print_exc()
return False
return True
if __name__ == "__main__":
# Set up basic logging
logging.basicConfig(level=logging.WARNING)
success = main()
sys.exit(0 if success else 1)
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Test self-reflection functionality in both Agent and LLM classes"""
from praisonaiagents import Agent
from praisonaiagents.llm import LLM
def test_agent_self_reflection():
"""Test self-reflection in Agent class"""
print("=== Testing Agent Self-Reflection ===")
agent = Agent(
name="ReflectiveAgent",
instructions="You are a helpful assistant.",
llm="gpt-4o-mini",
self_reflect=True,
min_reflect=1,
max_reflect=3
)
response = agent.start("What is 2+2? Be brief.")
print(f"Agent Response: {response}")
print()
def test_llm_self_reflection():
"""Test self-reflection in LLM class directly"""
print("=== Testing LLM Self-Reflection ===")
llm = LLM(model="gpt-4o-mini")
response = llm.get_response(
prompt="What is 2+2? Be brief.",
system_prompt="You are a helpful assistant.",
self_reflect=True,
min_reflect=1,
max_reflect=3,
verbose=True
)
print(f"LLM Response: {response}")
print()
def test_llm_no_reflection():
"""Test LLM without self-reflection for comparison"""
print("=== Testing LLM Without Self-Reflection ===")
llm = LLM(model="gpt-4o-mini")
response = llm.get_response(
prompt="What is 2+2? Be brief.",
system_prompt="You are a helpful assistant.",
self_reflect=False,
verbose=True
)
print(f"LLM Response: {response}")
print()
if __name__ == "__main__":
# Test without reflection first
test_llm_no_reflection()
# Test with reflection
test_llm_self_reflection()
# Test agent reflection
test_agent_self_reflection()
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
from praisonaiagents import Agent, MCP
import os
def test_agent_direct():
"""Test using gpt-4o-mini directly (agent.py path)"""
print("=" * 50)
print("Testing gpt-4o-mini (agent.py path)")
print("=" * 50)
agent = Agent(
instructions="You are a helpful assistant that can break down complex problems.",
llm="gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
result = agent.start("What are 3 steps to make coffee?")
print("✅ Agent direct path completed successfully")
return result
def test_llm_class():
"""Test using openai/gpt-4o-mini (llm.py path)"""
print("\n" + "=" * 50)
print("Testing openai/gpt-4o-mini (llm.py path)")
print("=" * 50)
agent = Agent(
instructions="You are a helpful assistant that can break down complex problems.",
llm="openai/gpt-4o-mini",
tools=MCP("npx -y @modelcontextprotocol/server-sequential-thinking")
)
result = agent.start("What are 3 steps to make coffee?")
print("✅ LLM class path completed successfully")
return result
if __name__ == "__main__":
try:
# Test both approaches
result1 = test_agent_direct()
result2 = test_llm_class()
print("\n" + "=" * 50)
print("SUMMARY")
print("=" * 50)
print("✅ Both formats work correctly!")
print("✅ gpt-4o-mini uses agent.py direct OpenAI calls")
print("✅ openai/gpt-4o-mini uses llm.py LiteLLM wrapper")
print("✅ Both support sequential tool calling and MCP integration")
print("✅ Sequential thinking tool works properly in both modes")
except Exception as e:
print(f"❌ Error occurred: {e}")
import traceback
traceback.print_exc()
@@ -0,0 +1,157 @@
#!/usr/bin/env python3
"""
Example demonstrating guardrails functionality in PraisonAI Agents.
This example shows both function-based and LLM-based guardrails
for validating task outputs.
"""
import sys
import os
from typing import Tuple, Any
# Add project to path
sys.path.insert(0, os.path.abspath('..'))
from praisonaiagents import Agent, Task, TaskOutput
def email_validator(task_output: TaskOutput) -> Tuple[bool, Any]:
"""
Function-based guardrail to validate email content.
Args:
task_output: The task output to validate
Returns:
Tuple of (success, result_or_error)
"""
content = task_output.raw.lower()
# Check for required email components
if "subject:" not in content:
return False, "Email must include a subject line"
if "dear" not in content and "hello" not in content:
return False, "Email must include a proper greeting"
if len(content) < 50:
return False, "Email content is too short"
if "error" in content or "problem" in content:
return False, "Email should not mention errors or problems"
return True, task_output
def main():
"""Run the guardrails example."""
print("PraisonAI Agents - Guardrails Example")
print("=====================================\n")
# Create an agent
agent = Agent(
name="Email Assistant",
role="Professional Email Writer",
goal="Write clear, professional emails",
backstory="I am an AI assistant specialized in writing professional emails"
)
print("1. Testing Function-based Guardrail")
print("------------------------------------")
# Create task with function-based guardrail
task_with_function_guardrail = Task(
description="Write a professional email to a client about project completion",
expected_output="A well-formatted professional email",
agent=agent,
guardrail=email_validator, # Function-based guardrail
max_retries=2
)
print(f"Task created with function guardrail: {email_validator.__name__}")
print(f"Max retries: {task_with_function_guardrail.max_retries}")
# Simulate a task output that should pass
good_output = TaskOutput(
description="Email task",
raw="""Subject: Project Completion Update
Dear Client,
I am pleased to inform you that your project has been completed successfully.
All deliverables have been reviewed and are ready for your review.
Please let me know if you have any questions.
Best regards,
Project Team""",
agent="Email Assistant"
)
result = task_with_function_guardrail._process_guardrail(good_output)
print(f"Good email result: {'PASSED' if result.success else 'FAILED'}")
if not result.success:
print(f"Error: {result.error}")
# Simulate a task output that should fail
bad_output = TaskOutput(
description="Email task",
raw="Hi there, there was an error with your project.",
agent="Email Assistant"
)
result = task_with_function_guardrail._process_guardrail(bad_output)
print(f"Bad email result: {'PASSED' if result.success else 'FAILED'}")
if not result.success:
print(f"Error: {result.error}")
print("\n2. Testing String-based LLM Guardrail")
print("-------------------------------------")
# Create task with string-based guardrail
task_with_llm_guardrail = Task(
description="Write a marketing email for a new product launch",
expected_output="Engaging marketing content",
agent=agent,
guardrail="Ensure the content is professional, engaging, includes a clear call-to-action, and is free of errors",
max_retries=3
)
print("Task created with LLM-based guardrail")
print("Guardrail description: 'Ensure the content is professional, engaging, includes a clear call-to-action, and is free of errors'")
print(f"Max retries: {task_with_llm_guardrail.max_retries}")
print("\n3. Backward Compatibility")
print("-------------------------")
# Create task without guardrail (backward compatible)
task_without_guardrail = Task(
description="Write a simple thank you note",
expected_output="A brief thank you message",
agent=agent
)
print("Task created without guardrail (backward compatible)")
print(f"Guardrail function: {task_without_guardrail._guardrail_fn}")
# Test that it doesn't break existing functionality
simple_output = TaskOutput(
description="Thank you task",
raw="Thank you for your business!",
agent="Email Assistant"
)
result = task_without_guardrail._process_guardrail(simple_output)
print(f"No guardrail result: {'PASSED' if result.success else 'FAILED'}")
print("\n✅ Guardrails example completed successfully!")
print("\nKey Features Demonstrated:")
print("- Function-based guardrails with custom validation logic")
print("- String-based LLM guardrails using natural language")
print("- Configurable retry mechanism")
print("- Backward compatibility with existing tasks")
print("- Integration with TaskOutput validation")
if __name__ == "__main__":
main()
+3 -1
View File
@@ -2398,7 +2398,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.97"
version = "0.0.102"
source = { editable = "." }
dependencies = [
{ name = "mcp" },
@@ -2444,6 +2444,7 @@ mcp = [
]
memory = [
{ name = "chromadb" },
{ name = "litellm" },
]
[package.metadata]
@@ -2455,6 +2456,7 @@ requires-dist = [
{ name = "fastapi", marker = "extra == 'api'", specifier = ">=0.115.0" },
{ name = "fastapi", marker = "extra == 'mcp'", specifier = ">=0.115.0" },
{ name = "litellm", marker = "extra == 'llm'", specifier = ">=1.50.0" },
{ name = "litellm", marker = "extra == 'memory'", specifier = ">=1.50.0" },
{ name = "markitdown", extras = ["all"], marker = "extra == 'knowledge'", specifier = ">=0.1.0" },
{ name = "mcp", specifier = ">=1.6.0" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.6.0" },
@@ -191,3 +191,17 @@ const agent = new Agent({
// Start the agent with a prompt that will trigger tool usage
agent.start("What's the weather in Paris, the time in Tokyo, and what is 25 * 4?");
```
## MCP SSE Tool Integration
PraisonAI can use remote tools exposed via the Model Context Protocol over Server-Sent Events.
1. Start the Python SSE server in another terminal:
```bash
python ../../praisonai-agents/tests/mcp-sse-direct-server.py --host 127.0.0.1 --port 8080
```
2. Run the TypeScript example which connects to this server:
```bash
npx ts-node examples/tools/mcp-sse.ts
```
@@ -0,0 +1,70 @@
import { Agent } from '../../src/agent';
import { MCP } from '../../src/tools/mcpSse';
async function main() {
// Connect to the running SSE server
const mcp = new MCP('http://127.0.0.1:8080/sse');
try {
await mcp.initialize();
} catch (error) {
console.error('Failed to connect to MCP SSE server:', error);
console.error('Please ensure the server is running at http://127.0.0.1:8080/sse');
process.exit(1);
}
// Create tool functions that call the remote MCP tools
const toolFunctions: Record<string, (...args: any[]) => Promise<any>> = {};
if (mcp.tools.length === 0) {
console.warn('Warning: No MCP tools available. Make sure the MCP server is running.');
}
for (const tool of mcp) {
if (!tool || typeof tool.name !== 'string') {
console.warn('Skipping invalid tool:', tool);
continue;
}
const paramNames = Object.keys(tool.schemaProperties || {});
toolFunctions[tool.name] = async (...args: any[]) => {
const params: Record<string, any> = {};
if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) {
// If single object argument, use it directly as params
Object.assign(params, args[0]);
} else {
// Map positional arguments with validation
if (args.length > paramNames.length) {
console.warn(
`Tool ${tool.name}: Too many arguments provided. Expected ${paramNames.length}, got ${args.length}`
);
}
for (let i = 0; i < Math.min(args.length, paramNames.length); i++) {
params[paramNames[i]] = args[i];
}
}
return tool.execute(params);
};
}
const agent = new Agent({
instructions: 'Use the tools to greet people and report the weather.',
name: 'MCPAgent',
tools: mcp.toOpenAITools(),
toolFunctions
});
try {
const result = await agent.start('Say hello to John and tell the weather in London.');
console.log('\nFinal Result:', result);
} catch (error) {
console.error('Agent execution failed:', error);
} finally {
// Clean up MCP connection
await mcp.close();
}
}
if (require.main === module) {
main();
}
+2 -1
View File
@@ -62,7 +62,8 @@
"fast-xml-parser": "^4.5.1",
"node-fetch": "^2.6.9",
"openai": "^4.81.0",
"praisonai": "^1.0.19"
"praisonai": "^1.0.19",
"@modelcontextprotocol/sdk": "^1.12.1"
},
"optionalDependencies": {
"boxen": "^7.1.1",
+4862
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -20,4 +20,5 @@ export class BaseTool implements Tool {
}
// Export all tool modules
export * from './arxivTools';
export * from './arxivTools';
export * from './mcpSse';
+116
View File
@@ -0,0 +1,116 @@
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { BaseTool } from './index';
export interface MCPToolInfo {
name: string;
description?: string;
inputSchema?: any;
}
export class MCPTool extends BaseTool {
private client: Client;
private inputSchema: any;
constructor(info: MCPToolInfo, client: Client) {
super(info.name, info.description || `Call the ${info.name} tool`);
this.client = client;
this.inputSchema = info.inputSchema || { type: 'object', properties: {}, required: [] };
}
get schemaProperties(): Record<string, any> | undefined {
return this.inputSchema?.properties;
}
async execute(args: any = {}): Promise<any> {
try {
const result: any = await this.client.callTool({ name: this.name, arguments: args });
if (result.structuredContent) {
return result.structuredContent;
}
if (Array.isArray(result.content) && result.content.length > 0) {
const item = result.content[0];
if (typeof item.text === 'string') return item.text;
}
return result;
} catch (error) {
throw new Error(`Failed to execute tool ${this.name}: ${error instanceof Error ? error.message : String(error)}`);
}
}
toOpenAITool() {
return {
type: 'function',
function: {
name: this.name,
description: this.description,
parameters: this.inputSchema
}
};
}
}
export class MCP implements Iterable<MCPTool> {
tools: MCPTool[] = [];
private client: Client | null = null;
constructor(private url: string, private debug = false) {
if (debug) {
console.log(`MCP client initialized for URL: ${url}`);
}
}
async initialize(): Promise<void> {
if (this.client) {
if (this.debug) console.log('MCP client already initialized');
return;
}
try {
this.client = new Client({ name: 'praisonai-ts-mcp', version: '1.0.0' });
const transport = new SSEClientTransport(new URL(this.url));
await this.client.connect(transport);
const { tools } = await this.client.listTools();
this.tools = tools.map((t: any) => new MCPTool({
name: t.name,
description: t.description,
inputSchema: t.inputSchema
}, this.client as Client));
if (this.debug) console.log(`Initialized MCP with ${this.tools.length} tools`);
} catch (error) {
if (this.client) {
await this.client.close().catch(() => {});
this.client = null;
}
throw new Error(`Failed to initialize MCP client: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
[Symbol.iterator](): Iterator<MCPTool> {
return this.tools[Symbol.iterator]();
}
toOpenAITools() {
return this.tools.map(t => t.toOpenAITool());
}
async close(): Promise<void> {
if (this.client) {
try {
await this.client.close();
} catch (error) {
if (this.debug) {
console.warn('Error closing MCP client:', error);
}
} finally {
this.client = null;
this.tools = [];
}
}
}
get isConnected(): boolean {
return this.client !== null;
}
}
+2 -2
View File
@@ -3,8 +3,8 @@ class Praisonai < Formula
desc "AI tools for various AI applications"
homepage "https://github.com/MervinPraison/PraisonAI"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.26.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.26.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.30.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.30.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+1 -1
View File
@@ -56,7 +56,7 @@ class CloudDeployer:
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.26 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.30 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.2.26"
version = "2.2.30"
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 = ""
@@ -12,7 +12,7 @@ dependencies = [
"rich>=13.7",
"markdown>=3.5",
"pyparsing>=3.0.0",
"praisonaiagents>=0.0.97",
"praisonaiagents>=0.0.102",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.2.26"
version = "2.2.30"
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 = ""
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.97"
praisonaiagents = ">=0.0.102"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Comprehensive test script to verify all import scenarios work correctly
Tests the original failing import from the GitHub issue
"""
import sys
import os
# Add the praisonai-agents source to Python path
sys.path.insert(0, '/home/runner/work/PraisonAI/PraisonAI/src/praisonai-agents')
def test_original_failing_import():
"""Test the exact import that was failing in the GitHub issue"""
print("=== Testing Original Failing Import ===")
try:
from praisonaiagents.agents.agents import Agent, Task, PraisonAIAgents
print('✅ SUCCESS: from praisonaiagents.agents.agents import Agent, Task, PraisonAIAgents')
return True
except ImportError as e:
print(f'❌ ERROR: {e}')
import traceback
traceback.print_exc()
return False
except Exception as e:
print(f'❌ UNEXPECTED ERROR: {e}')
import traceback
traceback.print_exc()
return False
def test_memory_direct_import():
"""Test direct Memory import"""
print("\n=== Testing Direct Memory Import ===")
try:
from praisonaiagents.memory import Memory
print('✅ SUCCESS: from praisonaiagents.memory import Memory')
return True
except ImportError as e:
print(f'❌ ERROR: {e}')
import traceback
traceback.print_exc()
return False
except Exception as e:
print(f'❌ UNEXPECTED ERROR: {e}')
import traceback
traceback.print_exc()
return False
def test_memory_from_package_root():
"""Test Memory import from package root"""
print("\n=== Testing Memory Import from Package Root ===")
try:
from praisonaiagents import Memory
print('✅ SUCCESS: from praisonaiagents import Memory')
return True
except ImportError as e:
print(f'❌ ERROR: {e}')
import traceback
traceback.print_exc()
return False
except Exception as e:
print(f'❌ UNEXPECTED ERROR: {e}')
import traceback
traceback.print_exc()
return False
def test_session_import():
"""Test Session import which depends on Memory"""
print("\n=== Testing Session Import ===")
try:
from praisonaiagents.session import Session
print('✅ SUCCESS: from praisonaiagents.session import Session')
return True
except ImportError as e:
print(f'❌ ERROR: {e}')
import traceback
traceback.print_exc()
return False
except Exception as e:
print(f'❌ UNEXPECTED ERROR: {e}')
import traceback
traceback.print_exc()
return False
def test_memory_instantiation():
"""Test that Memory can be instantiated without errors"""
print("\n=== Testing Memory Instantiation ===")
try:
from praisonaiagents.memory import Memory
# Test with minimal config (no external dependencies)
config = {"provider": "none"}
memory = Memory(config=config)
print('✅ SUCCESS: Memory instance created with provider="none"')
# Test basic methods don't fail immediately
memory.store_short_term("test content", metadata={"test": True})
results = memory.search_short_term("test", limit=1)
print('✅ SUCCESS: Basic memory operations work')
return True
except Exception as e:
print(f'❌ ERROR: {e}')
import traceback
traceback.print_exc()
return False
def run_all_tests():
"""Run all tests and report results"""
print("🔍 Running comprehensive import tests...")
tests = [
test_original_failing_import,
test_memory_direct_import,
test_memory_from_package_root,
test_session_import,
test_memory_instantiation
]
results = []
for test in tests:
results.append(test())
print(f"\n📊 Test Results: {sum(results)}/{len(results)} tests passed")
if all(results):
print("🎉 ALL TESTS PASSED! The Memory import issue has been resolved.")
else:
print("❌ Some tests failed. The issue may not be fully resolved.")
return all(results)
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""
Test script to verify Memory import works correctly
"""
import sys
import os
# Add the praisonai-agents source to Python path
sys.path.insert(0, '/home/runner/work/PraisonAI/PraisonAI/src/praisonai-agents')
try:
from praisonaiagents.memory import Memory
print('SUCCESS: Memory import works correctly')
print('Memory class found:', Memory)
# Try to create a minimal instance to ensure it doesn't fail immediately
config = {"provider": "none"} # Use the simplest provider
memory = Memory(config=config)
print('SUCCESS: Memory instance created successfully')
except ImportError as e:
print('ERROR:', e)
import traceback
traceback.print_exc()
except Exception as e:
print('UNEXPECTED ERROR:', e)
import traceback
traceback.print_exc()
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""
Test script to verify session.py import works correctly (which imports Memory)
"""
import sys
import os
# Add the praisonai-agents source to Python path
sys.path.insert(0, '/home/runner/work/PraisonAI/PraisonAI/src/praisonai-agents')
try:
from praisonaiagents.session import Session
print('SUCCESS: Session import works correctly')
print('Session class found:', Session)
# This was the failing import chain
from praisonaiagents.agents.agents import Agent, Task, PraisonAIAgents
print('SUCCESS: Agent, Task, PraisonAIAgents import works correctly')
except ImportError as e:
print('ERROR:', e)
import traceback
traceback.print_exc()
except Exception as e:
print('UNEXPECTED ERROR:', e)
import traceback
traceback.print_exc()
+2582 -2584
View File
File diff suppressed because it is too large. Load diff