mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-13 05:01:35 -10:00
Compare commits
205
Commits
add-valkey
...
v4.6.62
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a855c4700 | ||
|
|
d51073710a | ||
|
|
6b4b59fbe7 | ||
|
|
709a038785 | ||
|
|
64f7fdeb84 | ||
|
|
f834d42448 | ||
|
|
705fae31f8 | ||
|
|
b4270173d4 | ||
|
|
6256744348 | ||
|
|
15d597da37 | ||
|
|
04d5e07e1b | ||
|
|
754386022c | ||
|
|
1e27a73633 | ||
|
|
2adfe7e832 | ||
|
|
1ad58ca029 | ||
|
|
2f9677abb2 | ||
|
|
9991e00f52 | ||
|
|
86aeaa4d89 | ||
|
|
a66db1836a | ||
|
|
e90d922318 | ||
|
|
43b9552f9b | ||
|
|
d5c77c244b | ||
|
|
bcb6957dac | ||
|
|
fe78664261 | ||
|
|
d9a7c1a01e | ||
|
|
6a122c3671 | ||
|
|
1f1cfa974b | ||
|
|
34d60ae8fa | ||
|
|
095653d78a | ||
|
|
7f6a483e62 | ||
|
|
c6883def69 | ||
|
|
d3c4a2afad | ||
|
|
034138ebc9 | ||
|
|
70b2df135b | ||
|
|
3532600887 | ||
|
|
13ba1ed954 | ||
|
|
4a1fe173e8 | ||
|
|
147bcce448 | ||
|
|
b5e8052809 | ||
|
|
8a93bc5096 | ||
|
|
f65b83614c | ||
|
|
25000d9b26 | ||
|
|
98cfdcc9ad | ||
|
|
6f2df7db2a | ||
|
|
495d4604fe | ||
|
|
a4386796c9 | ||
|
|
e1f9ae5b6b | ||
|
|
aa5315b90e | ||
|
|
b9d3ede110 | ||
|
|
441b428cc9 | ||
|
|
bcae07cf58 | ||
|
|
bf28787705 | ||
|
|
5a8f4aedf8 | ||
|
|
459a12a656 | ||
|
|
d9224adc70 | ||
|
|
82f1d8881b | ||
|
|
ca07ebf813 | ||
|
|
2b2443ff63 | ||
|
|
c8938c6aeb | ||
|
|
fc072d37a3 | ||
|
|
ccfc66092b | ||
|
|
03e5f0b11a | ||
|
|
6b051dcafb | ||
|
|
ade64e2b9c | ||
|
|
3791c6ea64 | ||
|
|
ef8e1fa6b6 | ||
|
|
aa273b7c36 | ||
|
|
98cf202178 | ||
|
|
f49d5daf15 | ||
|
|
011f54fbc5 | ||
|
|
89eefb87fe | ||
|
|
a14db7f39a | ||
|
|
ee126c60b4 | ||
|
|
4f8cfb783f | ||
|
|
00872ee314 | ||
|
|
4ee593cabb | ||
|
|
4ed1412820 | ||
|
|
f8031a4349 | ||
|
|
b0a2aa35d4 | ||
|
|
74273677a8 | ||
|
|
7c9899d0da | ||
|
|
5bff3e51bc | ||
|
|
f55cc2c4ee | ||
|
|
0f9425010f | ||
|
|
222d4f68eb | ||
|
|
aad319c8d5 | ||
|
|
d51191d073 | ||
|
|
8cb0e52a95 | ||
|
|
744ed799af | ||
|
|
c88bd1eeea | ||
|
|
7890723aa6 | ||
|
|
8567f44a4c | ||
|
|
6d26ff96a9 | ||
|
|
2dbc1bcbdf | ||
|
|
197f8d73ca | ||
|
|
ce97667156 | ||
|
|
a9f4bd593e | ||
|
|
998dd9477f | ||
|
|
0aa0d5b598 | ||
|
|
676f2d6588 | ||
|
|
90aacf064b | ||
|
|
0a8ce9eb7d | ||
|
|
9fcac3a951 | ||
|
|
6878780000 | ||
|
|
e55789e8cf | ||
|
|
e0fb8e7dd1 | ||
|
|
bc73dd1db9 | ||
|
|
5216274924 | ||
|
|
2e5eca1db6 | ||
|
|
f51fe294e7 | ||
|
|
a1b7ed5c91 | ||
|
|
aaed51eee8 | ||
|
|
48407be580 | ||
|
|
0ad42ff2c5 | ||
|
|
2943c3b77e | ||
|
|
84627f593c | ||
|
|
927020cea8 | ||
|
|
23ca2a80d6 | ||
|
|
f931173e60 | ||
|
|
ba12269d5d | ||
|
|
7839196391 | ||
|
|
e958968307 | ||
|
|
1c26a5bf14 | ||
|
|
ba8cd6b3c1 | ||
|
|
8327f8e8b8 | ||
|
|
f78ba6348c | ||
|
|
4f3daa8628 | ||
|
|
d7f68a0870 | ||
|
|
1bf62b0416 | ||
|
|
d5f1114aaf | ||
|
|
d364fa5020 | ||
|
|
c1db6317c8 | ||
|
|
f88727cb75 | ||
|
|
7ab0479e82 | ||
|
|
0e0241d15f | ||
|
|
5815dc61bf | ||
|
|
e720c0bc3d | ||
|
|
cd448fa710 | ||
|
|
c35dadebdb | ||
|
|
f9ae9e9aa4 | ||
|
|
9d9a9a7ad8 | ||
|
|
44d9d8c4c0 | ||
|
|
bf2656cee9 | ||
|
|
d55cd989ea | ||
|
|
0305214695 | ||
|
|
3f26bf166c | ||
|
|
ab19a13cc4 | ||
|
|
28d487b66d | ||
|
|
a06239fb15 | ||
|
|
dabe2cf671 | ||
|
|
619787c9da | ||
|
|
2fa6a6efd0 | ||
|
|
8acf77c531 | ||
|
|
5ff79d39c9 | ||
|
|
6902147f13 | ||
|
|
321b6b28a9 | ||
|
|
1c5b58aa13 | ||
|
|
71834d39d7 | ||
|
|
6971d8ecc9 | ||
|
|
8348c2b7ab | ||
|
|
9cfc636a92 | ||
|
|
b43e93c502 | ||
|
|
b09450d9dc | ||
|
|
710d607f6e | ||
|
|
a7910c2eb0 | ||
|
|
c98e1a1972 | ||
|
|
aee9552dd0 | ||
|
|
6507b8e7ef | ||
|
|
50652531e7 | ||
|
|
a949579759 | ||
|
|
79702ba1ff | ||
|
|
e9b75bfef9 | ||
|
|
982b7dfe4f | ||
|
|
f372821b9e | ||
|
|
445003042e | ||
|
|
00b40e8cc4 | ||
|
|
6dca959eda | ||
|
|
5ba8782bf7 | ||
|
|
a2402063f8 | ||
|
|
302bbcbec2 | ||
|
|
ac309e99ea | ||
|
|
727ee626c8 | ||
|
|
1dc61d3946 | ||
|
|
50a0399f1c | ||
|
|
40bfd66797 | ||
|
|
0da4a90c80 | ||
|
|
dc75c51fea | ||
|
|
b95a7f6ded | ||
|
|
92953fdbe6 | ||
|
|
b3cd7ecfd0 | ||
|
|
01ea0f5a05 | ||
|
|
eddcae8d31 | ||
|
|
71f891003e | ||
|
|
13750a4ccd | ||
|
|
c9e1d1a202 | ||
|
|
5515290329 | ||
|
|
cb1cefd94b | ||
|
|
a90666a19f | ||
|
|
89df701c2a | ||
|
|
480848c26d | ||
|
|
a064a286f3 | ||
|
|
1d78204b0b | ||
|
|
bba5d43cc1 | ||
|
|
c2dfa859c3 | ||
|
|
bb8a56e33c |
No files matched your search
@@ -200,6 +200,7 @@ jobs:
|
||||
allowed_bots: 'praisonai-triage-agent[bot]'
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ steps.app-token.outputs.token }}
|
||||
model: opus
|
||||
trigger_phrase: "@claude"
|
||||
label_trigger: "claude"
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
name: Cross-Repo Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/praisonai/praisonai/integration/**'
|
||||
- 'src/praisonai/tests/integration/test_aiui_*'
|
||||
- 'src/praisonai/pyproject.toml'
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/praisonai/praisonai/integration/**'
|
||||
- 'src/praisonai/tests/integration/test_aiui_*'
|
||||
- 'src/praisonai/pyproject.toml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
cross-repo-integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout PraisonAI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: PraisonAI
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Checkout PraisonAIUI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: MervinPraison/PraisonAIUI
|
||||
path: PraisonAIUI
|
||||
ref: main
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
# Install PraisonAI packages in development mode
|
||||
cd PraisonAI/src/praisonai-agents
|
||||
pip install -e .
|
||||
cd ../praisonai
|
||||
pip install -e ".[ui,dev]"
|
||||
# Install PraisonAIUI in development mode
|
||||
cd ../../../PraisonAIUI
|
||||
pip install -e .
|
||||
|
||||
- name: Verify versions
|
||||
run: |
|
||||
python -c "import praisonaiagents; print(f'praisonaiagents: {praisonaiagents.__version__}')"
|
||||
python -c "import praisonai; print(f'praisonai: {praisonai.__version__}')"
|
||||
python -c "import praisonaiui; print(f'praisonaiui: {praisonaiui.__version__}')"
|
||||
|
||||
- name: Run PraisonAI integration tests
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
python -m pytest tests/integration/test_aiui_* -v
|
||||
|
||||
- name: Run PraisonAIUI integration tests
|
||||
run: |
|
||||
cd PraisonAIUI
|
||||
if [ -f tests/integration/test_agentic_roundtrip.py ]; then
|
||||
python -m pytest tests/integration/test_agentic_roundtrip.py -v
|
||||
fi
|
||||
if [ -f tests/test_feature_sdk_backends.py ]; then
|
||||
python -m pytest tests/test_feature_sdk_backends.py -v
|
||||
fi
|
||||
|
||||
- name: Test Pattern C CLI
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
# Test that ui-gateway command exists
|
||||
python -m praisonai serve --help | grep ui-gateway
|
||||
|
||||
- name: Test public API exports
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
python -c "from praisonai import run_integrated_gateway; print('✓ run_integrated_gateway imported')"
|
||||
python -c "from praisonai import configure_host; print('✓ configure_host imported')"
|
||||
|
||||
- name: Integration smoke test
|
||||
env:
|
||||
PRAISONAI_TEST_MODE: "1"
|
||||
PYTHONPATH: ${{ github.workspace }}/PraisonAI/src/praisonai:${{ github.workspace }}/PraisonAI/src/praisonai-agents:${{ github.workspace }}/PraisonAIUI
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
python -c "
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
from praisonai.integration.host_app import configure_host
|
||||
from praisonai import run_integrated_gateway
|
||||
print('✓ Integration imports successful')
|
||||
|
||||
# Test configure_host with new parameters
|
||||
try:
|
||||
configure_host(
|
||||
style='dashboard',
|
||||
context_paths=['AGENTS.md'],
|
||||
title='Test App'
|
||||
)
|
||||
print('✓ configure_host with new parameters works')
|
||||
except Exception as e:
|
||||
print(f'× configure_host failed: {e}')
|
||||
sys.exit(1)
|
||||
"
|
||||
|
||||
# Optional job with API key for agentic tests (if secret is available)
|
||||
agentic-integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
if: ${{ vars.RUN_AGENTIC_TESTS == 'true' }}
|
||||
needs: cross-repo-integration
|
||||
|
||||
steps:
|
||||
- name: Checkout PraisonAI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: PraisonAI
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Checkout PraisonAIUI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: MervinPraison/PraisonAIUI
|
||||
path: PraisonAIUI
|
||||
ref: main
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
cd PraisonAI/src/praisonai-agents
|
||||
pip install -e .
|
||||
cd ../praisonai
|
||||
pip install -e ".[ui,dev]"
|
||||
cd ../../../PraisonAIUI
|
||||
pip install -e .
|
||||
|
||||
- name: Run agentic integration test
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
if [ -n "$OPENAI_API_KEY" ]; then
|
||||
python -m pytest tests/integration/test_aiui_host_agentic.py -v
|
||||
else
|
||||
echo "OPENAI_API_KEY not available, skipping agentic tests"
|
||||
fi
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Sync Claude Secrets to PraisonAIUI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync secrets to PraisonAIUI
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.SECRETS_ADMIN_PAT }}
|
||||
TARGET: MervinPraison/PraisonAIUI
|
||||
CLAUDE_APP_ID: ${{ secrets.CLAUDE_APP_ID }}
|
||||
CLAUDE_APP_PRIVATE_KEY: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
GH_TOKEN_VALUE: ${{ secrets.GH_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${GH_TOKEN:-}" ]; then
|
||||
echo "SECRETS_ADMIN_PAT is not configured in this repository"
|
||||
exit 1
|
||||
fi
|
||||
echo -n "$CLAUDE_APP_ID" | gh secret set CLAUDE_APP_ID --repo "$TARGET"
|
||||
echo -n "$CLAUDE_APP_PRIVATE_KEY" | gh secret set CLAUDE_APP_PRIVATE_KEY --repo "$TARGET"
|
||||
echo -n "$CLAUDE_CODE_OAUTH_TOKEN" | gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo "$TARGET"
|
||||
echo -n "$GH_TOKEN_VALUE" | gh secret set GH_TOKEN --repo "$TARGET"
|
||||
echo "Synced CLAUDE_APP_ID, CLAUDE_APP_PRIVATE_KEY, CLAUDE_CODE_OAUTH_TOKEN, GH_TOKEN → $TARGET"
|
||||
@@ -0,0 +1,10 @@
|
||||
# Agent Instructions
|
||||
|
||||
You are working on the PraisonAI project.
|
||||
|
||||
## Project Guidelines
|
||||
- Follow the existing code style and conventions
|
||||
- Be concise and helpful in responses
|
||||
- Test implementation thoroughly
|
||||
- Ensure backward compatibility with existing APIs
|
||||
- Follow protocol-driven design: core protocols in praisonaiagents/, heavy implementations in praisonai/
|
||||
@@ -0,0 +1,14 @@
|
||||
# Contributing to PraisonAI
|
||||
|
||||
Thank you for your interest! Here's how to get started:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Submit a pull request
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Follow existing code style
|
||||
- Add tests for new features
|
||||
- Update documentation as needed
|
||||
@@ -382,7 +382,7 @@ praisonai flow
|
||||
|
||||
Open **http://localhost:7861** — use the **Agent** and **Agent Team** components to create sequential or parallel workflows. Connect Chat Input → Agent Team → Chat Output for instant multi-agent pipelines.
|
||||
|
||||
> 📖 [Full Flow docs](https://docs.praison.ai/docs/concepts/flow) — visual agent building, component reference, and deployment
|
||||
> 📖 [Full Flow docs](https://docs.praison.ai/docs/concepts/agentflow) — visual agent building, component reference, and deployment
|
||||
|
||||
### 8. PraisonAI UI 🤖 (Clean Chat)
|
||||
|
||||
@@ -770,11 +770,11 @@ agent.start('Write a movie script about a robot in Mars');
|
||||
|
||||
## ⚡ Performance
|
||||
|
||||
PraisonAI is built for speed, with agent instantiation in under 4μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
|
||||
PraisonAI is built for speed, with agent instantiation in around 14μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
|
||||
|
||||
| Performance Metric | PraisonAI |
|
||||
|--------------------|-----------|
|
||||
| Avg Instantiation Time | **3.77 μs** |
|
||||
| Avg Instantiation Time | **14 μs** |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ Methods:
|
||||
* <code title="class GuardrailResult">GuardrailResult.<a href="./src/praisonai-agents/praisonaiagents/guardrails/guardrail_result.py">from_tuple</a>(result: Tuple[bool, Any]) -> 'GuardrailResult'</code>
|
||||
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_dict</a>(data: Dict[str, Any]) -> 'HandoffConfig'</code>
|
||||
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_dict</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class HandoffResult">HandoffResult.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_outcome</a>(outcome: AgentRunOutcome, target_agent: Optional[str] = None, source_agent: Optional[str] = None, handoff_depth: int = 0) -> 'HandoffResult'</code>
|
||||
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">json</a>() -> Optional[str]</code>
|
||||
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">to_dict</a>() -> dict</code>
|
||||
* <code title="class ToolResult">ToolResult.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">to_dict</a>() -> Dict[str, Any]</code>
|
||||
@@ -40,6 +41,7 @@ Methods:
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">chat_with_context</a>(message: str, context: 'ContextPack', **kwargs) -> str</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">checkpoints</a>() -> Optional[bool]</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">checkpoints</a>(value: Optional[bool]) -> None</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">clone_for_channel</a>() -> 'Agent'</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">close</a>() -> None</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">console</a>() -> Optional[Any]</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">context_manager</a>() -> Optional[Any]</code>
|
||||
@@ -57,6 +59,8 @@ Methods:
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to_async</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">is_closed</a>() -> bool</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_instance</a>()</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_instance</a>(value)</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_model</a>() -> Optional[str]</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>() -> Optional[str]</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>(value: Optional[str]) -> None</code>
|
||||
@@ -74,7 +78,7 @@ Methods:
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until_async</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">skill_manager</a>() -> Optional[Any]</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', **kwargs: Any) -> None</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', action: str = 'add', **kwargs: Any) -> None</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>() -> Optional[Any]</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>(value: Optional[Any]) -> None</code>
|
||||
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">thinking_budget</a>() -> Optional[int]</code>
|
||||
@@ -171,8 +175,10 @@ Methods:
|
||||
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">safe_run</a>(**kwargs) -> ToolResult</code>
|
||||
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate</a>() -> bool</code>
|
||||
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_class</a>() -> bool</code>
|
||||
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_schema_roundtrip</a>() -> bool</code>
|
||||
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">__call__</a>(*args, **kwargs) -> Any</code>
|
||||
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">check_availability</a>() -> tuple[bool, str]</code>
|
||||
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">get_schema</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">injected_params</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">run</a>(**kwargs) -> Any</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">clear</a>() -> None</code>
|
||||
@@ -180,16 +186,18 @@ Methods:
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">discover_single_file_plugins</a>() -> int</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_all</a>() -> Dict[str, Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None) -> List[Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool_definitions</a>() -> List[Dict[str, Any]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_trust_level</a>(name: str) -> Optional[str]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None, ttl_seconds: Optional[float] = None) -> List[Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_base_tools</a>() -> List[BaseTool]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools</a>() -> List[str]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools_with_allowed_filter</a>(context: Optional[Dict[str, Any]] = None) -> List[str]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False) -> None</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">unregister</a>(name: str) -> bool</code>
|
||||
* <code title="class Tools">Tools.<a href="./src/praisonai-agents/praisonaiagents/tools/tools.py">internet_search</a>(*args, **kwargs)</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_registry</a>() -> ToolRegistry</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None) -> None</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">tool</a>(func: Optional[Callable] = None) -> Union[FunctionTool, Callable[[Callable], FunctionTool]]</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_tool</a>(tool: Any) -> bool</code>
|
||||
|
||||
@@ -234,6 +242,7 @@ from praisonaiagents import Memory, MemoryBackend, MemoryConfig
|
||||
|
||||
Methods:
|
||||
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_cache_optimized_context</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_cache_boundary: bool = True, include_in_output: Optional[bool] = None) -> Dict[str, str]</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_context_for_task</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_in_output: Optional[bool] = None) -> str</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">calculate_quality_metrics</a>(output: str, expected_output: str, llm: Optional[str] = None, custom_prompt: Optional[str] = None) -> Dict[str, float]</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">close_connections</a>()</code>
|
||||
@@ -351,7 +360,7 @@ Methods:
|
||||
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_tool_function</a>(source_agent: 'Agent') -> Callable</code>
|
||||
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_description</a>() -> str</code>
|
||||
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_name</a>() -> str</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None) -> Handoff</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None, tool_policy_mode: Optional[Literal['intersect', 'passthrough']] = None, blocked_tools: Optional[List[str]] = None) -> Handoff</code>
|
||||
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">compress_history</a>(data: HandoffInputData) -> HandoffInputData</code>
|
||||
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">keep_last_n_messages</a>(n: int) -> Callable[[HandoffInputData], HandoffInputData]</code>
|
||||
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">remove_all_tools</a>(data: HandoffInputData) -> HandoffInputData</code>
|
||||
@@ -477,8 +486,10 @@ Methods:
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">edit_skill</a>(name: str, content: str) -> dict</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_allowed_tools</a>(name: str) -> List[str]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_available_skills</a>() -> List[SkillMetadata]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_available_skills_by_state</a>(state: SkillState) -> List[LoadedSkill]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_instructions</a>(name: str) -> Optional[str]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_skill</a>(name: str) -> Optional[LoadedSkill]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_skills_diagnostics</a>() -> Dict[str, ValidationResult]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_user_invocable_skills</a>() -> List[LoadedSkill]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">invoke</a>(name: str, raw_args: str = '', session_id: Optional[str] = None, shell_exec: bool = False) -> Optional[str]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">load_resources</a>(name: str) -> bool</code>
|
||||
@@ -487,6 +498,7 @@ Methods:
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">skill_names</a>() -> List[str]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">skills</a>() -> List[LoadedSkill]</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">to_prompt</a>() -> str</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">validate_skill_capabilities</a>(skill_name: str, force_refresh: bool = False) -> ValidationResult</code>
|
||||
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">write_skill_file</a>(name: str, file_path: str, file_content: str) -> dict</code>
|
||||
* <code title="class SkillMetadata">SkillMetadata.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">from_properties</a>(props: SkillProperties) -> 'SkillMetadata'</code>
|
||||
* <code title="class SkillProperties">SkillProperties.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">to_dict</a>() -> dict</code>
|
||||
@@ -602,6 +614,13 @@ Methods:
|
||||
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">set_tool_budget</a>(tool_name: str, max_tokens: int, protected: bool = False) -> None</code>
|
||||
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">truncate_tool_output</a>(tool_name: str, output: str) -> str</code>
|
||||
|
||||
# UI
|
||||
|
||||
Types:
|
||||
```python
|
||||
from praisonaiagents import A2UI
|
||||
```
|
||||
|
||||
# Config
|
||||
|
||||
Types:
|
||||
@@ -614,6 +633,7 @@ Methods:
|
||||
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">effective_track_changes</a>() -> bool</code>
|
||||
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">from_dict</a>(data: Dict[str, Any]) -> 'AutonomyConfig'</code>
|
||||
* <code title="class CachingConfig">CachingConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">from_dict</a>(data: Dict[str, Any]) -> 'ExecutionConfig'</code>
|
||||
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class GuardrailConfig">GuardrailConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class HooksConfig">HooksConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
|
||||
@@ -681,7 +701,7 @@ Methods:
|
||||
|
||||
Types:
|
||||
```python
|
||||
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, aembedding, aembeddings, embedding, embeddings, get_dimensions
|
||||
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, __version__, aembedding, aembeddings, embedding, embeddings, get_dimensions
|
||||
```
|
||||
|
||||
Methods:
|
||||
@@ -706,12 +726,16 @@ Methods:
|
||||
* <code title="class AgentFlow">AgentFlow.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">verbose</a>(value: bool)</code>
|
||||
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">get_app</a>() -> Any</code>
|
||||
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">serve</a>(host: Optional[str] = None, port: Optional[int] = None, reload: bool = False, **kwargs: Any) -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aannounce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">add_task</a>(task)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aexecute_task</a>(task_id)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">announce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">append_to_state</a>(key: str, value: Any, max_length: Optional[int] = None) -> List[Any]</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_all_tasks</a>()</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_task</a>(task_id)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aspawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">astart</a>(content = None, return_dict = False, **kwargs)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">await_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clean_json_output</a>(output: str) -> str</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clear_state</a>() -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">close</a>() -> None</code>
|
||||
@@ -726,6 +750,7 @@ Methods:
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_all_tasks_status</a>()</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_detailed_token_report</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_plan_markdown</a>() -> str</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_spawned_agents</a>() -> List[SpawnedSubAgent]</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_state</a>(key: str, default: Any = None) -> Any</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_details</a>(task_id)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_result</a>(task_id)</code>
|
||||
@@ -742,10 +767,12 @@ Methods:
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_output_to_file</a>(task, task_output)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_session_state</a>(session_id: str, include_memory: bool = True) -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">set_state</a>(key: str, value: Any) -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">spawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">start</a>(content = None, return_dict = False, output = None, **kwargs)</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">todo_list</a>()</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_plan_step_status</a>(step_id: str, status: str) -> bool</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_state</a>(updates: Dict) -> None</code>
|
||||
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">wait_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
|
||||
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval</a>(request: ApprovalRequest) -> ApprovalDecision</code>
|
||||
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval_sync</a>(request: ApprovalRequest) -> ApprovalDecision</code>
|
||||
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/embedding/embed.py">aembedding</a>(input: Union[str, List[str]], model: str = 'text-embedding-3-small', dimensions: Optional[int] = None, encoding_format: str = 'float', timeout: float = 600.0, api_key: Optional[str] = None, api_base: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs) -> EmbeddingResult</code>
|
||||
@@ -756,7 +783,7 @@ Methods:
|
||||
|
||||
Types:
|
||||
```python
|
||||
from praisonai import Agent, AgentApp, AgentOS, AnthropicManagedAgent, CloudProvider, DB, Deploy, DeployConfig, DeployType, HostedAgent, HostedAgentConfig, LocalAgent, LocalAgentConfig, LocalManagedAgent, LocalManagedConfig, ManagedAgent, ManagedConfig, PraisonAI, __version__
|
||||
from praisonai import Agent, AgentApp, AgentOS, AnthropicManagedAgent, CloudProvider, DB, Deploy, DeployConfig, DeployType, HostedAgent, HostedAgentConfig, LocalAgent, LocalAgentConfig, LocalManagedAgent, LocalManagedConfig, ManagedAgent, ManagedConfig, PraisonAI, __version__, arun, run, run_integrated_gateway
|
||||
```
|
||||
|
||||
# CLI
|
||||
@@ -861,6 +888,7 @@ Methods:
|
||||
* <code title="cli">praisonai doctor packaging <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
|
||||
* <code title="cli">praisonai doctor performance <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
|
||||
* <code title="cli">praisonai doctor selftest <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
|
||||
* <code title="cli">praisonai doctor skills <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
|
||||
* <code title="cli">praisonai doctor tools <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
|
||||
* <code title="cli">praisonai doctor troubleshoot <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
|
||||
* <code title="cli">praisonai endpoints list <a href="./src/praisonai/praisonai/cli/commands/endpoints.py">--help</a></code>
|
||||
@@ -889,14 +917,30 @@ Methods:
|
||||
* <code title="cli">praisonai gateway install <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway logs <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway mint-link <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway pause <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway reconnect <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway resume <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway send <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway start <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway status <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway stop <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai gateway uninstall <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
|
||||
* <code title="cli">praisonai github triage <a href="./src/praisonai/praisonai/cli/commands/github.py">--help</a></code>
|
||||
* <code title="cli">praisonai hooks add <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
|
||||
* <code title="cli">praisonai hooks list <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
|
||||
* <code title="cli">praisonai hooks remove <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban block <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban boards <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban comment <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban complete <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban create <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban dispatch <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban link <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban list <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban move <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban reclaim <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban show <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai kanban unblock <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
|
||||
* <code title="cli">praisonai knowledge add <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
|
||||
* <code title="cli">praisonai knowledge index <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
|
||||
* <code title="cli">praisonai knowledge list <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
|
||||
@@ -1054,6 +1098,7 @@ Methods:
|
||||
* <code title="cli">praisonai serve status <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
|
||||
* <code title="cli">praisonai serve stop <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
|
||||
* <code title="cli">praisonai serve ui <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
|
||||
* <code title="cli">praisonai serve ui-gateway <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
|
||||
* <code title="cli">praisonai serve unified <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
|
||||
* <code title="cli">praisonai session delete <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
|
||||
* <code title="cli">praisonai session export <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
|
||||
@@ -1263,7 +1308,7 @@ export { // Python parity additions
|
||||
export { // Subagent Tool (agent-as-tool pattern)
|
||||
SubagentTool, BaseTool, FunctionTool, ToolRegistry, ToolResult, ToolValidationError, createDelegator, createSubagentTool, createSubagentTools, createTool, getRegistry, getTool, registerTool, tool, validateTool } from "./tools";
|
||||
export { airweaveSearch, bedrockBrowserClick, bedrockBrowserFill, bedrockBrowserNavigate, bedrockCodeInterpreter, codeExecution, codeMode, createCustomTool, exaSearch, firecrawlCrawl, firecrawlScrape, parallelSearch, perplexitySearch, registerCustomTool, registerLocalTool, registerNpmTool, superagentGuard, superagentRedact, superagentVerify, tavilyCrawl, tavilyExtract, tavilySearch, valyuBioSearch, valyuCompanyResearch, valyuEconomicsSearch, valyuFinanceSearch, valyuPaperSearch, valyuPatentSearch, valyuSecSearch, valyuWebSearch } from "./tools/builtins";
|
||||
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getRegistry, getTool, getToolsRegistry, get_registry, get_tool, registerTool, register_tool, resetToolsRegistry, validateTool, validate_tool } from "./tools/registry";
|
||||
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getToolsRegistry, get_registry, get_tool, register_tool, resetToolsRegistry, validate_tool } from "./tools/registry";
|
||||
export type { InstallHints, PraisonTool, RedactionHooks, RegisteredTool, ToolCapabilities, ToolExecutionContext, ToolExecutionResult, ToolFactory, ToolHooks, ToolInstallStatus, ToolLimits, ToolLogger, ToolMetadata, ToolMiddleware, ToolParameterProperty, ToolParameterSchema } from "./tools/registry";
|
||||
export { registerBuiltinTools, tools } from "./tools/tools";
|
||||
export { // Classes
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.43" \
|
||||
"praisonai>=4.6.62" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.43" \
|
||||
"praisonai>=4.6.62" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.43" \
|
||||
"praisonai>=4.6.62" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ def run_quick_comparison():
|
||||
prompt="What is 2+2?",
|
||||
iterations=2,
|
||||
paths=["openai_sdk", "praisonai_agent"],
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Print comparison table
|
||||
@@ -56,7 +56,7 @@ def run_full_benchmark():
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="Hi",
|
||||
iterations=3,
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Print full report
|
||||
@@ -78,7 +78,7 @@ def run_agent_benchmark():
|
||||
prompt="Explain Python in one sentence",
|
||||
iterations=3,
|
||||
paths=["openai_sdk", "praisonai_agent"],
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Show timeline diagrams
|
||||
|
||||
@@ -22,7 +22,7 @@ custom_modes = {
|
||||
}
|
||||
|
||||
# Create handler with verbose output
|
||||
handler = CompareHandler(output="verbose")
|
||||
handler = CompareHandler(verbose=True)
|
||||
|
||||
# Example: Manually create comparison results for analysis
|
||||
# (useful for testing without API calls)
|
||||
|
||||
@@ -22,7 +22,7 @@ modes = parse_modes("basic,tools,planning")
|
||||
print(f"\nParsed modes: {modes}")
|
||||
|
||||
# Create compare handler
|
||||
handler = CompareHandler(output="verbose")
|
||||
handler = CompareHandler(verbose=True)
|
||||
|
||||
# Compare modes (this will run actual agent calls)
|
||||
# Uncomment to run:
|
||||
|
||||
@@ -15,7 +15,7 @@ agent = Agent(
|
||||
# With array [preset, overrides]
|
||||
agent_override = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory=["redis", {"user_id": "custom_user"}],
|
||||
memory=MemoryConfig(backend="redis", user_id="custom_user"),
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -32,7 +32,7 @@ workflow = AgentFlow(
|
||||
),
|
||||
],
|
||||
# Workflow-level consolidated params
|
||||
output=WorkflowOutputConfig(output="verbose", stream=True),
|
||||
output=WorkflowOutputConfig(verbose=True, stream=True),
|
||||
planning=WorkflowPlanningConfig(enabled=True, reasoning=True),
|
||||
memory=WorkflowMemoryConfig(backend="file"),
|
||||
# Agent-like params
|
||||
|
||||
@@ -5,12 +5,12 @@ Demonstrates web search with consolidated params.
|
||||
Presets: duckduckgo, tavily, google, bing, serper, search_only, fetch_only
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents import Agent, WebConfig
|
||||
|
||||
# Basic: Enable web search with preset
|
||||
# Basic: Enable web search with preset (search only — fetch needs Claude)
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant with web access.",
|
||||
web="duckduckgo", # Presets: duckduckgo, tavily, google, bing, serper
|
||||
web=WebConfig(search=True, fetch=False, search_provider="duckduckgo"),
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -15,8 +15,20 @@ workflow = AgentFlow(
|
||||
],
|
||||
output="verbose",
|
||||
planning=True,
|
||||
variables={"topic": "AI agents"},
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = workflow.run(variables={"topic": "AI agents"})
|
||||
import os
|
||||
import sys
|
||||
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Set OPENAI_API_KEY to run this example.")
|
||||
sys.exit(0)
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
result = workflow.run("AI agents")
|
||||
print(result)
|
||||
@@ -24,7 +24,7 @@ if has_api_key:
|
||||
input_text="What is 2 + 2?",
|
||||
expected_output="4",
|
||||
num_iterations=3, # Run 3 times for statistical significance
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run evaluation
|
||||
@@ -42,7 +42,7 @@ else:
|
||||
input_text="What is 2 + 2?",
|
||||
expected_output="4",
|
||||
num_iterations=1,
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Test with mock output
|
||||
|
||||
@@ -27,7 +27,7 @@ if has_api_key:
|
||||
scoring_type="numeric", # Score 1-10
|
||||
threshold=7.0, # Pass if score >= 7
|
||||
num_iterations=2,
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run evaluation
|
||||
@@ -45,7 +45,7 @@ if has_api_key:
|
||||
agent=agent,
|
||||
input_text="Tell me a joke",
|
||||
scoring_type="binary", # Pass or Fail
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
binary_result = binary_evaluator.run(print_summary=True)
|
||||
|
||||
@@ -26,7 +26,7 @@ if has_api_key:
|
||||
num_iterations=10, # Run 10 benchmark iterations
|
||||
warmup_runs=2, # 2 warmup runs before measurement
|
||||
track_memory=True, # Track memory usage
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run evaluation
|
||||
|
||||
@@ -16,8 +16,19 @@ def search_web(query: str) -> str:
|
||||
|
||||
|
||||
def calculate(expression: str) -> str:
|
||||
"""Calculate a math expression."""
|
||||
return str(eval(expression))
|
||||
"""Calculate a math expression safely."""
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
return str(_ev(ast.parse(expression, mode="eval")))
|
||||
|
||||
|
||||
# Check if we have an API key
|
||||
@@ -37,7 +48,7 @@ if has_api_key:
|
||||
input_text="Search for the weather in Paris and calculate 25 * 4",
|
||||
expected_tools=["search_web", "calculate"], # Tools that should be called
|
||||
forbidden_tools=["delete_file"], # Tools that should NOT be called
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run evaluation
|
||||
@@ -59,7 +70,7 @@ evaluator = ReliabilityEvaluator(
|
||||
agent=mock_agent,
|
||||
expected_tools=["search_web", "calculate"], # Tools that should be called
|
||||
forbidden_tools=["delete_file"], # Tools that should NOT be called
|
||||
output="verbose"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
result2 = evaluator.evaluate_tool_calls(
|
||||
|
||||
@@ -184,7 +184,7 @@ Answer questions based ONLY on the provided knowledge context.
|
||||
When asked about codes, provide the EXACT code from the documents.
|
||||
Be concise but accurate.""",
|
||||
knowledge=[temp_dir],
|
||||
user_id="compression_demo_user",
|
||||
memory={"user_id": "compression_demo_user"},
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ Answer questions based ONLY on the provided knowledge context.
|
||||
When asked about department codes, provide the EXACT code from the documents.
|
||||
Be specific about which department you're referring to.""",
|
||||
knowledge=[temp_dir],
|
||||
user_id="hierarchy_demo_user",
|
||||
memory={"user_id": "hierarchy_demo_user"},
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ def main():
|
||||
# Show strategy selection for different corpus sizes
|
||||
print(f"\nStrategy selection by corpus size:")
|
||||
for size in [100, 1000, 10000, 50000, 100000]:
|
||||
strategy = select_strategy(corpus_tokens=size)
|
||||
strategy = select_strategy(CorpusStats(file_count=max(1, size // 1000), total_tokens=size))
|
||||
print(f" {size:>6} tokens -> {strategy.value}")
|
||||
|
||||
except ImportError as e:
|
||||
@@ -142,7 +142,7 @@ Answer questions based ONLY on the provided knowledge context.
|
||||
When asked about verification codes, provide the EXACT code from the documents.
|
||||
If information is not in the context, say 'Information not found in documents.'""",
|
||||
knowledge=[temp_dir],
|
||||
user_id="strategy_demo_user",
|
||||
memory={"user_id": "strategy_demo_user"},
|
||||
output="verbose", # Use new consolidated param
|
||||
)
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ def main():
|
||||
name="PolicyExpert",
|
||||
instructions="You are a company policy expert. Answer questions based ONLY on the provided knowledge. Quote exact values when asked about codes or numbers.",
|
||||
knowledge=[temp_dir], # Add directory with documents
|
||||
user_id="example_user", # Required for mem0 backend
|
||||
memory={"user_id": "example_user"},
|
||||
)
|
||||
|
||||
print("\n1. Testing Agent.chat() with context-required question:")
|
||||
|
||||
@@ -50,7 +50,7 @@ def main():
|
||||
name="HR_Assistant_User1",
|
||||
instructions="You are an HR assistant.",
|
||||
knowledge=[temp_dir],
|
||||
user_id="user_alice", # Knowledge scoped to Alice
|
||||
memory={"user_id": "user_alice"},
|
||||
)
|
||||
|
||||
response = user1_agent.chat("What is the policy on remote work?")
|
||||
@@ -80,7 +80,7 @@ def main():
|
||||
name="Personal_HR_Bot",
|
||||
instructions="You are a personal HR assistant.",
|
||||
knowledge=[temp_dir],
|
||||
user_id="user_bob",
|
||||
memory={"user_id": "user_bob"},
|
||||
agent_id="hr_bot_v2",
|
||||
)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ Only authorized personnel with code WRENCH-992 may access this project.
|
||||
name="AgentA_Ingester",
|
||||
instructions="You ingest and store knowledge.",
|
||||
knowledge=[temp_dir],
|
||||
user_id="user_alice", # Scoped to Alice
|
||||
memory={"user_id": "user_alice"},
|
||||
output="silent",
|
||||
)
|
||||
|
||||
@@ -103,7 +103,7 @@ Only authorized personnel with code WRENCH-992 may access this project.
|
||||
name="AgentB_Querier",
|
||||
instructions="Answer questions based on the provided knowledge. If you don't have the information, say so.",
|
||||
knowledge=[temp_dir],
|
||||
user_id="user_alice", # Same scope as Agent A
|
||||
memory={"user_id": "user_alice"},
|
||||
output="silent",
|
||||
)
|
||||
|
||||
|
||||
@@ -19,20 +19,43 @@ import os
|
||||
|
||||
def calculate(expression: str) -> str:
|
||||
"""
|
||||
A simple calculator tool that evaluates mathematical expressions.
|
||||
|
||||
A simple calculator tool that evaluates mathematical expressions safely.
|
||||
|
||||
Args:
|
||||
expression: A mathematical expression to evaluate (e.g., "2 + 2")
|
||||
|
||||
|
||||
Returns:
|
||||
The result of the calculation
|
||||
"""
|
||||
import ast
|
||||
import operator
|
||||
|
||||
_OPS = {
|
||||
ast.Add: operator.add,
|
||||
ast.Sub: operator.sub,
|
||||
ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv,
|
||||
ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod,
|
||||
ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg,
|
||||
ast.UAdd: operator.pos,
|
||||
}
|
||||
|
||||
def _safe_eval(node):
|
||||
if isinstance(node, ast.Expression):
|
||||
return _safe_eval(node.body)
|
||||
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
|
||||
return node.value
|
||||
if isinstance(node, ast.UnaryOp) and type(node.op) in _OPS:
|
||||
return _OPS[type(node.op)](_safe_eval(node.operand))
|
||||
if isinstance(node, ast.BinOp) and type(node.op) in _OPS:
|
||||
return _OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
|
||||
raise ValueError(f"Unsupported expression: {ast.dump(node)}")
|
||||
|
||||
try:
|
||||
# Safe evaluation of mathematical expressions
|
||||
allowed_chars = set("0123456789+-*/.() ")
|
||||
if not all(c in allowed_chars for c in expression):
|
||||
return "Error: Invalid characters in expression"
|
||||
result = eval(expression)
|
||||
tree = ast.parse(expression, mode="eval")
|
||||
result = _safe_eval(tree)
|
||||
return f"Result: {result}"
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
@@ -24,12 +24,18 @@ def main():
|
||||
def calculate(expression: str) -> str:
|
||||
"""Safely evaluate a mathematical expression."""
|
||||
try:
|
||||
# Only allow safe math operations
|
||||
allowed = set("0123456789+-*/.(). ")
|
||||
if not all(c in allowed for c in expression):
|
||||
return "Error: Invalid characters in expression"
|
||||
result = eval(expression) # Safe due to character filtering
|
||||
return f"Result: {result}"
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
return f"Result: {_ev(ast.parse(expression, mode='eval'))}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
|
||||
@@ -50,10 +50,10 @@ print("=== Qdrant Knowledge Store Demo (Agent-First) ===")
|
||||
agent = Agent(
|
||||
name="KnowledgeAssistant",
|
||||
instructions="You are a helpful assistant with access to technical documentation.",
|
||||
knowledge=[doc_path],
|
||||
knowledge={
|
||||
"sources": [doc_path],
|
||||
"vector_store": "qdrant",
|
||||
"url": "http://localhost:6333"
|
||||
"url": "http://localhost:6333",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -21,9 +21,36 @@ def search_web(query: str) -> str:
|
||||
return f"Search results for: {query}"
|
||||
|
||||
def calculate(expression: str) -> str:
|
||||
"""Calculate a mathematical expression."""
|
||||
"""Calculate a mathematical expression safely."""
|
||||
import ast
|
||||
import operator
|
||||
|
||||
_OPS = {
|
||||
ast.Add: operator.add,
|
||||
ast.Sub: operator.sub,
|
||||
ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv,
|
||||
ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod,
|
||||
ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg,
|
||||
ast.UAdd: operator.pos,
|
||||
}
|
||||
|
||||
def _eval(node):
|
||||
if isinstance(node, ast.Expression):
|
||||
return _eval(node.body)
|
||||
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
|
||||
return node.value
|
||||
if isinstance(node, ast.UnaryOp) and type(node.op) in _OPS:
|
||||
return _OPS[type(node.op)](_eval(node.operand))
|
||||
if isinstance(node, ast.BinOp) and type(node.op) in _OPS:
|
||||
return _OPS[type(node.op)](_eval(node.left), _eval(node.right))
|
||||
raise ValueError(f"Unsupported expression: {ast.dump(node)}")
|
||||
|
||||
try:
|
||||
return f"Result: {eval(expression)}"
|
||||
tree = ast.parse(expression, mode="eval")
|
||||
return f"Result: {_eval(tree)}"
|
||||
except Exception:
|
||||
return "Invalid expression"
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Minimal A2UI example — agent + send_a2ui_messages tool.
|
||||
|
||||
Install:
|
||||
pip install praisonaiagents[a2ui]
|
||||
|
||||
Run:
|
||||
python send_surface.py
|
||||
|
||||
For live rendering, use PraisonAIUI with AG-UI (/agui) — see integrate-a2ui-frontend docs.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.tools.a2ui_tools import send_a2ui_messages
|
||||
|
||||
agent = Agent(
|
||||
name="ui-assistant",
|
||||
instructions=(
|
||||
"When asked for UI, call send_a2ui_messages with A2UI v0.9 JSON "
|
||||
"(createSurface on surface id 'main')."
|
||||
),
|
||||
tools=[send_a2ui_messages],
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = agent.start("Show a simple welcome card on surface main")
|
||||
print(result)
|
||||
@@ -6,10 +6,18 @@ It includes self-reflection for improving code quality and multiple specialized
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import CodeInterpreter
|
||||
|
||||
# Initialize code execution tool
|
||||
code_interpreter = CodeInterpreter()
|
||||
def run_python_code(code: str) -> str:
|
||||
"""Execute Python code safely and return stdout or errors."""
|
||||
import io
|
||||
import contextlib
|
||||
try:
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf):
|
||||
exec(code, {"__builtins__": __builtins__})
|
||||
return buf.getvalue() or "Executed successfully (no output)"
|
||||
except Exception as e:
|
||||
return f"Execution error: {e}"
|
||||
|
||||
# Custom code analysis tool
|
||||
def analyze_code_complexity(code: str) -> str:
|
||||
@@ -32,9 +40,7 @@ code_writer = Agent(
|
||||
goal="Write clean, efficient, and well-documented Python code",
|
||||
backstory="You are a senior Python developer with 10+ years of experience in writing production-quality code.",
|
||||
instructions="Write Python code that follows PEP 8 standards, includes proper error handling, and has clear documentation.",
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
code_reviewer = Agent(
|
||||
@@ -52,7 +58,7 @@ code_executor = Agent(
|
||||
goal="Safely execute and test Python code",
|
||||
backstory="You are responsible for running code and reporting results or errors.",
|
||||
instructions="Execute the provided code safely and report the output or any errors encountered.",
|
||||
tools=[code_interpreter]
|
||||
tools=[run_python_code]
|
||||
)
|
||||
|
||||
# Example tasks
|
||||
@@ -87,7 +93,7 @@ def demonstrate_code_agent():
|
||||
workflow = AgentTeam(
|
||||
agents=[code_writer, code_reviewer, code_executor],
|
||||
tasks=[write_task, review_task, execute_task],
|
||||
process="sequential", output="verbose"
|
||||
process="sequential", output="silent"
|
||||
)
|
||||
|
||||
# Run the workflow
|
||||
@@ -106,10 +112,8 @@ general_code_agent = Agent(
|
||||
3. Explain code concepts
|
||||
4. Refactor code for better performance
|
||||
5. Add tests and documentation""",
|
||||
tools=[code_interpreter, analyze_code_complexity],
|
||||
reflection=True,
|
||||
|
||||
|
||||
tools=[run_python_code, analyze_code_complexity],
|
||||
output="silent",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -41,21 +41,25 @@ def calculate_statistics(numbers: list) -> str:
|
||||
"""Calculate basic statistics for a list of numbers."""
|
||||
if not numbers:
|
||||
return "No numbers provided"
|
||||
|
||||
n = len(numbers)
|
||||
mean = sum(numbers) / n
|
||||
sorted_nums = sorted(numbers)
|
||||
try:
|
||||
nums = [float(x) for x in numbers]
|
||||
except (TypeError, ValueError):
|
||||
return "Error: all values must be numbers"
|
||||
|
||||
n = len(nums)
|
||||
mean = sum(nums) / n
|
||||
sorted_nums = sorted(nums)
|
||||
median = sorted_nums[n//2] if n % 2 else (sorted_nums[n//2-1] + sorted_nums[n//2]) / 2
|
||||
variance = sum((x - mean)**2 for x in numbers) / n
|
||||
variance = sum((x - mean)**2 for x in nums) / n
|
||||
std_dev = math.sqrt(variance)
|
||||
|
||||
|
||||
return f"""Statistics:
|
||||
- Count: {n}
|
||||
- Mean: {mean:.4f}
|
||||
- Median: {median:.4f}
|
||||
- Standard Deviation: {std_dev:.4f}
|
||||
- Min: {min(numbers)}
|
||||
- Max: {max(numbers)}"""
|
||||
- Min: {min(nums)}
|
||||
- Max: {max(nums)}"""
|
||||
|
||||
# Create specialized math agents
|
||||
calculation_agent = Agent(
|
||||
@@ -66,8 +70,7 @@ calculation_agent = Agent(
|
||||
instructions="Perform the requested calculations step by step, showing your work clearly.",
|
||||
tools=[basic_calculator, solve_quadratic, calculate_statistics],
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
problem_solver_agent = Agent(
|
||||
@@ -82,8 +85,7 @@ problem_solver_agent = Agent(
|
||||
4. Show each step of the solution
|
||||
5. Verify the answer makes sense""",
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
math_tutor_agent = Agent(
|
||||
@@ -91,7 +93,8 @@ math_tutor_agent = Agent(
|
||||
role="Mathematics tutor and explainer",
|
||||
goal="Explain mathematical concepts clearly and help users understand math",
|
||||
backstory="You are a patient math tutor who can explain concepts at any level from elementary to advanced.",
|
||||
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding."
|
||||
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding.",
|
||||
output="silent",
|
||||
)
|
||||
|
||||
# Example workflow for solving complex problems
|
||||
@@ -126,7 +129,8 @@ def solve_math_problem_workflow():
|
||||
workflow = AgentTeam(
|
||||
agents=[problem_solver_agent, calculation_agent, math_tutor_agent],
|
||||
tasks=[understand_task, solve_task, verify_task],
|
||||
process="sequential", output="verbose"
|
||||
process="sequential",
|
||||
output="silent",
|
||||
)
|
||||
|
||||
return workflow.start()
|
||||
@@ -148,8 +152,7 @@ math_assistant = Agent(
|
||||
Always show your work step by step.""",
|
||||
tools=[basic_calculator, solve_quadratic, calculate_statistics],
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example: Bot In-Flight Run Control
|
||||
|
||||
Demonstrates how to use SessionRunControl to provide better UX for bots
|
||||
during long-running agent operations. Shows busy feedback, pending message
|
||||
handling, and /stop command support.
|
||||
|
||||
This example shows the "before" and "after" behavior described in issue #1914.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
# Mock agent for demonstration
|
||||
class MockAgent:
|
||||
"""Simple mock agent that simulates long-running tasks."""
|
||||
|
||||
def __init__(self, name="assistant"):
|
||||
self.name = name
|
||||
self.chat_history = []
|
||||
self._interrupt_controller = None
|
||||
|
||||
def chat(self, prompt: str) -> str:
|
||||
"""Simulate a long-running chat response."""
|
||||
print(f"[Agent] Starting to process: '{prompt[:50]}...'")
|
||||
|
||||
# Simulate long processing with interrupt checking
|
||||
for i in range(30): # 3 second task (30 x 0.1s)
|
||||
time.sleep(0.1)
|
||||
|
||||
# Check for interruption
|
||||
if (self._interrupt_controller and
|
||||
hasattr(self._interrupt_controller, 'is_set') and
|
||||
self._interrupt_controller.is_set()):
|
||||
reason = getattr(self._interrupt_controller, 'reason', 'unknown')
|
||||
print(f"[Agent] ⚠️ Interrupted: {reason}")
|
||||
raise InterruptedError(f"Task cancelled: {reason}")
|
||||
|
||||
response = f"Completed analysis of: {prompt}"
|
||||
print(f"[Agent] ✅ Finished: {response}")
|
||||
return response
|
||||
|
||||
|
||||
async def demo_without_run_control():
|
||||
"""Demonstrate the old behavior (silent blocking)."""
|
||||
print("\n" + "="*60)
|
||||
print("🔴 BEFORE: Without Run Control (Silent Blocking)")
|
||||
print("="*60)
|
||||
|
||||
from praisonai.bots._session import BotSessionManager
|
||||
|
||||
# Standard session manager (no run control)
|
||||
session_mgr = BotSessionManager()
|
||||
agent = MockAgent("research-agent")
|
||||
user_id = "user123"
|
||||
|
||||
print("\n1. User sends long task:")
|
||||
print(" User: 'research quantum computing trends'")
|
||||
|
||||
# Start first task (this will run for 3 seconds)
|
||||
task1 = asyncio.create_task(
|
||||
session_mgr.chat(agent, user_id, "research quantum computing trends")
|
||||
)
|
||||
|
||||
# Simulate user sending follow-up messages during execution
|
||||
await asyncio.sleep(0.5)
|
||||
print("\n2. User sends follow-up (this will block silently):")
|
||||
print(" User: 'actually focus on business impact'")
|
||||
|
||||
task2 = asyncio.create_task(
|
||||
session_mgr.chat(agent, user_id, "actually focus on business impact")
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
print("\n3. User tries to send /stop (this will also block):")
|
||||
print(" User: '/stop'")
|
||||
|
||||
task3 = asyncio.create_task(
|
||||
session_mgr.chat(agent, user_id, "/stop")
|
||||
)
|
||||
|
||||
print("\n⏳ Waiting for all messages to complete...")
|
||||
results = await asyncio.gather(task1, task2, task3)
|
||||
|
||||
print("\n📊 Results (all processed sequentially with no feedback):")
|
||||
for i, result in enumerate(results, 1):
|
||||
print(f" {i}. {result}")
|
||||
|
||||
print("\n❌ Problems with this approach:")
|
||||
print(" - No immediate feedback on follow-up messages")
|
||||
print(" - No way to cancel long-running tasks")
|
||||
print(" - User doesn't know if bot is working or broken")
|
||||
|
||||
|
||||
async def demo_with_run_control():
|
||||
"""Demonstrate the new behavior with run control."""
|
||||
print("\n" + "="*60)
|
||||
print("🟢 AFTER: With Run Control (Better UX)")
|
||||
print("="*60)
|
||||
|
||||
from praisonai.bots._run_control import SessionRunControl
|
||||
from praisonai.bots._session import BotSessionManager
|
||||
from praisonai.bots._commands import handle_stop_command
|
||||
|
||||
# Session manager with run control
|
||||
run_control = SessionRunControl(
|
||||
busy_mode="queue",
|
||||
busy_ack_template="⏳ {action} — will process after current task finishes"
|
||||
)
|
||||
session_mgr = BotSessionManager(run_control=run_control)
|
||||
agent = MockAgent("research-agent")
|
||||
user_id = "user123"
|
||||
|
||||
print("\n1. User sends long task:")
|
||||
print(" User: 'research quantum computing trends'")
|
||||
|
||||
# Start first task
|
||||
task1 = asyncio.create_task(
|
||||
session_mgr.chat_with_run_control(agent, user_id, "research quantum computing trends")
|
||||
)
|
||||
|
||||
# Immediate follow-up messages get acknowledgments
|
||||
await asyncio.sleep(0.5)
|
||||
print("\n2. User sends follow-up (gets immediate feedback):")
|
||||
print(" User: 'actually focus on business impact'")
|
||||
|
||||
result2 = await session_mgr.chat_with_run_control(
|
||||
agent, user_id, "actually focus on business impact"
|
||||
)
|
||||
print(f" Bot: {result2['response']}")
|
||||
print(f" Metadata: {result2['metadata']}")
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
print("\n3. User decides to stop current task:")
|
||||
print(" User: '/stop'")
|
||||
|
||||
stop_response = await handle_stop_command(user_id, run_control)
|
||||
print(f" Bot: {stop_response}")
|
||||
|
||||
# Wait for first task to complete (it should be cancelled)
|
||||
try:
|
||||
result1 = await task1
|
||||
print(f"\n📊 Task 1 result: {result1['response']}")
|
||||
print(f" Metadata: {result1['metadata']}")
|
||||
except asyncio.CancelledError:
|
||||
print("\n📊 Task 1 was cancelled as expected")
|
||||
|
||||
print("\n4. User sends fresh request after stopping:")
|
||||
print(" User: 'what's the weather like?'")
|
||||
|
||||
result4 = await session_mgr.chat_with_run_control(
|
||||
agent, user_id, "what's the weather like?"
|
||||
)
|
||||
print(f" Bot: {result4['response']}")
|
||||
|
||||
print("\n✅ Benefits of this approach:")
|
||||
print(" - Immediate feedback on all messages")
|
||||
print(" - Can cancel long-running tasks with /stop")
|
||||
print(" - Pending messages are queued and processed in order")
|
||||
print(" - User always knows what's happening")
|
||||
|
||||
|
||||
async def demo_interrupt_mode():
|
||||
"""Demonstrate interrupt mode."""
|
||||
print("\n" + "="*60)
|
||||
print("⚡ BONUS: Interrupt Mode (Cancel and Restart)")
|
||||
print("="*60)
|
||||
|
||||
from praisonai.bots._run_control import SessionRunControl
|
||||
from praisonai.bots._session import BotSessionManager
|
||||
|
||||
# Use interrupt mode instead of queue mode
|
||||
run_control = SessionRunControl(busy_mode="interrupt")
|
||||
session_mgr = BotSessionManager(run_control=run_control)
|
||||
agent = MockAgent("research-agent")
|
||||
user_id = "user123"
|
||||
|
||||
print("\n1. User starts long task:")
|
||||
print(" User: 'research renewable energy for 30 pages'")
|
||||
|
||||
task1 = asyncio.create_task(
|
||||
session_mgr.chat_with_run_control(agent, user_id, "research renewable energy for 30 pages")
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
print("\n2. User changes mind (interrupts and restarts):")
|
||||
print(" User: 'actually just summarize solar panel efficiency'")
|
||||
|
||||
result2 = await session_mgr.chat_with_run_control(
|
||||
agent, user_id, "actually just summarize solar panel efficiency"
|
||||
)
|
||||
print(f" Bot: {result2['response']}")
|
||||
|
||||
# Wait for original task (should be interrupted)
|
||||
try:
|
||||
result1 = await task1
|
||||
print(f"\n📊 Original task result: {result1['response']}")
|
||||
if result1['metadata'].get('interrupted'):
|
||||
print(" ✅ Original task was properly interrupted")
|
||||
except:
|
||||
print("\n📊 Original task was cancelled")
|
||||
|
||||
print("\n✅ Interrupt mode is great for:")
|
||||
print(" - When users frequently change direction")
|
||||
print(" - Interactive sessions where latest intent matters most")
|
||||
print(" - Real-time collaboration scenarios")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run all demonstrations."""
|
||||
print("🚀 Bot In-Flight Run Control Demo")
|
||||
print("Solving the silent lock problem described in issue #1914")
|
||||
|
||||
# Show the problem
|
||||
await demo_without_run_control()
|
||||
|
||||
# Show the solution
|
||||
await demo_with_run_control()
|
||||
|
||||
# Show advanced feature
|
||||
await demo_interrupt_mode()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("🎯 Summary")
|
||||
print("="*60)
|
||||
print("SessionRunControl solves the silent blocking problem by providing:")
|
||||
print("1. Immediate feedback for mid-run messages")
|
||||
print("2. /stop command support via InterruptController")
|
||||
print("3. Configurable policies (queue/interrupt/steer)")
|
||||
print("4. Run generation tracking to prevent race conditions")
|
||||
print("5. Pending message merging and ordering")
|
||||
print("\nThis makes bot interactions feel responsive and predictable!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -8,7 +8,7 @@ Docs: https://docs.praison.ai/cli/git-integration
|
||||
from praisonai.cli.features import GitIntegrationHandler
|
||||
|
||||
# Initialize
|
||||
handler = GitIntegrationHandler(output="verbose")
|
||||
handler = GitIntegrationHandler(verbose=True)
|
||||
git = handler.initialize(repo_path=".")
|
||||
|
||||
# Check if it's a git repo
|
||||
|
||||
@@ -8,7 +8,7 @@ Docs: https://docs.praison.ai/cli/repo-map
|
||||
from praisonai.cli.features import RepoMapHandler
|
||||
|
||||
# Initialize with current directory
|
||||
handler = RepoMapHandler(output="verbose")
|
||||
handler = RepoMapHandler(verbose=True)
|
||||
repo_map = handler.initialize(root=".")
|
||||
|
||||
# Get the repository map
|
||||
|
||||
@@ -9,7 +9,7 @@ from praisonai.cli.features import SandboxExecutorHandler
|
||||
from praisonai.cli.features.sandbox_executor import SandboxMode
|
||||
|
||||
# Initialize with basic sandbox
|
||||
handler = SandboxExecutorHandler(output="verbose")
|
||||
handler = SandboxExecutorHandler(verbose=True)
|
||||
sandbox = handler.initialize(mode="basic")
|
||||
|
||||
print(f"Sandbox mode: {handler.get_mode()}")
|
||||
|
||||
@@ -25,8 +25,7 @@ pdf_agent = Agent(
|
||||
goal="Help users understand and extract information from PDF documents",
|
||||
backstory="You are an expert at reading, analyzing, and answering questions about PDF documents. You provide accurate, detailed answers based on the document content.",
|
||||
instructions="Read the provided PDF document and answer questions about its content. Be specific and cite relevant sections when possible.",
|
||||
knowledge=["document.pdf"], # Replace with your PDF file path
|
||||
knowledge=config,
|
||||
knowledge={**config, "sources": ["document.pdf"]}, # Replace with your PDF path
|
||||
reflection=True,
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from pathlib import Path
|
||||
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
@@ -17,14 +20,18 @@ config = {
|
||||
}
|
||||
}
|
||||
|
||||
_sample_path = Path(__file__).with_name("sample_knowledge.txt")
|
||||
if not _sample_path.exists():
|
||||
_sample_path.write_text("Mervin Praison is the creator of PraisonAI.\n", encoding="utf-8")
|
||||
|
||||
# Create an agent with knowledge capabilities
|
||||
knowledge_agent = Agent(
|
||||
name="KnowledgeAgent",
|
||||
role="Information Specialist",
|
||||
goal="Store and retrieve knowledge efficiently",
|
||||
backstory="Expert in managing and utilizing stored knowledge",
|
||||
knowledge=["sample.pdf"],
|
||||
knowledge=config)
|
||||
knowledge={**config, "sources": [str(_sample_path)]},
|
||||
)
|
||||
|
||||
# Define a task for the agent
|
||||
knowledge_task = Task(
|
||||
@@ -39,8 +46,16 @@ agents = AgentTeam(
|
||||
agents=[knowledge_agent],
|
||||
tasks=[knowledge_task],
|
||||
process="sequential",
|
||||
user_id="user1"
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
if __name__ == "__main__":
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Set OPENAI_API_KEY to run this example.")
|
||||
sys.exit(0)
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
result = agents.start()
|
||||
print(result)
|
||||
@@ -138,8 +138,7 @@ def main():
|
||||
agent = Agent(
|
||||
name="AI Research Assistant",
|
||||
instructions="You are an AI research assistant. Use your knowledge base to answer questions about artificial intelligence topics.",
|
||||
knowledge=sample_documents, # Will use default knowledge config
|
||||
knowledge=rerank_config, # Use reranking config
|
||||
knowledge={**rerank_config, "sources": sample_documents},
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
|
||||
@@ -32,8 +32,12 @@ rag_task = Task(
|
||||
agents = AgentTeam(
|
||||
agents=[rag_agent],
|
||||
tasks=[rag_task],
|
||||
user_id="user1"
|
||||
)
|
||||
|
||||
# Start Agents
|
||||
agents.start()
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
import sys
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Set OPENAI_API_KEY to run this example.")
|
||||
sys.exit(0)
|
||||
agents.start()
|
||||
@@ -49,7 +49,18 @@ def calculate_with_tracking(expression: str) -> str:
|
||||
time.sleep(0.05)
|
||||
|
||||
try:
|
||||
result = eval(expression)
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
result = _ev(ast.parse(expression, mode="eval"))
|
||||
return f"Result: {result}"
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
def main():
|
||||
# Initialize memory config
|
||||
memory_config = {
|
||||
"provider": "rag",
|
||||
"backend": "sqlite",
|
||||
"use_embedding": True,
|
||||
"storage": {
|
||||
"type": "sqlite",
|
||||
@@ -105,7 +105,7 @@ def main():
|
||||
agents=[researcher, retriever],
|
||||
tasks=[store_task, verify_task, query_task, query_both_task], # Use same verbose level as memory
|
||||
memory={
|
||||
"provider": "rag",
|
||||
"backend": "sqlite",
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
|
||||
@@ -36,10 +36,18 @@ def calculate(expression: str) -> dict:
|
||||
expression: Math expression (e.g., "2 + 2 * 3")
|
||||
"""
|
||||
try:
|
||||
allowed = set("0123456789+-*/.() ")
|
||||
if not all(c in allowed for c in expression):
|
||||
return {"error": "Invalid characters"}
|
||||
return {"expression": expression, "result": eval(expression)}
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
return {"expression": expression, "result": _ev(ast.parse(expression, mode="eval"))}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
@@ -47,12 +47,18 @@ def calculate(expression: str) -> Dict[str, Any]:
|
||||
The result of the calculation
|
||||
"""
|
||||
try:
|
||||
# Safe evaluation of mathematical expressions
|
||||
allowed_chars = set("0123456789+-*/.() ")
|
||||
if not all(c in allowed_chars for c in expression):
|
||||
return {"error": "Invalid characters in expression"}
|
||||
|
||||
result = eval(expression) # Use safer alternative in production
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
result = _ev(ast.parse(expression, mode="eval"))
|
||||
return {"expression": expression, "result": result}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@@ -49,13 +49,18 @@ def calculate(expression: str) -> Dict[str, Any]:
|
||||
The result of the calculation
|
||||
"""
|
||||
try:
|
||||
# Safe evaluation of mathematical expressions
|
||||
# In production, use a proper math parser
|
||||
allowed_chars = set("0123456789+-*/.() ")
|
||||
if not all(c in allowed_chars for c in expression):
|
||||
return {"error": "Invalid characters in expression"}
|
||||
|
||||
result = eval(expression) # Note: Use safer alternative in production
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
result = _ev(ast.parse(expression, mode="eval"))
|
||||
return {"expression": expression, "result": result}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@@ -60,8 +60,8 @@ query_task = Task(
|
||||
agents_system = AgentTeam(
|
||||
agents=[knowledge_agent],
|
||||
tasks=[build_task, query_task],
|
||||
memory=True,
|
||||
memory=memory_config, output="verbose"
|
||||
memory=memory_config,
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
print("Starting graph memory demonstration...")
|
||||
|
||||
@@ -61,8 +61,8 @@ search_task = Task(
|
||||
agents_system = AgentTeam(
|
||||
agents=[research_agent],
|
||||
tasks=[build_memory_task, search_task],
|
||||
memory=True,
|
||||
memory=memory_config, output="verbose"
|
||||
memory=memory_config,
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
print("Starting memory search optimization demonstration...")
|
||||
|
||||
@@ -86,7 +86,7 @@ def main():
|
||||
agents=[researcher, retriever],
|
||||
tasks=[store_task, retrieve_task],
|
||||
memory={
|
||||
"provider": "rag",
|
||||
"backend": "sqlite",
|
||||
"embedder": {
|
||||
"provider": "gemini",
|
||||
"config": {
|
||||
|
||||
@@ -152,8 +152,7 @@ def main():
|
||||
agents_system = AgentTeam(
|
||||
agents=[researcher],
|
||||
tasks=[build_knowledge_task, query_knowledge_task, expand_knowledge_task],
|
||||
memory=True,
|
||||
memory=memory_config
|
||||
memory=memory_config,
|
||||
)
|
||||
|
||||
print("\n🚀 Starting graph memory demonstration...")
|
||||
|
||||
@@ -30,7 +30,7 @@ def main():
|
||||
try:
|
||||
# Create memory instance
|
||||
memory = Memory(config={
|
||||
"provider": "rag",
|
||||
"backend": "sqlite",
|
||||
"use_embedding": False,
|
||||
"agent_name": "demo_agent", # Used in trace events
|
||||
})
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Message Steering Example
|
||||
|
||||
Demonstrates real-time message steering for agents during execution.
|
||||
Shows how to send guidance messages to agents while they're running tasks.
|
||||
|
||||
Usage:
|
||||
# Python API
|
||||
python message_steering_example.py
|
||||
|
||||
# CLI with steering enabled
|
||||
praisonai --message-steering "Write a detailed report about AI trends"
|
||||
|
||||
# YAML (add message_steering: true to agent config)
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
import time
|
||||
from praisonaiagents import Agent
|
||||
|
||||
|
||||
def basic_message_steering():
|
||||
"""Basic message steering example - enable steering and send guidance."""
|
||||
print("🎯 Basic Message Steering Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Create agent with message steering enabled
|
||||
agent = Agent(
|
||||
name="researcher",
|
||||
instructions="You are a helpful research assistant",
|
||||
message_steering=True, # Enable real-time message steering
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print("✅ Agent created with message steering enabled")
|
||||
print(f"📊 Steering status: {agent.get_steering_status()}")
|
||||
|
||||
# Send some guidance
|
||||
msg_id_1 = agent.steer("Focus on recent developments from 2024")
|
||||
msg_id_2 = agent.steer("Keep response under 200 words", priority=10)
|
||||
|
||||
print(f"📨 Sent steering messages: {msg_id_1}, {msg_id_2}")
|
||||
print(f"📊 Steering status: {agent.get_steering_status()}")
|
||||
|
||||
# Execute task - steering will be automatically injected
|
||||
result = agent.start("Summarize the latest AI trends")
|
||||
|
||||
print("\n📝 Result:")
|
||||
print(result)
|
||||
|
||||
|
||||
def threaded_message_steering():
|
||||
"""Advanced example - send steering messages while agent is running."""
|
||||
print("\n🚀 Threaded Message Steering Example")
|
||||
print("=" * 50)
|
||||
|
||||
agent = Agent(
|
||||
name="writer",
|
||||
instructions="You are a content writer",
|
||||
message_steering=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
result_holder = [None]
|
||||
|
||||
def run_agent():
|
||||
"""Run agent in background thread."""
|
||||
result = agent.start("Write a comprehensive article about quantum computing")
|
||||
result_holder[0] = result
|
||||
|
||||
# Start agent in background
|
||||
thread = threading.Thread(target=run_agent)
|
||||
thread.start()
|
||||
|
||||
# Send steering messages while it's running
|
||||
time.sleep(0.5) # Let execution start
|
||||
agent.steer("Make it accessible to beginners")
|
||||
|
||||
time.sleep(1.0)
|
||||
agent.steer("Include practical applications", priority=15)
|
||||
|
||||
time.sleep(1.5)
|
||||
agent.steer("Keep it under 300 words total", priority=20)
|
||||
|
||||
# Wait for completion
|
||||
thread.join(timeout=60)
|
||||
|
||||
if result_holder[0]:
|
||||
print("\n📝 Final Result:")
|
||||
print(result_holder[0])
|
||||
else:
|
||||
print("⚠️ Agent execution timed out")
|
||||
|
||||
|
||||
async def async_message_steering():
|
||||
"""Async example showing message steering with async execution."""
|
||||
print("\n⚡ Async Message Steering Example")
|
||||
print("=" * 50)
|
||||
|
||||
agent = Agent(
|
||||
name="analyst",
|
||||
instructions="You are a data analyst",
|
||||
message_steering=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Start async task
|
||||
async def run_analysis():
|
||||
return await agent.astart("Analyze the impact of AI on job markets")
|
||||
|
||||
# Create task
|
||||
task = asyncio.create_task(run_analysis())
|
||||
|
||||
# Send steering while running
|
||||
await asyncio.sleep(0.3)
|
||||
agent.steer("Focus on positive opportunities")
|
||||
|
||||
await asyncio.sleep(0.7)
|
||||
agent.steer("Include specific examples", priority=12)
|
||||
|
||||
# Wait for result
|
||||
result = await task
|
||||
|
||||
print("\n📝 Analysis Result:")
|
||||
print(result)
|
||||
|
||||
|
||||
def cli_yaml_examples():
|
||||
"""Show CLI and YAML usage examples."""
|
||||
print("\n💻 CLI & YAML Examples")
|
||||
print("=" * 50)
|
||||
|
||||
print("🔧 CLI Usage:")
|
||||
print(" praisonai --message-steering 'Your task here'")
|
||||
print(" # Then in another terminal:")
|
||||
print(" # agent.steer('Additional guidance')")
|
||||
|
||||
print("\n📄 YAML Configuration:")
|
||||
yaml_example = """
|
||||
# agents.yaml
|
||||
framework: praisonai
|
||||
roles:
|
||||
researcher:
|
||||
role: Research Assistant
|
||||
goal: Conduct thorough research
|
||||
backstory: Expert researcher with steering support
|
||||
message_steering: true # Enable steering for this agent
|
||||
tools:
|
||||
- internet_search
|
||||
- write_file
|
||||
tasks:
|
||||
research_task:
|
||||
description: Research AI trends and write report
|
||||
expected_output: Comprehensive research report
|
||||
"""
|
||||
print(yaml_example.strip())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run examples
|
||||
basic_message_steering()
|
||||
threaded_message_steering()
|
||||
|
||||
# Run async example
|
||||
asyncio.run(async_message_steering())
|
||||
|
||||
# Show CLI/YAML usage
|
||||
cli_yaml_examples()
|
||||
|
||||
print("\n✨ Message steering examples completed!")
|
||||
print("📚 See docs for more advanced patterns and priority levels")
|
||||
@@ -32,9 +32,8 @@ def init_agent():
|
||||
return Agent(
|
||||
name="Knowledge Agent",
|
||||
instructions="You answer questions based on the provided knowledge.",
|
||||
knowledge=["kag-research-paper.pdf"],
|
||||
knowledge=config,
|
||||
user_id="user1",
|
||||
knowledge={**config, "sources": ["kag-research-paper.pdf"]},
|
||||
memory={"user_id": "user1"},
|
||||
llm="deepseek-r1"
|
||||
)
|
||||
|
||||
|
||||
@@ -30,9 +30,8 @@ config = {
|
||||
agent = Agent(
|
||||
name="Knowledge Agent",
|
||||
instructions="You answer questions based on the provided knowledge.",
|
||||
knowledge=["kag-research-paper.pdf"], # Indexing
|
||||
knowledge=config,
|
||||
user_id="user1",
|
||||
knowledge={**config, "sources": ["kag-research-paper.pdf"]},
|
||||
memory={"user_id": "user1"},
|
||||
llm="deepseek-r1"
|
||||
)
|
||||
|
||||
|
||||
@@ -189,8 +189,8 @@ def main():
|
||||
business_system = AgentTeam(
|
||||
agents=[data_manager, knowledge_curator, business_analyst, customer_service],
|
||||
tasks=business_tasks,
|
||||
memory=True,
|
||||
memory=mongodb_memory_config, output="verbose"
|
||||
memory=mongodb_memory_config,
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
# Execute the comprehensive business pipeline
|
||||
|
||||
@@ -55,8 +55,10 @@ def main():
|
||||
retrieve information from a comprehensive MongoDB knowledge base. You excel
|
||||
at finding relevant information, synthesizing knowledge from multiple sources,
|
||||
and providing accurate, context-aware responses.""",
|
||||
knowledge=mongodb_knowledge_config,
|
||||
knowledge=[os.path.join(os.path.dirname(__file__), "llms.md")],
|
||||
knowledge={
|
||||
**mongodb_knowledge_config,
|
||||
"sources": [os.path.join(os.path.dirname(__file__), "llms.md")],
|
||||
},
|
||||
memory=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
@@ -93,8 +93,8 @@ def main():
|
||||
research_system = AgentTeam(
|
||||
agents=[research_agent],
|
||||
tasks=research_tasks,
|
||||
memory=True,
|
||||
memory=mongodb_memory_config, output="verbose"
|
||||
memory=mongodb_memory_config,
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
# Execute the research pipeline
|
||||
|
||||
@@ -50,7 +50,7 @@ agent = Agent(
|
||||
name="TechSupport",
|
||||
instructions="You are a technical support assistant.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="comparison_user",
|
||||
memory={"user_id": "comparison_user"},
|
||||
llm="openai/gpt-4o-mini",
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -20,8 +20,9 @@ if not os.environ.get("OPENAI_API_KEY"):
|
||||
exit(1)
|
||||
|
||||
# Create a sample document for testing
|
||||
sample_doc = "/tmp/sample_doc.txt"
|
||||
with open(sample_doc, "w") as f:
|
||||
from pathlib import Path
|
||||
sample_doc = str(Path(__file__).with_name("sample_doc.txt"))
|
||||
with open(sample_doc, "w", encoding="utf-8") as f:
|
||||
f.write("""
|
||||
PraisonAI Framework Documentation
|
||||
|
||||
@@ -44,7 +45,7 @@ agent = Agent(
|
||||
name="DocBot",
|
||||
instructions="You are a helpful documentation assistant. Answer questions based on the provided context.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="example_user", # Required for RAG retrieval
|
||||
memory={"user_id": "example_user"},
|
||||
llm="openai/gpt-4o-mini",
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ agent = Agent(
|
||||
name="PolicyBot",
|
||||
instructions="You are a customer service assistant.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="policy_user",
|
||||
memory={"user_id": "policy_user"},
|
||||
llm="openai/gpt-4o-mini",
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ agent = Agent(
|
||||
name="MLExpert",
|
||||
instructions="You are a machine learning expert. Answer questions based on the provided context.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="rag_user",
|
||||
memory={"user_id": "rag_user"},
|
||||
llm="openai/gpt-4o-mini",
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ agent = Agent(
|
||||
name="APIDocBot",
|
||||
instructions="You are an API documentation assistant.",
|
||||
knowledge=[sample_doc],
|
||||
user_id="hybrid_user",
|
||||
memory={"user_id": "hybrid_user"},
|
||||
llm="openai/gpt-4o-mini",
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ agent = Agent(
|
||||
name="ResearchBot",
|
||||
instructions="You are a research assistant. Always cite your sources.",
|
||||
knowledge=[doc1, doc2],
|
||||
user_id="citation_user",
|
||||
memory={"user_id": "citation_user"},
|
||||
llm="openai/gpt-4o-mini",
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -172,7 +172,7 @@ print("="*70)
|
||||
# Create sessions for different users working on the same project
|
||||
reviewer_session = Session(
|
||||
session_id="peer_review_001",
|
||||
user_id="user_reviewer_01", # Different user
|
||||
user_id="user_reviewer_01",
|
||||
storage_path=session_dir
|
||||
)
|
||||
|
||||
|
||||
@@ -14,13 +14,12 @@ def main():
|
||||
|
||||
# Initialize memory with quality-based configuration
|
||||
memory_config = {
|
||||
"provider": "rag",
|
||||
"use_embedding": True,
|
||||
"rag_db_path": "quality_memory_db"
|
||||
"backend": "sqlite",
|
||||
"user_id": "quality_demo_user",
|
||||
}
|
||||
|
||||
memory = Memory(config=memory_config, verbose=5)
|
||||
print(f"📦 Initialized memory system with provider: {memory_config['provider']}")
|
||||
print(f"📦 Initialized memory system with backend: {memory_config['backend']}")
|
||||
|
||||
# Create agent with memory
|
||||
agent = Agent(
|
||||
@@ -28,7 +27,6 @@ def main():
|
||||
role="Information Quality Specialist",
|
||||
instructions="Store and retrieve information with quality assessment",
|
||||
memory=memory,
|
||||
user_id="quality_demo_user"
|
||||
)
|
||||
|
||||
print(f"🤖 Created agent: {agent.name}")
|
||||
@@ -181,7 +179,7 @@ def main():
|
||||
|
||||
context = memory.build_context_for_task(
|
||||
task_descr="Explain AI alignment challenges",
|
||||
user_id="quality_demo_user",
|
||||
memory={"user_id": "quality_demo_user"},
|
||||
max_items=3
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ def main():
|
||||
# Create a session with persistent storage
|
||||
session = Session(
|
||||
session_id="demo_chat_001",
|
||||
user_id="demo_user"
|
||||
memory={"user_id": "demo_user"},
|
||||
)
|
||||
|
||||
print(f"📝 Created session: {session}")
|
||||
|
||||
@@ -88,9 +88,8 @@ def main():
|
||||
workflow = AgentTeam(
|
||||
agents=[researcher, analyzer, writer],
|
||||
tasks=[research_task, analysis_task, report_task],
|
||||
memory=True,
|
||||
memory={"user_id": "research_project_001"},
|
||||
process="workflow",
|
||||
user_id="research_project_001",
|
||||
)
|
||||
|
||||
print(f"🤖 Created workflow with {len(workflow.agents)} agents and {len(workflow.tasks)} tasks")
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
"""
|
||||
Named Toolsets Example
|
||||
|
||||
This example demonstrates how to use named toolsets to organize and reuse
|
||||
groups of tools across agents and workflows.
|
||||
|
||||
Toolsets provide:
|
||||
- Organized tool collections (web, research, development, etc.)
|
||||
- Progressive disclosure (safe → research → development)
|
||||
- Composable design (toolsets can include other toolsets)
|
||||
- YAML configuration support
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.toolsets import (
|
||||
list_toolsets,
|
||||
resolve_toolset,
|
||||
register_toolset
|
||||
)
|
||||
|
||||
# Set up environment (replace with your actual API key)
|
||||
os.environ.setdefault("OPENAI_API_KEY", "your-openai-api-key-here")
|
||||
|
||||
|
||||
def demo_basic_toolsets():
|
||||
"""Demonstrate basic toolset functionality."""
|
||||
print("=== Basic Toolsets Demo ===")
|
||||
|
||||
# List available toolsets
|
||||
available_toolsets = list_toolsets()
|
||||
print(f"Available toolsets: {available_toolsets}")
|
||||
|
||||
# Show what tools are in each prebuilt toolset
|
||||
for toolset_name in ["safe", "web", "research"]:
|
||||
tools = resolve_toolset(toolset_name)
|
||||
print(f"\n{toolset_name.upper()} toolset contains: {tools}")
|
||||
|
||||
|
||||
def demo_agent_with_toolsets():
|
||||
"""Demonstrate agents using toolsets."""
|
||||
print("\n=== Agent with Toolsets Demo ===")
|
||||
|
||||
# Create a research agent with research toolset
|
||||
researcher = Agent(
|
||||
name="researcher",
|
||||
role="Research Specialist",
|
||||
goal="Research topics thoroughly using web tools",
|
||||
instructions="You are a research specialist with access to web search and file tools.",
|
||||
toolsets=["research"], # includes web tools + file tools
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print(f"Research agent has {len(researcher.tools)} tools available")
|
||||
print(f"Tools: {[getattr(t, '__name__', str(t)) for t in researcher.tools]}")
|
||||
|
||||
# Create a safe agent with minimal tools
|
||||
safe_agent = Agent(
|
||||
name="safe_assistant",
|
||||
role="Safe Assistant",
|
||||
goal="Help users safely without risky operations",
|
||||
instructions="You are a helpful assistant with access to only safe, read-only tools.",
|
||||
toolsets=["safe"],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print(f"\nSafe agent has {len(safe_agent.tools)} tools available")
|
||||
print(f"Safe tools: {[getattr(t, '__name__', str(t)) for t in safe_agent.tools]}")
|
||||
|
||||
|
||||
def demo_custom_toolsets():
|
||||
"""Demonstrate creating and using custom toolsets."""
|
||||
print("\n=== Custom Toolsets Demo ===")
|
||||
|
||||
# Register a custom toolset for data analysis
|
||||
register_toolset(
|
||||
"data_analysis",
|
||||
tools=["read_file", "write_file"],
|
||||
includes=["safe"], # Include safe tools too
|
||||
description="Tools for data analysis workflows"
|
||||
)
|
||||
|
||||
# Create an agent with the custom toolset
|
||||
analyst = Agent(
|
||||
name="data_analyst",
|
||||
role="Data Analyst",
|
||||
goal="Analyze data files and generate reports",
|
||||
instructions="You specialize in data analysis. Read files, process data, and write reports.",
|
||||
toolsets=["data_analysis"],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print(f"Data analyst has {len(analyst.tools)} tools available")
|
||||
print(f"Analysis tools: {[getattr(t, '__name__', str(t)) for t in analyst.tools]}")
|
||||
|
||||
|
||||
def demo_mixed_tools_and_toolsets():
|
||||
"""Demonstrate mixing explicit tools with toolsets."""
|
||||
print("\n=== Mixed Tools and Toolsets Demo ===")
|
||||
|
||||
# Create agent with both explicit tools and toolsets
|
||||
hybrid_agent = Agent(
|
||||
name="hybrid_assistant",
|
||||
role="Hybrid Assistant",
|
||||
goal="Assist with both research and custom tasks",
|
||||
instructions="You can research topics and perform custom analysis tasks.",
|
||||
tools=["write_file"], # Explicit tool
|
||||
toolsets=["research"], # Toolset with web + file tools
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print(f"Hybrid agent has {len(hybrid_agent.tools)} tools available")
|
||||
tool_names = [getattr(t, '__name__', str(t)) for t in hybrid_agent.tools]
|
||||
print(f"Hybrid tools: {tool_names}")
|
||||
|
||||
# Note: write_file might appear twice (once explicit, once from toolset)
|
||||
# The agent handles deduplication automatically
|
||||
|
||||
|
||||
def demo_multi_agent_with_toolsets():
|
||||
"""Demonstrate multi-agent workflows with different toolsets."""
|
||||
print("\n=== Multi-Agent with Toolsets Demo ===")
|
||||
|
||||
# Create agents with different toolsets for different roles
|
||||
researcher = Agent(
|
||||
name="researcher",
|
||||
role="Research Specialist",
|
||||
goal="Research topics and gather information",
|
||||
toolsets=["research"],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="writer",
|
||||
role="Content Writer",
|
||||
goal="Write content based on research",
|
||||
toolsets=["safe"], # Safe tools for writing
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create tasks for each agent
|
||||
research_task = Task(
|
||||
name="research_task",
|
||||
description="Research the benefits of renewable energy",
|
||||
expected_output="A summary of key benefits of renewable energy",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
writing_task = Task(
|
||||
name="writing_task",
|
||||
description="Write a blog post about renewable energy based on the research",
|
||||
expected_output="A well-written blog post about renewable energy benefits",
|
||||
agent=writer
|
||||
)
|
||||
|
||||
# Create team with different toolsets per agent
|
||||
team = AgentTeam(
|
||||
agents=[researcher, writer],
|
||||
tasks=[research_task, writing_task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
print(f"Team created with {len(team.agents)} agents using different toolsets")
|
||||
print(f"Research agent tools: {len(researcher.tools)}")
|
||||
print(f"Writer agent tools: {len(writer.tools)}")
|
||||
|
||||
|
||||
def demo_yaml_config():
|
||||
"""Show how toolsets work in YAML configuration."""
|
||||
print("\n=== YAML Configuration Example ===")
|
||||
|
||||
yaml_config = """
|
||||
# agents.yaml - Example using toolsets
|
||||
agents:
|
||||
researcher:
|
||||
role: Research Specialist
|
||||
goal: Research topics thoroughly
|
||||
toolsets:
|
||||
- research # Includes web search, file tools
|
||||
llm: gpt-4o-mini
|
||||
|
||||
analyst:
|
||||
role: Data Analyst
|
||||
goal: Analyze research data
|
||||
tools:
|
||||
- write_file # Explicit tool
|
||||
toolsets:
|
||||
- safe # Safe toolset
|
||||
llm: gpt-4o-mini
|
||||
|
||||
tasks:
|
||||
research_task:
|
||||
description: Research renewable energy trends
|
||||
agent: researcher
|
||||
expected_output: Research summary
|
||||
|
||||
analysis_task:
|
||||
description: Analyze the research data
|
||||
agent: analyst
|
||||
expected_output: Analysis report
|
||||
"""
|
||||
|
||||
print("Example YAML configuration with toolsets:")
|
||||
print(yaml_config)
|
||||
print("Use: praisonai run 'Research renewable energy' --toolset research")
|
||||
print("Or: praisonai chat --toolset web,files")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🛠️ PraisonAI Named Toolsets Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Run all demos
|
||||
demo_basic_toolsets()
|
||||
demo_agent_with_toolsets()
|
||||
demo_custom_toolsets()
|
||||
demo_mixed_tools_and_toolsets()
|
||||
demo_multi_agent_with_toolsets()
|
||||
demo_yaml_config()
|
||||
|
||||
print("\n✅ Toolsets example completed!")
|
||||
print("\n💡 Key Benefits:")
|
||||
print(" • Organized tool collections")
|
||||
print(" • Progressive disclosure (safe → research → development)")
|
||||
print(" • Reusable across agents and workflows")
|
||||
print(" • YAML configuration support")
|
||||
print(" • Composable design with includes")
|
||||
@@ -32,9 +32,8 @@ def init_agent():
|
||||
return Agent(
|
||||
name="Knowledge Agent",
|
||||
instructions="You answer questions based on the provided knowledge.",
|
||||
knowledge=["kag-research-paper.pdf"],
|
||||
knowledge=config,
|
||||
user_id="user1",
|
||||
knowledge={**config, "sources": ["kag-research-paper.pdf"]},
|
||||
memory={"user_id": "user1"},
|
||||
llm="deepseek-r1"
|
||||
)
|
||||
|
||||
|
||||
@@ -264,13 +264,12 @@ agents = AgentTeam(
|
||||
tasks=[tool_query_task, pentester_analysis_task, ciso_review_task],
|
||||
process="hierarchical",
|
||||
manager_llm="gpt-4o-mini",
|
||||
memory=True, # Enable shared memory for domain context
|
||||
memory={
|
||||
"provider": "rag",
|
||||
"backend": "sqlite",
|
||||
"config": {
|
||||
"collection_name": f"{domain.replace('.', '_')}_security_analysis"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# Main Execution Workflow
|
||||
|
||||
@@ -5,7 +5,7 @@ Demonstrates using Agent instances directly as workflow steps.
|
||||
Each agent processes the input and passes output to the next agent.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Workflow
|
||||
from praisonaiagents import Agent, AgentFlow
|
||||
|
||||
# Create agents with specific roles
|
||||
researcher = Agent(
|
||||
|
||||
@@ -6,7 +6,6 @@ to different next steps based on output content.
|
||||
"""
|
||||
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
from praisonaiagents import AgentFlowManager
|
||||
|
||||
# Create a workflow with branching
|
||||
workflow = AgentFlow(
|
||||
@@ -36,16 +35,9 @@ workflow = AgentFlow(
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create manager and register workflow
|
||||
manager = WorkflowManager()
|
||||
manager.workflows["Decision Workflow"] = workflow
|
||||
|
||||
# Execute - will branch to success_handler since 42 is positive
|
||||
result = manager.execute(
|
||||
"Decision Workflow",
|
||||
default_llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
print("Workflow completed!")
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['status']}")
|
||||
for step in result.get("steps", []):
|
||||
name = step.get("step", step.get("name", "unknown"))
|
||||
status = step.get("status", "completed")
|
||||
print(f" {name}: {status}")
|
||||
@@ -5,7 +5,7 @@ Demonstrates using should_run to conditionally execute agent steps
|
||||
based on input or previous results.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Workflow, Task
|
||||
from praisonaiagents import Agent, AgentFlow, Task
|
||||
|
||||
# Condition functions
|
||||
def is_technical(ctx) -> bool:
|
||||
@@ -54,11 +54,13 @@ workflow = AgentFlow(
|
||||
analyzer, # Always runs
|
||||
Task(
|
||||
name="tech_review",
|
||||
description="Review technical aspects of the content",
|
||||
agent=tech_reviewer,
|
||||
should_run=is_technical # Only runs for technical content
|
||||
),
|
||||
Task(
|
||||
name="creative_enhance",
|
||||
description="Enhance the creative aspects of the content",
|
||||
agent=creative_enhancer,
|
||||
should_run=needs_creative # Only runs for creative content
|
||||
),
|
||||
|
||||
@@ -6,9 +6,8 @@ functions that return StepResult with stop_workflow=True.
|
||||
"""
|
||||
|
||||
from praisonaiagents import (
|
||||
Workflow, Task, WorkflowContext, StepResult
|
||||
AgentFlow, Task, WorkflowContext, StepResult
|
||||
)
|
||||
from praisonaiagents import AgentFlowManager
|
||||
|
||||
# Custom validator that can stop the workflow
|
||||
def validate_data(context: WorkflowContext) -> StepResult:
|
||||
@@ -50,20 +49,13 @@ workflow = AgentFlow(
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
manager = WorkflowManager()
|
||||
manager.workflows["Data Processing"] = workflow
|
||||
|
||||
# Test with invalid data (will stop early)
|
||||
print("=== Testing with invalid data ===")
|
||||
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
|
||||
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['output']}")
|
||||
|
||||
# Test with valid data (will complete)
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
for step in result.get("steps", []):
|
||||
print(f" {step.get('step', 'step')}: {step.get('output', step.get('status'))}")
|
||||
|
||||
print("\n=== Testing with valid data ===")
|
||||
workflow.variables["data"] = {"value": 42}
|
||||
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
|
||||
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['status']}")
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
for step in result.get("steps", []):
|
||||
print(f" {step.get('step', 'step')}: {step.get('status', 'completed')}")
|
||||
@@ -6,7 +6,6 @@ Each item is processed individually with the loop variable.
|
||||
"""
|
||||
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
from praisonaiagents import AgentFlowManager
|
||||
|
||||
# Create a workflow with loop
|
||||
workflow = AgentFlow(
|
||||
@@ -30,18 +29,12 @@ workflow = AgentFlow(
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create manager and register workflow
|
||||
manager = WorkflowManager()
|
||||
manager.workflows["Process Items"] = workflow
|
||||
|
||||
# Execute - will process each fruit in the list
|
||||
result = manager.execute(
|
||||
"Process Items",
|
||||
default_llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
print("Workflow completed!")
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['status']}")
|
||||
if isinstance(step_result.get("output"), list):
|
||||
print(f" Loop processed {len(step_result['output'])} items")
|
||||
for step in result.get("steps", []):
|
||||
name = step.get("step", step.get("name", "unknown"))
|
||||
status = step.get("status", "completed")
|
||||
print(f" {name}: {status}")
|
||||
out = step.get("output")
|
||||
if isinstance(out, list):
|
||||
print(f" Loop processed {len(out)} items")
|
||||
@@ -53,7 +53,7 @@ workflow = AgentFlow(
|
||||
# Step 4: Function - formats output
|
||||
format_output
|
||||
],
|
||||
default_llm="gpt-4o-mini" # Used for action-based steps
|
||||
llm="gpt-4o-mini" # Used for action-based steps
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -5,7 +5,7 @@ Demonstrates running multiple agents concurrently and
|
||||
combining their results with an aggregator agent.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Workflow
|
||||
from praisonaiagents import Agent, AgentFlow
|
||||
from praisonaiagents.workflows import parallel
|
||||
|
||||
# Create parallel research agents
|
||||
|
||||
@@ -5,7 +5,7 @@ Demonstrates decision-based routing where an agent classifier
|
||||
routes to specialized handler agents based on the request type.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Workflow
|
||||
from praisonaiagents import Agent, AgentFlow
|
||||
from praisonaiagents.workflows import route
|
||||
|
||||
# Create classifier agent
|
||||
|
||||
@@ -207,7 +207,7 @@ def api_augmented_rag():
|
||||
instructions="""You are a financial assistant with access to market APIs.
|
||||
Provide accurate financial information and context.
|
||||
Always note that prices are subject to change.""",
|
||||
user_id="api_demo"
|
||||
memory={"user_id": "api_demo"},
|
||||
)
|
||||
|
||||
queries = [
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
# Multi-Channel Bot Deployment Guide
|
||||
|
||||
This guide explains how to set up multiple bots on the same platform (e.g., multiple Telegram bots) for different roles using PraisonAI, similar to Hermes-style AI workforces.
|
||||
|
||||
## Overview
|
||||
|
||||
PraisonAI supports running multiple bots on the same platform, each routing to different agents with specialized knowledge and capabilities. This is useful for:
|
||||
|
||||
- **Role-based bots**: `@company_cfo_bot`, `@company_ops_bot`, `@company_content_bot`
|
||||
- **Department-specific agents**: Each with different knowledge brains and tool access
|
||||
- **Workflow separation**: Different bots handle different types of requests
|
||||
|
||||
## Quick Start with Onboard Wizard
|
||||
|
||||
The easiest way to set up multiple channels is using the onboard wizard:
|
||||
|
||||
```bash
|
||||
praisonai onboard
|
||||
```
|
||||
|
||||
The wizard now supports:
|
||||
1. **Multi-channel setup**: Add multiple bots for the same platform
|
||||
2. **Role-based naming**: Automatically generates `TELEGRAM_<ROLE>_BOT_TOKEN` environment variables
|
||||
3. **Agent routing**: Each channel routes to a different agent
|
||||
|
||||
### Onboard Wizard Flow
|
||||
|
||||
1. **Choose platform**: Start with your first platform (e.g., telegram)
|
||||
2. **Add more channels**: Wizard asks "Add another bot channel?"
|
||||
3. **Specify role**: Enter role name (e.g., "cfo", "ops", "content")
|
||||
4. **Configure tokens**: Each channel gets its own environment variable
|
||||
5. **Test connections**: Wizard tests each bot separately
|
||||
|
||||
## Environment Variable Convention
|
||||
|
||||
For multiple bots on the same platform, use this naming convention:
|
||||
|
||||
| Channel | Environment Variable | Bot Purpose |
|
||||
|---------|---------------------|-------------|
|
||||
| `telegram_cfo` | `TELEGRAM_CFO_BOT_TOKEN` | CFO-related tasks |
|
||||
| `telegram_ops` | `TELEGRAM_OPS_BOT_TOKEN` | Operations tasks |
|
||||
| `telegram_content` | `TELEGRAM_CONTENT_BOT_TOKEN` | Content creation |
|
||||
| `telegram_support` | `TELEGRAM_SUPPORT_BOT_TOKEN` | Customer support |
|
||||
|
||||
### Setting Up Tokens
|
||||
|
||||
1. **Create bots in @BotFather**:
|
||||
```text
|
||||
/newbot
|
||||
Bot name: Company CFO Bot
|
||||
Username: @company_cfo_bot
|
||||
-> Copy token
|
||||
|
||||
/newbot
|
||||
Bot name: Company Ops Bot
|
||||
Username: @company_ops_bot
|
||||
-> Copy token
|
||||
```
|
||||
|
||||
2. **Set environment variables**:
|
||||
```bash
|
||||
export TELEGRAM_CFO_BOT_TOKEN="123456789:ABC..."
|
||||
export TELEGRAM_OPS_BOT_TOKEN="987654321:DEF..."
|
||||
export TELEGRAM_CONTENT_BOT_TOKEN="456789123:GHI..."
|
||||
```
|
||||
|
||||
3. **Or use `~/.praisonai/.env`**:
|
||||
```env
|
||||
TELEGRAM_CFO_BOT_TOKEN=123456789:ABC...
|
||||
TELEGRAM_OPS_BOT_TOKEN=987654321:DEF...
|
||||
TELEGRAM_CONTENT_BOT_TOKEN=456789123:GHI...
|
||||
TELEGRAM_ALLOWED_USERS=123456789,987654321
|
||||
```
|
||||
|
||||
## Example Configuration
|
||||
|
||||
See `multi-telegram-hermes-workforce.yaml` for a complete example.
|
||||
|
||||
### Key Features
|
||||
|
||||
1. **Multiple Channels**:
|
||||
```yaml
|
||||
channels:
|
||||
telegram_cfo:
|
||||
platform: telegram
|
||||
token: ${TELEGRAM_CFO_BOT_TOKEN}
|
||||
routes:
|
||||
default: cfo
|
||||
|
||||
telegram_ops:
|
||||
platform: telegram
|
||||
token: ${TELEGRAM_OPS_BOT_TOKEN}
|
||||
routes:
|
||||
default: ops
|
||||
```
|
||||
|
||||
2. **Specialized Agents**:
|
||||
```yaml
|
||||
agents:
|
||||
cfo:
|
||||
instructions: "You are a CFO agent. Help with financial analysis..."
|
||||
model: gpt-4o-mini
|
||||
|
||||
ops:
|
||||
instructions: "You are an ops agent. Help with system monitoring..."
|
||||
model: gpt-4o-mini
|
||||
```
|
||||
|
||||
## Validation with Doctor
|
||||
|
||||
Use the doctor command to validate your multi-channel setup:
|
||||
|
||||
```bash
|
||||
praisonai doctor
|
||||
```
|
||||
|
||||
The doctor checks for:
|
||||
- ✅ **Unique tokens**: Each channel uses a different bot token
|
||||
- ✅ **Naming convention**: Follows `PLATFORM_ROLE_BOT_TOKEN` pattern
|
||||
- ✅ **Missing tokens**: All referenced environment variables are set
|
||||
- ❌ **Duplicate tokens**: Same token used by multiple channels (fails)
|
||||
|
||||
## Deployment
|
||||
|
||||
### Start Gateway
|
||||
|
||||
```bash
|
||||
# Start the multi-bot gateway
|
||||
praisonai gateway start --config multi-telegram-hermes-workforce.yaml
|
||||
|
||||
# Or install as daemon
|
||||
praisonai onboard # Wizard installs daemon automatically
|
||||
praisonai gateway status
|
||||
```
|
||||
|
||||
### Verify Setup
|
||||
|
||||
1. **Check health**: `curl http://127.0.0.1:8765/health`
|
||||
2. **View dashboard**: `praisonai claw` → http://127.0.0.1:8082
|
||||
3. **Test bots**: Send messages to each bot to verify routing
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Basic Multi-Role Setup
|
||||
```yaml
|
||||
# Single platform, multiple roles
|
||||
channels:
|
||||
telegram_support:
|
||||
token: ${TELEGRAM_SUPPORT_BOT_TOKEN}
|
||||
routes:
|
||||
default: support_agent
|
||||
|
||||
telegram_sales:
|
||||
token: ${TELEGRAM_SALES_BOT_TOKEN}
|
||||
routes:
|
||||
default: sales_agent
|
||||
```
|
||||
|
||||
### Cross-Platform Workforce
|
||||
```yaml
|
||||
# Multiple platforms, multiple roles
|
||||
channels:
|
||||
telegram_cfo:
|
||||
platform: telegram
|
||||
token: ${TELEGRAM_CFO_BOT_TOKEN}
|
||||
routes:
|
||||
default: cfo
|
||||
|
||||
discord_ops:
|
||||
platform: discord
|
||||
token: ${DISCORD_OPS_BOT_TOKEN}
|
||||
routes:
|
||||
default: ops
|
||||
|
||||
slack_content:
|
||||
platform: slack
|
||||
token: ${SLACK_CONTENT_BOT_TOKEN}
|
||||
routes:
|
||||
default: content
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"Token is used by multiple channels"**
|
||||
- Each bot needs its own unique token from @BotFather
|
||||
- Check environment variables for duplicates
|
||||
|
||||
2. **"Channel skipped - no token"**
|
||||
- Set the environment variable: `export TELEGRAM_CFO_BOT_TOKEN="..."`
|
||||
- Or update `~/.praisonai/.env`
|
||||
|
||||
3. **"Bot not responding"**
|
||||
- Verify token is correct: `praisonai doctor`
|
||||
- Check bot is not already running elsewhere
|
||||
- Ensure allowlist includes your user ID
|
||||
|
||||
### Debugging Commands
|
||||
|
||||
```bash
|
||||
# Check configuration
|
||||
praisonai doctor
|
||||
|
||||
# View logs
|
||||
praisonai gateway logs
|
||||
|
||||
# Test connection
|
||||
curl -H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
||||
http://127.0.0.1:8765/info
|
||||
```
|
||||
|
||||
## Migration from Single-Bot Setup
|
||||
|
||||
If you currently have a single bot setup, you can add more channels:
|
||||
|
||||
1. **Run onboard wizard**: `praisonai onboard`
|
||||
2. **Add new channels**: Wizard will preserve existing setup
|
||||
3. **Update configuration**: New channels added to existing `bot.yaml`
|
||||
4. **Restart gateway**: `praisonai gateway restart`
|
||||
|
||||
The wizard preserves backward compatibility with single-bot configurations.
|
||||
@@ -0,0 +1,19 @@
|
||||
# A2UI canvas agent — YAML example
|
||||
#
|
||||
# Usage:
|
||||
# pip install praisonaiagents[a2ui]
|
||||
# praisonai agent run --file a2ui-canvas.yaml --prompt "Show a welcome card"
|
||||
#
|
||||
# For live A2UI rendering, use PraisonAIUI (AG-UI / surfaces):
|
||||
# pip install praisonaiui
|
||||
# See examples/python/a2ui/send_surface.py and PraisonAIUI example 29.
|
||||
|
||||
agent:
|
||||
name: "ui-assistant"
|
||||
instructions: |
|
||||
You are a helpful assistant. When the user asks for UI elements
|
||||
(buttons, cards, forms), call send_a2ui_messages with valid A2UI v0.9 JSON.
|
||||
Use surface id "main" unless told otherwise.
|
||||
llm: gpt-4o-mini
|
||||
tools:
|
||||
- send_a2ui_messages
|
||||
@@ -0,0 +1,40 @@
|
||||
# Example: Agent with MCP Tool Integration
|
||||
#
|
||||
# This example shows how to configure an agent with MCP (Model Context Protocol)
|
||||
# servers for additional tool capabilities.
|
||||
#
|
||||
# Usage:
|
||||
# praisonai workflow run --file examples/yaml/agent-with-mcp.yaml
|
||||
|
||||
agent:
|
||||
name: mcp_assistant
|
||||
instructions: |
|
||||
You are a helpful assistant with access to filesystem and time tools.
|
||||
Use the MCP tools when needed to help users with file operations or time queries.
|
||||
llm: gpt-4o-mini
|
||||
|
||||
# MCP server configuration
|
||||
mcp:
|
||||
servers:
|
||||
filesystem:
|
||||
# Filesystem access for the agent
|
||||
command: npx
|
||||
args:
|
||||
- "-y"
|
||||
- "@modelcontextprotocol/server-filesystem"
|
||||
- "."
|
||||
env: {}
|
||||
enabled: true
|
||||
|
||||
time:
|
||||
# Time and date utilities
|
||||
command: npx
|
||||
args:
|
||||
- "-y"
|
||||
- "@modelcontextprotocol/server-time"
|
||||
env: {}
|
||||
enabled: true
|
||||
|
||||
# NOTE: Per-server tool filtering (tools.include/exclude) is not yet supported
|
||||
# by the MCP server config schema. Tool filtering should be applied after
|
||||
# calling mcp.get_tools() in your application code.
|
||||
@@ -0,0 +1,47 @@
|
||||
framework: praisonai
|
||||
topic: AI Research with Message Steering
|
||||
|
||||
roles:
|
||||
researcher:
|
||||
role: AI Research Analyst
|
||||
goal: Conduct comprehensive AI research with real-time guidance
|
||||
backstory: You are an expert AI researcher who can receive and incorporate real-time guidance during your work
|
||||
message_steering: true # Enable real-time message steering
|
||||
tools:
|
||||
- internet_search
|
||||
- write_file
|
||||
tasks:
|
||||
research_task:
|
||||
description: |
|
||||
Research the latest developments in AI and write a comprehensive report.
|
||||
You may receive additional guidance during execution via message steering.
|
||||
expected_output: |
|
||||
A well-structured research report covering recent AI developments,
|
||||
incorporating any real-time guidance received during execution.
|
||||
|
||||
writer:
|
||||
role: Technical Writer
|
||||
goal: Write clear, engaging content based on research
|
||||
backstory: You are a skilled technical writer who can adapt content based on real-time feedback
|
||||
message_steering: true # Enable steering for dynamic content adjustment
|
||||
tools:
|
||||
- read_file
|
||||
- write_file
|
||||
tasks:
|
||||
writing_task:
|
||||
description: |
|
||||
Take the research findings and create an accessible article for a general audience.
|
||||
Adjust style and focus based on any steering messages received.
|
||||
expected_output: |
|
||||
A polished article that transforms technical research into accessible content,
|
||||
refined based on real-time steering guidance.
|
||||
|
||||
dependencies:
|
||||
- writing_task: [research_task]
|
||||
|
||||
# Usage Instructions:
|
||||
# 1. Run: praisonai --agent-file message_steering_example.yaml
|
||||
# 2. While running, use steering API to send guidance:
|
||||
# agent.steer("Focus more on practical applications")
|
||||
# agent.steer("Keep it under 500 words", priority=15)
|
||||
# agent.steer("Add more examples", priority=10)
|
||||
@@ -0,0 +1,66 @@
|
||||
# Multi-Telegram Channel Hermes Workforce Example
|
||||
# Generated by `praisonai onboard` with multi-channel support
|
||||
# Start with: praisonai gateway start --config multi-telegram-hermes-workforce.yaml
|
||||
|
||||
gateway:
|
||||
host: "127.0.0.1"
|
||||
port: 8765
|
||||
|
||||
# Legacy single-agent block for backward compatibility
|
||||
agent:
|
||||
name: "assistant"
|
||||
instructions: "You are a helpful AI assistant. You can search the web, manage schedules, and remember past conversations. Use your tools proactively."
|
||||
model: "gpt-4o-mini"
|
||||
|
||||
# Multi-agent workforce with different specialized roles
|
||||
agents:
|
||||
cfo:
|
||||
instructions: "You are a CFO agent. You are a helpful AI assistant. Use your tools proactively to help with CFO-related tasks like financial analysis, budget planning, and cost optimization."
|
||||
model: gpt-4o-mini
|
||||
memory: false
|
||||
|
||||
ops:
|
||||
instructions: "You are an ops agent. You are a helpful AI assistant. Use your tools proactively to help with ops-related tasks like system monitoring, deployment, and infrastructure management."
|
||||
model: gpt-4o-mini
|
||||
memory: false
|
||||
|
||||
content:
|
||||
instructions: "You are a content agent. You are a helpful AI assistant. Use your tools proactively to help with content-related tasks like writing, editing, and content strategy."
|
||||
model: gpt-4o-mini
|
||||
memory: false
|
||||
|
||||
# Multiple Telegram channels - each with unique tokens and routing
|
||||
channels:
|
||||
telegram_cfo:
|
||||
platform: telegram
|
||||
token: ${TELEGRAM_CFO_BOT_TOKEN}
|
||||
group_policy: mention_only
|
||||
allowed_users: ${TELEGRAM_ALLOWED_USERS}
|
||||
# auto_approve_tools: true
|
||||
# Chat bots cannot show CLI approval prompts; safe tools auto-approved by default.
|
||||
routes:
|
||||
default: cfo
|
||||
|
||||
telegram_ops:
|
||||
platform: telegram
|
||||
token: ${TELEGRAM_OPS_BOT_TOKEN}
|
||||
group_policy: mention_only
|
||||
allowed_users: ${TELEGRAM_ALLOWED_USERS}
|
||||
# auto_approve_tools: true
|
||||
# Chat bots cannot show CLI approval prompts; safe tools auto-approved by default.
|
||||
routes:
|
||||
default: ops
|
||||
|
||||
telegram_content:
|
||||
platform: telegram
|
||||
token: ${TELEGRAM_CONTENT_BOT_TOKEN}
|
||||
group_policy: mention_only
|
||||
allowed_users: ${TELEGRAM_ALLOWED_USERS}
|
||||
# auto_approve_tools: true
|
||||
# Chat bots cannot show CLI approval prompts; safe tools auto-approved by default.
|
||||
routes:
|
||||
default: content
|
||||
|
||||
# Legacy routing block
|
||||
routing:
|
||||
default: assistant
|
||||
@@ -0,0 +1,148 @@
|
||||
# Named Toolsets YAML Example
|
||||
# This example demonstrates using toolsets in YAML configuration
|
||||
|
||||
# Global configuration
|
||||
config:
|
||||
llm: gpt-4o-mini
|
||||
memory: true
|
||||
|
||||
# Define agents with different toolsets
|
||||
agents:
|
||||
# Research agent with research toolset (includes web tools)
|
||||
researcher:
|
||||
role: Senior Research Analyst
|
||||
goal: Research topics thoroughly using web search and analysis tools
|
||||
instructions: |
|
||||
You are a senior research analyst with access to web search and file tools.
|
||||
Research topics comprehensively and provide detailed findings.
|
||||
toolsets:
|
||||
- research # Includes: web search, file tools, basic analysis
|
||||
|
||||
# Writer agent with safe toolset for writing tasks
|
||||
writer:
|
||||
role: Content Writer
|
||||
goal: Create engaging content based on research
|
||||
instructions: |
|
||||
You are a skilled content writer. Transform research into clear,
|
||||
engaging content for the target audience.
|
||||
toolsets:
|
||||
- safe # Safe, read-only tools for writing
|
||||
|
||||
# Developer agent with development toolset
|
||||
developer:
|
||||
role: Software Developer
|
||||
goal: Create and modify code based on requirements
|
||||
instructions: |
|
||||
You are a software developer with access to development tools.
|
||||
Write, test, and debug code according to specifications.
|
||||
toolsets:
|
||||
- development # Includes: code tools, file operations, testing
|
||||
|
||||
# Data analyst with mixed tools and toolsets
|
||||
analyst:
|
||||
role: Data Analyst
|
||||
goal: Analyze data and generate insights
|
||||
instructions: |
|
||||
You are a data analyst. Process data files and generate
|
||||
actionable insights and visualizations.
|
||||
tools:
|
||||
- read_file # Explicit tool
|
||||
- write_file # Explicit tool
|
||||
toolsets:
|
||||
- safe # Safe toolset for analysis
|
||||
|
||||
# Security-focused agent with minimal toolset
|
||||
security_auditor:
|
||||
role: Security Auditor
|
||||
goal: Review and audit for security issues
|
||||
instructions: |
|
||||
You are a security auditor with minimal tool access.
|
||||
Focus on reading and analyzing without making changes.
|
||||
toolsets:
|
||||
- safe # Minimal, safe tools only
|
||||
|
||||
# Define tasks for the workflow
|
||||
tasks:
|
||||
# Research task using research toolset
|
||||
research_phase:
|
||||
description: |
|
||||
Research the latest trends in AI agent frameworks.
|
||||
Focus on:
|
||||
- Popular frameworks and their features
|
||||
- Industry adoption patterns
|
||||
- Security considerations
|
||||
- Performance benchmarks
|
||||
expected_output: |
|
||||
A comprehensive research report covering AI agent frameworks,
|
||||
including trends, adoption, security, and performance data.
|
||||
agent: researcher
|
||||
|
||||
# Content creation using safe toolset
|
||||
content_creation:
|
||||
description: |
|
||||
Create a technical blog post based on the research findings.
|
||||
The post should be:
|
||||
- Well-structured with clear sections
|
||||
- Accessible to technical and non-technical audiences
|
||||
- Include actionable insights
|
||||
- 1000-1500 words
|
||||
expected_output: |
|
||||
A polished blog post about AI agent frameworks suitable
|
||||
for publication on a technical blog.
|
||||
agent: writer
|
||||
|
||||
# Code development using development toolset
|
||||
code_examples:
|
||||
description: |
|
||||
Create code examples demonstrating key concepts from the research.
|
||||
Include:
|
||||
- Simple "hello world" style examples
|
||||
- Intermediate use cases
|
||||
- Best practices examples
|
||||
- Proper documentation
|
||||
expected_output: |
|
||||
A collection of well-documented code examples that illustrate
|
||||
the concepts discussed in the research and blog post.
|
||||
agent: developer
|
||||
|
||||
# Data analysis using mixed tools/toolsets
|
||||
trend_analysis:
|
||||
description: |
|
||||
Analyze the research data to identify trends and patterns.
|
||||
Create:
|
||||
- Summary statistics
|
||||
- Trend analysis
|
||||
- Recommendations based on findings
|
||||
expected_output: |
|
||||
A data analysis report with trends, patterns, and
|
||||
actionable recommendations for AI framework adoption.
|
||||
agent: analyst
|
||||
|
||||
# Security review using minimal toolset
|
||||
security_review:
|
||||
description: |
|
||||
Review all outputs for security considerations.
|
||||
Check for:
|
||||
- Potential security vulnerabilities in code examples
|
||||
- Security best practices in recommendations
|
||||
- Privacy considerations in data handling
|
||||
expected_output: |
|
||||
A security review report identifying any security concerns
|
||||
and recommendations for secure implementation.
|
||||
agent: security_auditor
|
||||
|
||||
# Workflow process (sequential execution)
|
||||
process: sequential
|
||||
|
||||
# Optional: Define workflow dependencies
|
||||
# tasks:
|
||||
# research_phase:
|
||||
# dependencies: []
|
||||
# content_creation:
|
||||
# dependencies: [research_phase]
|
||||
# code_examples:
|
||||
# dependencies: [research_phase]
|
||||
# trend_analysis:
|
||||
# dependencies: [research_phase]
|
||||
# security_review:
|
||||
# dependencies: [content_creation, code_examples, trend_analysis]
|
||||
@@ -95,4 +95,5 @@ chroma_db/
|
||||
.windsurf
|
||||
.ruff_cache
|
||||
*.sfdx
|
||||
.sfdx/
|
||||
.sfdx/
|
||||
.cursor/
|
||||
@@ -378,7 +378,7 @@ Agent parameters are consolidated into Config objects following the pattern:
|
||||
|
||||
| Config Object | Replaces (Deprecated) |
|
||||
|--------------|----------------------|
|
||||
| `execution=ExecutionConfig(code_execution=True, code_mode="safe", rate_limiter=obj)` | `allow_code_execution`, `code_execution_mode`, `rate_limiter` |
|
||||
| `execution=ExecutionConfig(code_execution=True, code_mode="safe", rate_limiter=obj, context_compaction=False)` | `allow_code_execution`, `code_execution_mode`, `rate_limiter` |
|
||||
| `memory=MemoryConfig(auto_save="session_name")` | `auto_save` |
|
||||
| `autonomy=AutonomyConfig(verification_hooks=[...])` | `verification_hooks` |
|
||||
| `handoffs=[other_agent]` | `allow_delegation` |
|
||||
@@ -390,6 +390,8 @@ Agent parameters are consolidated into Config objects following the pattern:
|
||||
|
||||
**Deprecated standalone params still work** (backward compatible) but emit `DeprecationWarning`.
|
||||
|
||||
> **📢 Deprecation Notice:** `ExecutionConfig.context_compaction` currently defaults to `False` during a deprecation period. In the next release, it will default to `True` for automatic proactive context overflow protection. To opt out explicitly, set `context_compaction=False`. To use the new behavior early, set `context_compaction=True`.
|
||||
|
||||
```python
|
||||
# ❌ Old way (deprecated)
|
||||
agent = Agent(
|
||||
@@ -413,6 +415,7 @@ agent = Agent(
|
||||
code_execution=True,
|
||||
code_mode="safe",
|
||||
rate_limiter=my_limiter,
|
||||
context_compaction=False, # prepare for next release when default becomes True; warning still fires during deprecation period
|
||||
),
|
||||
memory=MemoryConfig(auto_save="my_session"),
|
||||
autonomy=AutonomyConfig(verification_hooks=[my_hook]),
|
||||
|
||||
@@ -1,35 +1,70 @@
|
||||
# PraisonAI Agents - Benchmark Results
|
||||
|
||||
**Generated:** 2025-12-18 14:40:22
|
||||
**Iterations:** 100
|
||||
**Test:** Agent instantiation (without tools)
|
||||
**Generated:** 2026-06-17
|
||||
**Methodology:** 10 warmup runs + 1000 measured iterations (`performance_benchmark.py` config)
|
||||
**Config:** `Agent(name=..., instructions=..., model="gpt-4o-mini", tools=[get_weather], output="silent")`
|
||||
|
||||
## Results
|
||||
## Agent Instantiation Time
|
||||
|
||||
| Framework | Avg Time (μs) | Relative |
|
||||
|-----------|---------------|----------|
|
||||
| **PraisonAI** | **3.77** | **1.00x (fastest)** |
|
||||
| OpenAI Agents SDK | 5.26 | 1.39x |
|
||||
| Agno | 5.64 | 1.49x |
|
||||
| PraisonAI (LiteLLM) | 7.56 | 2.00x |
|
||||
| PydanticAI | 226.94 | 60.16x |
|
||||
| LangGraph | 4,558.71 | 1,209x |
|
||||
| CrewAI | 15,607.92 | 4,138x |
|
||||
| Framework | Avg Time (μs) | Min (μs) | Median (μs) | Relative |
|
||||
|-----------|---------------|----------|-------------|----------|
|
||||
| OpenAI Agents SDK | 5.05 | — | — | 1.00x (fastest) |
|
||||
| Agno | 5.27 | — | — | 1.04x |
|
||||
| **PraisonAI** | **13.62** | **12.58** | **12.96** | **2.69x** |
|
||||
| PraisonAI (LiteLLM) | 14.49 | 13.37 | 13.71 | 2.86x |
|
||||
|
||||
### PraisonAI detail (1000 iter)
|
||||
|
||||
| Variant | Avg (μs) | Min (μs) | Median (μs) | Std (μs) |
|
||||
|---------|----------|----------|-------------|----------|
|
||||
| With tools (`gpt-4o-mini`) | 13.62 | 12.58 | 12.96 | 2.73 |
|
||||
| With tools (`openai/gpt-4o-mini`) | 14.49 | 13.37 | 13.71 | 4.14 |
|
||||
| Minimal, no tools | 13.40 | — | — | — |
|
||||
|
||||
## Historical baseline
|
||||
|
||||
| Source | Config | Avg (μs) | Notes |
|
||||
|--------|--------|----------|-------|
|
||||
| `performance_benchmark.py` (Dec 2025) | tools + 1000 iter | **3.20** | Pre-init-bloat baseline |
|
||||
| `simple_benchmark.py` (Dec 2025) | no tools, 100 iter | **3.77** | Saved in prior `BENCHMARK_RESULTS.md` |
|
||||
| Current (Jun 2026) | tools + 1000 iter | **13.62** | After lazy-init optimisations (was ~31 μs pre-fix) |
|
||||
|
||||
## Package Versions
|
||||
|
||||
| Package | Version |
|
||||
|---------|--------|
|
||||
| PraisonAI | 0.1.5 |
|
||||
| Agno | 2.3.14 |
|
||||
| PydanticAI | 1.35.0 |
|
||||
| OpenAI Agents SDK | 0.6.3 |
|
||||
| LangGraph | 1.0.5 |
|
||||
| CrewAI | 1.6.1 |
|
||||
|---------|---------|
|
||||
| PraisonAI | 1.6.60 |
|
||||
| Agno | 2.4.2 |
|
||||
| OpenAI Agents SDK | 0.7.0 |
|
||||
|
||||
## How to Reproduce
|
||||
|
||||
PraisonAI-only (fast; avoids CrewAI/LangGraph import hang):
|
||||
|
||||
```bash
|
||||
cd praisonai-agents
|
||||
python benchmarks/simple_benchmark.py
|
||||
PYTHONWARNINGS='ignore::DeprecationWarning' python -c "
|
||||
import time, statistics
|
||||
from typing import Literal
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def get_weather(city: Literal['nyc', 'sf']):
|
||||
return 'cloudy' if city == 'nyc' else 'sunny'
|
||||
|
||||
def bench(fn, n=1000, w=10):
|
||||
for _ in range(w): fn()
|
||||
times = []
|
||||
for _ in range(n):
|
||||
s = time.perf_counter(); fn(); times.append(time.perf_counter()-s)
|
||||
print(f'avg={statistics.mean(times)*1e6:.2f} μs')
|
||||
|
||||
bench(lambda: Agent(name='Test', instructions='Hi', model='gpt-4o-mini', tools=[get_weather], output='silent'))
|
||||
"
|
||||
```
|
||||
|
||||
Full multi-framework benchmark:
|
||||
|
||||
```bash
|
||||
cd praisonai-agents
|
||||
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/performance_benchmark.py --save
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
# Agent Instantiation — Root Cause Analysis
|
||||
|
||||
**Date:** 2026-06-17
|
||||
**Version:** praisonaiagents 1.6.58 (dev)
|
||||
**Methodology:** 10 warmup + 1000 measured iterations, `time.perf_counter()`
|
||||
|
||||
## Summary
|
||||
|
||||
Instantiation regressed from **~3.2 μs** (Dec 2025) to **~31 μs** (~10×). After fixes, steady-state is **~16 μs** (~2× historical). PraisonAI is competitive with Agno (~5 μs) on warmed benchmarks using `model=`.
|
||||
|
||||
## Before vs After Fixes
|
||||
|
||||
| Config | Historical | Pre-fix | Post-fix |
|
||||
|--------|------------|---------|----------|
|
||||
| Minimal (`name`, `output="silent"`) | 3.20 μs | 31.36 μs | **16.35 μs** |
|
||||
| `model="gpt-4o-mini"` | 3.77 μs | 32.19 μs | **16.86 μs** |
|
||||
| `llm="gpt-4o-mini"` (deprecated) | 3.77 μs | 46.48 μs | **~18 μs** (warn once) |
|
||||
| LiteLLM `openai/gpt-4o-mini` cold | 7.31 μs | **3.7 s** | **53.63 μs** |
|
||||
| LiteLLM warmed instantiate | — | 60 μs | **19.83 μs** |
|
||||
| Import `Agent` | — | 20.8 ms | **29.6 ms** |
|
||||
|
||||
## Root Causes (validated)
|
||||
|
||||
| ID | Cause | Status | Fix applied |
|
||||
|----|-------|--------|-------------|
|
||||
| H1 | Config loader cold start (+9 ms first init) | CONFIRMED | Fast path when no config defaults (`_defaults_has_any_values`) |
|
||||
| H2 | LoopGuard on every init (~2 μs) | CONFIRMED | Lazy `_ensure_loop_guard()` on first tool call |
|
||||
| H3 | Approval presets | REJECTED | No change needed |
|
||||
| H4 | `llm=` deprecation every init (+16 μs) | CONFIRMED | Warn once per process in `warn_deprecated_param` |
|
||||
| H5 | Init bloat / config resolution (~25 μs) | CONFIRMED | Partially addressed via loader fast path |
|
||||
| H6 | LiteLLM eager `LLM()` import (+3.7 s) | CONFIRMED | Deferred `_llm_init_params` + lazy `llm_instance` property |
|
||||
| H7 | `ExecutionConfig` deprecation every init | CONFIRMED | Fixed stack walk; internal SDK path cached |
|
||||
|
||||
## cProfile — Post-fix (500 warmed `model=` inits)
|
||||
|
||||
| Function | Time (ms) | Per call |
|
||||
|----------|-----------|----------|
|
||||
| `Agent.__init__` | 8 | ~16 μs |
|
||||
| `apply_config_defaults` | 1 | ~2 μs |
|
||||
| `ExecutionConfig.__post_init__` | 1 | ~2 μs (first call only walks stack) |
|
||||
| `_warnings.warn` | **0** | removed from hot path |
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. **`praisonaiagents/config/feature_configs.py`** — Fix `ExecutionConfig.__post_init__` deprecation guard; cache internal SDK detection.
|
||||
2. **`praisonaiagents/utils/deprecation.py`** — Warn once per process for deprecated params.
|
||||
3. **`praisonaiagents/config/loader.py`** — Skip `apply_config_defaults` lookups when config has no defaults.
|
||||
4. **`praisonaiagents/agent/agent.py`** — Lazy LoopGuard, lazy LLM instance, deferred LiteLLM params.
|
||||
5. **`benchmarks/simple_benchmark.py`** — Use `model=`, add 10-iteration warmup.
|
||||
6. **`benchmarks/performance_benchmark.py`** — Use `model=` instead of deprecated `llm=`.
|
||||
|
||||
## Remaining gap (~16 μs vs ~3 μs)
|
||||
|
||||
The residual ~13 μs is primarily **`Agent.__init__` size** (~1200 lines of consolidated config resolution added since v0.1.5). Further optimisation requires a dedicated slim fast-path for `Agent(name=..., output="silent")` without full config object churn.
|
||||
|
||||
## How to reproduce
|
||||
|
||||
```bash
|
||||
cd praisonai-agents
|
||||
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/simple_benchmark.py
|
||||
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/performance_benchmark.py
|
||||
```
|
||||
@@ -120,8 +120,8 @@ def run_benchmark(iterations: int = 100) -> List[ParamResult]:
|
||||
('backstory', 'I am an AI assistant.', 'backstory="..."'),
|
||||
|
||||
# LLM configuration
|
||||
('llm', 'gpt-4o-mini', 'llm="gpt-4o-mini"'),
|
||||
('llm', 'openai/gpt-4o-mini', 'llm="openai/gpt-4o-mini" (LiteLLM)'),
|
||||
('model', 'gpt-4o-mini', 'model="gpt-4o-mini"'),
|
||||
('model', 'openai/gpt-4o-mini', 'model="openai/gpt-4o-mini" (LiteLLM)'),
|
||||
|
||||
# Tools
|
||||
('tools', [sample_tool], 'tools=[sample_tool]'),
|
||||
@@ -298,7 +298,7 @@ def save_results(results: List[ParamResult], filename: str = 'PARAM_IMPACT_RESUL
|
||||
f.write('### Best Practices\n\n')
|
||||
f.write('1. Use `output="silent"` for production workloads\n')
|
||||
f.write('2. Only enable `context=True` when needed for long conversations\n')
|
||||
f.write('3. Use `llm="gpt-4o-mini"` (OpenAI SDK) for fastest instantiation\n')
|
||||
f.write('3. Use `model="gpt-4o-mini"` for fastest instantiation\n')
|
||||
f.write('4. Avoid `memory=True` unless persistent memory is required\n')
|
||||
|
||||
f.write('\n## How to Reproduce\n\n')
|
||||
|
||||
@@ -185,7 +185,7 @@ def instantiate_praisonai_agent():
|
||||
return PraisonAgent(
|
||||
name="Test Agent",
|
||||
instructions="Be concise, reply with one sentence.",
|
||||
llm="gpt-4o-mini",
|
||||
model="gpt-4o-mini",
|
||||
tools=tools,
|
||||
output="silent"
|
||||
)
|
||||
@@ -196,7 +196,7 @@ def instantiate_praisonai_litellm_agent():
|
||||
return PraisonAgent(
|
||||
name="Test Agent",
|
||||
instructions="Be concise, reply with one sentence.",
|
||||
llm="openai/gpt-4o-mini",
|
||||
model="openai/gpt-4o-mini",
|
||||
tools=tools,
|
||||
output="silent"
|
||||
)
|
||||
|
||||
@@ -79,10 +79,20 @@ def get_time(timezone: Literal["est", "pst"]) -> str:
|
||||
|
||||
|
||||
def calculate(expression: str) -> str:
|
||||
"""Calculate a math expression."""
|
||||
"""Calculate a math expression safely."""
|
||||
try:
|
||||
result = eval(expression)
|
||||
return f"Result: {result}"
|
||||
import ast, operator
|
||||
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
|
||||
ast.Mod: operator.mod, ast.Pow: operator.pow,
|
||||
ast.USub: operator.neg, ast.UAdd: operator.pos}
|
||||
def _ev(n):
|
||||
if isinstance(n, ast.Expression): return _ev(n.body)
|
||||
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
|
||||
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
|
||||
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
|
||||
raise ValueError(f"Unsupported: {ast.dump(n)}")
|
||||
return f"Result: {_ev(ast.parse(expression, mode='eval'))}"
|
||||
except Exception:
|
||||
return "Error: Invalid expression"
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import argparse
|
||||
|
||||
|
||||
ITERATIONS = 100
|
||||
WARMUP = 10
|
||||
|
||||
|
||||
def sample_tool(city: Literal['nyc', 'sf']):
|
||||
@@ -23,8 +24,10 @@ def sample_tool(city: Literal['nyc', 'sf']):
|
||||
return 'sunny'
|
||||
|
||||
|
||||
def measure_instantiation(create_fn, iterations=ITERATIONS):
|
||||
def measure_instantiation(create_fn, iterations=ITERATIONS, warmup=WARMUP):
|
||||
"""Measure average instantiation time in microseconds."""
|
||||
for _ in range(warmup):
|
||||
create_fn()
|
||||
times = []
|
||||
for _ in range(iterations):
|
||||
start = time.perf_counter()
|
||||
@@ -48,11 +51,11 @@ def run_benchmark():
|
||||
from praisonaiagents import Agent as PraisonAgent
|
||||
|
||||
results['PraisonAI'] = measure_instantiation(
|
||||
lambda: PraisonAgent(name='Test', llm='gpt-4o-mini', output="silent")
|
||||
lambda: PraisonAgent(name='Test', model='gpt-4o-mini', output="silent")
|
||||
)
|
||||
|
||||
results['PraisonAI (LiteLLM)'] = measure_instantiation(
|
||||
lambda: PraisonAgent(name='Test', llm='openai/gpt-4o-mini', output="silent")
|
||||
lambda: PraisonAgent(name='Test', model='openai/gpt-4o-mini', output="silent")
|
||||
)
|
||||
|
||||
# Other frameworks for comparison
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Context Compaction Example - Python API
|
||||
|
||||
Demonstrates how to configure context compaction policies in PraisonAI agents.
|
||||
This provides examples for CLI/YAML parity.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.config.feature_configs import ExecutionConfig
|
||||
from praisonaiagents.context.adapters import ContextCompactionPolicyAdapter
|
||||
from praisonaiagents.context.protocols import CompactionStrategy
|
||||
|
||||
|
||||
def example_default_behavior():
|
||||
"""Example 1: Default behavior (during deprecation period)."""
|
||||
print("=== Example 1: Default Behavior ===")
|
||||
|
||||
# Currently defaults to False with deprecation warning
|
||||
agent = Agent(
|
||||
name="default_agent",
|
||||
instructions="You are a helpful assistant."
|
||||
)
|
||||
|
||||
print(f"Context compaction enabled: {agent.execution.context_compaction}")
|
||||
print("Note: Currently False during deprecation period, will change to True in next release\n")
|
||||
|
||||
|
||||
def example_explicit_disable():
|
||||
"""Example 2: Explicitly disable context compaction."""
|
||||
print("=== Example 2: Explicitly Disabled ===")
|
||||
|
||||
agent = Agent(
|
||||
name="no_compaction_agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
execution=ExecutionConfig(context_compaction=False)
|
||||
)
|
||||
|
||||
print(f"Context compaction enabled: {agent.execution.context_compaction}")
|
||||
print("No automatic compaction will occur\n")
|
||||
|
||||
|
||||
def example_default_policy():
|
||||
"""Example 3: Use default compaction policy."""
|
||||
print("=== Example 3: Default Policy ===")
|
||||
|
||||
agent = Agent(
|
||||
name="default_policy_agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
execution=ExecutionConfig(context_compaction=True)
|
||||
)
|
||||
|
||||
print(f"Context compaction enabled: {agent.execution.context_compaction}")
|
||||
print("Will use balanced default policy (trigger at 90%, preserve 5 recent turns)\n")
|
||||
|
||||
|
||||
def example_conservative_policy():
|
||||
"""Example 4: Conservative compaction policy."""
|
||||
print("=== Example 4: Conservative Policy ===")
|
||||
|
||||
conservative_policy = ContextCompactionPolicyAdapter(
|
||||
trigger_at=0.80, # Trigger at 80% capacity
|
||||
strategy=CompactionStrategy.DROP_OLDEST_TOOLS,
|
||||
preserve_last_n_turns=8, # Keep more conversation history
|
||||
target_utilization=0.60, # Target 60% utilization after compaction
|
||||
aggressive_tool_truncation=False
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="conservative_agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
execution=ExecutionConfig(context_compaction=conservative_policy)
|
||||
)
|
||||
|
||||
print(f"Policy trigger: {conservative_policy.trigger_at}")
|
||||
print(f"Strategy: {conservative_policy.strategy}")
|
||||
print(f"Preserve turns: {conservative_policy.preserve_last_n_turns}")
|
||||
print(f"Target utilization: {conservative_policy.target_utilization}\n")
|
||||
|
||||
|
||||
def example_aggressive_policy():
|
||||
"""Example 5: Aggressive compaction policy."""
|
||||
print("=== Example 5: Aggressive Policy ===")
|
||||
|
||||
aggressive_policy = ContextCompactionPolicyAdapter(
|
||||
trigger_at=0.95, # Wait until 95% capacity
|
||||
strategy=CompactionStrategy.SUMMARISE, # Use LLM summarization
|
||||
preserve_last_n_turns=3, # Keep only recent exchanges
|
||||
target_utilization=0.75, # Target higher utilization
|
||||
aggressive_tool_truncation=True, # Truncate large tool outputs
|
||||
model_overrides={
|
||||
"gpt-4": {"trigger_at": 0.92}, # Different threshold for gpt-4
|
||||
"claude-3": {"trigger_at": 0.88}
|
||||
}
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="aggressive_agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
execution=ExecutionConfig(context_compaction=aggressive_policy)
|
||||
)
|
||||
|
||||
print(f"Policy trigger: {aggressive_policy.trigger_at}")
|
||||
print(f"Strategy: {aggressive_policy.strategy}")
|
||||
print(f"Preserve turns: {aggressive_policy.preserve_last_n_turns}")
|
||||
print(f"Model overrides: {aggressive_policy.model_overrides}\n")
|
||||
|
||||
|
||||
def example_yaml_config():
|
||||
"""Example 6: YAML configuration equivalent."""
|
||||
print("=== Example 6: YAML Configuration Equivalent ===")
|
||||
print("""
|
||||
# agent_config.yaml
|
||||
name: "helpful_agent"
|
||||
instructions: "You are a helpful assistant."
|
||||
llm: "gpt-4o-mini"
|
||||
execution:
|
||||
context_compaction:
|
||||
trigger_at: 0.85
|
||||
strategy: "drop_oldest_tools"
|
||||
preserve_last_n_turns: 6
|
||||
target_utilization: 0.65
|
||||
aggressive_tool_truncation: true
|
||||
model_overrides:
|
||||
"gpt-4":
|
||||
trigger_at: 0.90
|
||||
strategy: "summarise"
|
||||
""")
|
||||
|
||||
# Python equivalent:
|
||||
policy = ContextCompactionPolicyAdapter(
|
||||
trigger_at=0.85,
|
||||
strategy="drop_oldest_tools",
|
||||
preserve_last_n_turns=6,
|
||||
target_utilization=0.65,
|
||||
aggressive_tool_truncation=True,
|
||||
model_overrides={
|
||||
"gpt-4": {
|
||||
"trigger_at": 0.90,
|
||||
"strategy": "summarise"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="helpful_agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
llm="gpt-4o-mini",
|
||||
execution=ExecutionConfig(context_compaction=policy)
|
||||
)
|
||||
|
||||
print(f"Loaded policy trigger: {policy.trigger_at}")
|
||||
print(f"Loaded strategy: {policy.strategy}\n")
|
||||
|
||||
|
||||
def example_cli_equivalent():
|
||||
"""Example 7: CLI equivalent commands."""
|
||||
print("=== Example 7: CLI Equivalent ===")
|
||||
print("""
|
||||
# Basic usage (will use default policy when enabled)
|
||||
praisonai agent run --name "assistant" --instructions "Be helpful"
|
||||
|
||||
# Disable compaction explicitly
|
||||
praisonai agent run --name "assistant" --execution '{"context_compaction": false}'
|
||||
|
||||
# Use conservative policy
|
||||
praisonai agent run --name "assistant" \\
|
||||
--execution '{
|
||||
"context_compaction": {
|
||||
"trigger_at": 0.80,
|
||||
"strategy": "drop_oldest_tools",
|
||||
"preserve_last_n_turns": 8
|
||||
}
|
||||
}'
|
||||
|
||||
# Load from YAML config file
|
||||
praisonai agent run --config agent_config.yaml
|
||||
""")
|
||||
|
||||
|
||||
def example_serialization_round_trip():
|
||||
"""Example 8: Serialization round-trip."""
|
||||
print("=== Example 8: Serialization Round-Trip ===")
|
||||
|
||||
# Create agent with custom policy
|
||||
original_policy = ContextCompactionPolicyAdapter(
|
||||
trigger_at=0.88,
|
||||
strategy=CompactionStrategy.SUMMARISE
|
||||
)
|
||||
|
||||
config = ExecutionConfig(context_compaction=original_policy)
|
||||
|
||||
# Serialize to dict (for JSON/YAML storage)
|
||||
config_dict = config.to_dict()
|
||||
print(f"Serialized context_compaction type: {type(config_dict['context_compaction'])}")
|
||||
|
||||
# Deserialize back to object
|
||||
restored_config = ExecutionConfig.from_dict(config_dict)
|
||||
restored_policy = restored_config.context_compaction
|
||||
|
||||
print(f"Restored policy type: {type(restored_policy)}")
|
||||
print(f"Attributes preserved: trigger_at={restored_policy.trigger_at}, strategy={restored_policy.strategy}")
|
||||
print("✓ Round-trip serialization successful\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Context Compaction Configuration Examples\n")
|
||||
print("=" * 50)
|
||||
|
||||
try:
|
||||
example_default_behavior()
|
||||
example_explicit_disable()
|
||||
example_default_policy()
|
||||
example_conservative_policy()
|
||||
example_aggressive_policy()
|
||||
example_yaml_config()
|
||||
example_cli_equivalent()
|
||||
example_serialization_round_trip()
|
||||
|
||||
print("=" * 50)
|
||||
print("✅ All examples completed successfully!")
|
||||
print("\nFor more information, see:")
|
||||
print("- Documentation: https://docs.praisonai.com/context-compaction")
|
||||
print("- Examples: examples/context_compaction_example.py")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Example failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -114,6 +114,9 @@ def _get_lazy_cache():
|
||||
# ============================================================================
|
||||
|
||||
_LAZY_IMPORTS = {
|
||||
# Version information (lazy loaded to avoid file I/O on import)
|
||||
'__version__': ('praisonaiagents._version', '__version__'),
|
||||
|
||||
# Tools (moved from eager imports for lazy loading)
|
||||
'Tools': ('praisonaiagents.tools.tools', 'Tools'),
|
||||
'BaseTool': ('praisonaiagents.tools.base', 'BaseTool'),
|
||||
@@ -127,6 +130,23 @@ _LAZY_IMPORTS = {
|
||||
'get_tool': ('praisonaiagents.tools.registry', 'get_tool'),
|
||||
'ToolRegistry': ('praisonaiagents.tools.registry', 'ToolRegistry'),
|
||||
|
||||
# Toolsets (named toolset groups)
|
||||
'ToolsetSpec': ('praisonaiagents.toolsets', 'ToolsetSpec'),
|
||||
'ToolsetRegistry': ('praisonaiagents.toolsets', 'ToolsetRegistry'),
|
||||
'get_toolset_registry': ('praisonaiagents.toolsets', 'get_toolset_registry'),
|
||||
'register_toolset': ('praisonaiagents.toolsets', 'register_toolset'),
|
||||
'resolve_toolset': ('praisonaiagents.toolsets', 'resolve_toolset'),
|
||||
'resolve_toolsets': ('praisonaiagents.toolsets', 'resolve_toolsets'),
|
||||
'list_toolsets': ('praisonaiagents.toolsets', 'list_toolsets'),
|
||||
'get_toolset': ('praisonaiagents.toolsets', 'get_toolset'),
|
||||
'unregister_toolset': ('praisonaiagents.toolsets', 'unregister_toolset'),
|
||||
'has_toolset': ('praisonaiagents.toolsets', 'has_toolset'),
|
||||
|
||||
# Run outcomes - typed validation and agent execution results
|
||||
'AgentRunOutcome': ('praisonaiagents.run_outcome', 'AgentRunOutcome'),
|
||||
'RunStatus': ('praisonaiagents.run_outcome', 'RunStatus'),
|
||||
'validate_decision_string': ('praisonaiagents.run_outcome', 'validate_decision_string'),
|
||||
|
||||
# Main display utilities (imports rich)
|
||||
'TaskOutput': ('praisonaiagents.main', 'TaskOutput'),
|
||||
'ReflectionOutput': ('praisonaiagents.main', 'ReflectionOutput'),
|
||||
@@ -174,6 +194,7 @@ _LAZY_IMPORTS = {
|
||||
'RECOMMENDED_PROMPT_PREFIX': ('praisonaiagents.agent.handoff', 'RECOMMENDED_PROMPT_PREFIX'),
|
||||
'prompt_with_handoff_instructions': ('praisonaiagents.agent.handoff', 'prompt_with_handoff_instructions'),
|
||||
'HandoffConfig': ('praisonaiagents.agent.handoff', 'HandoffConfig'),
|
||||
'HandoffToolPolicy': ('praisonaiagents.agent.handoff', 'HandoffToolPolicy'),
|
||||
'HandoffResult': ('praisonaiagents.agent.handoff', 'HandoffResult'),
|
||||
'HandoffInputData': ('praisonaiagents.agent.handoff', 'HandoffInputData'),
|
||||
'ContextPolicy': ('praisonaiagents.agent.handoff', 'ContextPolicy'),
|
||||
@@ -343,6 +364,7 @@ _LAZY_IMPORTS = {
|
||||
# UI (optional)
|
||||
'AGUI': ('praisonaiagents.ui.agui', 'AGUI'),
|
||||
'A2A': ('praisonaiagents.ui.a2a', 'A2A'),
|
||||
'A2UI': ('praisonaiagents.ui.a2ui', 'A2UI'),
|
||||
|
||||
# Feature configs
|
||||
'MemoryConfig': ('praisonaiagents.config.feature_configs', 'MemoryConfig'),
|
||||
@@ -383,6 +405,18 @@ _LAZY_IMPORTS = {
|
||||
'MultiAgentExecutionConfig': ('praisonaiagents.config.feature_configs', 'MultiAgentExecutionConfig'),
|
||||
'MultiAgentPlanningConfig': ('praisonaiagents.config.feature_configs', 'MultiAgentPlanningConfig'),
|
||||
'MultiAgentMemoryConfig': ('praisonaiagents.config.feature_configs', 'MultiAgentMemoryConfig'),
|
||||
'ToolSearchConfig': ('praisonaiagents.config.feature_configs', 'ToolSearchConfig'),
|
||||
|
||||
# Context compaction policies
|
||||
'ContextCompactionPolicy': ('praisonaiagents.context.policy', 'ContextCompactionPolicy'),
|
||||
'ContextCompactionPolicyProtocol': ('praisonaiagents.context.policy', 'ContextCompactionPolicyProtocol'),
|
||||
'CompactionRoute': ('praisonaiagents.context.policy', 'CompactionRoute'),
|
||||
'CompactionStrategy': ('praisonaiagents.context.policy', 'CompactionStrategy'),
|
||||
'ContextBudgetResult': ('praisonaiagents.context.policy', 'ContextBudgetResult'),
|
||||
'get_default_policy': ('praisonaiagents.context.policy', 'get_default_policy'),
|
||||
'CONSERVATIVE_POLICY': ('praisonaiagents.context.policy', 'CONSERVATIVE_POLICY'),
|
||||
'BALANCED_POLICY': ('praisonaiagents.context.policy', 'BALANCED_POLICY'),
|
||||
'AGGRESSIVE_POLICY': ('praisonaiagents.context.policy', 'AGGRESSIVE_POLICY'),
|
||||
|
||||
# Parameter resolver
|
||||
'resolve': ('praisonaiagents.config.param_resolver', 'resolve'),
|
||||
@@ -624,6 +658,11 @@ def _custom_handler(name, cache):
|
||||
mod = importlib.import_module('.db', 'praisonaiagents')
|
||||
cache['db'] = mod
|
||||
return mod
|
||||
if name == 'toolsets':
|
||||
import importlib
|
||||
mod = importlib.import_module('.toolsets', 'praisonaiagents')
|
||||
cache['toolsets'] = mod
|
||||
return mod
|
||||
|
||||
raise AttributeError(f"Not handled by custom_handler: {name}")
|
||||
|
||||
@@ -739,6 +778,9 @@ def warmup(include_litellm: bool = False, include_openai: bool = True) -> dict:
|
||||
# ============================================================================
|
||||
|
||||
__all__ = [
|
||||
# Version information
|
||||
'__version__',
|
||||
|
||||
# Core classes - the essentials
|
||||
'Agent',
|
||||
'AgentTeam', # Primary class for multi-agent coordination (v1.0+)
|
||||
@@ -814,6 +856,6 @@ def __dir__():
|
||||
# - Workflow, Task, Route, Parallel, Loop, Repeat, Pipeline, etc.
|
||||
# - MCP, FlowDisplay, track_workflow, FastContext, etc.
|
||||
# - Plan, PlanStep, TodoList, PlanningAgent, ApprovalCallback, etc.
|
||||
# - RAG, RAGConfig, RAGResult, AGUI, A2A, etc.
|
||||
# - RAG, RAGConfig, RAGResult, AGUI, A2A, A2UI, etc.
|
||||
# - All telemetry, display, and utility functions
|
||||
# ============================================================================
|
||||
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
Version utilities for praisonaiagents package.
|
||||
|
||||
This module provides version information by reading from pyproject.toml
|
||||
to avoid duplication and ensure the single source of truth.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
"""
|
||||
Get the version string from pyproject.toml.
|
||||
|
||||
Returns:
|
||||
str: Version string (e.g., "1.6.52")
|
||||
|
||||
Raises:
|
||||
RuntimeError: If version cannot be found or parsed
|
||||
"""
|
||||
try:
|
||||
# Get the package root (praisonaiagents directory)
|
||||
package_root = Path(__file__).parent
|
||||
# Go up to src/praisonai-agents directory
|
||||
pyproject_path = package_root.parent / "pyproject.toml"
|
||||
|
||||
if not pyproject_path.exists():
|
||||
raise RuntimeError(f"pyproject.toml not found at {pyproject_path}")
|
||||
|
||||
content = pyproject_path.read_text()
|
||||
|
||||
# Look for version = "X.Y.Z" pattern
|
||||
match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content)
|
||||
if not match:
|
||||
raise RuntimeError("Version not found in pyproject.toml")
|
||||
|
||||
return match.group(1)
|
||||
|
||||
except Exception as e:
|
||||
# Fallback to "unknown" to avoid breaking imports
|
||||
import warnings
|
||||
warnings.warn(
|
||||
f"Failed to read version from pyproject.toml: {e}. Using 'unknown'.",
|
||||
RuntimeWarning
|
||||
)
|
||||
return "unknown"
|
||||
|
||||
|
||||
# Cache the version to avoid reading the file multiple times
|
||||
__version__ = get_version()
|
||||
@@ -114,11 +114,12 @@ def __getattr__(name):
|
||||
|
||||
# Handoff - lightweight
|
||||
_handoff_names = {
|
||||
'Handoff', 'handoff', 'handoff_filters', 'parallel_handoffs',
|
||||
'Handoff', 'handoff_filters', 'parallel_handoffs',
|
||||
'RECOMMENDED_PROMPT_PREFIX', 'prompt_with_handoff_instructions',
|
||||
'HandoffConfig', 'HandoffResult', 'HandoffInputData',
|
||||
'ContextPolicy', 'HandoffError', 'HandoffCycleError',
|
||||
'HandoffDepthError', 'HandoffTimeoutError'
|
||||
'HandoffDepthError', 'HandoffTimeoutError', 'HandoffValidationError',
|
||||
'TypedHandoff'
|
||||
}
|
||||
if name in _handoff_names:
|
||||
from . import handoff as _handoff_module
|
||||
@@ -165,6 +166,21 @@ def __getattr__(name):
|
||||
_lazy_cache[name] = value
|
||||
return value
|
||||
|
||||
# Message Steering - lightweight protocols and implementations
|
||||
_steering_names = {
|
||||
'MessageSteeringProtocol', 'AgentSteeringProtocol', 'SteeringPriority',
|
||||
'SteeringMessage', 'MessageSteering', 'SteeringMixin'
|
||||
}
|
||||
if name in _steering_names:
|
||||
if name in {'MessageSteeringProtocol', 'AgentSteeringProtocol', 'SteeringPriority', 'SteeringMessage'}:
|
||||
from . import protocols as _protocols_module
|
||||
value = getattr(_protocols_module, name)
|
||||
else:
|
||||
from . import message_steering as _steering_module
|
||||
value = getattr(_steering_module, name)
|
||||
_lazy_cache[name] = value
|
||||
return value
|
||||
|
||||
# Decomposed mixins - for advanced use cases
|
||||
if name == 'ToolExecutionMixin':
|
||||
from .tool_execution import ToolExecutionMixin
|
||||
@@ -230,6 +246,8 @@ __all__ = [
|
||||
'HandoffCycleError',
|
||||
'HandoffDepthError',
|
||||
'HandoffTimeoutError',
|
||||
'HandoffValidationError',
|
||||
'TypedHandoff',
|
||||
'RouterAgent',
|
||||
'DeepResearchAgent',
|
||||
'DeepResearchResponse',
|
||||
@@ -253,6 +271,13 @@ __all__ = [
|
||||
'MemoryAwareAgentProtocol',
|
||||
'FullAgentProtocol',
|
||||
'ContextEngineerProtocol',
|
||||
# Message Steering
|
||||
'MessageSteeringProtocol',
|
||||
'AgentSteeringProtocol',
|
||||
'SteeringPriority',
|
||||
'SteeringMessage',
|
||||
'MessageSteering',
|
||||
'SteeringMixin',
|
||||
# Decomposed mixins (for advanced use cases)
|
||||
'ToolExecutionMixin',
|
||||
'ChatHandlerMixin',
|
||||
|
||||
@@ -22,6 +22,7 @@ from .session_manager import SessionManagerMixin
|
||||
from .async_safety import AsyncSafeState
|
||||
from .unified_execution_mixin import UnifiedExecutionMixin
|
||||
from .sandbox_mixin import SandboxMixin
|
||||
from .message_steering import SteeringMixin
|
||||
|
||||
# Module-level logger for thread safety errors and debugging
|
||||
logger = get_logger(__name__)
|
||||
@@ -242,7 +243,7 @@ def _get_default_server_registry() -> ServerRegistry:
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..approval.protocols import ApprovalConfig, ApprovalProtocol
|
||||
from ..config.feature_configs import LearnConfig, MemoryConfig
|
||||
from ..config.feature_configs import LearnConfig, MemoryConfig, ToolConfig
|
||||
from ..context.models import ContextConfig
|
||||
from ..context.manager import ContextManager
|
||||
from ..knowledge.knowledge import Knowledge
|
||||
@@ -252,11 +253,12 @@ if TYPE_CHECKING:
|
||||
from .handoff import Handoff, HandoffConfig, HandoffResult
|
||||
from ..rag.models import RAGResult, ContextPack
|
||||
from ..eval.results import EvaluationLoopResult
|
||||
from ..tools.retry import RetryPolicy
|
||||
|
||||
# Import structured error from central errors module
|
||||
from ..errors import BudgetExceededError
|
||||
|
||||
class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandlerMixin, SessionManagerMixin, ChatMixin, ExecutionMixin, MemoryMixin, AsyncMemoryMixin):
|
||||
class Agent(SteeringMixin, SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandlerMixin, SessionManagerMixin, ChatMixin, ExecutionMixin, MemoryMixin, AsyncMemoryMixin):
|
||||
# Class-level counter for generating unique display names for nameless agents
|
||||
_agent_counter = 0
|
||||
_agent_counter_lock = threading.Lock()
|
||||
@@ -311,6 +313,38 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
cls._default_model_checked = True
|
||||
return cls._default_model
|
||||
|
||||
def _ensure_llm_instance(self):
|
||||
"""Lazy-create LLM instance from deferred init params (avoids import at Agent())."""
|
||||
if self._llm_instance is not None:
|
||||
return self._llm_instance
|
||||
if self._llm_init_params:
|
||||
try:
|
||||
from ..llm.llm import LLM
|
||||
self._llm_instance = LLM(**self._llm_init_params)
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"LLM features requested but dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[llm]\""
|
||||
) from e
|
||||
return self._llm_instance
|
||||
|
||||
@property
|
||||
def llm_instance(self):
|
||||
return self._ensure_llm_instance()
|
||||
|
||||
@llm_instance.setter
|
||||
def llm_instance(self, value):
|
||||
self._llm_instance = value
|
||||
if value is not None:
|
||||
self._llm_init_params = None
|
||||
|
||||
def _ensure_loop_guard(self):
|
||||
"""Lazy-create loop guard on first tool execution."""
|
||||
if self._loop_guard is None:
|
||||
from ..escalation.loop_guard import LoopGuard, LoopGuardConfig
|
||||
self._loop_guard = LoopGuard(LoopGuardConfig(enabled=True))
|
||||
return self._loop_guard
|
||||
|
||||
@classmethod
|
||||
def _configure_logging(cls):
|
||||
"""Configure logging settings once for all agent instances."""
|
||||
@@ -523,6 +557,7 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
auth: Optional[str] = None, # Subscription auth provider: "claude-code", "codex", etc.
|
||||
# Tools
|
||||
tools: Optional[List[Any]] = None,
|
||||
toolsets: Optional[List[str]] = None, # Named toolset groups to resolve
|
||||
allow_delegation: bool = False, # Deprecated: use handoffs= instead
|
||||
allow_code_execution: Optional[bool] = False, # Deprecated: use execution=ExecutionConfig(code_execution=True)
|
||||
code_execution_mode: Literal["safe", "unsafe"] = "safe", # Deprecated: use execution=ExecutionConfig(code_mode="safe")
|
||||
@@ -550,12 +585,13 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
hooks: Optional[Union[List[Any], Dict[str, Any], 'HooksConfig']] = None,
|
||||
skills: Optional[Union[List[str], str, Dict[str, Any], 'SkillsConfig']] = None,
|
||||
approval: Optional[Union[bool, str, Dict[str, Any], 'ApprovalConfig', 'ApprovalProtocol']] = None,
|
||||
tool_timeout: Optional[int] = None, # P8/G11: Timeout in seconds for each tool call
|
||||
parallel_tool_calls: bool = False, # Gap 2: Enable parallel execution of batched LLM tool calls
|
||||
tool_config: Optional[Union[bool, 'ToolConfig']] = None, # Tool execution configuration (timeout, retry, parallel)
|
||||
learn: Optional[Union[bool, str, Dict[str, Any], 'LearnConfig']] = None, # Continuous learning (peer to memory)
|
||||
backend: Optional[Any] = None, # External managed agent backend (e.g., ManagedAgentIntegration)
|
||||
cli_backend: Optional[Union[str, Any]] = None, # CLI backend for delegating turns (e.g., "claude-code")
|
||||
interrupt_controller: Optional['InterruptController'] = None, # G2: Cooperative cancellation
|
||||
tool_search: Optional[Union[bool, str, Dict[str, Any], 'ToolSearchConfig']] = False, # Progressive tool disclosure
|
||||
message_steering: Optional[Union[bool, 'MessageSteeringProtocol']] = False, # Real-time message steering during execution
|
||||
sandbox: Optional[Union[bool, 'SandboxConfig']] = None, # Sandbox for safe code execution
|
||||
):
|
||||
"""Initialize an Agent instance.
|
||||
@@ -644,10 +680,10 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
- LearnConfig: Custom configuration
|
||||
Learning is a first-class citizen, peer to memory. It captures patterns,
|
||||
preferences, and insights from interactions to improve future responses.
|
||||
parallel_tool_calls: Enable parallel execution of batched LLM tool calls (default False).
|
||||
When True and LLM returns multiple tool calls in a single response, they execute
|
||||
concurrently instead of sequentially. Provides ~3x speedup for I/O-bound tools.
|
||||
Maintains backward compatibility with False default.
|
||||
tool_config: Tool execution configuration. Accepts:
|
||||
- bool: True enables defaults, False disables
|
||||
- ToolConfig: Custom configuration for timeout, retry policy, parallel execution
|
||||
Consolidates tool timeout, retry policy, and parallel execution settings.
|
||||
backend: External managed agent backend for hybrid execution. Accepts:
|
||||
- ManagedAgentIntegration: External managed agent service
|
||||
- None: Use local execution (default)
|
||||
@@ -660,6 +696,14 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
When provided, agent delegates entire conversation turns to the CLI tool
|
||||
instead of using the built-in LLM. Enables session continuity and
|
||||
tool integration through external AI coding assistants.
|
||||
tool_search: Progressive tool disclosure configuration. Accepts:
|
||||
- bool: False=disabled (default), True=auto mode
|
||||
- str: Mode ("auto", "on", "off")
|
||||
- Dict[str, Any]: Config overrides (e.g. {"threshold_pct": 15})
|
||||
- ToolSearchConfig: Custom configuration
|
||||
When enabled, replaces large tool schemas with bridge tools (tool_search,
|
||||
tool_describe, tool_call) to save context. Core SDK tools never defer.
|
||||
Auto mode activates based on token threshold. Opt-in feature.
|
||||
|
||||
Raises:
|
||||
ValueError: If all of name, role, goal, backstory, and instructions are None.
|
||||
@@ -679,6 +723,7 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
- auto_save → memory=MemoryConfig(auto_save=)
|
||||
- rate_limiter → execution=ExecutionConfig(rate_limiter=)
|
||||
- verification_hooks → autonomy=AutonomyConfig(verification_hooks=)
|
||||
- Use tool_config=ToolConfig(...) for tool execution configuration
|
||||
"""
|
||||
# Add check at start if memory is requested
|
||||
if memory is not None:
|
||||
@@ -705,16 +750,16 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
# Precedence: Explicit params > Config file > Built-in defaults
|
||||
# Only applies when param is None (not explicitly set)
|
||||
# ============================================================
|
||||
from ..config.loader import apply_config_defaults, get_default
|
||||
from ..config.loader import apply_config_defaults, get_default, _defaults_has_any_values
|
||||
|
||||
# Apply config defaults for LLM if not explicitly set
|
||||
if llm is None and model is None:
|
||||
if llm is None and model is None and _defaults_has_any_values():
|
||||
config_model = get_default("model")
|
||||
if config_model:
|
||||
llm = config_model
|
||||
|
||||
# Apply config defaults for base_url if not explicitly set
|
||||
if base_url is None:
|
||||
if base_url is None and _defaults_has_any_values():
|
||||
config_base_url = get_default("base_url")
|
||||
if config_base_url:
|
||||
base_url = config_base_url
|
||||
@@ -789,8 +834,7 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
alternative="use 'execution=ExecutionConfig(rate_limiter=obj)' instead",
|
||||
stacklevel=3
|
||||
)
|
||||
# Note: parallel_tool_calls is NOT deprecated - it's a new Gap 2 feature
|
||||
# Both direct parameter and ExecutionConfig.parallel_tool_calls are supported
|
||||
# Note: parallel_tool_calls moved to tool_config pattern
|
||||
if verification_hooks is not None:
|
||||
warn_deprecated_param(
|
||||
"verification_hooks",
|
||||
@@ -966,8 +1010,8 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
allow_code_execution = True
|
||||
if _exec_config.code_mode != "safe":
|
||||
code_execution_mode = _exec_config.code_mode
|
||||
# Get parallel_tool_calls from ExecutionConfig, fall back to parameter
|
||||
parallel_tool_calls = getattr(_exec_config, 'parallel_tool_calls', parallel_tool_calls)
|
||||
# Get parallel_tool_calls from ExecutionConfig
|
||||
parallel_tool_calls = getattr(_exec_config, 'parallel_tool_calls', False)
|
||||
# Budget guard extraction
|
||||
_max_budget = getattr(_exec_config, 'max_budget', None)
|
||||
_on_budget_exceeded = getattr(_exec_config, 'on_budget_exceeded', 'stop') or 'stop'
|
||||
@@ -975,7 +1019,8 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
max_iter, max_rpm, max_execution_time, max_retry_limit = 20, None, None, 2
|
||||
_max_budget = None
|
||||
_on_budget_exceeded = 'stop'
|
||||
# Keep parallel_tool_calls parameter value when no ExecutionConfig provided
|
||||
# Default to False when no ExecutionConfig provided
|
||||
parallel_tool_calls = False
|
||||
# (already set from parameter, no need to override)
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
@@ -1430,12 +1475,43 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
web_search = False
|
||||
web_fetch = False
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Resolve TOOL_SEARCH param
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Fast path: None/False -> disabled (zero overhead)
|
||||
if tool_search is None or tool_search is False:
|
||||
self._tool_search_config = None
|
||||
elif tool_search is True:
|
||||
# True -> auto mode with defaults
|
||||
from ..tools.tool_search import ToolSearchConfig as _ToolSearchConfig
|
||||
self._tool_search_config = _ToolSearchConfig(enabled="auto")
|
||||
elif isinstance(tool_search, str):
|
||||
# String mode ("auto", "on", "off")
|
||||
from ..tools.tool_search import ToolSearchConfig as _ToolSearchConfig
|
||||
self._tool_search_config = _ToolSearchConfig.from_raw(tool_search)
|
||||
elif isinstance(tool_search, dict):
|
||||
# Dict -> config overrides
|
||||
from ..tools.tool_search import ToolSearchConfig as _ToolSearchConfig
|
||||
self._tool_search_config = _ToolSearchConfig(**tool_search)
|
||||
else:
|
||||
from ..tools.tool_search import ToolSearchConfig as _ToolSearchConfig
|
||||
if isinstance(tool_search, _ToolSearchConfig):
|
||||
self._tool_search_config = tool_search
|
||||
else:
|
||||
raise TypeError(
|
||||
"tool_search must be False/None, True, a mode string, "
|
||||
"a dict of ToolSearchConfig fields, or ToolSearchConfig"
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# END CONSOLIDATED PARAMS EXTRACTION
|
||||
# ============================================================
|
||||
|
||||
# Initialize autonomy features (agent-centric escalation/doom-loop)
|
||||
self._init_autonomy(autonomy, verification_hooks=verification_hooks)
|
||||
|
||||
# Initialize loop guard lazily on first tool execution (zero init overhead)
|
||||
self._loop_guard = None
|
||||
|
||||
# If instructions are provided, use them to set role, goal, and backstory
|
||||
if instructions:
|
||||
@@ -1469,12 +1545,20 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
self.self_reflect = True if self_reflect is None else self_reflect
|
||||
|
||||
self.instructions = instructions
|
||||
|
||||
# Resolve tool_config for tool execution settings
|
||||
_tool_config = Agent._resolve_tool_config(
|
||||
tool_config=tool_config
|
||||
)
|
||||
|
||||
# Gap 2: Store parallel tool calls setting for ToolCallExecutor selection
|
||||
self.parallel_tool_calls = parallel_tool_calls
|
||||
self.parallel_tool_calls = _tool_config.parallel if _tool_config else parallel_tool_calls
|
||||
# G2: Store interrupt controller for cooperative cancellation
|
||||
self.interrupt_controller = interrupt_controller
|
||||
# Check for model name in environment variable if not provided
|
||||
self._using_custom_llm = False
|
||||
self._llm_instance = None
|
||||
self._llm_init_params = None
|
||||
# Flag to track if final result has been displayed to prevent duplicates
|
||||
self._final_display_shown = False
|
||||
|
||||
@@ -1513,95 +1597,67 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
self.base_url = base_url
|
||||
self.api_key = api_key
|
||||
|
||||
# If base_url is provided, always create a custom LLM instance
|
||||
# If base_url is provided, defer custom LLM instance creation
|
||||
if base_url:
|
||||
try:
|
||||
from ..llm.llm import LLM
|
||||
# Handle different llm parameter types with base_url
|
||||
if isinstance(llm, dict):
|
||||
# Merge base_url and api_key into the dict
|
||||
llm_config = llm.copy()
|
||||
llm_config['base_url'] = base_url
|
||||
if api_key:
|
||||
llm_config['api_key'] = api_key
|
||||
if auth:
|
||||
llm_config['auth'] = auth
|
||||
llm_config['metrics'] = metrics
|
||||
self.llm_instance = LLM(**llm_config)
|
||||
self.llm = llm.get('model', Agent._get_default_model())
|
||||
else:
|
||||
# Create LLM with model string and base_url (cached for performance)
|
||||
model_name = llm or Agent._get_default_model()
|
||||
self.llm_instance = LLM(
|
||||
model=model_name,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
auth=auth,
|
||||
metrics=metrics,
|
||||
web_search=web_search,
|
||||
web_fetch=web_fetch,
|
||||
prompt_caching=prompt_caching,
|
||||
claude_memory=claude_memory
|
||||
)
|
||||
self.llm = model_name
|
||||
self._using_custom_llm = True
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"LLM features requested but dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[llm]\""
|
||||
) from e
|
||||
if isinstance(llm, dict):
|
||||
llm_config = llm.copy()
|
||||
llm_config['base_url'] = base_url
|
||||
if api_key:
|
||||
llm_config['api_key'] = api_key
|
||||
if auth:
|
||||
llm_config['auth'] = auth
|
||||
llm_config['metrics'] = metrics
|
||||
llm_config['max_iter'] = max_iter
|
||||
self._llm_init_params = llm_config
|
||||
self.llm = llm.get('model', Agent._get_default_model())
|
||||
else:
|
||||
model_name = llm or Agent._get_default_model()
|
||||
self._llm_init_params = {
|
||||
'model': model_name,
|
||||
'base_url': base_url,
|
||||
'api_key': api_key,
|
||||
'auth': auth,
|
||||
'metrics': metrics,
|
||||
'max_iter': max_iter,
|
||||
'web_search': web_search,
|
||||
'web_fetch': web_fetch,
|
||||
'prompt_caching': prompt_caching,
|
||||
'claude_memory': claude_memory,
|
||||
}
|
||||
self.llm = model_name
|
||||
self._using_custom_llm = True
|
||||
# If the user passes a dictionary (for advanced configuration)
|
||||
elif isinstance(llm, dict) and "model" in llm:
|
||||
try:
|
||||
from ..llm.llm import LLM
|
||||
# Add api_key if provided and not in dict
|
||||
if api_key and 'api_key' not in llm:
|
||||
llm = llm.copy()
|
||||
llm['api_key'] = api_key
|
||||
# Add auth if provided and not in dict
|
||||
if auth and 'auth' not in llm:
|
||||
llm = llm.copy()
|
||||
llm['auth'] = auth
|
||||
# Add metrics parameter
|
||||
llm = llm.copy()
|
||||
llm['metrics'] = metrics
|
||||
self.llm_instance = LLM(**llm) # Pass all dict items as kwargs
|
||||
self._using_custom_llm = True
|
||||
self.llm = llm.get('model', Agent._get_default_model())
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"LLM features requested but dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[llm]\""
|
||||
) from e
|
||||
# If the user passes a string with a slash (provider/model)
|
||||
llm_config = llm.copy()
|
||||
if api_key and 'api_key' not in llm_config:
|
||||
llm_config['api_key'] = api_key
|
||||
if auth and 'auth' not in llm_config:
|
||||
llm_config['auth'] = auth
|
||||
llm_config['metrics'] = metrics
|
||||
llm_config['max_iter'] = max_iter
|
||||
self._llm_init_params = llm_config
|
||||
self._using_custom_llm = True
|
||||
self.llm = llm_config.get('model', Agent._get_default_model())
|
||||
# If the user passes a string with a slash (provider/model) — defer LiteLLM import
|
||||
elif isinstance(llm, str) and "/" in llm:
|
||||
try:
|
||||
from ..llm.llm import LLM
|
||||
# Pass the entire string so LiteLLM can parse provider/model
|
||||
llm_params = {'model': llm}
|
||||
if api_key:
|
||||
llm_params['api_key'] = api_key
|
||||
if auth:
|
||||
llm_params['auth'] = auth
|
||||
llm_params['metrics'] = metrics
|
||||
llm_params['web_search'] = web_search
|
||||
llm_params['web_fetch'] = web_fetch
|
||||
llm_params['prompt_caching'] = prompt_caching
|
||||
llm_params['claude_memory'] = claude_memory
|
||||
self.llm_instance = LLM(**llm_params)
|
||||
self._using_custom_llm = True
|
||||
self.llm = llm
|
||||
|
||||
# Ensure tools are properly accessible when using custom LLM
|
||||
if tools:
|
||||
logging.debug(f"Tools passed to Agent with custom LLM: {tools}")
|
||||
# Store the tools for later use
|
||||
self.tools = tools
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"LLM features requested but dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[llm]\""
|
||||
) from e
|
||||
llm_params = {'model': llm}
|
||||
if api_key:
|
||||
llm_params['api_key'] = api_key
|
||||
if auth:
|
||||
llm_params['auth'] = auth
|
||||
llm_params['metrics'] = metrics
|
||||
llm_params['web_search'] = web_search
|
||||
llm_params['web_fetch'] = web_fetch
|
||||
llm_params['prompt_caching'] = prompt_caching
|
||||
llm_params['claude_memory'] = claude_memory
|
||||
llm_params['max_iter'] = max_iter
|
||||
self._llm_init_params = llm_params
|
||||
self._using_custom_llm = True
|
||||
self.llm = llm
|
||||
|
||||
if tools:
|
||||
logging.debug(f"Tools passed to Agent with custom LLM: {tools}")
|
||||
self.tools = tools
|
||||
# Otherwise, fall back to OpenAI environment/name (cached for performance)
|
||||
else:
|
||||
self.llm = llm or Agent._get_default_model()
|
||||
@@ -1621,6 +1677,32 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
else:
|
||||
self.tools = tools or []
|
||||
|
||||
# Handle toolsets parameter - resolve named toolset groups
|
||||
if toolsets:
|
||||
try:
|
||||
from ..toolsets import resolve_toolsets
|
||||
toolset_tool_names = resolve_toolsets(toolsets)
|
||||
# Remove duplicates with existing tools
|
||||
existing_tool_names = set()
|
||||
for tool in self.tools:
|
||||
name = getattr(tool, 'name', getattr(tool, '__name__', str(tool)))
|
||||
existing_tool_names.add(name)
|
||||
|
||||
unique_tool_names = [name for name in toolset_tool_names if name not in existing_tool_names]
|
||||
toolset_tools = self._resolve_tool_names(unique_tool_names)
|
||||
self.tools.extend(toolset_tools)
|
||||
logging.debug(f"Resolved toolsets {toolsets} to {len(toolset_tools)} tools: {[getattr(t, '__name__', str(t)) for t in toolset_tools]}")
|
||||
except (ValueError, KeyError) as e:
|
||||
raise ValueError(
|
||||
f"Agent '{getattr(self, 'display_name', 'unknown')}' failed to resolve toolsets {toolsets}: {e}. "
|
||||
"Verify names with praisonaiagents.toolsets.list_toolsets()."
|
||||
) from e
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
f"Agent '{getattr(self, 'display_name', 'unknown')}' failed to import toolsets module: {e}. "
|
||||
"Ensure praisonaiagents is properly installed."
|
||||
) from e
|
||||
|
||||
# Inject default tools for autonomy mode (after self.tools is initialized)
|
||||
# ONLY inject if caller didn't provide tools - avoid duplicates with CLI/wrapper tools
|
||||
if self.autonomy_enabled and hasattr(self, '_autonomy_config_obj'):
|
||||
@@ -1656,6 +1738,7 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
self.max_execution_time = max_execution_time
|
||||
self._memory_instance = None
|
||||
self._init_memory(memory, user_id)
|
||||
self._init_message_steering(message_steering)
|
||||
self.verbose = verbose
|
||||
self._has_explicit_output_config = _has_explicit_output # Track if user set output mode
|
||||
self.tool_output_limit = tool_output_limit # Configurable tool output limit
|
||||
@@ -1680,6 +1763,9 @@ class Agent(SandboxMixin, UnifiedExecutionMixin, ToolExecutionMixin, ChatHandler
|
||||
self.embedder_config = embedder_config
|
||||
self.knowledge = knowledge
|
||||
self.use_system_prompt = use_system_prompt
|
||||
|
||||
# Store ExecutionConfig for context compaction policy access
|
||||
self.execution = _exec_config
|
||||
# Async-safe chat_history with dual-lock protection
|
||||
self.__chat_history_state = AsyncSafeState([])
|
||||
|
||||
@@ -1835,8 +1921,14 @@ Your Goal: {self.goal}
|
||||
self._pending_approvals = {}
|
||||
self._approvals_lock = asyncio.Lock()
|
||||
|
||||
# Store the resolved tool_config for cloning (resolved earlier in __init__)
|
||||
self._tool_config = _tool_config
|
||||
|
||||
# P8/G11: Tool timeout - prevent slow tools from blocking
|
||||
self._tool_timeout = tool_timeout
|
||||
self._tool_timeout = _tool_config.timeout if _tool_config else None
|
||||
|
||||
# Store tool retry policy for tool execution with exponential backoff
|
||||
self._tool_retry_policy = _tool_config.retry_policy if _tool_config else None
|
||||
|
||||
# Cache for system prompts and formatted tools with eager thread-safe lock
|
||||
# Use OrderedDict for LRU behavior
|
||||
@@ -1952,6 +2044,142 @@ Your Goal: {self.goal}
|
||||
# Sandbox configuration - initialize SandboxMixin
|
||||
super().__init__(sandbox=sandbox)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_tool_config(tool_config):
|
||||
"""Resolve tool_config parameter with backward compatibility."""
|
||||
# Import here to avoid circular imports
|
||||
from ..config.feature_configs import ToolConfig, resolve_tools
|
||||
|
||||
# Handle the new consolidated parameter
|
||||
if tool_config is not None:
|
||||
resolved_config = resolve_tools(tool_config)
|
||||
return resolved_config
|
||||
|
||||
return None
|
||||
|
||||
def __deepcopy__(self, memo: dict) -> "Agent":
|
||||
"""Custom deepcopy that creates fresh threading primitives.
|
||||
|
||||
threading.RLock (self.__cache_lock) and threading.Lock (self._cost_lock)
|
||||
cannot be deep-copied on CPython < 3.13. This hook deep-copies every
|
||||
other attribute normally and replaces the locks with new instances so
|
||||
that copy.deepcopy(agent) works in any Python version.
|
||||
"""
|
||||
import copy
|
||||
cls = self.__class__
|
||||
result = cls.__new__(cls)
|
||||
memo[id(self)] = result
|
||||
for k, v in self.__dict__.items():
|
||||
if k in ("_Agent__cache_lock",):
|
||||
object.__setattr__(result, k, threading.RLock())
|
||||
elif k == "_cost_lock":
|
||||
object.__setattr__(result, k, threading.Lock())
|
||||
else:
|
||||
object.__setattr__(result, k, copy.deepcopy(v, memo))
|
||||
return result
|
||||
|
||||
def clone_for_channel(self) -> "Agent":
|
||||
"""Return a fully independent copy of this agent for a gateway channel.
|
||||
|
||||
This method safely clones an agent for use in multi-channel scenarios
|
||||
by avoiding deepcopy issues with tools/handoffs and creating fresh
|
||||
instances of mutable objects like interrupt_controller.
|
||||
|
||||
Returns:
|
||||
Agent: A new Agent instance with copied configuration but fresh
|
||||
locks, interrupt controller, and shallow-copied tools.
|
||||
"""
|
||||
import copy
|
||||
from ..tools.base import BaseTool
|
||||
|
||||
# Build kwargs for new Agent instance, copying core attributes
|
||||
clone_kwargs = {
|
||||
# Core identity
|
||||
'name': self.name,
|
||||
'role': self.role,
|
||||
'goal': self.goal,
|
||||
'backstory': self.backstory,
|
||||
'instructions': self.instructions,
|
||||
|
||||
# LLM configuration
|
||||
'llm': self.llm,
|
||||
'base_url': getattr(self, 'base_url', None),
|
||||
'api_key': getattr(self, 'api_key', None),
|
||||
'auth': getattr(self, 'auth', None),
|
||||
|
||||
# Shallow copy tools to avoid deepcopy issues with nested objects
|
||||
'tools': list(self.tools) if self.tools else None,
|
||||
|
||||
# Skip handoffs entirely - they shouldn't be shared across channels
|
||||
# and can contain nested Agent instances that cause RLock issues
|
||||
'handoffs': None,
|
||||
|
||||
# Feature configurations - check for actual stored config objects
|
||||
'memory': getattr(self, '_memory_config', None),
|
||||
'knowledge': getattr(self, '_knowledge_config', None),
|
||||
'planning': getattr(self, '_planning_config', None),
|
||||
'reflection': getattr(self, '_reflection_config', None),
|
||||
'guardrails': getattr(self, '_guardrails_config', None),
|
||||
'web': getattr(self, '_web_config', None),
|
||||
'context': getattr(self, '_context_config', None),
|
||||
'autonomy': getattr(self, '_autonomy_config', None),
|
||||
'output': getattr(self, '_output_config', None),
|
||||
'execution': getattr(self, '_execution_config', None),
|
||||
'templates': getattr(self, '_template_config', None),
|
||||
'caching': getattr(self, '_caching_config', None),
|
||||
'hooks': getattr(self, '_hooks_config', None),
|
||||
'skills': getattr(self, '_skills_config', None),
|
||||
'approval': getattr(self, '_approval_config', None),
|
||||
'learn': getattr(self, '_learn_config', None),
|
||||
'tool_search': getattr(self, '_tool_search_config', None),
|
||||
|
||||
# Tool configuration - use consolidated config when available
|
||||
'tool_config': getattr(self, '_tool_config', None),
|
||||
|
||||
# CLI backend
|
||||
'cli_backend': getattr(self, '_cli_backend', None),
|
||||
|
||||
# Create fresh interrupt controller to avoid shared state
|
||||
'interrupt_controller': None, # Let new instance create its own
|
||||
|
||||
# Sandbox config
|
||||
'sandbox': getattr(self, '_sandbox_config', None),
|
||||
}
|
||||
|
||||
# Handle deprecated parameters for backward compatibility
|
||||
import warnings
|
||||
|
||||
# Check for deprecated standalone attributes and emit warnings
|
||||
deprecated_attrs = [
|
||||
('allow_code_execution', 'execution'),
|
||||
('code_execution_mode', 'execution'),
|
||||
('auto_save', 'memory'),
|
||||
('rate_limiter', 'execution'),
|
||||
('allow_delegation', 'handoffs'),
|
||||
('verification_hooks', 'autonomy')
|
||||
]
|
||||
|
||||
for old_attr, new_param in deprecated_attrs:
|
||||
if hasattr(self, old_attr):
|
||||
value = getattr(self, old_attr)
|
||||
if value is not None and value != False: # Skip None/False defaults
|
||||
warnings.warn(
|
||||
f"Deprecated attribute '{old_attr}' found in agent clone. "
|
||||
f"Use '{new_param}=' parameter instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
# For basic compatibility, include in clone_kwargs if not already set
|
||||
if old_attr == 'allow_code_execution' and clone_kwargs['execution'] is None:
|
||||
from ..config.execution import ExecutionConfig
|
||||
clone_kwargs['execution'] = ExecutionConfig(code_execution=value)
|
||||
elif old_attr == 'auto_save' and clone_kwargs['memory'] is None:
|
||||
from ..config.memory import MemoryConfig
|
||||
clone_kwargs['memory'] = MemoryConfig(auto_save=value)
|
||||
|
||||
# Create new Agent instance
|
||||
return self.__class__(**{k: v for k, v in clone_kwargs.items() if v is not None})
|
||||
|
||||
@property
|
||||
def _telemetry(self):
|
||||
"""Lazy-loaded telemetry instance for performance."""
|
||||
@@ -4084,33 +4312,95 @@ Summary:"""
|
||||
|
||||
return ""
|
||||
|
||||
def store_memory(self, content: str, memory_type: str = "short_term", **kwargs: Any) -> None:
|
||||
def store_memory(self, content: str, memory_type: str = "short_term", action: str = "add", **kwargs: Any) -> None:
|
||||
"""
|
||||
Store content in memory.
|
||||
|
||||
Args:
|
||||
content: Content to store
|
||||
memory_type: Type of memory (short_term, long_term, entity, episodic)
|
||||
action: Type of operation ("add", "replace", "remove")
|
||||
**kwargs: Additional arguments for the memory method
|
||||
"""
|
||||
if not self._memory_instance:
|
||||
return
|
||||
|
||||
# Use protocol names first (store_*), fallback to legacy names (add_*)
|
||||
if memory_type == "short_term":
|
||||
if hasattr(self._memory_instance, 'store_short_term'):
|
||||
self._memory_instance.store_short_term(content, **kwargs)
|
||||
elif hasattr(self._memory_instance, 'add_short_term'):
|
||||
self._memory_instance.add_short_term(content, **kwargs)
|
||||
elif memory_type == "long_term":
|
||||
if hasattr(self._memory_instance, 'store_long_term'):
|
||||
self._memory_instance.store_long_term(content, **kwargs)
|
||||
elif hasattr(self._memory_instance, 'add_long_term'):
|
||||
self._memory_instance.add_long_term(content, **kwargs)
|
||||
elif memory_type == "entity" and hasattr(self._memory_instance, 'add_entity'):
|
||||
self._memory_instance.add_entity(content, **kwargs)
|
||||
elif memory_type == "episodic" and hasattr(self._memory_instance, 'add_episodic'):
|
||||
self._memory_instance.add_episodic(content, **kwargs)
|
||||
# Validate memory_type
|
||||
supported_types = ["short_term", "long_term", "entity", "episodic"]
|
||||
if memory_type not in supported_types:
|
||||
raise ValueError(f"Unsupported memory_type '{memory_type}'. Supported: {supported_types}")
|
||||
|
||||
# Perform the actual storage operation first
|
||||
storage_success = False
|
||||
try:
|
||||
if action == "add":
|
||||
# Use protocol names first (store_*), fallback to legacy names (add_*)
|
||||
if memory_type == "short_term":
|
||||
if hasattr(self._memory_instance, 'store_short_term'):
|
||||
self._memory_instance.store_short_term(content, **kwargs)
|
||||
storage_success = True
|
||||
elif hasattr(self._memory_instance, 'add_short_term'):
|
||||
self._memory_instance.add_short_term(content, **kwargs)
|
||||
storage_success = True
|
||||
elif memory_type == "long_term":
|
||||
if hasattr(self._memory_instance, 'store_long_term'):
|
||||
self._memory_instance.store_long_term(content, **kwargs)
|
||||
storage_success = True
|
||||
elif hasattr(self._memory_instance, 'add_long_term'):
|
||||
self._memory_instance.add_long_term(content, **kwargs)
|
||||
storage_success = True
|
||||
elif memory_type == "entity" and hasattr(self._memory_instance, 'add_entity'):
|
||||
self._memory_instance.add_entity(content, **kwargs)
|
||||
storage_success = True
|
||||
elif memory_type == "episodic" and hasattr(self._memory_instance, 'add_episodic'):
|
||||
self._memory_instance.add_episodic(content, **kwargs)
|
||||
storage_success = True
|
||||
elif action == "replace":
|
||||
# For replace operations - look for replace_ or update_ methods
|
||||
method_name = f"replace_{memory_type}" if hasattr(self._memory_instance, f'replace_{memory_type}') else f"update_{memory_type}"
|
||||
if hasattr(self._memory_instance, method_name):
|
||||
getattr(self._memory_instance, method_name)(content, **kwargs)
|
||||
storage_success = True
|
||||
else:
|
||||
raise ValueError(f"Memory provider does not support 'replace' for {memory_type}")
|
||||
elif action == "remove":
|
||||
# For remove operations - look for remove_ or delete_ methods
|
||||
method_name = f"remove_{memory_type}" if hasattr(self._memory_instance, f'remove_{memory_type}') else f"delete_{memory_type}"
|
||||
if hasattr(self._memory_instance, method_name):
|
||||
getattr(self._memory_instance, method_name)(content, **kwargs)
|
||||
storage_success = True
|
||||
else:
|
||||
raise ValueError(f"Memory provider does not support 'remove' for {memory_type}")
|
||||
else:
|
||||
raise ValueError(f"Unsupported action '{action}'. Supported: add, replace, remove")
|
||||
|
||||
if not storage_success:
|
||||
raise ValueError(f"Memory provider does not support {memory_type} storage")
|
||||
|
||||
except Exception as e:
|
||||
logging.warning(f"[{self.name}] Memory storage failed: {e}")
|
||||
raise
|
||||
|
||||
# Only call on_memory_write hook AFTER successful storage
|
||||
if storage_success:
|
||||
try:
|
||||
metadata = kwargs.get('metadata', None)
|
||||
# Try async version first if in async context, fallback to sync
|
||||
import asyncio
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
# In async context - prefer async hook if available
|
||||
if hasattr(self._memory_instance, 'aon_memory_write'):
|
||||
# Schedule async hook without blocking (fire and forget)
|
||||
asyncio.create_task(self._memory_instance.aon_memory_write(action, memory_type, content, metadata))
|
||||
elif hasattr(self._memory_instance, 'on_memory_write'):
|
||||
self._memory_instance.on_memory_write(action, memory_type, content, metadata)
|
||||
except RuntimeError:
|
||||
# Not in async context - use sync hook
|
||||
if hasattr(self._memory_instance, 'on_memory_write'):
|
||||
self._memory_instance.on_memory_write(action, memory_type, content, metadata)
|
||||
except Exception as e:
|
||||
logging.warning(f"[{self.name}] Memory on_memory_write hook failed: {e}")
|
||||
|
||||
def _display_memory_info(self):
|
||||
"""Display memory information to user in a friendly format."""
|
||||
@@ -4616,60 +4906,57 @@ Answer:"""
|
||||
error=f"Agent guardrail validation error: {str(e)}"
|
||||
)
|
||||
|
||||
def _apply_guardrail_with_retry(self, response_text, prompt, temperature=1.0, tools=None, task_name=None, task_description=None, task_id=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
|
||||
"""
|
||||
def _validate_with_guardrail(self, response_text):
|
||||
"""Validate response with guardrail. Returns (success, result, error)."""
|
||||
if not self._guardrail_fn:
|
||||
return response_text
|
||||
return True, response_text, None
|
||||
|
||||
from ..main import TaskOutput
|
||||
|
||||
task_output = TaskOutput(
|
||||
description="Agent response output",
|
||||
raw=response_text,
|
||||
agent=self.name
|
||||
)
|
||||
|
||||
guardrail_result = self._process_guardrail(task_output)
|
||||
|
||||
if guardrail_result.success:
|
||||
# Return the potentially modified result
|
||||
if guardrail_result.result and hasattr(guardrail_result.result, 'raw'):
|
||||
return True, guardrail_result.result.raw, None
|
||||
elif guardrail_result.result:
|
||||
return True, str(guardrail_result.result), None
|
||||
else:
|
||||
return True, response_text, None
|
||||
else:
|
||||
return False, None, guardrail_result.error
|
||||
|
||||
def _apply_guardrail_with_retry(self, response_text, prompt, temperature=1.0, tools=None, task_name=None, task_description=None, task_id=None):
|
||||
"""Apply guardrail validation with retry logic (sync version)."""
|
||||
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
|
||||
)
|
||||
success, result, error = self._validate_with_guardrail(current_response)
|
||||
|
||||
# Process guardrail
|
||||
guardrail_result = self._process_guardrail(task_output)
|
||||
|
||||
if guardrail_result.success:
|
||||
if 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
|
||||
return result
|
||||
|
||||
# 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}"
|
||||
f"Last error: {error}"
|
||||
)
|
||||
|
||||
retry_count += 1
|
||||
logging.warning(f"Agent {self.name}: Guardrail validation failed (retry {retry_count}/{self.max_guardrail_retries}): {guardrail_result.error}")
|
||||
logging.warning(f"Agent {self.name}: Guardrail validation failed (retry {retry_count}/{self.max_guardrail_retries}): {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."
|
||||
retry_prompt = f"{prompt}\n\nNote: Previous response failed validation due to: {error}. Please provide an improved response."
|
||||
response = self._chat_completion([{"role": "user", "content": retry_prompt}], temperature, tools, task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
if response and response.choices:
|
||||
content = response.choices[0].message.content
|
||||
@@ -4678,10 +4965,46 @@ Answer:"""
|
||||
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
|
||||
|
||||
async def _aapply_guardrail_with_retry(self, response_text, prompt, temperature=1.0, tools=None, task_name=None, task_description=None, task_id=None):
|
||||
"""Apply guardrail validation with retry logic (async version)."""
|
||||
retry_count = 0
|
||||
current_response = response_text
|
||||
|
||||
while retry_count <= self.max_guardrail_retries:
|
||||
success, result, error = self._validate_with_guardrail(current_response)
|
||||
|
||||
if success:
|
||||
logging.info(f"Agent {self.name}: Guardrail validation passed")
|
||||
return result
|
||||
|
||||
# Guardrail failed
|
||||
if retry_count >= self.max_guardrail_retries:
|
||||
raise Exception(
|
||||
f"Agent {self.name} guardrail retry failed: {e}"
|
||||
f"Agent {self.name} response failed guardrail validation after {self.max_guardrail_retries} retries. "
|
||||
f"Last error: {error}"
|
||||
)
|
||||
|
||||
retry_count += 1
|
||||
logging.warning(f"Agent {self.name}: Guardrail validation failed (retry {retry_count}/{self.max_guardrail_retries}): {error}")
|
||||
|
||||
# Regenerate response for retry (async version)
|
||||
try:
|
||||
retry_prompt = f"{prompt}\n\nNote: Previous response failed validation due to: {error}. Please provide an improved response."
|
||||
response = await self._execute_unified_achat_completion([{"role": "user", "content": retry_prompt}], temperature, tools, task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
if response and hasattr(response, 'choices') and response.choices:
|
||||
content = response.choices[0].message.content
|
||||
current_response = content.strip() if content else ""
|
||||
elif isinstance(response, str):
|
||||
current_response = response.strip()
|
||||
else:
|
||||
raise Exception("Failed to generate retry response")
|
||||
except Exception as e:
|
||||
logging.error(f"Agent {self.name}: Error during guardrail retry: {e}")
|
||||
raise Exception(f"Agent {self.name} guardrail retry failed: {e}")
|
||||
|
||||
return current_response
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 549 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user