mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-09 03:39:37 -10:00
Compare commits
318
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e36f94b8c9 | ||
|
|
3cccd045a7 | ||
|
|
84ae06c8ed | ||
|
|
9c80d225b8 | ||
|
|
69fece5151 | ||
|
|
ebdc3abbb7 | ||
|
|
c059cbf9d9 | ||
|
|
13540dfc94 | ||
|
|
689220c88e | ||
|
|
a4e00f5876 | ||
|
|
2aa757d9cb | ||
|
|
87a600ab13 | ||
|
|
18cb2c4965 | ||
|
|
6a50d2db44 | ||
|
|
13d3c6c6db | ||
|
|
fc7481150e | ||
|
|
90459c878f | ||
|
|
a96f96c331 | ||
|
|
04ad922c50 | ||
|
|
bc84bb5c95 | ||
|
|
8b857acc07 | ||
|
|
c9cb5b53f0 | ||
|
|
636a69976b | ||
|
|
4ccf732153 | ||
|
|
49a52bb294 | ||
|
|
3c1b2f74ae | ||
|
|
83c87cf4da | ||
|
|
1f0b661e2b | ||
|
|
bc2e452dea | ||
|
|
d78d502612 | ||
|
|
53d3885415 | ||
|
|
bbb4277802 | ||
|
|
32118412e8 | ||
|
|
d8bdcd04d7 | ||
|
|
c302853636 | ||
|
|
956011a10d | ||
|
|
532d6cd307 | ||
|
|
006f26f04b | ||
|
|
685554380d | ||
|
|
7e7e490f73 | ||
|
|
fad32c3e09 | ||
|
|
75c7ff47f4 | ||
|
|
ced249976c | ||
|
|
956de9c9e0 | ||
|
|
07759f7ac6 | ||
|
|
6b0fb3b215 | ||
|
|
a9de78f87f | ||
|
|
25e7321015 | ||
|
|
8e688b2428 | ||
|
|
0a5afb8518 | ||
|
|
07e07401a3 | ||
|
|
aeddb104b9 | ||
|
|
97987b8b67 | ||
|
|
5a4a83419c | ||
|
|
2de59d98ce | ||
|
|
7531858dfb | ||
|
|
46a3185e27 | ||
|
|
38ac277fbb | ||
|
|
db33177801 | ||
|
|
64620faac5 | ||
|
|
405a78df3e | ||
|
|
2b27e5c42b | ||
|
|
a843e13641 | ||
|
|
d389ff348f | ||
|
|
58f09a2e05 | ||
|
|
ca6ce653f7 | ||
|
|
879dc6bc69 | ||
|
|
8b89ad9c38 | ||
|
|
f367321179 | ||
|
|
1c7900c0ee | ||
|
|
5202d2fed6 | ||
|
|
b75992fca9 | ||
|
|
f7ef7f70b8 | ||
|
|
c44caafc20 | ||
|
|
0d19bce147 | ||
|
|
fd600d81d1 | ||
|
|
2e1ac193a5 | ||
|
|
7faf5434b6 | ||
|
|
2d4194f082 | ||
|
|
8c1bb29792 | ||
|
|
85e79552a2 | ||
|
|
2abfe1514e | ||
|
|
69e95fd883 | ||
|
|
d164826b9f | ||
|
|
5477612f60 | ||
|
|
ce7910894f | ||
|
|
72db7e8a40 | ||
|
|
72850de9ed | ||
|
|
eef358e296 | ||
|
|
989890ee36 | ||
|
|
10ed1f662c | ||
|
|
aa0c8f55d3 | ||
|
|
c149087a92 | ||
|
|
1d370b2806 | ||
|
|
2333c4f396 | ||
|
|
a15f2e6eeb | ||
|
|
8bd748ba6b | ||
|
|
94e3716f18 | ||
|
|
e838cff428 | ||
|
|
6b0f6ec4ea | ||
|
|
76fa6402d1 | ||
|
|
111427e2b5 | ||
|
|
e09f41b290 | ||
|
|
3fc5e269c2 | ||
|
|
b81e41c42a | ||
|
|
8987d3c8b4 | ||
|
|
d28633f098 | ||
|
|
8384839fb9 | ||
|
|
32bd327c3a | ||
|
|
68968a2e3a | ||
|
|
47dec3e4ae | ||
|
|
4835437eed | ||
|
|
9404f97fce | ||
|
|
29a90b1dfd | ||
|
|
19d627cac7 | ||
|
|
20d31bb119 | ||
|
|
5b599ce6c6 | ||
|
|
e81e911147 | ||
|
|
a955b826de | ||
|
|
37ad800485 | ||
|
|
19bd0529b9 | ||
|
|
fa5a926769 | ||
|
|
e5384d01ee | ||
|
|
9a8170c45d | ||
|
|
dcab6382b4 | ||
|
|
f207f036f3 | ||
|
|
6937ad83fb | ||
|
|
fbb9dd63a9 | ||
|
|
b73775e4ca | ||
|
|
14b1df135e | ||
|
|
8aedd113fa | ||
|
|
51d589e822 | ||
|
|
059a71daed | ||
|
|
59defc8038 | ||
|
|
b4b606a63a | ||
|
|
0c668502af | ||
|
|
a39f256bba | ||
|
|
413d176f91 | ||
|
|
0543c9129c | ||
|
|
a83abc5286 | ||
|
|
8767e838b9 | ||
|
|
22b9a4213e | ||
|
|
7db77db244 | ||
|
|
6f0d8b5756 | ||
|
|
f42b0aaac8 | ||
|
|
bfbed92602 | ||
|
|
f3a2cf4c0e | ||
|
|
a93cd4f8fc | ||
|
|
d11d22bf83 | ||
|
|
124d9ceb99 | ||
|
|
93f7c5d20a | ||
|
|
5aee48cdcf | ||
|
|
2bff144bb7 | ||
|
|
518d5ed6c1 | ||
|
|
8127983959 | ||
|
|
a64aa50107 | ||
|
|
944fd64a4c | ||
|
|
4663c7ce0d | ||
|
|
0a5e3077fd | ||
|
|
a0917e0cbf | ||
|
|
3ac32b8941 | ||
|
|
773b32cb1b | ||
|
|
6aee25471a | ||
|
|
5c9d46709c | ||
|
|
1bfea73818 | ||
|
|
12bf827733 | ||
|
|
d283b6f50c | ||
|
|
3d31db95f7 | ||
|
|
bcf240fd85 | ||
|
|
13d897c310 | ||
|
|
db7bdf311b | ||
|
|
49ccd1cace | ||
|
|
7fc59ea1ad | ||
|
|
301fcaa0c2 | ||
|
|
11fbf8894d | ||
|
|
01770262a9 | ||
|
|
aecaef3152 | ||
|
|
0d9435f7f6 | ||
|
|
a96a70cf88 | ||
|
|
63d7b62d1b | ||
|
|
5835937cb6 | ||
|
|
fd315847b2 | ||
|
|
9ecbfccab2 | ||
|
|
4f585a5173 | ||
|
|
75f5103f51 | ||
|
|
4755b9986d | ||
|
|
5e7228c08d | ||
|
|
e5b9419f9e | ||
|
|
f074ea5eda | ||
|
|
1176304c75 | ||
|
|
8d154a7f75 | ||
|
|
1eb78a2b23 | ||
|
|
fcc012e102 | ||
|
|
02212d9e43 | ||
|
|
307ecdab8a | ||
|
|
d5ff3ac6b6 | ||
|
|
2c86220353 | ||
|
|
e219157860 | ||
|
|
4d212809bd | ||
|
|
a5453696fe | ||
|
|
8d53817179 | ||
|
|
243989cdda | ||
|
|
e722cb224f | ||
|
|
cd482b8aa6 | ||
|
|
a433888e22 | ||
|
|
1337ac849d | ||
|
|
8b0845d7d3 | ||
|
|
319db3c637 | ||
|
|
5275a72008 | ||
|
|
5560d06ff4 | ||
|
|
0846933194 | ||
|
|
6d6729833f | ||
|
|
82edd4dabe | ||
|
|
6cfbbc7b3c | ||
|
|
488690e8a1 | ||
|
|
b1fdeaaa1c | ||
|
|
5494649813 | ||
|
|
aea1506e5c | ||
|
|
c3d93eed56 | ||
|
|
0e1b171738 | ||
|
|
be8f9b4341 | ||
|
|
8c1f2b701b | ||
|
|
83e77cb406 | ||
|
|
12132a6881 | ||
|
|
a3f279f5ba | ||
|
|
e0655b4a9f | ||
|
|
541473bcac | ||
|
|
1a55c2a8db | ||
|
|
b9c53815cf | ||
|
|
66b4d2d14b | ||
|
|
9281d2a647 | ||
|
|
b1c17766ab | ||
|
|
2b3d85ff7b | ||
|
|
3365904bec | ||
|
|
b176128a76 | ||
|
|
5672c03208 | ||
|
|
d840d29011 | ||
|
|
b2ea827b42 | ||
|
|
4e015f7e0b | ||
|
|
dee363b092 | ||
|
|
eb8eae0154 | ||
|
|
b2a6d094b7 | ||
|
|
f88433b33d | ||
|
|
f3de61130c | ||
|
|
02c8bba1ca | ||
|
|
83ca07a9a9 | ||
|
|
0b4b96f4c2 | ||
|
|
4d32005f39 | ||
|
|
5a1256069f | ||
|
|
330540eb50 | ||
|
|
c278710f4f | ||
|
|
12940a092c | ||
|
|
5bafdca272 | ||
|
|
abdcbc6641 | ||
|
|
91a3d74fab | ||
|
|
4dc19bed16 | ||
|
|
aeb93919b6 | ||
|
|
0dfab2b8d9 | ||
|
|
be1e4b3424 | ||
|
|
304952e84d | ||
|
|
911b0ef941 | ||
|
|
496e97ecb5 | ||
|
|
96d55eb062 | ||
|
|
0ea9b7aae5 | ||
|
|
89ae110284 | ||
|
|
6a3b5d2659 | ||
|
|
63ddb0eb63 | ||
|
|
f39b1df91a | ||
|
|
bcf92d24d0 | ||
|
|
96f76f10ae | ||
|
|
418939ec40 | ||
|
|
30d95297b2 | ||
|
|
a945014bb5 | ||
|
|
844f918fc5 | ||
|
|
32bd6e78f5 | ||
|
|
21dad0f233 | ||
|
|
2efcf6e75d | ||
|
|
bb67223e25 | ||
|
|
64b47a2388 | ||
|
|
92f93323c4 | ||
|
|
7f791345fa | ||
|
|
221e2657db | ||
|
|
50609853ba | ||
|
|
2ac3ef9156 | ||
|
|
f9f6f59e08 | ||
|
|
6a96a17fdf | ||
|
|
5b3917103e | ||
|
|
05c2627318 | ||
|
|
785756abd1 | ||
|
|
f8d6ef4344 | ||
|
|
94636251cd | ||
|
|
f0f3d22d79 | ||
|
|
b6e73aaaf6 | ||
|
|
20a82bd597 | ||
|
|
261cc5fd32 | ||
|
|
2cdb1846f2 | ||
|
|
3cf7919bba | ||
|
|
be82da65bb | ||
|
|
21ae15b6e0 | ||
|
|
738542086e | ||
|
|
6afe96aa48 | ||
|
|
bc6801b799 | ||
|
|
38f8733ad7 | ||
|
|
16063130ef | ||
|
|
b6cc7234a5 | ||
|
|
2258268f6f | ||
|
|
d946bf5ef9 | ||
|
|
a6eb5c5bbf | ||
|
|
27f3cdd84f | ||
|
|
d8e958f2d2 | ||
|
|
2bad9a508b | ||
|
|
3598f05d45 | ||
|
|
a645f15b2d | ||
|
|
f901cf963d | ||
|
|
0150468abc | ||
|
|
3131c1078f | ||
|
|
ef667c545b | ||
|
|
f56098f40f |
No files matched your search
@@ -0,0 +1,145 @@
|
||||
name: Performance Benchmarks
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
iterations:
|
||||
description: 'Number of benchmark iterations'
|
||||
required: false
|
||||
default: '100'
|
||||
fail_on_regression:
|
||||
description: 'Fail if performance regresses'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e src/praisonai-agents[all]
|
||||
pip install agno crewai langgraph
|
||||
|
||||
- name: Run Import Time Benchmark
|
||||
id: import_time
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/import_time.py 2>&1 | tee import_time_output.txt
|
||||
|
||||
# Extract import time
|
||||
IMPORT_TIME=$(grep -oP 'Median:\s+\K[\d.]+' import_time_output.txt || echo "0")
|
||||
echo "import_time=$IMPORT_TIME" >> $GITHUB_OUTPUT
|
||||
|
||||
# Check threshold (200ms)
|
||||
if (( $(echo "$IMPORT_TIME > 200" | bc -l) )); then
|
||||
echo "::warning::Import time ${IMPORT_TIME}ms exceeds 200ms threshold"
|
||||
if [ "${{ inputs.fail_on_regression }}" == "true" ]; then
|
||||
echo "import_passed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "import_passed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run Memory Usage Benchmark
|
||||
id: memory
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/memory_usage.py 2>&1 | tee memory_output.txt
|
||||
|
||||
# Extract peak memory
|
||||
PEAK_MEMORY=$(grep -oP 'Peak:\s+\K[\d.]+' memory_output.txt || echo "0")
|
||||
echo "peak_memory=$PEAK_MEMORY" >> $GITHUB_OUTPUT
|
||||
|
||||
# Check threshold (30MB)
|
||||
if (( $(echo "$PEAK_MEMORY > 30" | bc -l) )); then
|
||||
echo "::warning::Memory usage ${PEAK_MEMORY}MB exceeds 30MB threshold"
|
||||
if [ "${{ inputs.fail_on_regression }}" == "true" ]; then
|
||||
echo "memory_passed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "memory_passed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run Simple Benchmark
|
||||
id: simple
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/simple_benchmark.py 2>&1 | tee simple_output.txt
|
||||
|
||||
# Extract PraisonAI time
|
||||
PRAISON_TIME=$(grep -oP 'PraisonAI\s+\K[\d.]+' simple_output.txt | head -1 || echo "0")
|
||||
echo "praison_time=$PRAISON_TIME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Tools Benchmark
|
||||
id: tools
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/tools_benchmark.py 2>&1 | tee tools_output.txt
|
||||
|
||||
# Extract PraisonAI time with tools
|
||||
PRAISON_TOOLS_TIME=$(grep -oP 'PraisonAI\s+\K[\d.]+' tools_output.txt | head -1 || echo "0")
|
||||
echo "praison_tools_time=$PRAISON_TOOLS_TIME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Parameter Impact Benchmark
|
||||
id: param_impact
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/param_impact_benchmark.py --no-save --iterations ${{ inputs.iterations }} 2>&1 | tee param_impact_output.txt
|
||||
|
||||
- name: Check Lazy Imports
|
||||
id: lazy_imports
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python -c "
|
||||
import sys
|
||||
from praisonaiagents import Agent
|
||||
agent = Agent(name='Test', output='silent')
|
||||
|
||||
# Check litellm is NOT loaded
|
||||
if 'litellm' in sys.modules:
|
||||
print('FAIL: litellm loaded during silent agent creation')
|
||||
exit(1)
|
||||
else:
|
||||
print('PASS: litellm not loaded (lazy import working)')
|
||||
"
|
||||
|
||||
- name: Generate Benchmark Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Performance Benchmark Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Metric | Value | Threshold | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|--------|-------|-----------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Import Time | ${{ steps.import_time.outputs.import_time }}ms | <200ms | ${{ steps.import_time.outputs.import_passed == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Peak Memory | ${{ steps.memory.outputs.peak_memory }}MB | <30MB | ${{ steps.memory.outputs.memory_passed == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Agent Instantiation | ${{ steps.simple.outputs.praison_time }}μs | - | ℹ️ |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Agent with Tools | ${{ steps.tools.outputs.praison_tools_time }}μs | - | ℹ️ |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Lazy Import Check" >> $GITHUB_STEP_SUMMARY
|
||||
echo "✅ litellm not loaded during silent agent creation" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Fail on Regression
|
||||
if: inputs.fail_on_regression == 'true'
|
||||
run: |
|
||||
if [ "${{ steps.import_time.outputs.import_passed }}" == "false" ]; then
|
||||
echo "::error::Import time regression detected"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ steps.memory.outputs.memory_passed }}" == "false" ]; then
|
||||
echo "::error::Memory usage regression detected"
|
||||
exit 1
|
||||
fi
|
||||
echo "All performance checks passed!"
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Update Feature Parity Tracker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'src/praisonai-agents/praisonaiagents/**/*.py'
|
||||
- 'src/praisonai-ts/src/**/*.ts'
|
||||
- 'src/praisonai-rust/src/**/*.rs'
|
||||
- '.github/workflows/update-parity-tracker.yml'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-parity-tracker:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
persist-credentials: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Generate parity trackers
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
python3 src/praisonai/scripts/generate_parity_tracker.py
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
if git diff --quiet src/praisonai-ts/FEATURE_PARITY_TRACKER.json src/praisonai-ts/PARITY.md src/praisonai-rust/FEATURE_PARITY_TRACKER.json src/praisonai-rust/PARITY.md 2>/dev/null; then
|
||||
echo "changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes to parity trackers"
|
||||
else
|
||||
echo "changes=true" >> $GITHUB_OUTPUT
|
||||
echo "Parity trackers have been updated"
|
||||
git diff src/praisonai-ts/FEATURE_PARITY_TRACKER.json | head -50
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.check_changes.outputs.changes == 'true'
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "docs: auto-update feature parity trackers [skip ci]"
|
||||
file_pattern: 'src/praisonai-ts/FEATURE_PARITY_TRACKER.json src/praisonai-ts/PARITY.md src/praisonai-rust/FEATURE_PARITY_TRACKER.json src/praisonai-rust/PARITY.md'
|
||||
commit_user_name: "github-actions[bot]"
|
||||
commit_user_email: "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
commit_author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
|
||||
|
||||
+8
-1
@@ -84,4 +84,11 @@ issue_details.json
|
||||
.mcpregistry_*
|
||||
|
||||
*.log
|
||||
.internal
|
||||
.internal
|
||||
.praisonai
|
||||
|
||||
# Rust / Cargo
|
||||
target/
|
||||
Cargo.lock
|
||||
**/*.rs.bk
|
||||
*.pdb
|
||||
@@ -137,7 +137,6 @@ PraisonAI Agents is the **fastest AI agent framework** for agent instantiation.
|
||||
| PraisonAI (LiteLLM) | 7.56 | 2.00x |
|
||||
| PydanticAI | 226.94 | 60.16x |
|
||||
| LangGraph | 4,558.71 | 1,209x |
|
||||
| CrewAI | 15,607.92 | 4,138x |
|
||||
|
||||
<details>
|
||||
<summary>Run benchmarks yourself</summary>
|
||||
|
||||
@@ -13,6 +13,12 @@ GOOGLE_API_KEY=your_google_api_key_here
|
||||
GROQ_API_KEY=your_groq_api_key_here
|
||||
COHERE_API_KEY=your_cohere_api_key_here
|
||||
|
||||
# Bot Tokens (for messaging bot services)
|
||||
SLACK_BOT_TOKEN=xoxb-your-slack-bot-token
|
||||
SLACK_APP_TOKEN=xapp-your-slack-app-token
|
||||
DISCORD_BOT_TOKEN=your-discord-bot-token
|
||||
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
|
||||
|
||||
# Optional: Service Configuration
|
||||
FLASK_ENV=development
|
||||
CHAINLIT_HOST=0.0.0.0
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.9.4" \
|
||||
"praisonai>=4.4.4" \
|
||||
"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>=3.9.4" \
|
||||
"praisonai>=4.4.4" \
|
||||
"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>=3.9.4" \
|
||||
"praisonai>=4.4.4" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Bot Environment Variables
|
||||
# Copy to .env and fill in your values
|
||||
|
||||
# Required: LLM API Key
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
|
||||
# Slack Bot (Socket Mode)
|
||||
SLACK_BOT_TOKEN=xoxb-your-slack-bot-token
|
||||
SLACK_APP_TOKEN=xapp-your-slack-app-token
|
||||
|
||||
# Discord Bot
|
||||
DISCORD_BOT_TOKEN=your-discord-bot-token
|
||||
|
||||
# Telegram Bot
|
||||
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
|
||||
@@ -0,0 +1,42 @@
|
||||
# Dockerfile for PraisonAI Slack Bot
|
||||
# Secrets are passed at runtime via environment variables
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
curl \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create praison config directory
|
||||
RUN mkdir -p /root/.praison
|
||||
|
||||
# Install praisonai with bot dependencies
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai \
|
||||
slack-bolt \
|
||||
slack-sdk \
|
||||
"discord.py>=2.0.0" \
|
||||
"python-telegram-bot>=20.0"
|
||||
|
||||
# Set environment variables for directory management
|
||||
ENV PRAISON_CONFIG_DIR=/root/.praison
|
||||
ENV DOCKER_CONTAINER=true
|
||||
|
||||
# Bot tokens should be passed at runtime (not baked into image)
|
||||
# SLACK_BOT_TOKEN=xoxb-...
|
||||
# SLACK_APP_TOKEN=xapp-...
|
||||
|
||||
# Optional: Copy custom agent configuration
|
||||
# COPY agent.yaml /app/agent.yaml
|
||||
|
||||
# Expose no ports - Socket Mode doesn't need them
|
||||
# For webhook mode, expose port 3000
|
||||
|
||||
# Default command - tokens from environment
|
||||
CMD ["praisonai", "bot", "slack"]
|
||||
@@ -0,0 +1,66 @@
|
||||
# PraisonAI Bot Docker Deployment
|
||||
|
||||
Deploy Slack, Discord, or Telegram bots using Docker.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 1. Copy environment template
|
||||
cp .env.template .env
|
||||
|
||||
# 2. Edit .env with your tokens
|
||||
nano .env
|
||||
|
||||
# 3. Run specific bot
|
||||
docker compose up slack-bot -d
|
||||
|
||||
# Or run all bots
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `OPENAI_API_KEY` | Yes | LLM API key |
|
||||
| `SLACK_BOT_TOKEN` | For Slack | Bot token (xoxb-...) |
|
||||
| `SLACK_APP_TOKEN` | For Slack | App token (xapp-...) |
|
||||
| `DISCORD_BOT_TOKEN` | For Discord | Bot token |
|
||||
| `TELEGRAM_BOT_TOKEN` | For Telegram | Bot token |
|
||||
|
||||
## Slack Setup
|
||||
|
||||
1. Create app at https://api.slack.com/apps
|
||||
2. Enable **Socket Mode** → Generate App-Level Token with `connections:write`
|
||||
3. Add **OAuth Scopes**: `chat:write`, `app_mentions:read`, `im:history`
|
||||
4. Enable **Event Subscriptions** → Subscribe to `app_mention`, `message.im`
|
||||
5. **Install to Workspace**
|
||||
6. Copy Bot Token and App Token to `.env`
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Start Slack bot
|
||||
docker compose up slack-bot -d
|
||||
|
||||
# View logs
|
||||
docker compose logs -f slack-bot
|
||||
|
||||
# Stop bot
|
||||
docker compose down slack-bot
|
||||
|
||||
# Rebuild after updates
|
||||
docker compose build --no-cache slack-bot
|
||||
```
|
||||
|
||||
## Custom Agent Configuration
|
||||
|
||||
Mount your agent config:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
slack-bot:
|
||||
volumes:
|
||||
- ./agent.yaml:/app/agent.yaml
|
||||
command: ["praisonai", "bot", "slack", "--agent", "/app/agent.yaml"]
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
version: '3.8'
|
||||
|
||||
# PraisonAI Bot Services
|
||||
# Run specific bots: docker compose up slack-bot
|
||||
# Run all bots: docker compose up
|
||||
|
||||
services:
|
||||
# Slack Bot
|
||||
slack-bot:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: docker/bots/Dockerfile
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
SLACK_BOT_TOKEN: ${SLACK_BOT_TOKEN}
|
||||
SLACK_APP_TOKEN: ${SLACK_APP_TOKEN}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
command: [ "praisonai", "bot", "slack" ]
|
||||
restart: unless-stopped
|
||||
|
||||
# Discord Bot
|
||||
discord-bot:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: docker/bots/Dockerfile
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
DISCORD_BOT_TOKEN: ${DISCORD_BOT_TOKEN}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
command: [ "praisonai", "bot", "discord" ]
|
||||
restart: unless-stopped
|
||||
|
||||
# Telegram Bot
|
||||
telegram-bot:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: docker/bots/Dockerfile
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
command: [ "praisonai", "bot", "telegram" ]
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
praison_data:
|
||||
driver: local
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
CHAINLIT_DB_DIR: /root/.praison
|
||||
command: praisonai ui --host 0.0.0.0
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8082"]
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:8082" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -49,7 +49,7 @@ services:
|
||||
CHAINLIT_DB_DIR: /root/.praison
|
||||
command: praisonai chat --host 0.0.0.0 --port 8083
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8083"]
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:8083" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -69,7 +69,7 @@ services:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:8080/health" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -89,6 +89,7 @@ services:
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
networks:
|
||||
- praison_network
|
||||
# For bot services (Slack, Discord, Telegram), see docker/bots/
|
||||
|
||||
volumes:
|
||||
praison_data:
|
||||
@@ -96,4 +97,4 @@ volumes:
|
||||
|
||||
networks:
|
||||
praison_network:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
@@ -2,12 +2,14 @@
|
||||
Embeddings Capability Example
|
||||
|
||||
Demonstrates text embedding generation using PraisonAI capabilities.
|
||||
Both `embed` and `embedding` work identically - use whichever you prefer.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import embed
|
||||
from praisonai import embed, embedding # Both work from top-level
|
||||
# Or: from praisonai.capabilities import embed, embedding
|
||||
|
||||
# Single text embedding
|
||||
print("=== Single Text Embedding ===")
|
||||
# Single text embedding using embed()
|
||||
print("=== Single Text Embedding (using embed) ===")
|
||||
result = embed(
|
||||
input="Hello, world!",
|
||||
model="text-embedding-3-small"
|
||||
@@ -16,6 +18,14 @@ print(f"Embedding dimensions: {len(result.embeddings[0])}")
|
||||
print(f"First 5 values: {result.embeddings[0][:5]}")
|
||||
print(f"Usage: {result.usage}")
|
||||
|
||||
# Same thing using embedding() alias
|
||||
print("\n=== Single Text Embedding (using embedding alias) ===")
|
||||
result = embedding(
|
||||
input="Hello, world!",
|
||||
model="text-embedding-3-small"
|
||||
)
|
||||
print(f"Embedding dimensions: {len(result.embeddings[0])}")
|
||||
|
||||
# Multiple text embeddings
|
||||
print("\n=== Multiple Text Embeddings ===")
|
||||
result = embed(
|
||||
@@ -25,3 +35,12 @@ result = embed(
|
||||
print(f"Number of embeddings: {len(result.embeddings)}")
|
||||
for i, emb in enumerate(result.embeddings):
|
||||
print(f" Text {i+1}: {len(emb)} dimensions")
|
||||
|
||||
# With custom dimensions (for models that support it)
|
||||
print("\n=== Custom Dimensions ===")
|
||||
result = embed(
|
||||
input="Hello world",
|
||||
model="text-embedding-3-large",
|
||||
dimensions=256
|
||||
)
|
||||
print(f"Reduced dimensions: {len(result.embeddings[0])}")
|
||||
File renamed without changes.
@@ -1,30 +1,30 @@
|
||||
"""Advanced Workflow with all consolidated params configured."""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.workflows import Workflow, WorkflowStep
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
from praisonaiagents.workflows.workflow_configs import (
|
||||
WorkflowOutputConfig, WorkflowPlanningConfig, WorkflowMemoryConfig,
|
||||
)
|
||||
|
||||
# Full-featured workflow with all consolidated params
|
||||
workflow = Workflow(
|
||||
workflow = AgentFlow(
|
||||
name="FullFeaturedWorkflow",
|
||||
description="Demonstrates all agent-like consolidated params",
|
||||
steps=[
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="researcher",
|
||||
action="Research: {{input}}",
|
||||
agent=Agent(instructions="You are a researcher."),
|
||||
knowledge=["research_docs/"], # Step-specific knowledge
|
||||
web="duckduckgo", # Use DuckDuckGo for this step
|
||||
),
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="analyzer",
|
||||
action="Analyze the research",
|
||||
agent=Agent(instructions="You are an analyst."),
|
||||
reflection="thorough", # Thorough reflection for analysis
|
||||
guardrails="strict", # Strict validation
|
||||
),
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="writer",
|
||||
action="Write the final report",
|
||||
agent=Agent(instructions="You are a writer."),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"""Basic Agents (multi-agent) example with consolidated params."""
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
|
||||
# Create agents
|
||||
writer = Agent(instructions="You write content.")
|
||||
editor = Agent(instructions="You edit content.")
|
||||
|
||||
# Multi-agent with consolidated params
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[writer, editor],
|
||||
memory=True,
|
||||
planning=True,
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
"""
|
||||
Basic Memory Example - Agent-Centric API
|
||||
|
||||
Demonstrates the simplest memory usage with consolidated params.
|
||||
"""
|
||||
|
||||
"""Basic memory example - minimal usage."""
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Basic: Enable memory with preset
|
||||
# Simple enable (uses default file-based memory)
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with memory.",
|
||||
memory="sqlite", # Preset: sqlite, redis, postgres, file
|
||||
memory=True,
|
||||
)
|
||||
|
||||
# Run a simple task
|
||||
response = agent.start("Remember that my favorite color is blue.")
|
||||
print(response)
|
||||
# With preset
|
||||
agent_redis = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory="redis", # Uses redis preset
|
||||
)
|
||||
|
||||
# Follow-up to test memory
|
||||
response = agent.start("What is my favorite color?")
|
||||
print(response)
|
||||
# With URL
|
||||
agent_postgres = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory="postgresql://localhost/mydb", # URL auto-detected
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.chat("Remember that my favorite color is blue.")
|
||||
print(response)
|
||||
File renamed without changes.
@@ -1,18 +1,18 @@
|
||||
"""Basic example showing step-level override of workflow defaults."""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.workflows import Workflow, WorkflowStep
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
|
||||
# Workflow with defaults
|
||||
workflow = Workflow(
|
||||
workflow = AgentFlow(
|
||||
name="StepOverrideWorkflow",
|
||||
steps=[
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="step1",
|
||||
action="Process with workflow defaults",
|
||||
agent=Agent(instructions="You are a processor."),
|
||||
# Uses workflow-level web=True (inherited)
|
||||
),
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="step2",
|
||||
action="Process with step override",
|
||||
agent=Agent(instructions="You are a processor."),
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
"""
|
||||
Basic Workflow Example - Agent-Centric API
|
||||
|
||||
Demonstrates minimal workflow with consolidated params.
|
||||
"""
|
||||
|
||||
"""Basic workflow example with consolidated params."""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.workflows import Workflow, WorkflowStep
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
|
||||
# Create a simple workflow
|
||||
workflow = Workflow(
|
||||
name="simple_workflow",
|
||||
output="verbose", # Workflow-level output preset
|
||||
# Create agents
|
||||
writer = Agent(instructions="You are a content writer.")
|
||||
editor = Agent(instructions="You are an editor.")
|
||||
|
||||
# Create workflow with consolidated params
|
||||
workflow = AgentFlow(
|
||||
name="Content Pipeline",
|
||||
steps=[
|
||||
Task(name="write", agent=writer, action="Write about {{topic}}"),
|
||||
Task(name="edit", agent=editor, action="Edit the content", context=["write"]),
|
||||
],
|
||||
output="verbose",
|
||||
planning=True,
|
||||
)
|
||||
|
||||
# Add a step
|
||||
step = WorkflowStep(
|
||||
name="writer",
|
||||
action="Write a haiku about programming",
|
||||
agent=Agent(instructions="You are a creative poet."),
|
||||
output="result.txt", # Save output to file
|
||||
)
|
||||
|
||||
# Note: This is a structural example - actual execution requires workflow.run()
|
||||
print("Workflow created with output preset:", workflow.output)
|
||||
print("Step output file:", step.output_file)
|
||||
if __name__ == "__main__":
|
||||
result = workflow.run(variables={"topic": "AI agents"})
|
||||
print(result)
|
||||
@@ -1,17 +1,17 @@
|
||||
"""Basic Workflow with Agent-like consolidated params."""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.workflows import Workflow, WorkflowStep
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
|
||||
# Workflow with agent-like params (knowledge, web, guardrails, reflection)
|
||||
workflow = Workflow(
|
||||
workflow = AgentFlow(
|
||||
name="AgentLikeWorkflow",
|
||||
steps=[
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="researcher",
|
||||
action="Research the topic: {{input}}",
|
||||
agent=Agent(instructions="You are a researcher."),
|
||||
),
|
||||
WorkflowStep(
|
||||
Task(
|
||||
name="writer",
|
||||
action="Write about the research findings",
|
||||
agent=Agent(instructions="You are a writer."),
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"""
|
||||
Basic Context Management Example
|
||||
|
||||
Demonstrates enabling context management with ContextConfig.
|
||||
This manages token budgets, auto-compaction, and prevents context overflow.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.context import ContextConfig, OptimizerStrategy
|
||||
|
||||
# Example 1: Enable context with defaults
|
||||
agent_basic = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant",
|
||||
context=True # Auto-compact at 80% utilization
|
||||
)
|
||||
|
||||
# Example 2: Custom configuration
|
||||
agent_custom = Agent(
|
||||
name="CustomAgent",
|
||||
instructions="You are a helpful assistant with custom context settings",
|
||||
context=ContextConfig(
|
||||
auto_compact=True,
|
||||
compact_threshold=0.8, # Trigger at 80% utilization
|
||||
strategy=OptimizerStrategy.SMART,
|
||||
output_reserve=8000, # Reserve tokens for output
|
||||
keep_recent_turns=5, # Always keep last 5 turns
|
||||
tool_output_max=10000, # Max tokens per tool output
|
||||
)
|
||||
)
|
||||
|
||||
# Example 3: Sliding window strategy
|
||||
agent_sliding = Agent(
|
||||
name="SlidingAgent",
|
||||
instructions="You are a helpful assistant using sliding window",
|
||||
context=ContextConfig(
|
||||
strategy=OptimizerStrategy.SLIDING_WINDOW,
|
||||
keep_recent_turns=10, # Keep last 10 turns
|
||||
)
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== Basic Context Example ===")
|
||||
print("Context management is automatically enabled.")
|
||||
print()
|
||||
|
||||
# Test basic agent
|
||||
result = agent_basic.chat("Hello! What is 2 + 2?")
|
||||
print(f"Response: {result}")
|
||||
print()
|
||||
|
||||
# Show context config
|
||||
print("=== Context Configuration ===")
|
||||
config = ContextConfig(
|
||||
auto_compact=True,
|
||||
compact_threshold=0.8,
|
||||
strategy=OptimizerStrategy.SMART,
|
||||
)
|
||||
print(f"Config: {config.to_dict()}")
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Session Tracking Example
|
||||
|
||||
Demonstrates tracking conversation state (goal, plan, progress) across turns.
|
||||
Inspired by Agno's SessionContextStore pattern.
|
||||
"""
|
||||
|
||||
from praisonaiagents.context import SessionContextTracker
|
||||
|
||||
# Create session tracker
|
||||
tracker = SessionContextTracker(
|
||||
session_id="user123",
|
||||
track_summary=True,
|
||||
track_goal=True,
|
||||
track_plan=True,
|
||||
track_progress=True,
|
||||
)
|
||||
|
||||
# Set the user's goal
|
||||
tracker.update_goal("Build a Python web application with Flask")
|
||||
|
||||
# Define the plan
|
||||
tracker.update_plan([
|
||||
"Create Flask project structure",
|
||||
"Add routes for API endpoints",
|
||||
"Implement database models",
|
||||
"Add authentication",
|
||||
"Write tests",
|
||||
"Deploy to cloud"
|
||||
])
|
||||
|
||||
# Mark completed steps
|
||||
tracker.add_progress("Created Flask project structure")
|
||||
tracker.add_progress("Added routes for API endpoints")
|
||||
|
||||
# Update summary
|
||||
tracker.update_summary(
|
||||
"User is building a Flask web app. Completed project setup and API routes. "
|
||||
"Next: implement database models."
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== Session Tracking Example ===")
|
||||
print()
|
||||
|
||||
# Show current state
|
||||
print(f"Session ID: {tracker.session_id}")
|
||||
print(f"Goal: {tracker.goal}")
|
||||
print(f"Plan: {tracker.plan}")
|
||||
print(f"Progress: {tracker.progress}")
|
||||
print()
|
||||
|
||||
# Get context string (simple format)
|
||||
print("=== Context String ===")
|
||||
print(tracker.to_context_string())
|
||||
print()
|
||||
|
||||
# Get system prompt section (XML format with guidelines)
|
||||
print("=== System Prompt Section ===")
|
||||
print(tracker.to_system_prompt_section())
|
||||
print()
|
||||
|
||||
# Export to dict for persistence
|
||||
print("=== Export as Dict ===")
|
||||
data = tracker.to_dict()
|
||||
print(f"Exported: {data.keys()}")
|
||||
print()
|
||||
|
||||
# Recreate from dict
|
||||
new_tracker = SessionContextTracker.from_dict(data)
|
||||
print(f"Restored goal: {new_tracker.goal}")
|
||||
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Multi-Memory Aggregation Example
|
||||
|
||||
Demonstrates concurrent fetching from multiple context sources.
|
||||
Inspired by CrewAI's ContextualMemory pattern.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from praisonaiagents.context import ContextAggregator, create_aggregator_from_config
|
||||
|
||||
# Example 1: Basic aggregator with mock sources
|
||||
def mock_memory_search(query: str) -> str:
|
||||
"""Mock memory search - returns recent conversation context."""
|
||||
return f"[Memory] User previously asked about: {query}. They prefer Python."
|
||||
|
||||
def mock_knowledge_search(query: str) -> str:
|
||||
"""Mock knowledge search - returns facts from knowledge base."""
|
||||
return f"[Knowledge] Relevant facts: Python is a high-level programming language."
|
||||
|
||||
def mock_rag_retrieve(query: str) -> str:
|
||||
"""Mock RAG retrieval - returns document chunks."""
|
||||
return f"[RAG] Retrieved: Documentation about {query} from the codebase."
|
||||
|
||||
# Create aggregator
|
||||
aggregator = ContextAggregator(
|
||||
max_tokens=4000,
|
||||
separator="\n\n---\n\n",
|
||||
include_source_labels=True
|
||||
)
|
||||
|
||||
# Register sources with priorities (lower = higher priority)
|
||||
aggregator.register_source("memory", mock_memory_search, priority=10)
|
||||
aggregator.register_source("knowledge", mock_knowledge_search, priority=20)
|
||||
aggregator.register_source("rag", mock_rag_retrieve, priority=30)
|
||||
|
||||
async def demo_async():
|
||||
"""Demo async aggregation."""
|
||||
print("=== Async Aggregation ===")
|
||||
result = await aggregator.aggregate("Python web development")
|
||||
print(f"Sources used: {result.sources_used}")
|
||||
print(f"Tokens used: {result.tokens_used}")
|
||||
print(f"Fetch times: {result.fetch_times}")
|
||||
print()
|
||||
print("Context:")
|
||||
print(result.context)
|
||||
return result
|
||||
|
||||
def demo_sync():
|
||||
"""Demo sync aggregation."""
|
||||
print("=== Sync Aggregation ===")
|
||||
result = aggregator.aggregate_sync("Flask API development")
|
||||
print(f"Sources used: {result.sources_used}")
|
||||
print(f"Tokens used: {result.tokens_used}")
|
||||
print()
|
||||
print("Context:")
|
||||
print(result.context)
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== Multi-Memory Aggregation Example ===")
|
||||
print()
|
||||
|
||||
# Show registered sources
|
||||
print(f"Registered sources: {aggregator.sources}")
|
||||
print()
|
||||
|
||||
# Demo sync aggregation
|
||||
demo_sync()
|
||||
print()
|
||||
|
||||
# Demo async aggregation
|
||||
asyncio.run(demo_async())
|
||||
print()
|
||||
|
||||
# Demo selective sources
|
||||
print("=== Selective Sources ===")
|
||||
result = aggregator.aggregate_sync(
|
||||
"Find database info",
|
||||
sources=["memory", "knowledge"] # Only use these
|
||||
)
|
||||
print(f"Sources used: {result.sources_used}")
|
||||
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Compaction Strategies Example
|
||||
|
||||
Demonstrates different context compaction strategies.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.context import ContextConfig, OptimizerStrategy
|
||||
|
||||
# Strategy 1: TRUNCATE - Fast, removes oldest messages
|
||||
agent_truncate = Agent(
|
||||
name="TruncateAgent",
|
||||
instructions="You are a helpful assistant",
|
||||
context=ContextConfig(
|
||||
strategy=OptimizerStrategy.TRUNCATE,
|
||||
compact_threshold=0.8,
|
||||
)
|
||||
)
|
||||
|
||||
# Strategy 2: SLIDING_WINDOW - Keeps recent N turns
|
||||
agent_sliding = Agent(
|
||||
name="SlidingAgent",
|
||||
instructions="You are a helpful assistant",
|
||||
context=ContextConfig(
|
||||
strategy=OptimizerStrategy.SLIDING_WINDOW,
|
||||
keep_recent_turns=10,
|
||||
)
|
||||
)
|
||||
|
||||
# Strategy 3: SMART - Importance-based (default)
|
||||
agent_smart = Agent(
|
||||
name="SmartAgent",
|
||||
instructions="You are a helpful assistant",
|
||||
context=ContextConfig(
|
||||
strategy=OptimizerStrategy.SMART, # Default
|
||||
compact_threshold=0.8,
|
||||
)
|
||||
)
|
||||
|
||||
# Strategy 4: PRUNE_TOOLS - Truncate old tool outputs
|
||||
agent_prune = Agent(
|
||||
name="PruneAgent",
|
||||
instructions="You are a helpful assistant with tools",
|
||||
context=ContextConfig(
|
||||
strategy=OptimizerStrategy.PRUNE_TOOLS,
|
||||
tool_output_max=5000, # Max tokens per tool output
|
||||
)
|
||||
)
|
||||
|
||||
# Strategy 5: NON_DESTRUCTIVE - Only prune tools, keep all history
|
||||
agent_safe = Agent(
|
||||
name="SafeAgent",
|
||||
instructions="You are a helpful assistant (safety-critical)",
|
||||
context=ContextConfig(
|
||||
strategy=OptimizerStrategy.NON_DESTRUCTIVE,
|
||||
)
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== Compaction Strategies Example ===")
|
||||
print()
|
||||
|
||||
strategies = [
|
||||
("TRUNCATE", OptimizerStrategy.TRUNCATE, "Remove oldest messages"),
|
||||
("SLIDING_WINDOW", OptimizerStrategy.SLIDING_WINDOW, "Keep last N turns"),
|
||||
("SMART", OptimizerStrategy.SMART, "Importance-based (default)"),
|
||||
("PRUNE_TOOLS", OptimizerStrategy.PRUNE_TOOLS, "Truncate tool outputs"),
|
||||
("NON_DESTRUCTIVE", OptimizerStrategy.NON_DESTRUCTIVE, "Only prune tools"),
|
||||
]
|
||||
|
||||
for name, strategy, description in strategies:
|
||||
print(f" {name:20} - {description}")
|
||||
|
||||
print()
|
||||
print("Usage:")
|
||||
print(" context=ContextConfig(strategy=OptimizerStrategy.SLIDING_WINDOW)")
|
||||
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Context Monitoring Example
|
||||
|
||||
Demonstrates monitoring context usage with logging and metrics.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.context import ContextConfig, MonitorConfig, OptimizerStrategy
|
||||
|
||||
# Example 1: Human-readable monitoring
|
||||
agent_human = Agent(
|
||||
name="MonitoredAgent",
|
||||
instructions="You are a helpful assistant",
|
||||
context=ContextConfig(
|
||||
auto_compact=True,
|
||||
monitor=MonitorConfig(
|
||||
enabled=True,
|
||||
path="./context_logs/",
|
||||
format="human", # Human-readable format
|
||||
frequency="turn", # Log on every turn
|
||||
redact_sensitive=True, # Redact sensitive data
|
||||
multi_agent_files=True, # Separate files per agent
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Example 2: JSON monitoring (for parsing)
|
||||
agent_json = Agent(
|
||||
name="JSONMonitorAgent",
|
||||
instructions="You are a helpful assistant",
|
||||
context=ContextConfig(
|
||||
monitor=MonitorConfig(
|
||||
enabled=True,
|
||||
path="./context_logs/",
|
||||
format="json", # JSON format for parsing
|
||||
frequency="tool_call", # Log on tool calls
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Example 3: Overflow-only monitoring
|
||||
agent_overflow = Agent(
|
||||
name="OverflowAgent",
|
||||
instructions="You are a helpful assistant",
|
||||
context=ContextConfig(
|
||||
monitor=MonitorConfig(
|
||||
enabled=True,
|
||||
path="./context_logs/",
|
||||
format="human",
|
||||
frequency="overflow", # Only log on overflow
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== Context Monitoring Example ===")
|
||||
print()
|
||||
|
||||
# Show monitor configuration
|
||||
config = MonitorConfig(
|
||||
enabled=True,
|
||||
path="./context_logs/",
|
||||
format="human",
|
||||
frequency="turn",
|
||||
redact_sensitive=True,
|
||||
)
|
||||
|
||||
print("Monitor Config:")
|
||||
print(f" enabled: {config.enabled}")
|
||||
print(f" path: {config.path}")
|
||||
print(f" format: {config.format}")
|
||||
print(f" frequency: {config.frequency}")
|
||||
print(f" redact_sensitive: {config.redact_sensitive}")
|
||||
print()
|
||||
|
||||
print("Frequency options:")
|
||||
print(" 'turn' - Log on every conversation turn")
|
||||
print(" 'tool_call' - Log when tools are called")
|
||||
print(" 'manual' - Manual logging only")
|
||||
print(" 'overflow' - Log only when context overflows")
|
||||
print()
|
||||
|
||||
print("Format options:")
|
||||
print(" 'human' - Human-readable format")
|
||||
print(" 'json' - JSON format for parsing")
|
||||
@@ -189,8 +189,8 @@ def main():
|
||||
print("-" * 40)
|
||||
print("Auto-agents mode default: context=False (zero overhead)")
|
||||
print("To enable for multi-agent workflows:")
|
||||
print(" from praisonaiagents import Agents")
|
||||
print(" agents = Agents(agents=[...], context=True)")
|
||||
print(" from praisonaiagents import AgentTeam")
|
||||
print(" agents = AgentTeam(agents=[...], context=True)")
|
||||
print("")
|
||||
print("Recommended config for long tasks:")
|
||||
print(" context=ManagerConfig(")
|
||||
|
||||
@@ -6,10 +6,10 @@ Demonstrates how to use multi-agent context with Agents via context= param,
|
||||
and also shows low-level MultiAgentLedger usage for advanced scenarios.
|
||||
|
||||
Agent-Centric Quick Start:
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
from praisonaiagents.context import ManagerConfig
|
||||
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[agent1, agent2],
|
||||
context=ManagerConfig(policy="isolated"), # or "shared"
|
||||
)
|
||||
@@ -17,7 +17,7 @@ Agent-Centric Quick Start:
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
from praisonaiagents.context import (
|
||||
ManagerConfig,
|
||||
MultiAgentLedger,
|
||||
@@ -49,7 +49,7 @@ def agent_centric_example():
|
||||
# Multi-agent setup with isolated context policy
|
||||
# (each agent maintains its own context)
|
||||
print("Created agents with context=True")
|
||||
print("Use Agents(agents=[...], context=ManagerConfig(policy='isolated'))")
|
||||
print("Use AgentTeam(agents=[...], context=ManagerConfig(policy='isolated'))")
|
||||
print("for multi-agent context isolation")
|
||||
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ def example_5_multi_agent():
|
||||
print("Example 5: Multi-Agent Context Management")
|
||||
print("=" * 60)
|
||||
|
||||
from praisonaiagents import Agents, Task
|
||||
from praisonaiagents import AgentTeam, Task
|
||||
|
||||
config = ManagerConfig(
|
||||
auto_compact=True,
|
||||
@@ -181,7 +181,7 @@ def example_5_multi_agent():
|
||||
task2 = Task(description="Write a summary", agent=writer)
|
||||
|
||||
# Create multi-agent system with context management
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[researcher, writer],
|
||||
tasks=[task1, task2],
|
||||
context=config, # Shared context config
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
"""
|
||||
Example 15: Recursive Context Pattern
|
||||
|
||||
Demonstrates the RLM (Recursive Language Model) pattern for handling large context:
|
||||
- Store large documents as artifacts (not in LLM context)
|
||||
- Agent uses tools (grep/head/tail/chunk) to explore artifacts
|
||||
- Dramatically reduces token usage (~2K vs ~10K+)
|
||||
|
||||
This pattern solves "context rot" by treating context as programmatically
|
||||
explorable data instead of dumping everything into the LLM's context window.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import os
|
||||
from praisonaiagents import Agent
|
||||
from praisonai.context import FileSystemArtifactStore
|
||||
from praisonai.context.queue import create_artifact_tools
|
||||
from praisonaiagents.context.artifacts import ArtifactMetadata
|
||||
|
||||
|
||||
def create_sample_document():
|
||||
"""Create a sample large document for testing."""
|
||||
# Simulate a research paper / large document
|
||||
content = """
|
||||
# Attention Is All You Need
|
||||
|
||||
## Abstract
|
||||
|
||||
The dominant sequence transduction models are based on complex recurrent or
|
||||
convolutional neural networks that include an encoder and a decoder. The best
|
||||
performing models also connect the encoder and decoder through an attention
|
||||
mechanism. We propose a new simple network architecture, the Transformer,
|
||||
based solely on attention mechanisms, dispensing with recurrence and convolutions
|
||||
entirely.
|
||||
|
||||
## Introduction
|
||||
|
||||
Recurrent neural networks, long short-term memory and gated recurrent neural
|
||||
networks in particular, have been firmly established as state of the art
|
||||
approaches in sequence modeling and transduction problems such as language
|
||||
modeling and machine translation.
|
||||
|
||||
## Background
|
||||
|
||||
The goal of reducing sequential computation also forms the foundation of the
|
||||
Extended Neural GPU, ByteNet and ConvS2S, all of which use convolutional neural
|
||||
networks as basic building block.
|
||||
|
||||
## Model Architecture
|
||||
|
||||
### Encoder and Decoder Stacks
|
||||
|
||||
The encoder is composed of a stack of N = 6 identical layers. Each layer has
|
||||
two sub-layers. The first is a multi-head self-attention mechanism, and the
|
||||
second is a simple, position-wise fully connected feed-forward network.
|
||||
|
||||
### Attention
|
||||
|
||||
An attention function can be described as mapping a query and a set of key-value
|
||||
pairs to an output, where the query, keys, values, and output are all vectors.
|
||||
|
||||
#### Scaled Dot-Product Attention
|
||||
|
||||
We call our particular attention "Scaled Dot-Product Attention". The input
|
||||
consists of queries and keys of dimension dk, and values of dimension dv.
|
||||
|
||||
The formula is: Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V
|
||||
|
||||
### Multi-Head Attention
|
||||
|
||||
Multi-head attention allows the model to jointly attend to information from
|
||||
different representation subspaces at different positions.
|
||||
|
||||
## Results
|
||||
|
||||
### Machine Translation
|
||||
|
||||
On the WMT 2014 English-to-German translation task, the big transformer model
|
||||
outperforms the best previously reported models including ensembles.
|
||||
|
||||
The model achieved a BLEU score of 28.4 on the WMT 2014 English-to-German
|
||||
translation task, improving over the existing best results by over 2 BLEU.
|
||||
|
||||
On the WMT 2014 English-to-French translation task, our big model achieves
|
||||
a BLEU score of 41.8, outperforming all published single models.
|
||||
|
||||
### Training
|
||||
|
||||
Training took 3.5 days on 8 P100 GPUs. The big model was trained for 300,000
|
||||
steps (3.5 days) at a cost of approximately $1000 in cloud compute.
|
||||
|
||||
## Model Variations
|
||||
|
||||
| Model | Parameters | BLEU EN-DE | BLEU EN-FR |
|
||||
|-------|------------|------------|------------|
|
||||
| Base | 65M | 27.3 | 38.1 |
|
||||
| Big | 213M | 28.4 | 41.8 |
|
||||
|
||||
The base model has 65 million parameters (65 × 10^6).
|
||||
The big model has 213 million parameters.
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this work, we presented the Transformer, the first sequence transduction
|
||||
model based entirely on attention, replacing the recurrent layers most commonly
|
||||
used in encoder-decoder architectures with multi-headed self-attention.
|
||||
|
||||
The Transformer can be trained significantly faster than architectures based
|
||||
on recurrent or convolutional layers.
|
||||
|
||||
## Authors
|
||||
|
||||
Ashish Vaswani (Google Brain)
|
||||
Noam Shazeer (Google Brain)
|
||||
Niki Parmar (Google Research)
|
||||
Jakob Uszkoreit (Google Research)
|
||||
Llion Jones (Google Research)
|
||||
Aidan N. Gomez (University of Toronto)
|
||||
Łukasz Kaiser (Google Brain)
|
||||
Illia Polosukhin
|
||||
|
||||
## References
|
||||
|
||||
[1] Neural Machine Translation by Jointly Learning to Align and Translate
|
||||
[2] Sequence to Sequence Learning with Neural Networks
|
||||
[3] Learning Phrase Representations using RNN Encoder-Decoder
|
||||
"""
|
||||
# Repeat content to make it larger
|
||||
full_content = (content + "\n\n") * 10 # ~35KB
|
||||
return full_content
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print("Recursive Context Pattern - Token Efficiency Demo")
|
||||
print("=" * 70)
|
||||
|
||||
# Track token usage across methods
|
||||
token_stats = {
|
||||
"traditional": {"input": 0, "output": 0},
|
||||
"recursive": {"input": 0, "output": 0},
|
||||
}
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# 1. Create artifact store and tools
|
||||
store = FileSystemArtifactStore(base_dir=tmpdir)
|
||||
artifact_tools = create_artifact_tools(store=store)
|
||||
|
||||
print(f"\n1. Created FileSystemArtifactStore")
|
||||
print(f" Location: {tmpdir}")
|
||||
print(f" Tools available: {[t.__name__ for t in artifact_tools]}")
|
||||
|
||||
# 2. Create sample document and store as artifact
|
||||
document = create_sample_document()
|
||||
doc_size = len(document)
|
||||
doc_tokens = doc_size // 4 # Rough estimate: 4 chars per token
|
||||
|
||||
metadata = ArtifactMetadata(
|
||||
agent_id="research_agent",
|
||||
run_id="demo_run",
|
||||
tool_name="document_loader",
|
||||
turn_id=1
|
||||
)
|
||||
|
||||
ref = store.store(document, metadata)
|
||||
|
||||
print(f"\n2. Stored document as artifact")
|
||||
print(f" Size: {doc_size:,} chars (~{doc_tokens:,} tokens)")
|
||||
print(f" Path: {ref.path}")
|
||||
print(f" Summary: {ref.summary[:80]}...")
|
||||
|
||||
# 3. Create agent WITH artifact tools (Recursive Context approach)
|
||||
agent = Agent(
|
||||
instructions="""You are a research paper analyst with access to artifact exploration tools.
|
||||
When given an artifact path, use the tools to explore it efficiently:
|
||||
- artifact_grep: Search for patterns (USE THIS FIRST)
|
||||
- artifact_head: See first N lines
|
||||
- artifact_tail: See last N lines
|
||||
- artifact_chunk: Get specific line ranges
|
||||
|
||||
Be precise and cite line numbers when finding information.""",
|
||||
tools=artifact_tools,
|
||||
output='silent' # Quiet output for cleaner demo
|
||||
)
|
||||
|
||||
print(f"\n3. Created agent with {len(agent.tools)} artifact tools")
|
||||
|
||||
# 4. Query using RECURSIVE CONTEXT (small prompt + tools)
|
||||
print(f"\n4. RECURSIVE CONTEXT APPROACH")
|
||||
print("-" * 40)
|
||||
|
||||
prompt = f"""I have a research paper stored at: {ref.path}
|
||||
|
||||
Find the BLEU score on the WMT 2014 English-to-German translation task.
|
||||
Use artifact_grep to search, then report the answer with line number."""
|
||||
|
||||
prompt_tokens = len(prompt) // 4
|
||||
print(f" Prompt size: {len(prompt)} chars (~{prompt_tokens} tokens)")
|
||||
print(f" Document NOT in prompt context!")
|
||||
|
||||
response = agent.chat(prompt)
|
||||
|
||||
# Estimate output tokens (could use actual LiteLLM response if available)
|
||||
output_tokens = len(response) // 4
|
||||
|
||||
# Calculate recursive approach tokens (prompt + tool calls + response)
|
||||
# Tool call: ~200 tokens for tool desc + call
|
||||
# Tool response: ~300 tokens for grep results
|
||||
estimated_total = prompt_tokens + 200 + 300 + output_tokens
|
||||
token_stats["recursive"]["input"] = estimated_total
|
||||
|
||||
print(f"\n Agent Response:")
|
||||
print(f" {response[:200]}...")
|
||||
print(f"\n Estimated tokens used: ~{estimated_total:,}")
|
||||
|
||||
# 5. Compare with TRADITIONAL approach (full context)
|
||||
print(f"\n5. TRADITIONAL APPROACH (for comparison)")
|
||||
print("-" * 40)
|
||||
|
||||
traditional_prompt_tokens = prompt_tokens + doc_tokens
|
||||
token_stats["traditional"]["input"] = traditional_prompt_tokens
|
||||
|
||||
print(f" If we passed FULL document in prompt:")
|
||||
print(f" Prompt: {prompt_tokens} + Document: {doc_tokens} = {traditional_prompt_tokens:,} tokens")
|
||||
|
||||
# 6. Show token savings
|
||||
print(f"\n6. TOKEN COMPARISON")
|
||||
print("=" * 40)
|
||||
|
||||
savings = token_stats["traditional"]["input"] - token_stats["recursive"]["input"]
|
||||
savings_pct = (savings / token_stats["traditional"]["input"]) * 100
|
||||
|
||||
print(f" Traditional approach: ~{token_stats['traditional']['input']:,} tokens")
|
||||
print(f" Recursive approach: ~{token_stats['recursive']['input']:,} tokens")
|
||||
print(f" -----------------------------------")
|
||||
print(f" SAVINGS: ~{savings:,} tokens ({savings_pct:.0f}%)")
|
||||
|
||||
# 7. Demonstrate other artifact operations
|
||||
print(f"\n7. ARTIFACT OPERATIONS DEMO")
|
||||
print("-" * 40)
|
||||
|
||||
# head
|
||||
head_result = store.head(ref, lines=5)
|
||||
print(f" head(5 lines):")
|
||||
for line in head_result.split('\n')[:3]:
|
||||
print(f" {line[:60]}")
|
||||
|
||||
# grep
|
||||
grep_results = store.grep(ref, pattern=r"BLEU.*\d+", max_matches=3)
|
||||
print(f"\n grep('BLEU.*\\d+'):")
|
||||
for match in grep_results[:2]:
|
||||
print(f" Line {match.line_number}: {match.line_content[:50].strip()}...")
|
||||
|
||||
# chunk
|
||||
chunk_result = store.chunk(ref, start_line=50, end_line=55)
|
||||
print(f"\n chunk(lines 50-55):")
|
||||
for line in chunk_result.split('\n')[:3]:
|
||||
print(f" {line[:60]}")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("CONCLUSION: Recursive Context Pattern")
|
||||
print("=" * 70)
|
||||
print("""
|
||||
The Recursive Context pattern (RLM) provides:
|
||||
✓ Token efficiency: ~{savings_pct:.0f}% reduction in token usage
|
||||
✓ Scalability: Handle documents of any size
|
||||
✓ Precision: Agent searches for exactly what it needs
|
||||
✓ Cost savings: Lower API costs due to fewer tokens
|
||||
✓ Better accuracy: Avoids "context rot" from large contexts
|
||||
""".format(savings_pct=savings_pct))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,145 @@
|
||||
"""
|
||||
Agent-centric Embedding Example
|
||||
|
||||
This example shows how to use embeddings with PraisonAI Agents.
|
||||
Embeddings are used internally by Agent for:
|
||||
- Knowledge retrieval (RAG)
|
||||
- Memory storage and search
|
||||
- Semantic similarity
|
||||
|
||||
You can also use the embedding API directly for custom use cases.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, embedding
|
||||
|
||||
# =============================================================================
|
||||
# Example 1: Direct Embedding API
|
||||
# =============================================================================
|
||||
|
||||
def example_direct_embedding():
|
||||
"""Use the embedding API directly."""
|
||||
|
||||
# Single text embedding
|
||||
result = embedding("Hello, world!")
|
||||
print(f"Single embedding: {len(result.embeddings[0])} dimensions")
|
||||
|
||||
# Batch embedding
|
||||
texts = ["AI agents are powerful", "Machine learning is fascinating"]
|
||||
result = embedding(texts, model="text-embedding-3-small")
|
||||
print(f"Batch embeddings: {len(result.embeddings)} vectors")
|
||||
|
||||
# Custom dimensions (for models that support it)
|
||||
result = embedding("Hello", model="text-embedding-3-large", dimensions=256)
|
||||
print(f"Custom dimensions: {len(result.embeddings[0])} dimensions")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 2: Agent with Knowledge (uses embeddings internally)
|
||||
# =============================================================================
|
||||
|
||||
def example_agent_with_knowledge():
|
||||
"""Agent with knowledge uses embeddings for RAG."""
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with knowledge about AI.",
|
||||
knowledge=["AI agents can use tools to accomplish tasks."],
|
||||
knowledge_config={
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {"model": "text-embedding-3-small"}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# The agent uses embeddings internally for knowledge retrieval
|
||||
response = agent.chat("What can AI agents do?")
|
||||
print(f"Agent response: {response}")
|
||||
|
||||
return agent
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 3: Agent with Memory (uses embeddings internally)
|
||||
# =============================================================================
|
||||
|
||||
def example_agent_with_memory():
|
||||
"""Agent with memory uses embeddings for semantic search."""
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant that remembers conversations.",
|
||||
memory=True,
|
||||
memory_config={
|
||||
"embedding_model": "text-embedding-3-small"
|
||||
}
|
||||
)
|
||||
|
||||
# The agent uses embeddings internally for memory storage/retrieval
|
||||
agent.chat("My name is Alice and I love Python programming.")
|
||||
response = agent.chat("What do I love?")
|
||||
print(f"Agent remembered: {response}")
|
||||
|
||||
return agent
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 4: Custom Embedding for Similarity Search
|
||||
# =============================================================================
|
||||
|
||||
def example_similarity_search():
|
||||
"""Use embeddings for custom similarity search."""
|
||||
import math
|
||||
|
||||
def cosine_similarity(a, b):
|
||||
dot = sum(x * y for x, y in zip(a, b))
|
||||
norm_a = math.sqrt(sum(x * x for x in a))
|
||||
norm_b = math.sqrt(sum(x * x for x in b))
|
||||
return dot / (norm_a * norm_b) if norm_a and norm_b else 0
|
||||
|
||||
# Embed documents
|
||||
documents = [
|
||||
"Python is a programming language",
|
||||
"JavaScript runs in browsers",
|
||||
"AI agents can automate tasks"
|
||||
]
|
||||
doc_embeddings = embedding(documents)
|
||||
|
||||
# Embed query
|
||||
query = "What language is used for web development?"
|
||||
query_embedding = embedding(query)
|
||||
|
||||
# Find most similar document
|
||||
similarities = [
|
||||
cosine_similarity(query_embedding.embeddings[0], doc_emb)
|
||||
for doc_emb in doc_embeddings.embeddings
|
||||
]
|
||||
|
||||
best_idx = similarities.index(max(similarities))
|
||||
print(f"Most similar document: {documents[best_idx]}")
|
||||
print(f"Similarity score: {similarities[best_idx]:.4f}")
|
||||
|
||||
return similarities
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Example 1: Direct Embedding API")
|
||||
print("=" * 60)
|
||||
example_direct_embedding()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example 4: Custom Similarity Search")
|
||||
print("=" * 60)
|
||||
example_similarity_search()
|
||||
|
||||
# Uncomment to run agent examples (requires API key)
|
||||
# print("\n" + "=" * 60)
|
||||
# print("Example 2: Agent with Knowledge")
|
||||
# print("=" * 60)
|
||||
# example_agent_with_knowledge()
|
||||
|
||||
# print("\n" + "=" * 60)
|
||||
# print("Example 3: Agent with Memory")
|
||||
# print("=" * 60)
|
||||
# example_agent_with_memory()
|
||||
@@ -4,7 +4,7 @@ Fixed example demonstrating proper guardrail usage with PraisonAI Agents.
|
||||
This addresses the issues reported in issue #875.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, GuardrailResult, Agents, TaskOutput
|
||||
from praisonaiagents import Agent, Task, GuardrailResult, AgentTeam, TaskOutput
|
||||
from typing import Tuple, Any
|
||||
import trafilatura
|
||||
|
||||
@@ -103,7 +103,7 @@ task_with_llm_guardrail = Task(
|
||||
|
||||
# Run with GuardrailResult example
|
||||
print("=== Running with GuardrailResult guardrail ===")
|
||||
agents_gr = Agents(
|
||||
agents_gr = AgentTeam(
|
||||
agents=[agent],
|
||||
tasks=[task_with_guardrailresult]
|
||||
)
|
||||
@@ -113,7 +113,7 @@ agents_gr = Agents(
|
||||
|
||||
# Run with Tuple example
|
||||
print("\n=== Running with Tuple[bool, Any] guardrail ===")
|
||||
agents_tuple = Agents(
|
||||
agents_tuple = AgentTeam(
|
||||
agents=[agent],
|
||||
tasks=[task_with_tuple]
|
||||
)
|
||||
@@ -123,7 +123,7 @@ agents_tuple = Agents(
|
||||
|
||||
# Run with LLM guardrail example
|
||||
print("\n=== Running with LLM-based guardrail ===")
|
||||
agents_llm = Agents(
|
||||
agents_llm = AgentTeam(
|
||||
agents=[agent],
|
||||
tasks=[task_with_llm_guardrail]
|
||||
)
|
||||
|
||||
@@ -2,32 +2,34 @@
|
||||
"""
|
||||
Basic Hooks Example for PraisonAI Agents.
|
||||
|
||||
This example demonstrates how to use the hooks system to:
|
||||
This example demonstrates the simplified hook API using add_hook():
|
||||
1. Log tool calls before execution
|
||||
2. Block dangerous operations
|
||||
3. Add context to agent responses
|
||||
4. Monitor session lifecycle
|
||||
|
||||
For the simplest example, see simple_hooks.py
|
||||
|
||||
Usage:
|
||||
python basic_hooks.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.hooks import (
|
||||
HookRegistry, HookRunner, HookEvent, HookResult,
|
||||
add_hook, remove_hook, has_hook, get_default_registry,
|
||||
HookRunner, HookEvent, HookResult,
|
||||
BeforeToolInput, AfterToolInput
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# Create a hook registry
|
||||
registry = HookRegistry()
|
||||
# Clear any existing hooks from previous runs
|
||||
get_default_registry().clear()
|
||||
|
||||
# ==========================================================================
|
||||
# Hook 1: Log all tool calls
|
||||
# Hook 1: Log all tool calls (using string event name)
|
||||
# ==========================================================================
|
||||
@registry.on(HookEvent.BEFORE_TOOL)
|
||||
@add_hook('before_tool')
|
||||
def log_tool_calls(event_data: BeforeToolInput) -> HookResult:
|
||||
"""Log every tool call before execution."""
|
||||
print(f"\n📝 [LOG] Tool: {event_data.tool_name}")
|
||||
@@ -35,9 +37,9 @@ def main():
|
||||
return HookResult.allow()
|
||||
|
||||
# ==========================================================================
|
||||
# Hook 2: Block dangerous file operations (using function logic)
|
||||
# Hook 2: Block dangerous file operations
|
||||
# ==========================================================================
|
||||
@registry.on(HookEvent.BEFORE_TOOL)
|
||||
@add_hook('before_tool')
|
||||
def block_delete_operations(event_data: BeforeToolInput) -> HookResult:
|
||||
"""Block any delete or remove operations."""
|
||||
dangerous_keywords = ['delete', 'remove', 'rm', 'unlink']
|
||||
@@ -53,7 +55,7 @@ def main():
|
||||
# ==========================================================================
|
||||
# Hook 3: Require confirmation for write operations
|
||||
# ==========================================================================
|
||||
@registry.on(HookEvent.BEFORE_TOOL)
|
||||
@add_hook('before_tool')
|
||||
def confirm_write_operations(event_data: BeforeToolInput) -> HookResult:
|
||||
"""Request confirmation for write operations."""
|
||||
write_keywords = ['write', 'save', 'create', 'update']
|
||||
@@ -62,14 +64,13 @@ def main():
|
||||
if any(kw in tool_lower for kw in write_keywords):
|
||||
print(f"\n⚠️ [CONFIRM] Write operation: {event_data.tool_name}")
|
||||
# In a real scenario, you might prompt the user here
|
||||
# For this example, we'll auto-approve
|
||||
return HookResult.allow("Auto-approved for demo")
|
||||
return HookResult.allow()
|
||||
|
||||
# ==========================================================================
|
||||
# Hook 4: Add timing information after tool execution
|
||||
# ==========================================================================
|
||||
@registry.on(HookEvent.AFTER_TOOL)
|
||||
@add_hook('after_tool')
|
||||
def log_tool_completion(event_data: AfterToolInput) -> HookResult:
|
||||
"""Log tool completion with timing."""
|
||||
print(f"\n✅ [DONE] Tool: {event_data.tool_name}")
|
||||
@@ -82,10 +83,14 @@ def main():
|
||||
# Test the hooks
|
||||
# ==========================================================================
|
||||
print("=" * 60)
|
||||
print("Testing Hooks System")
|
||||
print("Testing Hooks System (using add_hook API)")
|
||||
print("=" * 60)
|
||||
|
||||
runner = HookRunner(registry)
|
||||
# Check hooks are registered
|
||||
print(f"\nHooks registered for 'before_tool': {has_hook('before_tool')}")
|
||||
print(f"Hooks registered for 'after_tool': {has_hook('after_tool')}")
|
||||
|
||||
runner = HookRunner(get_default_registry())
|
||||
|
||||
# Test 1: Normal tool call (should be logged and allowed)
|
||||
print("\n--- Test 1: Normal read operation ---")
|
||||
@@ -132,7 +137,7 @@ def main():
|
||||
print("\n" + "=" * 60)
|
||||
print("Registered Hooks:")
|
||||
print("=" * 60)
|
||||
for event, hooks in registry.list_hooks().items():
|
||||
for event, hooks in get_default_registry().list_hooks().items():
|
||||
print(f"\n{event}:")
|
||||
for hook in hooks:
|
||||
print(f" - {hook['name']} (matcher: {hook['matcher'] or '*'})")
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple Hooks Example for PraisonAI Agents.
|
||||
|
||||
This example shows the SIMPLEST way to use hooks with the add_hook API.
|
||||
Just a few lines of code to intercept tool calls!
|
||||
|
||||
No need to import HookResult - just return None/True to allow,
|
||||
False to deny, or a string to deny with a reason.
|
||||
|
||||
Usage:
|
||||
python simple_hooks.py
|
||||
"""
|
||||
|
||||
from praisonaiagents.hooks import add_hook, has_hook
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Register hooks with simple string event names
|
||||
# =============================================================================
|
||||
|
||||
@add_hook('before_tool')
|
||||
def log_tools(event_data):
|
||||
"""Log every tool call. Return nothing (None) to allow."""
|
||||
print(f"🔧 Tool: {event_data.tool_name}")
|
||||
# No return needed - defaults to allow
|
||||
|
||||
|
||||
@add_hook('before_tool')
|
||||
def block_dangerous(event_data):
|
||||
"""Block delete operations. Return False or string to deny."""
|
||||
if 'delete' in event_data.tool_name.lower():
|
||||
print(f"🚫 Blocked: {event_data.tool_name}")
|
||||
return "Delete operations are not allowed" # String = deny with reason
|
||||
# No return = allow
|
||||
|
||||
|
||||
@add_hook('after_tool')
|
||||
def log_completion(event_data):
|
||||
"""Log tool completion."""
|
||||
print(f"✅ Done: {event_data.tool_name} ({event_data.execution_time_ms:.0f}ms)")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Test the hooks
|
||||
# =============================================================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("Simple Hooks Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Check hooks are registered
|
||||
print(f"\nHooks registered for 'before_tool': {has_hook('before_tool')}")
|
||||
print(f"Hooks registered for 'after_tool': {has_hook('after_tool')}")
|
||||
|
||||
# You can now create an Agent and the hooks will be applied
|
||||
print("\n💡 Hooks are now globally registered!")
|
||||
print(" Any Agent will automatically use these hooks.")
|
||||
print("\n Example:")
|
||||
print(" from praisonaiagents import Agent")
|
||||
print(" agent = Agent(instructions='You are helpful')")
|
||||
print(" agent.start('Help me with files')")
|
||||
print("\n📝 Hook returns:")
|
||||
print(" - None or True → Allow the operation")
|
||||
print(" - False → Deny the operation")
|
||||
print(" - 'reason' → Deny with a custom message")
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* AudioAgent Integration Test
|
||||
*
|
||||
* Tests the AudioAgent for TTS and STT using AI SDK.
|
||||
* Requires OPENAI_API_KEY for live tests.
|
||||
*
|
||||
* Run: npx ts-node audio-agent.ts
|
||||
*/
|
||||
|
||||
import { AudioAgent, createAudioAgent } from '../../../src/praisonai-ts/dist';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
async function main() {
|
||||
console.log('=== AudioAgent Integration Test ===\n');
|
||||
|
||||
// Test 1: Create AudioAgent with defaults
|
||||
console.log('1. Testing AudioAgent creation:');
|
||||
const agent = new AudioAgent({
|
||||
name: 'TestAudioAgent',
|
||||
provider: 'openai',
|
||||
voice: 'alloy',
|
||||
verbose: true
|
||||
});
|
||||
console.log(' Agent ID:', agent.id);
|
||||
console.log(' Agent Name:', agent.name);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 2: Factory function
|
||||
console.log('\n2. Testing createAudioAgent() factory:');
|
||||
const agent2 = createAudioAgent({ provider: 'openai', voice: 'nova' });
|
||||
console.log(' Agent created:', typeof agent2);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 3: Live TTS (requires API key)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
console.log('\n3. Testing Text-to-Speech (live API):');
|
||||
try {
|
||||
const ttsAgent = new AudioAgent({
|
||||
provider: 'openai',
|
||||
voice: 'alloy',
|
||||
verbose: false
|
||||
});
|
||||
|
||||
const result = await ttsAgent.speak('Hello from PraisonAI AudioAgent!');
|
||||
console.log(' Audio format:', result.format);
|
||||
console.log(' Audio size:', result.audio.byteLength || result.audio.length, 'bytes');
|
||||
|
||||
// Optionally save to file
|
||||
const outputPath = path.join(__dirname, 'test-output.mp3');
|
||||
if (result.audio instanceof Buffer) {
|
||||
fs.writeFileSync(outputPath, result.audio);
|
||||
console.log(' Saved to:', outputPath);
|
||||
}
|
||||
console.log(' Success: ✅');
|
||||
} catch (error: any) {
|
||||
console.log(' ⚠️ TTS failed:', error.message);
|
||||
}
|
||||
|
||||
// Test 4: Live Transcription (requires audio file)
|
||||
console.log('\n4. Testing Speech-to-Text (live API):');
|
||||
try {
|
||||
const sttAgent = new AudioAgent({
|
||||
provider: 'openai',
|
||||
verbose: false
|
||||
});
|
||||
|
||||
// Check if we have the test output from TTS
|
||||
const audioPath = path.join(__dirname, 'test-output.mp3');
|
||||
if (fs.existsSync(audioPath)) {
|
||||
const result = await sttAgent.transcribe(audioPath);
|
||||
console.log(' Transcribed text:', result.text);
|
||||
console.log(' Language:', result.language || 'auto-detected');
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Cleanup
|
||||
fs.unlinkSync(audioPath);
|
||||
console.log(' Cleaned up test file');
|
||||
} else {
|
||||
console.log(' Skipped (no test audio file available)');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(' ⚠️ STT failed:', error.message);
|
||||
}
|
||||
|
||||
// Test 5: Chat method (auto-detect mode)
|
||||
console.log('\n5. Testing chat() method:');
|
||||
try {
|
||||
const chatAgent = new AudioAgent({ provider: 'openai', voice: 'echo' });
|
||||
|
||||
// Text input -> TTS
|
||||
const chatResult = await chatAgent.chat('Testing audio chat interface');
|
||||
console.log(' Chat result:', chatResult);
|
||||
console.log(' Success: ✅');
|
||||
} catch (error: any) {
|
||||
console.log(' ⚠️ Chat failed:', error.message);
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('\n3-5. Live API Tests: Skipped (OPENAI_API_KEY not set)');
|
||||
console.log(' Set OPENAI_API_KEY to run live tests');
|
||||
}
|
||||
|
||||
// Test 6: Different providers (just creation, no live calls)
|
||||
console.log('\n6. Testing provider configurations:');
|
||||
const providers = ['openai', 'elevenlabs', 'groq', 'deepgram'] as const;
|
||||
|
||||
for (const provider of providers) {
|
||||
try {
|
||||
const a = new AudioAgent({ provider });
|
||||
console.log(` ${provider}: ✅ (created)`);
|
||||
} catch (error: any) {
|
||||
console.log(` ${provider}: ⚠️ ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 7: Voice options
|
||||
console.log('\n7. Testing OpenAI voice options:');
|
||||
const voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'];
|
||||
for (const voice of voices) {
|
||||
const a = new AudioAgent({ provider: 'openai', voice });
|
||||
console.log(` ${voice}: ✅`);
|
||||
}
|
||||
|
||||
console.log('\n=== AudioAgent Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* ChonkieAdapter Integration Test
|
||||
*
|
||||
* Tests the ChonkieJS adapter for text chunking.
|
||||
* Works without ChonkieJS installed (uses native fallback).
|
||||
*
|
||||
* Run: npx ts-node chonkie-adapter.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
ChonkieAdapter,
|
||||
createChonkieAdapter,
|
||||
hasChonkie,
|
||||
Chunking
|
||||
} from '../../../src/praisonai-ts/dist';
|
||||
|
||||
async function main() {
|
||||
console.log('=== ChonkieAdapter Integration Test ===\n');
|
||||
|
||||
// Test 1: Check ChonkieJS availability
|
||||
console.log('1. Checking ChonkieJS availability:');
|
||||
const chonkieAvailable = await hasChonkie();
|
||||
console.log(` ChonkieJS installed: ${chonkieAvailable ? '✅' : '❌ (using native fallback)'}`);
|
||||
|
||||
// Test 2: Create adapter with default config
|
||||
console.log('\n2. Testing ChonkieAdapter creation:');
|
||||
const adapter = new ChonkieAdapter({
|
||||
strategy: 'token',
|
||||
chunkSize: 100,
|
||||
overlap: 20
|
||||
});
|
||||
console.log(' Adapter created: ✅');
|
||||
console.log(' Info:', adapter.getInfo());
|
||||
|
||||
// Test 3: Chunk text
|
||||
console.log('\n3. Testing text chunking:');
|
||||
const sampleText = `
|
||||
PraisonAI is a high-performance, agentic AI framework designed with minimal client-side API.
|
||||
It features extreme performance sensitivity, lazy loading everywhere, and optional dependencies only.
|
||||
The framework supports clean separation between Core SDK, Wrapper/integrations, and Tools/plugins.
|
||||
All features must have a relevant CLI integration and representation.
|
||||
`.trim();
|
||||
|
||||
const chunks = await adapter.chunk(sampleText);
|
||||
console.log(` Input length: ${sampleText.length} characters`);
|
||||
console.log(` Chunks created: ${chunks.length}`);
|
||||
chunks.forEach((chunk, i) => {
|
||||
console.log(` [${i}]: "${chunk.content.slice(0, 50)}..." (${chunk.content.length} chars)`);
|
||||
});
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 4: Different strategies
|
||||
console.log('\n4. Testing different chunking strategies:');
|
||||
const strategies = ['size', 'sentence', 'paragraph', 'semantic'] as const;
|
||||
|
||||
for (const strategy of strategies) {
|
||||
const strategyAdapter = createChonkieAdapter({ strategy, chunkSize: 80 });
|
||||
const strategyChunks = await strategyAdapter.chunk(sampleText);
|
||||
console.log(` ${strategy}: ${strategyChunks.length} chunks`);
|
||||
}
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 5: Native Chunking class
|
||||
console.log('\n5. Testing native Chunking class:');
|
||||
const nativeChunker = new Chunking({ chunkSize: 100, overlap: 10, strategy: 'sentence' });
|
||||
const nativeChunks = nativeChunker.chunk(sampleText);
|
||||
console.log(` Native chunks: ${nativeChunks.length}`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 6: Factory function
|
||||
console.log('\n6. Testing createChunking() factory:');
|
||||
const factoryChunker = createChunking({ chunkSize: 200 });
|
||||
const factoryChunks = factoryChunker.chunk(sampleText);
|
||||
console.log(` Factory chunks: ${factoryChunks.length}`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 7: Merge small chunks
|
||||
console.log('\n7. Testing mergeSmallChunks:');
|
||||
const tinyChunker = new Chunking({ chunkSize: 20, overlap: 5 });
|
||||
const tinyChunks = tinyChunker.chunk(sampleText);
|
||||
const mergedChunks = tinyChunker.mergeSmallChunks(tinyChunks, 50);
|
||||
console.log(` Before merge: ${tinyChunks.length} chunks`);
|
||||
console.log(` After merge: ${mergedChunks.length} chunks`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 8: Code chunking (if ChonkieJS available)
|
||||
if (chonkieAvailable) {
|
||||
console.log('\n8. Testing code chunking (ChonkieJS):');
|
||||
const codeAdapter = createChonkieAdapter({ strategy: 'code', language: 'typescript' });
|
||||
const codeText = `
|
||||
function hello() {
|
||||
console.log('Hello');
|
||||
}
|
||||
|
||||
class MyClass {
|
||||
constructor() {
|
||||
this.value = 42;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
`.trim();
|
||||
|
||||
const codeChunks = await codeAdapter.chunk(codeText);
|
||||
console.log(` Code chunks: ${codeChunks.length}`);
|
||||
console.log(' Success: ✅');
|
||||
} else {
|
||||
console.log('\n8. Code chunking: Skipped (ChonkieJS not installed)');
|
||||
}
|
||||
|
||||
// Test 9: Verify adapter info
|
||||
console.log('\n9. Verifying adapter state:');
|
||||
const info = adapter.getInfo();
|
||||
console.log(` Strategy: ${info.strategy}`);
|
||||
console.log(` Using ChonkieJS: ${info.usingChonkie}`);
|
||||
console.log(` Chunk size: ${info.config.chunkSize}`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
console.log('\n=== ChonkieAdapter Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* QueryEngine Integration Test
|
||||
*
|
||||
* Tests semantic and keyword search for RAG applications.
|
||||
*
|
||||
* Run: npx ts-node query-engine.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
QueryEngine,
|
||||
createQueryEngine,
|
||||
createSimpleQueryEngine
|
||||
} from '../../../src/praisonai-ts/dist';
|
||||
|
||||
async function main() {
|
||||
console.log('=== QueryEngine Integration Test ===\n');
|
||||
|
||||
// Test 1: Simple in-memory engine
|
||||
console.log('1. Testing simple in-memory QueryEngine:');
|
||||
const docs = [
|
||||
{ id: 'doc1', content: 'PraisonAI is a high-performance AI agent framework designed for production use.' },
|
||||
{ id: 'doc2', content: 'TypeScript provides static typing and better tooling for JavaScript projects.' },
|
||||
{ id: 'doc3', content: 'Agents can use tools, memory, and knowledge to complete complex tasks.' },
|
||||
{ id: 'doc4', content: 'RAG (Retrieval-Augmented Generation) improves AI responses with relevant context.' },
|
||||
{ id: 'doc5', content: 'Workflows enable sequential and parallel execution of agent tasks.' }
|
||||
];
|
||||
|
||||
const engine = createSimpleQueryEngine(docs);
|
||||
|
||||
const results1 = await engine.query('agent framework', { topK: 2 });
|
||||
console.log(' Query: "agent framework"');
|
||||
console.log(` Found: ${results1.length} results`);
|
||||
results1.forEach((r, i) => {
|
||||
console.log(` [${i + 1}] ${r.content.slice(0, 50)}... (score: ${r.score.toFixed(2)})`);
|
||||
});
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 2: Different queries
|
||||
console.log('\n2. Testing various queries:');
|
||||
const queries = ['TypeScript', 'RAG', 'workflow', 'tools memory'];
|
||||
|
||||
for (const q of queries) {
|
||||
const results = await engine.query(q, { topK: 1 });
|
||||
console.log(` "${q}" → ${results.length > 0 ? results[0].content.slice(0, 40) + '...' : 'No results'}`);
|
||||
}
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 3: Query for context string
|
||||
console.log('\n3. Testing queryForContext:');
|
||||
const context = await engine.queryForContext('agent tasks', { topK: 2 });
|
||||
console.log(' Context string (preview):');
|
||||
console.log(' ' + context.split('\n')[0]);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 4: Cache behavior
|
||||
console.log('\n4. Testing caching:');
|
||||
const start = Date.now();
|
||||
await engine.query('agent framework', { topK: 2 });
|
||||
const cachedTime = Date.now() - start;
|
||||
console.log(` Cached query time: ${cachedTime}ms (should be fast)`);
|
||||
|
||||
engine.clearCache();
|
||||
const start2 = Date.now();
|
||||
await engine.query('agent framework', { topK: 2 });
|
||||
const uncachedTime = Date.now() - start2;
|
||||
console.log(` Uncached query time: ${uncachedTime}ms`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 5: QueryEngine configuration
|
||||
console.log('\n5. Testing custom QueryEngine configuration:');
|
||||
const customEngine = new QueryEngine({
|
||||
keywordSearch: async (query, options) => {
|
||||
return docs
|
||||
.filter(d => d.content.toLowerCase().includes(query.toLowerCase()))
|
||||
.map(d => ({ id: d.id, content: d.content, score: 0.8 }))
|
||||
.slice(0, options?.topK ?? 5);
|
||||
},
|
||||
defaultOptions: { mode: 'keyword', topK: 3 }
|
||||
});
|
||||
|
||||
const customResults = await customEngine.query('TypeScript');
|
||||
console.log(` Custom engine results: ${customResults.length}`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 6: Empty results handling
|
||||
console.log('\n6. Testing empty results:');
|
||||
const emptyResults = await engine.query('nonexistent_query_12345');
|
||||
console.log(` Results for nonsense query: ${emptyResults.length}`);
|
||||
|
||||
const emptyContext = await engine.queryForContext('nonexistent_query_12345');
|
||||
console.log(` Context: "${emptyContext}"`);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
console.log('\n=== QueryEngine Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* MCP Client Integration Test
|
||||
*
|
||||
* Tests the MCP (Model Context Protocol) client.
|
||||
* Note: Requires an MCP server for full integration tests.
|
||||
*
|
||||
* Run: npx ts-node mcp-client.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
MCPClient,
|
||||
createMCPClient,
|
||||
getMCPTools,
|
||||
type MCPClientConfig
|
||||
} from '../../../src/praisonai-ts/dist/mcp';
|
||||
|
||||
async function main() {
|
||||
console.log('=== MCPClient Integration Test ===\n');
|
||||
|
||||
// Test 1: Create MCP client
|
||||
console.log('1. Testing MCPClient creation:');
|
||||
const client = new MCPClient({
|
||||
serverUrl: 'http://localhost:3000',
|
||||
transport: 'http',
|
||||
timeout: 5000,
|
||||
verbose: true
|
||||
});
|
||||
console.log(' Client ID:', client.id);
|
||||
console.log(' Connected:', client.isConnected());
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 2: Factory function
|
||||
console.log('\n2. Testing createMCPClient() factory:');
|
||||
const client2 = createMCPClient({ serverUrl: 'http://localhost:8080' });
|
||||
console.log(' Client created:', typeof client2);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 3: Transport configurations
|
||||
console.log('\n3. Testing transport configurations:');
|
||||
const transports: MCPClientConfig['transport'][] = ['http', 'websocket', 'sse', 'stdio'];
|
||||
|
||||
for (const transport of transports) {
|
||||
try {
|
||||
const c = new MCPClient({
|
||||
transport,
|
||||
serverUrl: transport !== 'stdio' ? 'http://localhost:3000' : undefined,
|
||||
command: transport === 'stdio' ? 'npx' : undefined,
|
||||
args: transport === 'stdio' ? ['mcp-server'] : undefined
|
||||
});
|
||||
console.log(` ${transport}: ✅ (configured)`);
|
||||
} catch (error: any) {
|
||||
console.log(` ${transport}: ⚠️ ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: Session info
|
||||
console.log('\n4. Testing session management:');
|
||||
const session = client.getSession();
|
||||
console.log(' Session ID:', session.id);
|
||||
console.log(' Connected:', session.connected);
|
||||
console.log(' Tools:', session.tools.length);
|
||||
console.log(' Resources:', session.resources.length);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 5: Live connection test (optional - requires running server)
|
||||
const testServerUrl = process.env.MCP_SERVER_URL;
|
||||
if (testServerUrl) {
|
||||
console.log('\n5. Testing live MCP connection:');
|
||||
try {
|
||||
const liveClient = new MCPClient({
|
||||
serverUrl: testServerUrl,
|
||||
transport: 'http',
|
||||
timeout: 10000,
|
||||
verbose: false
|
||||
});
|
||||
|
||||
await liveClient.connect();
|
||||
console.log(' Connected: ✅');
|
||||
|
||||
const tools = await liveClient.listTools();
|
||||
console.log(` Tools discovered: ${tools.length}`);
|
||||
|
||||
for (const tool of tools.slice(0, 3)) {
|
||||
console.log(` - ${tool.name}: ${tool.description || 'No description'}`);
|
||||
}
|
||||
|
||||
// Get tools in AI SDK format
|
||||
const aiTools = await liveClient.getToolsAsAISDK();
|
||||
console.log(` AI SDK tools: ${aiTools.length}`);
|
||||
|
||||
await liveClient.disconnect();
|
||||
console.log(' Disconnected: ✅');
|
||||
} catch (error: any) {
|
||||
console.log(` ⚠️ Connection failed: ${error.message}`);
|
||||
}
|
||||
} else {
|
||||
console.log('\n5. Live connection: Skipped (MCP_SERVER_URL not set)');
|
||||
console.log(' Set MCP_SERVER_URL to test live connections');
|
||||
}
|
||||
|
||||
// Test 6: Mock tool call simulation
|
||||
console.log('\n6. Testing tool call preparation:');
|
||||
const mockTool = {
|
||||
name: 'search',
|
||||
description: 'Search for information',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string', description: 'Search query' }
|
||||
},
|
||||
required: ['query']
|
||||
}
|
||||
};
|
||||
console.log(' Mock tool:', mockTool.name);
|
||||
console.log(' Schema:', JSON.stringify(mockTool.inputSchema));
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 7: API Key configuration
|
||||
console.log('\n7. Testing API key configuration:');
|
||||
const authClient = new MCPClient({
|
||||
serverUrl: 'http://api.example.com',
|
||||
apiKey: 'test-api-key-12345'
|
||||
});
|
||||
console.log(' Client with API key: ✅');
|
||||
|
||||
// Test 8: getMCPTools helper
|
||||
console.log('\n8. Testing getMCPTools helper signature:');
|
||||
console.log(' getMCPTools function:', typeof getMCPTools);
|
||||
console.log(' Returns: { client, tools }');
|
||||
console.log(' Success: ✅');
|
||||
|
||||
console.log('\n=== MCPClient Tests Complete ===');
|
||||
console.log('\nNote: For full integration tests, run an MCP server and set MCP_SERVER_URL');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* MemoryHooks Integration Test
|
||||
*
|
||||
* Tests pre/post hooks for memory operations.
|
||||
*
|
||||
* Run: npx ts-node memory-hooks.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
MemoryHooks,
|
||||
createMemoryHooks,
|
||||
createLoggingHooks,
|
||||
createValidationHooks,
|
||||
Memory,
|
||||
createMemory
|
||||
} from '../../../src/praisonai-ts/dist';
|
||||
|
||||
async function main() {
|
||||
console.log('=== MemoryHooks Integration Test ===\n');
|
||||
|
||||
// Test 1: Basic hooks
|
||||
console.log('1. Testing basic MemoryHooks:');
|
||||
const logs: string[] = [];
|
||||
|
||||
const hooks = new MemoryHooks({
|
||||
beforeStore: async (key, value) => {
|
||||
logs.push(`beforeStore: ${key}`);
|
||||
return { key, value };
|
||||
},
|
||||
afterStore: async (key) => {
|
||||
logs.push(`afterStore: ${key}`);
|
||||
},
|
||||
beforeRetrieve: async (key) => {
|
||||
logs.push(`beforeRetrieve: ${key}`);
|
||||
return key;
|
||||
},
|
||||
afterRetrieve: async (key, value) => {
|
||||
logs.push(`afterRetrieve: ${key}`);
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
await hooks.beforeStore('key1', { data: 'value1' });
|
||||
await hooks.afterStore('key1', { data: 'value1' });
|
||||
await hooks.beforeRetrieve('key1');
|
||||
await hooks.afterRetrieve('key1', { data: 'value1' });
|
||||
|
||||
console.log(' Triggered hooks:', logs);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 2: Logging hooks
|
||||
console.log('\n2. Testing createLoggingHooks:');
|
||||
const logMessages: string[] = [];
|
||||
const loggingHooks = createLoggingHooks((msg) => logMessages.push(msg));
|
||||
|
||||
await loggingHooks.beforeStore('test', 'data');
|
||||
await loggingHooks.afterStore('test', 'data');
|
||||
await loggingHooks.beforeRetrieve('test');
|
||||
await loggingHooks.afterRetrieve('test', 'data');
|
||||
|
||||
console.log(' Log messages:', logMessages.length);
|
||||
logMessages.forEach(m => console.log(' -', m));
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 3: Validation hooks
|
||||
console.log('\n3. Testing createValidationHooks:');
|
||||
const validationHooks = createValidationHooks((key, value) => {
|
||||
// Only allow non-empty strings
|
||||
if (typeof value !== 'string' || value.length === 0) {
|
||||
return { valid: false, reason: 'Value must be non-empty string' };
|
||||
}
|
||||
return { valid: true };
|
||||
});
|
||||
|
||||
const validResult = await validationHooks.beforeStore('good', 'valid string');
|
||||
console.log(' Valid data allowed:', validResult !== null);
|
||||
|
||||
const invalidResult = await validationHooks.beforeStore('bad', '');
|
||||
console.log(' Invalid data blocked:', invalidResult === null);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 4: Delete hooks
|
||||
console.log('\n4. Testing delete hooks:');
|
||||
const deleteHooks = new MemoryHooks({
|
||||
beforeDelete: async (key) => {
|
||||
console.log(` [Hook] About to delete: ${key}`);
|
||||
return true; // Allow deletion
|
||||
},
|
||||
afterDelete: async (key, success) => {
|
||||
console.log(` [Hook] Delete ${key}: ${success ? 'succeeded' : 'failed'}`);
|
||||
}
|
||||
});
|
||||
|
||||
const canDelete = await deleteHooks.beforeDelete('item-to-delete');
|
||||
console.log(' Deletion allowed:', canDelete);
|
||||
await deleteHooks.afterDelete('item-to-delete', true);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 5: Search hooks
|
||||
console.log('\n5. Testing search hooks:');
|
||||
const searchHooks = new MemoryHooks({
|
||||
beforeSearch: async (query, options) => {
|
||||
console.log(` [Hook] Searching: "${query}"`);
|
||||
// Could modify query here
|
||||
return { query: query.toLowerCase(), options };
|
||||
},
|
||||
afterSearch: async (query, results) => {
|
||||
console.log(` [Hook] Found ${results.length} results for "${query}"`);
|
||||
return results;
|
||||
}
|
||||
});
|
||||
|
||||
const searchParams = await searchHooks.beforeSearch('UPPERCASE QUERY');
|
||||
console.log(' Modified query:', searchParams?.query);
|
||||
|
||||
await searchHooks.afterSearch('query', [{ id: '1' }, { id: '2' }]);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 6: Dynamic hook management
|
||||
console.log('\n6. Testing dynamic hook management:');
|
||||
const dynamicHooks = new MemoryHooks({});
|
||||
|
||||
console.log(' Initial config:', Object.keys(dynamicHooks.getConfig()).length === 0 ? 'empty' : 'has hooks');
|
||||
|
||||
dynamicHooks.addHook('logging', true);
|
||||
dynamicHooks.setLogging(true);
|
||||
|
||||
console.log(' After adding logging:', dynamicHooks.getConfig().logging);
|
||||
|
||||
dynamicHooks.removeHook('logging');
|
||||
console.log(' After removing:', dynamicHooks.getConfig().logging ?? 'undefined');
|
||||
console.log(' Success: ✅');
|
||||
|
||||
console.log('\n=== MemoryHooks Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Feature Parity Integration Test Runner
|
||||
*
|
||||
* Runs all integration tests for the new TypeScript features.
|
||||
* Tests include real API calls when OPENAI_API_KEY is available.
|
||||
*
|
||||
* Run: npx ts-node run-feature-tests.ts
|
||||
*/
|
||||
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
interface TestResult {
|
||||
name: string;
|
||||
passed: boolean;
|
||||
duration: number;
|
||||
output: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const TEST_FILES = [
|
||||
// Workflow patterns
|
||||
'workflows/loop-pattern.ts',
|
||||
'workflows/repeat-pattern.ts',
|
||||
// Agents
|
||||
'agents/audio-agent.ts',
|
||||
// Knowledge
|
||||
'knowledge/chonkie-adapter.ts',
|
||||
// MCP
|
||||
'mcp/mcp-client-test.ts',
|
||||
];
|
||||
|
||||
async function runTest(testFile: string): Promise<TestResult> {
|
||||
const startTime = Date.now();
|
||||
const fullPath = path.join(__dirname, testFile);
|
||||
const name = path.basename(testFile, '.ts');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const proc = spawn('npx', ['ts-node', fullPath], {
|
||||
cwd: __dirname,
|
||||
env: { ...process.env },
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
proc.stdout.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
proc.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
const duration = Date.now() - startTime;
|
||||
resolve({
|
||||
name,
|
||||
passed: code === 0,
|
||||
duration,
|
||||
output: stdout,
|
||||
error: stderr || undefined
|
||||
});
|
||||
});
|
||||
|
||||
proc.on('error', (error) => {
|
||||
const duration = Date.now() - startTime;
|
||||
resolve({
|
||||
name,
|
||||
passed: false,
|
||||
duration,
|
||||
output: '',
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
|
||||
// Timeout after 60 seconds
|
||||
setTimeout(() => {
|
||||
proc.kill();
|
||||
resolve({
|
||||
name,
|
||||
passed: false,
|
||||
duration: 60000,
|
||||
output: stdout,
|
||||
error: 'Test timed out after 60 seconds'
|
||||
});
|
||||
}, 60000);
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ PraisonAI TypeScript Feature Parity Integration Tests ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
// Environment info
|
||||
console.log('Environment:');
|
||||
console.log(` OPENAI_API_KEY: ${process.env.OPENAI_API_KEY ? '✅ Set' : '❌ Not set'}`);
|
||||
console.log(` MCP_SERVER_URL: ${process.env.MCP_SERVER_URL ? '✅ Set' : '❌ Not set'}`);
|
||||
console.log(` Node version: ${process.version}`);
|
||||
console.log('');
|
||||
|
||||
// Check if test files exist
|
||||
console.log('Test Files:');
|
||||
for (const file of TEST_FILES) {
|
||||
const fullPath = path.join(__dirname, file);
|
||||
const exists = fs.existsSync(fullPath);
|
||||
console.log(` ${exists ? '✅' : '❌'} ${file}`);
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Run tests
|
||||
console.log('Running Tests...\n');
|
||||
console.log('─'.repeat(60));
|
||||
|
||||
const results: TestResult[] = [];
|
||||
for (const testFile of TEST_FILES) {
|
||||
const fullPath = path.join(__dirname, testFile);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
results.push({
|
||||
name: path.basename(testFile, '.ts'),
|
||||
passed: false,
|
||||
duration: 0,
|
||||
output: '',
|
||||
error: 'File not found'
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`\n▶ Running: ${testFile}`);
|
||||
const result = await runTest(testFile);
|
||||
results.push(result);
|
||||
|
||||
if (result.passed) {
|
||||
console.log(` ✅ PASSED (${result.duration}ms)`);
|
||||
} else {
|
||||
console.log(` ❌ FAILED (${result.duration}ms)`);
|
||||
if (result.error) {
|
||||
console.log(` Error: ${result.error.slice(0, 200)}...`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '─'.repeat(60));
|
||||
|
||||
// Summary
|
||||
const passed = results.filter(r => r.passed).length;
|
||||
const failed = results.filter(r => !r.passed).length;
|
||||
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
||||
|
||||
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ TEST SUMMARY ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝');
|
||||
console.log(` Total Tests: ${results.length}`);
|
||||
console.log(` Passed: ${passed} ✅`);
|
||||
console.log(` Failed: ${failed} ${failed > 0 ? '❌' : ''}`);
|
||||
console.log(` Duration: ${(totalDuration / 1000).toFixed(2)}s`);
|
||||
console.log('');
|
||||
|
||||
// Detailed results
|
||||
if (failed > 0) {
|
||||
console.log('Failed Tests:');
|
||||
for (const result of results.filter(r => !r.passed)) {
|
||||
console.log(` ❌ ${result.name}`);
|
||||
if (result.error) {
|
||||
console.log(` ${result.error.slice(0, 100)}`);
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Exit with appropriate code
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Test runner failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* SubagentTool Integration Test
|
||||
*
|
||||
* Tests the agent-as-tool pattern with real agents.
|
||||
*
|
||||
* Run: npx ts-node subagent-tool.ts
|
||||
* Requires: OPENAI_API_KEY
|
||||
*/
|
||||
|
||||
import {
|
||||
Agent,
|
||||
SubagentTool,
|
||||
createSubagentTool,
|
||||
createSubagentTools,
|
||||
createDelegator
|
||||
} from '../../../src/praisonai-ts/dist';
|
||||
|
||||
async function main() {
|
||||
console.log('=== SubagentTool Integration Test ===\n');
|
||||
|
||||
// Test 1: Create SubagentTool
|
||||
console.log('1. Testing SubagentTool creation:');
|
||||
const mockAgent = {
|
||||
name: 'MockAgent',
|
||||
chat: async (input: string) => `Echo: ${input}`
|
||||
};
|
||||
|
||||
const tool = new SubagentTool(mockAgent, {
|
||||
name: 'echo',
|
||||
description: 'Echo the input'
|
||||
});
|
||||
|
||||
console.log(' Tool name:', tool.name);
|
||||
console.log(' Tool ID:', tool.id);
|
||||
console.log(' Schema:', JSON.stringify(tool.getSchema()));
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 2: Execute tool with mock agent
|
||||
console.log('\n2. Testing tool execution:');
|
||||
const mockResult = await tool.execute({ input: 'Hello World' });
|
||||
console.log(' Input: "Hello World"');
|
||||
console.log(' Output:', mockResult);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 3: Factory function
|
||||
console.log('\n3. Testing createSubagentTool factory:');
|
||||
const factoryTool = createSubagentTool(mockAgent, { name: 'factory_echo' });
|
||||
console.log(' Tool name:', factoryTool.name);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 4: Multiple subagent tools
|
||||
console.log('\n4. Testing createSubagentTools (batch):');
|
||||
const agents = [
|
||||
{ name: 'Agent1', chat: async (i: string) => `A1: ${i}` },
|
||||
{ name: 'Agent2', chat: async (i: string) => `A2: ${i}` }
|
||||
];
|
||||
const tools = createSubagentTools(agents);
|
||||
console.log(' Created tools:', tools.length);
|
||||
tools.forEach(t => console.log(' -', t.name));
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 5: AI SDK format conversion
|
||||
console.log('\n5. Testing AI SDK format conversion:');
|
||||
const aiTool = tool.toAISDKTool();
|
||||
console.log(' Type:', aiTool.type);
|
||||
console.log(' Function name:', aiTool.function.name);
|
||||
console.log(' Has execute:', typeof aiTool.execute === 'function');
|
||||
console.log(' Success: ✅');
|
||||
|
||||
// Test 6: Live test with real agent
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
console.log('\n6. Testing with real Agent (live API):');
|
||||
try {
|
||||
const researcher = new Agent({
|
||||
name: 'QuickResearcher',
|
||||
instructions: 'Answer in exactly 5 words.',
|
||||
llm: 'openai/gpt-4o-mini'
|
||||
});
|
||||
|
||||
const researchTool = createSubagentTool(researcher, {
|
||||
name: 'quick_research',
|
||||
description: 'Get a quick 5-word answer'
|
||||
});
|
||||
|
||||
const result = await researchTool.execute({ input: 'What is AI?' });
|
||||
console.log(' Question: "What is AI?"');
|
||||
console.log(' Answer:', result);
|
||||
console.log(' Success: ✅');
|
||||
} catch (error: any) {
|
||||
console.log(' Error:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.log('\n6. Live test: Skipped (OPENAI_API_KEY not set)');
|
||||
}
|
||||
|
||||
// Test 7: Input/Output transforms
|
||||
console.log('\n7. Testing transforms:');
|
||||
const transformTool = createSubagentTool(mockAgent, {
|
||||
name: 'transform_echo',
|
||||
inputTransform: (input) => input.toUpperCase(),
|
||||
outputTransform: (output) => `[TRANSFORMED] ${output}`
|
||||
});
|
||||
|
||||
const transformResult = await transformTool.execute({ input: 'hello' });
|
||||
console.log(' Input: "hello"');
|
||||
console.log(' Output:', transformResult);
|
||||
console.log(' Success: ✅');
|
||||
|
||||
console.log('\n=== SubagentTool Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Loop Pattern Integration Test
|
||||
*
|
||||
* Tests the Loop workflow pattern with real data.
|
||||
*
|
||||
* Run: npx ts-node loop-pattern.ts
|
||||
*/
|
||||
|
||||
import { Loop, loopPattern, Agent, Workflow } from '../../../src/praisonai-ts/dist';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Loop Pattern Integration Test ===\n');
|
||||
|
||||
// Test 1: Basic Loop with array
|
||||
console.log('1. Testing Loop with array iteration:');
|
||||
const items = ['apple', 'banana', 'cherry'];
|
||||
|
||||
const processor = (item: string) => `Processed: ${item.toUpperCase()}`;
|
||||
const arrayLoop = new Loop(processor, { over: 'items' });
|
||||
|
||||
const result1 = await arrayLoop.run({ items });
|
||||
console.log(' Results:', result1.results);
|
||||
console.log(' Iterations:', result1.iterations);
|
||||
console.log(' Success:', result1.success ? '✅' : '❌');
|
||||
|
||||
// Test 2: Loop with convenience function
|
||||
console.log('\n2. Testing loop() convenience function:');
|
||||
const doubler = (n: number) => n * 2;
|
||||
const numberLoop = loopPattern(doubler, { over: 'numbers' });
|
||||
|
||||
const result2 = await numberLoop.run({ numbers: [1, 2, 3, 4, 5] });
|
||||
console.log(' Input: [1, 2, 3, 4, 5]');
|
||||
console.log(' Output:', result2.results);
|
||||
console.log(' Success:', result2.success ? '✅' : '❌');
|
||||
|
||||
// Test 3: Loop with Agent (requires OPENAI_API_KEY)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
console.log('\n3. Testing Loop with Agent (live API):');
|
||||
try {
|
||||
const agent = new Agent({
|
||||
name: 'Summarizer',
|
||||
instructions: 'Summarize the given topic in one sentence.',
|
||||
llm: 'openai/gpt-4o-mini'
|
||||
});
|
||||
|
||||
const topics = ['AI', 'TypeScript'];
|
||||
const agentLoop = new Loop(agent, { over: 'topics', varName: 'topic' });
|
||||
|
||||
console.log(' Processing topics:', topics);
|
||||
const result3 = await agentLoop.run({ topics });
|
||||
|
||||
result3.results.forEach((res, i) => {
|
||||
console.log(` [${topics[i]}]: ${res.slice(0, 80)}...`);
|
||||
});
|
||||
console.log(' Success:', result3.success ? '✅' : '❌');
|
||||
} catch (error: any) {
|
||||
console.log(' ⚠️ Agent test failed:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.log('\n3. Agent Loop Test: Skipped (OPENAI_API_KEY not set)');
|
||||
}
|
||||
|
||||
// Test 4: Loop with error handling
|
||||
console.log('\n4. Testing Loop with continueOnError:');
|
||||
const errorProneProcessor = (item: string) => {
|
||||
if (item === 'fail') throw new Error('Intentional failure');
|
||||
return `OK: ${item}`;
|
||||
};
|
||||
|
||||
const errorLoop = new Loop(errorProneProcessor, {
|
||||
over: 'items',
|
||||
continueOnError: true
|
||||
});
|
||||
|
||||
const result4 = await errorLoop.run({ items: ['a', 'fail', 'b'] });
|
||||
console.log(' Results:', result4.results);
|
||||
console.log(' Errors:', result4.errors.length);
|
||||
console.log(' Success:', result4.success ? '✅' : '❌ (expected - has errors)');
|
||||
|
||||
// Test 5: Loop type exports
|
||||
console.log('\n5. Verifying exports:');
|
||||
console.log(' Loop class:', typeof Loop);
|
||||
console.log(' loop function:', typeof loopPattern);
|
||||
|
||||
console.log('\n=== Loop Pattern Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Repeat Pattern Integration Test
|
||||
*
|
||||
* Tests the Repeat workflow pattern for evaluator-optimizer loops.
|
||||
*
|
||||
* Run: npx ts-node repeat-pattern.ts
|
||||
*/
|
||||
|
||||
import { Repeat, repeatPattern, Agent } from '../../../src/praisonai-ts/dist';
|
||||
|
||||
async function main() {
|
||||
console.log('=== Repeat Pattern Integration Test ===\n');
|
||||
|
||||
// Test 1: Basic repeat until condition
|
||||
console.log('1. Testing Repeat with condition:');
|
||||
let counter = 0;
|
||||
const incrementer = () => {
|
||||
counter++;
|
||||
return `Iteration ${counter}`;
|
||||
};
|
||||
|
||||
const repeat1 = new Repeat(incrementer, {
|
||||
until: (ctx) => ctx.iteration >= 3,
|
||||
maxIterations: 10
|
||||
});
|
||||
|
||||
const result1 = await repeat1.run('start');
|
||||
console.log(' Results:', result1.iterations);
|
||||
console.log(' Final:', result1.result);
|
||||
console.log(' Condition met:', result1.conditionMet ? '✅' : '❌');
|
||||
console.log(' Max reached:', result1.maxReached ? '⚠️' : 'No');
|
||||
|
||||
// Test 2: Repeat with content condition
|
||||
console.log('\n2. Testing Repeat with content-based condition:');
|
||||
let quality = 0;
|
||||
const improver = (input: string, prev: string | null) => {
|
||||
quality += 25;
|
||||
return `Quality: ${quality}% - ${prev || input}`;
|
||||
};
|
||||
|
||||
const repeat2 = new Repeat(improver, {
|
||||
until: (ctx) => ctx.lastResult?.includes('100%') ?? false,
|
||||
maxIterations: 5
|
||||
});
|
||||
|
||||
quality = 0; // Reset
|
||||
const result2 = await repeat2.run('Initial draft');
|
||||
console.log(' Iterations:', result2.count);
|
||||
console.log(' Final:', result2.result);
|
||||
console.log(' Condition met:', result2.conditionMet ? '✅' : '❌');
|
||||
|
||||
// Test 3: Repeat reaching max iterations
|
||||
console.log('\n3. Testing Repeat hitting max iterations:');
|
||||
const neverDone = () => 'still working...';
|
||||
|
||||
const repeat3 = new Repeat(neverDone, {
|
||||
until: () => false, // Never satisfied
|
||||
maxIterations: 3
|
||||
});
|
||||
|
||||
const result3 = await repeat3.run('start');
|
||||
console.log(' Iterations:', result3.count);
|
||||
console.log(' Max reached:', result3.maxReached ? '✅ (expected)' : '❌');
|
||||
|
||||
// Test 4: Repeat with Agent (requires OPENAI_API_KEY)
|
||||
if (process.env.OPENAI_API_KEY) {
|
||||
console.log('\n4. Testing Repeat with Agent (live API):');
|
||||
try {
|
||||
const agent = new Agent({
|
||||
name: 'Optimizer',
|
||||
instructions: 'Improve the given text. Add more detail and clarity. Say "DONE" when satisfied.',
|
||||
llm: 'openai/gpt-4o-mini'
|
||||
});
|
||||
|
||||
const repeat4 = new Repeat(agent, {
|
||||
until: (ctx) => ctx.lastResult?.includes('DONE') ?? false,
|
||||
maxIterations: 3
|
||||
});
|
||||
|
||||
const result4 = await repeat4.run('Make this better: Hello world');
|
||||
console.log(' Iterations:', result4.count);
|
||||
console.log(' Final (truncated):', result4.result.slice(0, 100) + '...');
|
||||
console.log(' Condition met:', result4.conditionMet ? '✅' : 'Max reached');
|
||||
} catch (error: any) {
|
||||
console.log(' ⚠️ Agent test failed:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.log('\n4. Agent Repeat Test: Skipped (OPENAI_API_KEY not set)');
|
||||
}
|
||||
|
||||
// Test 5: Convenience function
|
||||
console.log('\n5. Testing repeatPattern() convenience function:');
|
||||
let seq = 0;
|
||||
const sequencer = () => `seq-${++seq}`;
|
||||
|
||||
const repeat5 = repeatPattern(sequencer, {
|
||||
until: (ctx) => ctx.iteration >= 2,
|
||||
maxIterations: 5
|
||||
});
|
||||
|
||||
seq = 0; // Reset
|
||||
const result5 = await repeat5.run('');
|
||||
console.log(' Results:', result5.iterations);
|
||||
console.log(' Success:', result5.conditionMet ? '✅' : '❌');
|
||||
|
||||
// Test 6: Verify exports
|
||||
console.log('\n6. Verifying exports:');
|
||||
console.log(' Repeat class:', typeof Repeat);
|
||||
console.log(' repeatPattern function:', typeof repeatPattern);
|
||||
|
||||
console.log('\n=== Repeat Pattern Tests Complete ===');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -14,7 +14,7 @@ Usage:
|
||||
|
||||
from praisonai_tools.observability import obs
|
||||
from praisonai_tools.observability.base import SpanKind
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
|
||||
# Initialize observability
|
||||
obs.init()
|
||||
|
||||
@@ -13,7 +13,7 @@ Features demonstrated:
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '/Users/praison/praisonai-package/src/praisonai-agents')
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
@@ -106,7 +106,7 @@ def example_multi_agent():
|
||||
agent=writer
|
||||
)
|
||||
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[researcher, writer],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
|
||||
@@ -5,7 +5,7 @@ This example demonstrates hierarchical agent patterns using PraisonAI
|
||||
for structured task delegation and coordinated workflows.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import internet_search
|
||||
|
||||
print("=== AutoAgents Hierarchical Generation Example ===\n")
|
||||
@@ -54,7 +54,7 @@ execution_task = Task(
|
||||
)
|
||||
|
||||
# Run hierarchical workflow
|
||||
agents_system = Agents(
|
||||
agents_system = AgentTeam(
|
||||
agents=[manager_agent, research_agent],
|
||||
tasks=[planning_task, execution_task],
|
||||
process="sequential", output="verbose"
|
||||
|
||||
@@ -5,7 +5,7 @@ This example demonstrates a comprehensive code agent that can write, analyze, de
|
||||
It includes self-reflection for improving code quality and multiple specialized tools.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import CodeInterpreter
|
||||
|
||||
# Initialize code execution tool
|
||||
@@ -84,7 +84,7 @@ def demonstrate_code_agent():
|
||||
)
|
||||
|
||||
# Create workflow
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[code_writer, code_reviewer, code_executor],
|
||||
tasks=[write_task, review_task, execute_task],
|
||||
process="sequential", output="verbose"
|
||||
|
||||
@@ -4,7 +4,7 @@ from praisonaiagents.tools import duckduckgo
|
||||
agent = Agent(instructions="You are a Image Analysis Agent", tools=[duckduckgo])
|
||||
agent.start("I want to go London next week, find me a good hotel and flight")
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
# Create Image Analysis Agent
|
||||
image_agent = Agent(
|
||||
@@ -36,7 +36,7 @@ task2 = Task(
|
||||
)
|
||||
|
||||
# Create Agents instance
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[image_agent],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
|
||||
@@ -4,7 +4,7 @@ from praisonaiagents.tools import duckduckgo
|
||||
agent = Agent(instructions="You are a Image Analysis Agent", tools=[duckduckgo])
|
||||
agent.start("I want to go London next week, find me a good hotel and flight")
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
# Create Image Analysis Agent
|
||||
image_agent = Agent(
|
||||
@@ -36,7 +36,7 @@ task2 = Task(
|
||||
)
|
||||
|
||||
# Create Agents instance
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[image_agent],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
|
||||
@@ -5,7 +5,7 @@ This example demonstrates a mathematical problem-solving agent that can handle v
|
||||
including calculations, equation solving, and step-by-step explanations.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from typing import Dict, Any
|
||||
import math
|
||||
import re
|
||||
@@ -123,7 +123,7 @@ def solve_math_problem_workflow():
|
||||
context=[solve_task]
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[problem_solver_agent, calculation_agent, math_tutor_agent],
|
||||
tasks=[understand_task, solve_task, verify_task],
|
||||
process="sequential", output="verbose"
|
||||
|
||||
@@ -6,7 +6,7 @@ agent-based selection for cost optimization and performance.
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.agent import RouterAgent
|
||||
from praisonaiagents.llm.model_router import ModelRouter, ModelProfile, TaskComplexity
|
||||
|
||||
@@ -48,7 +48,7 @@ def example_auto_routing():
|
||||
)
|
||||
|
||||
# Run the tasks
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[research_agent],
|
||||
tasks=[simple_task, moderate_task, complex_task],
|
||||
process="sequential", output="verbose"
|
||||
@@ -113,7 +113,7 @@ def example_cost_optimized_workflow():
|
||||
)
|
||||
|
||||
# Run workflow
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[analyzer, writer],
|
||||
tasks=[analysis_task, writing_task],
|
||||
process="sequential", output="verbose"
|
||||
@@ -135,7 +135,7 @@ def example_auto_agents_multi_provider():
|
||||
from praisonaiagents.agents import AutoAgents
|
||||
|
||||
# Create AutoAgents that will automatically assign appropriate models
|
||||
auto_agents = AutoAgents(
|
||||
auto_agents = AutoAgentTeam(
|
||||
instructions="Create a market research report on electric vehicles. Include data analysis, competitor analysis, and future projections.",
|
||||
max_agents=3,
|
||||
llm="gpt-4o-mini", # Default model for agent generation
|
||||
@@ -239,7 +239,7 @@ def example_custom_routing():
|
||||
]
|
||||
|
||||
# Run tasks
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[coder],
|
||||
tasks=tasks,
|
||||
process="sequential", output="verbose"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
# Create Video Analysis Agent
|
||||
video_agent = Agent(
|
||||
@@ -25,7 +25,7 @@ task1 = Task(
|
||||
)
|
||||
|
||||
# Create Agents instance
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[video_agent],
|
||||
tasks=[task1],
|
||||
process="sequential",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language
|
||||
|
||||
agent = Agent(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
from praisonaiagents import Agent, AgentTeam, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents = AgentTeam(agents=[research_agent, summarise_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
@@ -1,8 +1,8 @@
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
from praisonaiagents import Agent, AgentTeam, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents2 = Agents(agents=[research_agent])
|
||||
agents = AgentTeam(agents=[research_agent, summarise_agent])
|
||||
agents2 = AgentTeam(agents=[research_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
agents2.launch(path="/agents2", port=3030)
|
||||
@@ -32,7 +32,7 @@ import os
|
||||
from pathlib import Path
|
||||
|
||||
# Import our market research system
|
||||
from praisonaiagents import Agent, Task, Agents, Tools
|
||||
from praisonaiagents import Agent, Task, AgentTeam, Tools
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(
|
||||
@@ -260,7 +260,7 @@ async def generate_market_research_report(job_id: str, config: MarketResearchCon
|
||||
job_status[job_id]["message"] = "Setting up research workflow..."
|
||||
|
||||
# Create workflow
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=list(agents.values()),
|
||||
tasks=tasks,
|
||||
process="sequential", output="minimal"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
from duckduckgo_search import DDGS
|
||||
|
||||
def internet_search_tool(query: str):
|
||||
@@ -15,5 +15,5 @@ def internet_search_tool(query: str):
|
||||
agent = Agent(name="SearchAgent", instructions="You Search the internet for information", tools=[internet_search_tool])
|
||||
agent2 = Agent(name="SummariseAgent", instructions="You Summarise the information")
|
||||
|
||||
agents = Agents(name="MultiAgents", agents=[agent, agent2])
|
||||
agents = AgentTeam(name="MultiAgents", agents=[agent, agent2])
|
||||
agents.launch(port=8080, protocol="mcp")
|
||||
@@ -0,0 +1,10 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
# Text to Speech
|
||||
tts = AudioAgent(llm="openai/tts-1")
|
||||
tts.say("Hello!", output="hello.mp3")
|
||||
|
||||
# Speech to Text
|
||||
stt = AudioAgent(llm="openai/whisper-1")
|
||||
text = stt.listen("hello.mp3")
|
||||
print(text)
|
||||
@@ -0,0 +1,7 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="groq/whisper-large-v3") # 10x faster
|
||||
text = agent.transcribe("audio.mp3", language="en")
|
||||
print(text)
|
||||
|
||||
# Models: openai/whisper-1, groq/whisper-large-v3, deepgram/nova-2
|
||||
@@ -0,0 +1,8 @@
|
||||
# Speech-to-Text with Groq (fastest)
|
||||
# Requires: export GROQ_API_KEY=your-key
|
||||
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="groq/whisper-large-v3")
|
||||
text = agent.listen("audio.mp3") # Replace with your audio file
|
||||
print(text)
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="deepgram/nova-2")
|
||||
text = agent.listen("audio.mp3")
|
||||
print(text)
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="groq/whisper-large-v3")
|
||||
text = agent.listen("audio.mp3")
|
||||
print(text)
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="openai/whisper-1")
|
||||
text = agent.listen("audio.mp3")
|
||||
print(text)
|
||||
@@ -0,0 +1,7 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="openai/tts-1-hd")
|
||||
agent.speech("Hello!", voice="nova", speed=1.2, output="hello.mp3")
|
||||
|
||||
# Voices: alloy, echo, fable, onyx, nova, shimmer
|
||||
# Models: openai/tts-1, openai/tts-1-hd, openai/gpt-4o-mini-tts
|
||||
@@ -0,0 +1,8 @@
|
||||
# Text-to-Speech with OpenAI
|
||||
# Requires: export OPENAI_API_KEY=your-key
|
||||
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="openai/tts-1")
|
||||
agent.say("Hello world!", output="hello.mp3")
|
||||
print("Saved to hello.mp3")
|
||||
@@ -0,0 +1,4 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="elevenlabs/eleven_multilingual_v2")
|
||||
agent.say("Hello world!", output="hello.mp3")
|
||||
@@ -0,0 +1,4 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="gemini/gemini-2.5-flash-preview-tts")
|
||||
agent.say("Hello world!", output="hello.mp3", voice="leda")
|
||||
@@ -0,0 +1,4 @@
|
||||
from praisonaiagents import AudioAgent
|
||||
|
||||
agent = AudioAgent(llm="openai/tts-1")
|
||||
agent.say("Hello world!", output="hello.mp3")
|
||||
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Messaging Bot Example - Deploy Agents to Telegram, Discord, Slack
|
||||
|
||||
This example demonstrates how to configure bots for different
|
||||
messaging platforms.
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, BotConfig
|
||||
|
||||
# Telegram Bot Configuration
|
||||
telegram_config = BotConfig(
|
||||
token=os.environ.get("TELEGRAM_BOT_TOKEN", ""),
|
||||
command_prefix="/",
|
||||
mention_required=True,
|
||||
typing_indicator=True,
|
||||
max_message_length=4096,
|
||||
metadata={"platform": "telegram"}
|
||||
)
|
||||
|
||||
# Discord Bot Configuration
|
||||
discord_config = BotConfig(
|
||||
token=os.environ.get("DISCORD_BOT_TOKEN", ""),
|
||||
command_prefix="!",
|
||||
mention_required=True,
|
||||
typing_indicator=True,
|
||||
metadata={
|
||||
"platform": "discord",
|
||||
"guild_ids": [] # Add your guild IDs here
|
||||
}
|
||||
)
|
||||
|
||||
# Slack Bot Configuration
|
||||
slack_config = BotConfig(
|
||||
token=os.environ.get("SLACK_BOT_TOKEN", ""),
|
||||
command_prefix="/",
|
||||
mention_required=True,
|
||||
typing_indicator=True,
|
||||
metadata={
|
||||
"platform": "slack",
|
||||
"app_token": os.environ.get("SLACK_APP_TOKEN", "")
|
||||
}
|
||||
)
|
||||
|
||||
# Create the agent that will handle messages
|
||||
assistant = Agent(
|
||||
name="assistant",
|
||||
instructions="""You are a helpful assistant that responds to messages
|
||||
from users on messaging platforms. Be friendly and concise.""",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Bot Configurations:")
|
||||
print()
|
||||
|
||||
print("Telegram Config:")
|
||||
print(f" Token configured: {bool(telegram_config.token)}")
|
||||
print(f" Command prefix: {telegram_config.command_prefix}")
|
||||
print(f" Mention required: {telegram_config.mention_required}")
|
||||
print()
|
||||
|
||||
print("Discord Config:")
|
||||
print(f" Token configured: {bool(discord_config.token)}")
|
||||
print(f" Command prefix: {discord_config.command_prefix}")
|
||||
print()
|
||||
|
||||
print("Slack Config:")
|
||||
print(f" Token configured: {bool(slack_config.token)}")
|
||||
print(f" Command prefix: {slack_config.command_prefix}")
|
||||
print()
|
||||
|
||||
print("To start a bot, use the CLI:")
|
||||
print(" praisonai bot telegram --token $TELEGRAM_BOT_TOKEN")
|
||||
print(" praisonai bot discord --token $DISCORD_BOT_TOKEN")
|
||||
print(" praisonai bot slack --token $SLACK_BOT_TOKEN")
|
||||
@@ -16,7 +16,7 @@ Environment Variables:
|
||||
|
||||
import cv2
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
|
||||
def capture_and_analyze():
|
||||
@@ -58,7 +58,7 @@ def capture_and_analyze():
|
||||
)
|
||||
|
||||
# Run analysis
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[vision_agent],
|
||||
tasks=[task],
|
||||
process="sequential",
|
||||
|
||||
@@ -18,7 +18,7 @@ import cv2
|
||||
import time
|
||||
import os
|
||||
from datetime import datetime
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
|
||||
class ContinuousCameraMonitor:
|
||||
@@ -71,7 +71,7 @@ class ContinuousCameraMonitor:
|
||||
images=[image_path]
|
||||
)
|
||||
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[self.vision_agent],
|
||||
tasks=[task],
|
||||
process="sequential",
|
||||
|
||||
@@ -17,7 +17,7 @@ Environment Variables:
|
||||
import cv2
|
||||
import os
|
||||
from datetime import datetime
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
|
||||
def capture_camera_frame(camera_id=0):
|
||||
@@ -114,7 +114,7 @@ def analyze_with_multiple_agents():
|
||||
)
|
||||
|
||||
# Run multi-agent analysis
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[security_agent, object_detector, scene_analyst],
|
||||
tasks=[security_task, object_task, scene_task],
|
||||
process="parallel", # Run all analyses in parallel
|
||||
|
||||
@@ -18,7 +18,7 @@ import cv2
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
|
||||
class CameraVideoAnalyzer:
|
||||
@@ -131,7 +131,7 @@ class CameraVideoAnalyzer:
|
||||
images=[video_path] # PraisonAI supports video files in images parameter
|
||||
)
|
||||
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[self.video_agent],
|
||||
tasks=[task],
|
||||
process="sequential",
|
||||
|
||||
@@ -24,7 +24,7 @@ from pathlib import Path
|
||||
project_root = Path(__file__).parent.parent.parent.parent / "src" / "praisonai-agents"
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents, create_context_agent
|
||||
from praisonaiagents import Agent, Task, AgentTeam, create_context_agent
|
||||
|
||||
class ContextEngineeringWorkflow:
|
||||
"""
|
||||
@@ -302,7 +302,7 @@ class ContextEngineeringWorkflow:
|
||||
print("\n⚙️ Executing Context Engineering Workflow")
|
||||
print("-" * 50)
|
||||
|
||||
agents_workflow = Agents(
|
||||
agents_workflow = AgentTeam(
|
||||
agents=[
|
||||
self.product_manager,
|
||||
self.architect,
|
||||
|
||||
@@ -11,7 +11,7 @@ Author: Generated for GitHub Issue #23
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import read_csv, write_csv
|
||||
|
||||
def create_sample_urls_csv():
|
||||
@@ -81,7 +81,7 @@ def method_1_simple_loop():
|
||||
)
|
||||
|
||||
# Initialize the agents system
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[loop_agent],
|
||||
tasks=[loop_task],
|
||||
process="workflow",
|
||||
@@ -145,7 +145,7 @@ def method_2_manual_csv():
|
||||
)
|
||||
|
||||
# Initialize and run
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[csv_agent],
|
||||
tasks=[csv_task],
|
||||
process="sequential"
|
||||
@@ -211,7 +211,7 @@ def method_3_url_processing():
|
||||
)
|
||||
|
||||
# Initialize and run
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[url_agent],
|
||||
tasks=[url_task],
|
||||
process="sequential"
|
||||
@@ -319,7 +319,7 @@ def method_4_advanced_processing():
|
||||
)
|
||||
|
||||
# Initialize and run with sequential processing
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[validator_agent, processor_agent],
|
||||
tasks=[validation_task, processing_task],
|
||||
process="sequential" # Validation must complete before processing
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -35,7 +35,7 @@ knowledge_task = Task(
|
||||
)
|
||||
|
||||
# Create and start the agents
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[knowledge_agent],
|
||||
tasks=[knowledge_task],
|
||||
process="sequential",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
# Define the configuration for the Knowledge instance
|
||||
config = {
|
||||
@@ -29,7 +29,7 @@ rag_task = Task(
|
||||
)
|
||||
|
||||
# Build Agents
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[rag_agent],
|
||||
tasks=[rag_task],
|
||||
user_id="user1"
|
||||
|
||||
@@ -8,7 +8,7 @@ This example demonstrates how to extract and utilize reasoning patterns in Prais
|
||||
- Decision rationale extraction
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from typing import Dict, List, Tuple
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
@@ -100,7 +100,7 @@ def demonstrate_reasoning_extraction():
|
||||
context=[problem_task]
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[cot_agent, reasoning_extractor],
|
||||
tasks=[problem_task, extract_task],
|
||||
process="sequential", output="verbose"
|
||||
@@ -183,7 +183,7 @@ def advanced_reasoning_patterns():
|
||||
context=[deductive_task, inductive_task, abductive_task]
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[cot_agent, reasoning_validator],
|
||||
tasks=[deductive_task, inductive_task, abductive_task, validation_task],
|
||||
process="sequential", output="verbose"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a loop agent that creating a loop of tasks.",
|
||||
@@ -12,7 +12,7 @@ task = Task(
|
||||
input_file="tasks.csv"
|
||||
)
|
||||
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="workflow",
|
||||
|
||||
@@ -8,7 +8,7 @@ This example demonstrates various routing patterns in PraisonAI including:
|
||||
- Dynamic task selection
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from typing import Dict, Any
|
||||
import random
|
||||
|
||||
@@ -77,7 +77,7 @@ def conditional_routing_example():
|
||||
agent=processor_agent
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[analyzer_agent, processor_agent],
|
||||
tasks=[analysis_task, premium_task, standard_task, basic_task],
|
||||
process="workflow", output="verbose"
|
||||
@@ -109,7 +109,7 @@ def loop_pattern_example():
|
||||
agent=validator_agent
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[processor_agent, validator_agent],
|
||||
tasks=[process_task, finalize_task],
|
||||
process="workflow", output="verbose"
|
||||
@@ -183,7 +183,7 @@ def decision_tree_example():
|
||||
|
||||
all_tasks = [categorize_task, technical_task, business_task] + terminal_tasks
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[analyzer_agent, processor_agent],
|
||||
tasks=all_tasks,
|
||||
process="workflow", output="verbose"
|
||||
@@ -269,7 +269,7 @@ def dynamic_routing_example():
|
||||
agent=processor_agent
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[dynamic_router, processor_agent],
|
||||
tasks=[route_task, escalate_task, automate_task, review_task, standard_task],
|
||||
process="workflow", output="verbose"
|
||||
|
||||
@@ -8,7 +8,7 @@ This example demonstrates the self-reflection capabilities in PraisonAI agents:
|
||||
- Impact on output quality
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from typing import Dict, Any
|
||||
import time
|
||||
|
||||
@@ -162,7 +162,7 @@ def reflection_workflow_example():
|
||||
context=[create_task, review_task]
|
||||
)
|
||||
|
||||
workflow = Agents(
|
||||
workflow = AgentTeam(
|
||||
agents=[creator_agent, reviewer_agent, polisher_agent],
|
||||
tasks=[create_task, review_task, polish_task],
|
||||
process="sequential", output="verbose"
|
||||
|
||||
@@ -5,7 +5,7 @@ This example demonstrates self-reflection capabilities using PraisonAI's
|
||||
built-in reflection features for iterative improvement and quality optimization.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import internet_search
|
||||
|
||||
print("=== Self-Reflection Optimization Patterns Example ===\n")
|
||||
|
||||
@@ -9,7 +9,7 @@ This is the simplest possible implementation for processing URLs from a CSV file
|
||||
Author: Generated for GitHub Issue #23
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import write_csv
|
||||
|
||||
def create_url_csv():
|
||||
@@ -59,7 +59,7 @@ def main():
|
||||
)
|
||||
|
||||
# Step 4: Run the agents
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[url_agent],
|
||||
tasks=[url_task],
|
||||
process="workflow",
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
"""
|
||||
Conditional Branching Example - PraisonAI Agents
|
||||
|
||||
This example demonstrates the `if:` pattern for conditional workflow execution:
|
||||
1. Basic if/then/else branching
|
||||
2. Numeric condition evaluation (>, <, >=, <=, ==, !=)
|
||||
3. String condition evaluation (==, !=)
|
||||
4. Contains condition (in, contains)
|
||||
5. Combining if: with loop: for per-item decisions
|
||||
|
||||
The if: pattern enables dynamic workflow paths based on
|
||||
runtime conditions and variable values.
|
||||
"""
|
||||
|
||||
from praisonaiagents import AgentFlow, WorkflowContext, StepResult
|
||||
from praisonaiagents.workflows import if_, loop
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 1: Basic If/Then/Else - Score-Based Routing
|
||||
# =============================================================================
|
||||
|
||||
def basic_if_example():
|
||||
"""
|
||||
Route to different handlers based on a score threshold.
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Example 1: Basic If/Then/Else - Score Routing")
|
||||
print("="*60)
|
||||
|
||||
def approve(ctx: WorkflowContext) -> StepResult:
|
||||
score = ctx.variables.get("score", 0)
|
||||
print(f" ✅ APPROVED: Score {score} meets threshold")
|
||||
return StepResult(output="Application approved")
|
||||
|
||||
def reject(ctx: WorkflowContext) -> StepResult:
|
||||
score = ctx.variables.get("score", 0)
|
||||
print(f" ❌ REJECTED: Score {score} below threshold")
|
||||
return StepResult(output="Application rejected")
|
||||
|
||||
# Test with passing score
|
||||
workflow_pass = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{score}} >= 70",
|
||||
then_steps=[approve],
|
||||
else_steps=[reject]
|
||||
)
|
||||
],
|
||||
variables={"score": 85}
|
||||
)
|
||||
|
||||
print("\nTest 1: Score = 85")
|
||||
workflow_pass.start("Evaluate application")
|
||||
|
||||
# Test with failing score
|
||||
workflow_fail = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{score}} >= 70",
|
||||
then_steps=[approve],
|
||||
else_steps=[reject]
|
||||
)
|
||||
],
|
||||
variables={"score": 55}
|
||||
)
|
||||
|
||||
print("\nTest 2: Score = 55")
|
||||
workflow_fail.start("Evaluate application")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 2: String Condition - Status-Based Processing
|
||||
# =============================================================================
|
||||
|
||||
def string_condition_example():
|
||||
"""
|
||||
Branch based on string equality.
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Example 2: String Condition - Status Processing")
|
||||
print("="*60)
|
||||
|
||||
def process_active(ctx: WorkflowContext) -> StepResult:
|
||||
print(" 🟢 Processing ACTIVE user")
|
||||
return StepResult(output="Active user processed")
|
||||
|
||||
def process_inactive(ctx: WorkflowContext) -> StepResult:
|
||||
print(" 🔴 Processing INACTIVE user")
|
||||
return StepResult(output="Inactive user archived")
|
||||
|
||||
# Test with active status
|
||||
workflow = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{status}} == active",
|
||||
then_steps=[process_active],
|
||||
else_steps=[process_inactive]
|
||||
)
|
||||
],
|
||||
variables={"status": "active"}
|
||||
)
|
||||
|
||||
print("\nTest: status = 'active'")
|
||||
workflow.start("Process user")
|
||||
|
||||
# Test with inactive status
|
||||
workflow2 = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{status}} == active",
|
||||
then_steps=[process_active],
|
||||
else_steps=[process_inactive]
|
||||
)
|
||||
],
|
||||
variables={"status": "inactive"}
|
||||
)
|
||||
|
||||
print("\nTest: status = 'inactive'")
|
||||
workflow2.start("Process user")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 3: Contains Condition - Content Filtering
|
||||
# =============================================================================
|
||||
|
||||
def contains_condition_example():
|
||||
"""
|
||||
Branch based on whether a string contains a substring.
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Example 3: Contains Condition - Content Filtering")
|
||||
print("="*60)
|
||||
|
||||
def flag_content(ctx: WorkflowContext) -> StepResult:
|
||||
print(" 🚨 FLAGGED: Message contains sensitive content")
|
||||
return StepResult(output="Content flagged for review")
|
||||
|
||||
def approve_content(ctx: WorkflowContext) -> StepResult:
|
||||
print(" ✅ APPROVED: Message is clean")
|
||||
return StepResult(output="Content approved")
|
||||
|
||||
# Test with flagged content
|
||||
workflow1 = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="error in {{message}}",
|
||||
then_steps=[flag_content],
|
||||
else_steps=[approve_content]
|
||||
)
|
||||
],
|
||||
variables={"message": "System error: connection failed"}
|
||||
)
|
||||
|
||||
print("\nTest 1: Message contains 'error'")
|
||||
workflow1.start("Filter content")
|
||||
|
||||
# Test with clean content
|
||||
workflow2 = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="error in {{message}}",
|
||||
then_steps=[flag_content],
|
||||
else_steps=[approve_content]
|
||||
)
|
||||
],
|
||||
variables={"message": "Operation completed successfully"}
|
||||
)
|
||||
|
||||
print("\nTest 2: Message is clean")
|
||||
workflow2.start("Filter content")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 4: If Inside Loop - Per-Item Decisions
|
||||
# =============================================================================
|
||||
|
||||
def if_inside_loop_example():
|
||||
"""
|
||||
Make decisions for each item in a collection.
|
||||
Combines iteration with conditional branching.
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Example 4: If Inside Loop - Per-Item Decisions")
|
||||
print("="*60)
|
||||
|
||||
results = {"premium": [], "standard": []}
|
||||
|
||||
def premium_service(ctx: WorkflowContext) -> StepResult:
|
||||
customer = ctx.variables.get("customer", {})
|
||||
name = customer.get("name", "Unknown")
|
||||
results["premium"].append(name)
|
||||
print(f" ⭐ {name}: Premium service activated")
|
||||
return StepResult(output=f"Premium service for {name}")
|
||||
|
||||
def standard_service(ctx: WorkflowContext) -> StepResult:
|
||||
customer = ctx.variables.get("customer", {})
|
||||
name = customer.get("name", "Unknown")
|
||||
results["standard"].append(name)
|
||||
print(f" 📦 {name}: Standard service activated")
|
||||
return StepResult(output=f"Standard service for {name}")
|
||||
|
||||
workflow = AgentFlow(
|
||||
steps=[
|
||||
loop(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{customer.tier}} == premium",
|
||||
then_steps=[premium_service],
|
||||
else_steps=[standard_service]
|
||||
)
|
||||
],
|
||||
over="customers",
|
||||
var_name="customer"
|
||||
)
|
||||
],
|
||||
variables={
|
||||
"customers": [
|
||||
{"name": "Alice", "tier": "premium"},
|
||||
{"name": "Bob", "tier": "standard"},
|
||||
{"name": "Charlie", "tier": "premium"},
|
||||
{"name": "Diana", "tier": "standard"}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
print("\nProcessing customers:")
|
||||
workflow.start("Assign service tiers")
|
||||
|
||||
print(f"\nSummary:")
|
||||
print(f" Premium customers: {results['premium']}")
|
||||
print(f" Standard customers: {results['standard']}")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 5: Nested Conditions - Multi-Level Decision Tree
|
||||
# =============================================================================
|
||||
|
||||
def nested_conditions_example():
|
||||
"""
|
||||
Demonstrate nested if conditions for complex decision trees.
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Example 5: Nested Conditions - Decision Tree")
|
||||
print("="*60)
|
||||
|
||||
def vip_treatment(ctx: WorkflowContext) -> StepResult:
|
||||
print(" 👑 VIP Treatment: Dedicated support + Priority queue")
|
||||
return StepResult(output="VIP treatment applied") # noqa: ARG001
|
||||
|
||||
def regular_premium(ctx: WorkflowContext) -> StepResult:
|
||||
print(" ⭐ Premium: Priority support")
|
||||
return StepResult(output="Premium support applied")
|
||||
|
||||
def basic_support(ctx: WorkflowContext) -> StepResult:
|
||||
print(" 📞 Basic: Standard support queue")
|
||||
return StepResult(output="Basic support applied")
|
||||
|
||||
# Outer condition: Is premium?
|
||||
# Inner condition (if premium): Is VIP?
|
||||
workflow = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{tier}} == premium",
|
||||
then_steps=[
|
||||
if_(
|
||||
condition="{{spend}} > 10000",
|
||||
then_steps=[vip_treatment],
|
||||
else_steps=[regular_premium]
|
||||
)
|
||||
],
|
||||
else_steps=[basic_support]
|
||||
)
|
||||
],
|
||||
variables={"tier": "premium", "spend": 15000}
|
||||
)
|
||||
|
||||
print("\nTest 1: Premium tier, high spend")
|
||||
workflow.start("Determine support level")
|
||||
|
||||
workflow2 = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{tier}} == premium",
|
||||
then_steps=[
|
||||
if_(
|
||||
condition="{{spend}} > 10000",
|
||||
then_steps=[vip_treatment],
|
||||
else_steps=[regular_premium]
|
||||
)
|
||||
],
|
||||
else_steps=[basic_support]
|
||||
)
|
||||
],
|
||||
variables={"tier": "premium", "spend": 5000}
|
||||
)
|
||||
|
||||
print("\nTest 2: Premium tier, low spend")
|
||||
workflow2.start("Determine support level")
|
||||
|
||||
workflow3 = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{tier}} == premium",
|
||||
then_steps=[
|
||||
if_(
|
||||
condition="{{spend}} > 10000",
|
||||
then_steps=[vip_treatment],
|
||||
else_steps=[regular_premium]
|
||||
)
|
||||
],
|
||||
else_steps=[basic_support]
|
||||
)
|
||||
],
|
||||
variables={"tier": "basic", "spend": 20000}
|
||||
)
|
||||
|
||||
print("\nTest 3: Basic tier (regardless of spend)")
|
||||
workflow3.start("Determine support level")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Example 6: No Else Branch - Optional Processing
|
||||
# =============================================================================
|
||||
|
||||
def no_else_example():
|
||||
"""
|
||||
If condition without else branch - only execute if true.
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print("Example 6: No Else Branch - Optional Processing")
|
||||
print("="*60)
|
||||
|
||||
def send_notification(ctx: WorkflowContext) -> StepResult:
|
||||
print(" 📧 Notification sent!")
|
||||
return StepResult(output="Notification sent")
|
||||
|
||||
def continue_processing(ctx: WorkflowContext) -> StepResult:
|
||||
print(" ➡️ Continuing with main workflow...")
|
||||
return StepResult(output="Continued")
|
||||
|
||||
# Only send notification if opted in
|
||||
workflow = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{notify}} == true",
|
||||
then_steps=[send_notification]
|
||||
# No else_steps - just skip if false
|
||||
),
|
||||
continue_processing
|
||||
],
|
||||
variables={"notify": "true"}
|
||||
)
|
||||
|
||||
print("\nTest 1: notify = true")
|
||||
workflow.start("Process with notification")
|
||||
|
||||
workflow2 = AgentFlow(
|
||||
steps=[
|
||||
if_(
|
||||
condition="{{notify}} == true",
|
||||
then_steps=[send_notification]
|
||||
),
|
||||
continue_processing
|
||||
],
|
||||
variables={"notify": "false"}
|
||||
)
|
||||
|
||||
print("\nTest 2: notify = false")
|
||||
workflow2.start("Process without notification")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Main - Run All Examples
|
||||
# =============================================================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n" + "="*60)
|
||||
print("PraisonAI Conditional Branching Examples")
|
||||
print("="*60)
|
||||
|
||||
basic_if_example()
|
||||
string_condition_example()
|
||||
contains_condition_example()
|
||||
if_inside_loop_example()
|
||||
nested_conditions_example()
|
||||
no_else_example()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("All conditional branching examples completed!")
|
||||
print("="*60)
|
||||
@@ -4,7 +4,7 @@ Example: Using Latency Tracking Tool with PraisonAI
|
||||
This example shows how to track latency without modifying core files.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
from latency_tracker_tool import (
|
||||
latency_tracking_tool,
|
||||
create_tracked_agent,
|
||||
|
||||
@@ -5,7 +5,7 @@ This shows how to add latency tracking to an MCP server
|
||||
without modifying PraisonAI core files.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Agents
|
||||
from praisonaiagents import Agent, AgentTeam
|
||||
from hosted_server import HostedMCPServer # Import from local file
|
||||
from latency_tracker_tool import tracker, get_latency_metrics
|
||||
import json
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import cot_save, cot_upload_to_huggingface
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
@@ -128,7 +128,7 @@ upload_to_huggingface_task = Task(
|
||||
)
|
||||
|
||||
# Initialize workflow
|
||||
agents = Agents(
|
||||
agents = AgentTeam(
|
||||
agents=[qa_generator, total_questions_evaluator, cot_generator, upload_to_huggingface],
|
||||
tasks=[generate_task, evaluate_total_questions_task, generate_cot_task, upload_to_huggingface_task],
|
||||
process="workflow",
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# EvaluationLoop Examples
|
||||
|
||||
Examples demonstrating iterative evaluation and improvement patterns.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY=your-key
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `basic_evaluation_loop.py` | Simple loop: Run → Judge → Improve |
|
||||
| `multi_step_task_loop.py` | Sequential tasks with context passing |
|
||||
| `recipe_optimization_loop.py` | CLI-based recipe optimization |
|
||||
|
||||
## Pattern
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐ ┌──────────┐
|
||||
│ Agent │ ──► │ Judge │ ──► │ Feedback │
|
||||
│ runs │ │ scores │ │ improves │
|
||||
└─────────┘ └─────────┘ └──────────┘
|
||||
▲ │
|
||||
└───────────────────────────────┘
|
||||
(repeat until 8.0+)
|
||||
```
|
||||
|
||||
## Run Examples
|
||||
|
||||
```bash
|
||||
# Basic loop
|
||||
python basic_evaluation_loop.py
|
||||
|
||||
# Multi-step with context
|
||||
python multi_step_task_loop.py
|
||||
|
||||
# CLI recipe loop
|
||||
python recipe_optimization_loop.py "research AI trends"
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Basic EvaluationLoop Example
|
||||
|
||||
Demonstrates the iterative evaluation loop pattern:
|
||||
Run agent → Judge output → Improve → Repeat until threshold met.
|
||||
|
||||
Usage:
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY=your-key
|
||||
python basic_evaluation_loop.py
|
||||
"""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.eval import EvaluationLoop
|
||||
|
||||
|
||||
def on_iteration(result):
|
||||
"""Callback showing progress after each iteration."""
|
||||
status = "✅" if result.score >= 8.0 else "⚠️" if result.score >= 6.0 else "❌"
|
||||
print(f"{status} Iteration {result.iteration}: {result.score:.1f}/10")
|
||||
if result.findings:
|
||||
print(f" → {result.findings[0][:60]}...")
|
||||
|
||||
|
||||
def main():
|
||||
# Create an agent
|
||||
agent = Agent(
|
||||
name="writer",
|
||||
instructions="You are a technical content writer. Write clear, accurate explanations."
|
||||
)
|
||||
|
||||
# Run evaluation loop
|
||||
loop = EvaluationLoop(
|
||||
agent=agent,
|
||||
criteria="""
|
||||
Evaluate the response on:
|
||||
1. CLARITY: Is the explanation easy to understand?
|
||||
2. ACCURACY: Is the technical content correct?
|
||||
3. COMPLETENESS: Does it cover the key aspects?
|
||||
4. EXAMPLES: Does it include practical examples?
|
||||
|
||||
Score 1-10 based on all criteria.
|
||||
""",
|
||||
threshold=8.0,
|
||||
max_iterations=5,
|
||||
on_iteration=on_iteration,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
print("╔════════════════════════════════════════════════════════════╗")
|
||||
print("║ Basic EvaluationLoop Example ║")
|
||||
print("║ Pattern: Run → Judge → Improve → Repeat ║")
|
||||
print("║ Target: 8.0/10 score ║")
|
||||
print("╚════════════════════════════════════════════════════════════╝\n")
|
||||
|
||||
result = loop.run("Explain how API rate limiting works and why it matters")
|
||||
|
||||
print("\n" + "─" * 60)
|
||||
print("RESULTS:")
|
||||
print("─" * 60)
|
||||
print(f"Success: {result.success}")
|
||||
print(f"Final Score: {result.final_score:.1f}/10")
|
||||
print(f"Iterations: {result.num_iterations}")
|
||||
print(f"Score History: {[f'{s:.1f}' for s in result.score_history]}")
|
||||
print(f"Duration: {result.total_duration_seconds:.2f}s")
|
||||
|
||||
print("\n" + "─" * 60)
|
||||
print("FINAL OUTPUT (first 500 chars):")
|
||||
print("─" * 60)
|
||||
print(result.final_output[:500] + "..." if len(result.final_output) > 500 else result.final_output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
Multi-Step Task Loop Example
|
||||
|
||||
Demonstrates evaluating sequential task steps where each step can
|
||||
reference output from previous steps.
|
||||
|
||||
Pattern:
|
||||
1. Research → Evaluate → Improve
|
||||
2. Analyze (using research) → Evaluate → Improve
|
||||
3. Report (using analysis) → Evaluate → Improve
|
||||
|
||||
Usage:
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY=your-key
|
||||
python multi_step_task_loop.py
|
||||
"""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.eval import EvaluationLoop
|
||||
|
||||
|
||||
def on_iteration(result):
|
||||
"""Show iteration progress."""
|
||||
status = "✅" if result.score >= 8.0 else "⚠️" if result.score >= 6.0 else "❌"
|
||||
print(f" {status} Iteration {result.iteration}: {result.score:.1f}/10")
|
||||
|
||||
|
||||
def run_step(step_name: str, agent: Agent, task: str, criteria: str, context: str = "") -> dict:
|
||||
"""Run a single step through an evaluation loop."""
|
||||
print(f"\n{'─'*60}")
|
||||
print(f"STEP: {step_name}")
|
||||
print(f"{'─'*60}")
|
||||
|
||||
# Include context from previous step if provided
|
||||
full_task = task
|
||||
if context:
|
||||
full_task = f"{task}\n\nContext from previous step:\n{context[:1000]}"
|
||||
|
||||
loop = EvaluationLoop(
|
||||
agent=agent,
|
||||
criteria=criteria,
|
||||
threshold=8.0,
|
||||
max_iterations=3,
|
||||
on_iteration=on_iteration,
|
||||
verbose=False,
|
||||
)
|
||||
|
||||
result = loop.run(full_task)
|
||||
|
||||
print(f" → Final: {result.final_score:.1f}/10 ({result.num_iterations} iterations)")
|
||||
|
||||
return {
|
||||
"name": step_name,
|
||||
"output": result.final_output,
|
||||
"score": result.final_score,
|
||||
"success": result.success,
|
||||
"iterations": result.num_iterations,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
print("╔════════════════════════════════════════════════════════════╗")
|
||||
print("║ Multi-Step Task Loop Example ║")
|
||||
print("║ Pattern: Step1 → Step2 → Step3 (with context passing) ║")
|
||||
print("║ Each step: Run → Judge → Improve until 8.0+ ║")
|
||||
print("╚════════════════════════════════════════════════════════════╝")
|
||||
|
||||
# Create specialized agents
|
||||
researcher = Agent(
|
||||
name="researcher",
|
||||
instructions="You are a research specialist. Find and summarize key information."
|
||||
)
|
||||
|
||||
analyst = Agent(
|
||||
name="analyst",
|
||||
instructions="You are an analyst. Identify patterns, insights, and implications."
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="writer",
|
||||
instructions="You are a report writer. Create clear, actionable summaries."
|
||||
)
|
||||
|
||||
# Define task steps
|
||||
steps = [
|
||||
{
|
||||
"name": "Research",
|
||||
"agent": researcher,
|
||||
"task": "Research the top 3 trends in AI for 2024-2025. Provide brief descriptions.",
|
||||
"criteria": "Covers 3 distinct trends with accurate descriptions.",
|
||||
},
|
||||
{
|
||||
"name": "Analysis",
|
||||
"agent": analyst,
|
||||
"task": "Analyze the implications of these AI trends for software development.",
|
||||
"criteria": "Identifies actionable insights and potential challenges.",
|
||||
},
|
||||
{
|
||||
"name": "Report",
|
||||
"agent": writer,
|
||||
"task": "Create a brief executive summary (3-4 bullet points) for a CTO.",
|
||||
"criteria": "Concise, actionable, appropriate for executive audience.",
|
||||
},
|
||||
]
|
||||
|
||||
# Run all steps with context passing
|
||||
results = []
|
||||
context = ""
|
||||
|
||||
for step in steps:
|
||||
result = run_step(
|
||||
step_name=step["name"],
|
||||
agent=step["agent"],
|
||||
task=step["task"],
|
||||
criteria=step["criteria"],
|
||||
context=context,
|
||||
)
|
||||
results.append(result)
|
||||
context = result["output"] # Pass output as context to next step
|
||||
|
||||
# Summary
|
||||
print("\n" + "═"*60)
|
||||
print("SUMMARY")
|
||||
print("═"*60)
|
||||
|
||||
total_iterations = sum(r["iterations"] for r in results)
|
||||
avg_score = sum(r["score"] for r in results) / len(results)
|
||||
all_success = all(r["success"] for r in results)
|
||||
|
||||
for r in results:
|
||||
status = "✅" if r["success"] else "❌"
|
||||
print(f" {status} {r['name']}: {r['score']:.1f}/10 ({r['iterations']} iterations)")
|
||||
|
||||
print(f"\n Total Iterations: {total_iterations}")
|
||||
print(f" Average Score: {avg_score:.1f}/10")
|
||||
print(f" All Steps Passed: {'Yes' if all_success else 'No'}")
|
||||
|
||||
print("\n" + "─"*60)
|
||||
print("FINAL REPORT OUTPUT:")
|
||||
print("─"*60)
|
||||
print(results[-1]["output"][:600])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,183 @@
|
||||
"""
|
||||
Recipe Optimization Loop (CLI-based)
|
||||
|
||||
Demonstrates the 5-cycle recipe improvement pattern using CLI commands:
|
||||
Create (once) → Run → Judge → Optimize (loop 5x)
|
||||
|
||||
Usage:
|
||||
pip install praisonai
|
||||
export OPENAI_API_KEY=your-key
|
||||
export TAVILY_API_KEY=your-key
|
||||
python recipe_optimization_loop.py "research AI trends in healthcare"
|
||||
"""
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_cli(cmd: str, cwd: str = None) -> tuple[int, str, str]:
|
||||
"""Run a CLI command and return (exit_code, stdout, stderr)."""
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=cwd,
|
||||
)
|
||||
return result.returncode, result.stdout, result.stderr
|
||||
|
||||
|
||||
def create_recipe(goal: str, work_dir: str) -> str:
|
||||
"""Create a recipe once. Returns recipe name."""
|
||||
print(f"\n{'═'*60}")
|
||||
print("STEP 1: CREATE RECIPE (once)")
|
||||
print(f"{'═'*60}")
|
||||
print(f"Goal: {goal}")
|
||||
|
||||
cmd = f'praisonai recipe create "{goal}" --no-optimize'
|
||||
code, stdout, stderr = run_cli(cmd, cwd=work_dir)
|
||||
|
||||
if code != 0:
|
||||
print(f"❌ Failed to create recipe: {stderr}")
|
||||
return None
|
||||
|
||||
# Extract recipe name from output
|
||||
for line in stdout.split('\n'):
|
||||
if 'Created recipe:' in line:
|
||||
recipe_name = line.split('Created recipe:')[1].strip()
|
||||
print(f"✅ Created: {recipe_name}")
|
||||
return recipe_name
|
||||
|
||||
print(f"✅ Recipe created")
|
||||
return None
|
||||
|
||||
|
||||
def run_recipe(recipe_name: str, trace_name: str, work_dir: str) -> tuple[bool, str]:
|
||||
"""Run the recipe and save trace. Returns (success, output)."""
|
||||
cmd = f'praisonai recipe run {recipe_name} --save --name {trace_name}'
|
||||
code, stdout, stderr = run_cli(cmd, cwd=work_dir)
|
||||
|
||||
success = code == 0 and 'status: completed' in stdout.lower()
|
||||
return success, stdout
|
||||
|
||||
|
||||
def judge_recipe(trace_name: str, work_dir: str) -> tuple[float, str]:
|
||||
"""Judge the trace. Returns (score, feedback)."""
|
||||
cmd = f'praisonai recipe judge {trace_name}'
|
||||
code, stdout, stderr = run_cli(cmd, cwd=work_dir)
|
||||
|
||||
# Extract score from output
|
||||
score = 0.0
|
||||
for line in stdout.split('\n'):
|
||||
if 'Overall Score:' in line:
|
||||
try:
|
||||
score = float(line.split(':')[1].strip().split('/')[0])
|
||||
except:
|
||||
pass
|
||||
|
||||
return score, stdout
|
||||
|
||||
|
||||
def optimize_recipe(recipe_name: str, iterations: int, work_dir: str) -> tuple[float, str]:
|
||||
"""Optimize the recipe. Returns (final_score, output)."""
|
||||
cmd = f'praisonai recipe optimize {recipe_name} --iterations {iterations}'
|
||||
code, stdout, stderr = run_cli(cmd, cwd=work_dir)
|
||||
|
||||
# Extract final score
|
||||
score = 0.0
|
||||
for line in stdout.split('\n'):
|
||||
if 'Final score:' in line or 'score:' in line.lower():
|
||||
try:
|
||||
score = float(line.split(':')[1].strip().split('/')[0])
|
||||
except:
|
||||
pass
|
||||
|
||||
return score, stdout
|
||||
|
||||
|
||||
def run_optimization_loop(goal: str, num_cycles: int = 5, threshold: float = 8.0):
|
||||
"""Run the full Create → Run → Judge → Optimize loop."""
|
||||
|
||||
print("╔════════════════════════════════════════════════════════════╗")
|
||||
print("║ Recipe Optimization Loop (CLI-based) ║")
|
||||
print("║ Pattern: Create (once) → Run → Judge → Optimize (loop) ║")
|
||||
print(f"║ Cycles: {num_cycles} | Threshold: {threshold}/10 ║")
|
||||
print("╚════════════════════════════════════════════════════════════╝")
|
||||
|
||||
# Create work directory
|
||||
work_dir = Path("/tmp/recipe-optimization-loop")
|
||||
work_dir.mkdir(parents=True, exist_ok=True)
|
||||
os.chdir(work_dir)
|
||||
|
||||
# Step 1: Create recipe (once)
|
||||
recipe_name = create_recipe(goal, str(work_dir))
|
||||
if not recipe_name:
|
||||
print("Failed to create recipe. Exiting.")
|
||||
return
|
||||
|
||||
# Step 2-N: Run → Judge → Optimize loop
|
||||
results = []
|
||||
|
||||
for cycle in range(1, num_cycles + 1):
|
||||
print(f"\n{'─'*60}")
|
||||
print(f"CYCLE {cycle}/{num_cycles}")
|
||||
print(f"{'─'*60}")
|
||||
|
||||
trace_name = f"cycle-{cycle}"
|
||||
|
||||
# Run
|
||||
print(f" 📝 Running recipe...")
|
||||
success, run_output = run_recipe(recipe_name, trace_name, str(work_dir))
|
||||
run_status = "✅" if success else "⚠️"
|
||||
print(f" {run_status} Run complete")
|
||||
|
||||
# Judge
|
||||
print(f" ⚖️ Judging output...")
|
||||
score, judge_output = judge_recipe(trace_name, str(work_dir))
|
||||
score_status = "✅" if score >= threshold else "⚠️" if score >= 6.0 else "❌"
|
||||
print(f" {score_status} Score: {score}/10")
|
||||
|
||||
# Check if threshold met
|
||||
if score >= threshold:
|
||||
print(f" 🎉 Threshold {threshold}/10 reached!")
|
||||
results.append({"cycle": cycle, "score": score, "status": "passed"})
|
||||
break
|
||||
|
||||
# Optimize
|
||||
print(f" 🔧 Optimizing recipe...")
|
||||
opt_score, opt_output = optimize_recipe(recipe_name, 1, str(work_dir))
|
||||
print(f" → Optimized score: {opt_score}/10")
|
||||
|
||||
results.append({
|
||||
"cycle": cycle,
|
||||
"initial_score": score,
|
||||
"optimized_score": opt_score,
|
||||
"status": "optimized" if opt_score > score else "no_change"
|
||||
})
|
||||
|
||||
# Summary
|
||||
print(f"\n{'═'*60}")
|
||||
print("SUMMARY")
|
||||
print(f"{'═'*60}")
|
||||
|
||||
for r in results:
|
||||
if "initial_score" in r:
|
||||
print(f" Cycle {r['cycle']}: {r['initial_score']} → {r['optimized_score']}/10")
|
||||
else:
|
||||
print(f" Cycle {r['cycle']}: {r['score']}/10 ✅ PASSED")
|
||||
|
||||
final_score = results[-1].get("optimized_score", results[-1].get("score", 0))
|
||||
success = final_score >= threshold
|
||||
|
||||
print(f"\n Final Score: {final_score}/10")
|
||||
print(f" Success: {'Yes ✅' if success else 'No ❌'}")
|
||||
print(f" Recipe: {work_dir}/{recipe_name}")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
goal = sys.argv[1] if len(sys.argv) > 1 else "research latest AI trends and create a summary report"
|
||||
run_optimization_loop(goal, num_cycles=5, threshold=8.0)
|
||||
@@ -0,0 +1,214 @@
|
||||
"""Example: Merge Strategies for Multi-Agent Workflows
|
||||
|
||||
This example demonstrates how to use and create custom merge strategies
|
||||
for combining outputs from parallel agent executions.
|
||||
"""
|
||||
from praisonaiagents.agents import (
|
||||
MergeStrategyProtocol,
|
||||
FirstWinsMerge,
|
||||
ConcatMerge,
|
||||
DictMerge,
|
||||
)
|
||||
|
||||
|
||||
def example_first_wins_merge():
|
||||
"""Demonstrate FirstWinsMerge strategy."""
|
||||
print("=" * 50)
|
||||
print("FirstWinsMerge Examples")
|
||||
print("=" * 50)
|
||||
|
||||
merge = FirstWinsMerge()
|
||||
|
||||
# 1. Basic usage - returns first non-None
|
||||
print("\n1. Basic Usage:")
|
||||
outputs = [None, "Agent 2 response", "Agent 3 response"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. All None returns None
|
||||
print("\n2. All None:")
|
||||
outputs = [None, None, None]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 3. First value wins
|
||||
print("\n3. First Value Wins:")
|
||||
outputs = ["Fast agent", "Slow agent", "Slowest agent"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Works with any type
|
||||
print("\n4. Various Types:")
|
||||
print(f" Dict: {merge.merge([None, {'key': 'value'}])}")
|
||||
print(f" List: {merge.merge([None, [1, 2, 3]])}")
|
||||
print(f" Number: {merge.merge([None, 42])}")
|
||||
|
||||
|
||||
def example_concat_merge():
|
||||
"""Demonstrate ConcatMerge strategy."""
|
||||
print("\n" + "=" * 50)
|
||||
print("ConcatMerge Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Default separator (double newline)
|
||||
print("\n1. Default Separator:")
|
||||
merge = ConcatMerge()
|
||||
outputs = ["Part 1: Introduction", "Part 2: Analysis", "Part 3: Conclusion"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Result:\n{result}")
|
||||
|
||||
# 2. Custom separator
|
||||
print("\n2. Custom Separator:")
|
||||
merge = ConcatMerge(separator=" | ")
|
||||
outputs = ["Alice", "Bob", "Charlie"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 3. Skips None values
|
||||
print("\n3. Skips None:")
|
||||
merge = ConcatMerge(separator=", ")
|
||||
outputs = ["A", None, "C", None, "E"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Converts non-strings
|
||||
print("\n4. Converts Non-Strings:")
|
||||
merge = ConcatMerge(separator=" + ")
|
||||
outputs = [1, 2, 3]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
|
||||
def example_dict_merge():
|
||||
"""Demonstrate DictMerge strategy."""
|
||||
print("\n" + "=" * 50)
|
||||
print("DictMerge Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Basic shallow merge
|
||||
print("\n1. Shallow Merge (default):")
|
||||
merge = DictMerge()
|
||||
outputs = [
|
||||
{"name": "Alice", "score": 10},
|
||||
{"name": "Bob", "level": 5},
|
||||
{"score": 20, "rank": 1}
|
||||
]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Later values override
|
||||
print("\n2. Override Behavior:")
|
||||
merge = DictMerge()
|
||||
outputs = [
|
||||
{"status": "pending"},
|
||||
{"status": "processing"},
|
||||
{"status": "complete"}
|
||||
]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 3. Deep merge for nested dicts
|
||||
print("\n3. Deep Merge:")
|
||||
merge = DictMerge(deep=True)
|
||||
outputs = [
|
||||
{"config": {"a": 1, "b": 2}},
|
||||
{"config": {"b": 3, "c": 4}},
|
||||
{"config": {"d": 5}}
|
||||
]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Skips non-dict values
|
||||
print("\n4. Skips Non-Dict Values:")
|
||||
merge = DictMerge()
|
||||
outputs = [{"a": 1}, "not a dict", None, {"b": 2}]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
|
||||
def example_custom_merge():
|
||||
"""Demonstrate creating custom merge strategies."""
|
||||
print("\n" + "=" * 50)
|
||||
print("Custom Merge Strategy Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Longest output merge
|
||||
class LongestOutputMerge:
|
||||
"""Returns the longest output."""
|
||||
def merge(self, outputs, context=None):
|
||||
valid = [o for o in outputs if o is not None]
|
||||
if not valid:
|
||||
return None
|
||||
return max(valid, key=lambda x: len(str(x)))
|
||||
|
||||
print("\n1. LongestOutputMerge:")
|
||||
merge = LongestOutputMerge()
|
||||
outputs = ["short", "medium length", "this is the longest response"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Majority vote merge
|
||||
class MajorityVoteMerge:
|
||||
"""Returns the most common output."""
|
||||
def merge(self, outputs, context=None):
|
||||
from collections import Counter
|
||||
valid = [o for o in outputs if o is not None]
|
||||
if not valid:
|
||||
return None
|
||||
counts = Counter(valid)
|
||||
return counts.most_common(1)[0][0]
|
||||
|
||||
print("\n2. MajorityVoteMerge:")
|
||||
merge = MajorityVoteMerge()
|
||||
outputs = ["yes", "yes", "no", "yes", "no"]
|
||||
result = merge.merge(outputs)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Result: {result} (3 votes)")
|
||||
|
||||
# 3. Weighted merge
|
||||
class WeightedMerge:
|
||||
"""Merges with agent weights from context."""
|
||||
def merge(self, outputs, context=None):
|
||||
weights = context.get("weights", [1] * len(outputs)) if context else [1] * len(outputs)
|
||||
best_idx = 0
|
||||
best_weight = 0
|
||||
for i, (output, weight) in enumerate(zip(outputs, weights)):
|
||||
if output is not None and weight > best_weight:
|
||||
best_idx = i
|
||||
best_weight = weight
|
||||
return outputs[best_idx] if outputs else None
|
||||
|
||||
print("\n3. WeightedMerge:")
|
||||
merge = WeightedMerge()
|
||||
outputs = ["junior answer", "senior answer", "expert answer"]
|
||||
context = {"weights": [1, 5, 10]}
|
||||
result = merge.merge(outputs, context=context)
|
||||
print(f" Inputs: {outputs}")
|
||||
print(f" Weights: {context['weights']}")
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Protocol compliance
|
||||
print("\n4. Protocol Compliance:")
|
||||
print(f" LongestOutputMerge: {isinstance(LongestOutputMerge(), MergeStrategyProtocol)}")
|
||||
print(f" MajorityVoteMerge: {isinstance(MajorityVoteMerge(), MergeStrategyProtocol)}")
|
||||
print(f" WeightedMerge: {isinstance(WeightedMerge(), MergeStrategyProtocol)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
example_first_wins_merge()
|
||||
example_concat_merge()
|
||||
example_dict_merge()
|
||||
example_custom_merge()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All examples completed!")
|
||||
print("=" * 50)
|
||||
@@ -0,0 +1,174 @@
|
||||
"""Example: Retry Policies and Fallback Chains
|
||||
|
||||
This example demonstrates how to configure retry behavior with
|
||||
exponential backoff, jitter, and fallback chains.
|
||||
"""
|
||||
from praisonaiagents.tools import (
|
||||
RetryPolicy,
|
||||
FallbackChain,
|
||||
ToolExecutionConfig,
|
||||
)
|
||||
|
||||
|
||||
def example_retry_policy():
|
||||
"""Demonstrate RetryPolicy configuration and usage."""
|
||||
print("=" * 50)
|
||||
print("RetryPolicy Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Default policy
|
||||
print("\n1. Default RetryPolicy:")
|
||||
policy = RetryPolicy()
|
||||
print(f" max_attempts: {policy.max_attempts}")
|
||||
print(f" backoff_factor: {policy.backoff_factor}")
|
||||
print(f" initial_delay_ms: {policy.initial_delay_ms}")
|
||||
print(f" max_delay_ms: {policy.max_delay_ms}")
|
||||
print(f" retry_on: {policy.retry_on}")
|
||||
|
||||
# 2. Exponential backoff calculation
|
||||
print("\n2. Exponential Backoff (no jitter):")
|
||||
policy = RetryPolicy(
|
||||
initial_delay_ms=1000,
|
||||
backoff_factor=2.0,
|
||||
max_delay_ms=30000,
|
||||
jitter=False
|
||||
)
|
||||
for attempt in range(5):
|
||||
delay = policy.get_delay_ms(attempt)
|
||||
print(f" Attempt {attempt}: delay = {delay}ms")
|
||||
|
||||
# 3. With jitter for production
|
||||
print("\n3. With Jitter (production-ready):")
|
||||
policy = RetryPolicy(
|
||||
initial_delay_ms=1000,
|
||||
backoff_factor=2.0,
|
||||
jitter=True,
|
||||
jitter_factor=0.25 # ±25%
|
||||
)
|
||||
print(" Delays for attempt 0 (10 samples):")
|
||||
delays = [policy.get_delay_ms(0) for _ in range(10)]
|
||||
print(f" {delays}")
|
||||
print(f" Range: {min(delays)} - {max(delays)} (expected: 750-1250)")
|
||||
|
||||
# 4. should_retry logic
|
||||
print("\n4. should_retry Logic:")
|
||||
policy = RetryPolicy(max_attempts=3, retry_on={"timeout", "rate_limit"})
|
||||
|
||||
test_cases = [
|
||||
("timeout", 0),
|
||||
("timeout", 2),
|
||||
("timeout", 3), # At max_attempts
|
||||
("unknown_error", 0), # Not in retry_on
|
||||
]
|
||||
for error_type, attempt in test_cases:
|
||||
result = policy.should_retry(error_type, attempt)
|
||||
print(f" should_retry('{error_type}', {attempt}) = {result}")
|
||||
|
||||
# 5. Custom error types
|
||||
print("\n5. Custom Error Types:")
|
||||
policy = RetryPolicy(retry_on={"api_error", "network_error", "throttled"})
|
||||
print(f" Retries on: {policy.retry_on}")
|
||||
print(f" should_retry('api_error', 0) = {policy.should_retry('api_error', 0)}")
|
||||
print(f" should_retry('timeout', 0) = {policy.should_retry('timeout', 0)}")
|
||||
|
||||
|
||||
def example_fallback_chain():
|
||||
"""Demonstrate FallbackChain configuration."""
|
||||
print("\n" + "=" * 50)
|
||||
print("FallbackChain Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Basic chain
|
||||
print("\n1. Basic FallbackChain:")
|
||||
chain = FallbackChain(
|
||||
tools=["web_search", "cached_search", "default_response"]
|
||||
)
|
||||
print(f" Tools: {chain.tools}")
|
||||
print(f" Length: {len(chain)}")
|
||||
print(f" stop_on_success: {chain.stop_on_success}")
|
||||
|
||||
# 2. Iteration
|
||||
print("\n2. Iterating over chain:")
|
||||
for i, tool in enumerate(chain):
|
||||
print(f" {i+1}. {tool}")
|
||||
|
||||
# 3. Try all (don't stop on success)
|
||||
print("\n3. Try All Mode:")
|
||||
chain = FallbackChain(
|
||||
tools=["validator_1", "validator_2", "validator_3"],
|
||||
stop_on_success=False
|
||||
)
|
||||
print(f" stop_on_success: {chain.stop_on_success}")
|
||||
print(" (All tools will be tried regardless of success)")
|
||||
|
||||
|
||||
def example_tool_execution_config():
|
||||
"""Demonstrate ToolExecutionConfig combining retry and fallback."""
|
||||
print("\n" + "=" * 50)
|
||||
print("ToolExecutionConfig Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Default config
|
||||
print("\n1. Default Config:")
|
||||
config = ToolExecutionConfig()
|
||||
print(f" retry_policy: {config.retry_policy}")
|
||||
print(f" fallback_chain: {config.fallback_chain}")
|
||||
print(f" timeout_ms: {config.timeout_ms}")
|
||||
|
||||
# 2. Using default() factory
|
||||
print("\n2. Default Factory:")
|
||||
config = ToolExecutionConfig.default()
|
||||
print(f" retry_policy.max_attempts: {config.retry_policy.max_attempts}")
|
||||
|
||||
# 3. Full configuration
|
||||
print("\n3. Full Configuration:")
|
||||
config = ToolExecutionConfig(
|
||||
retry_policy=RetryPolicy(
|
||||
max_attempts=5,
|
||||
backoff_factor=1.5,
|
||||
initial_delay_ms=500,
|
||||
jitter=True
|
||||
),
|
||||
fallback_chain=FallbackChain(
|
||||
tools=["primary_api", "backup_api", "cache"]
|
||||
),
|
||||
timeout_ms=10000
|
||||
)
|
||||
print(f" retry_policy.max_attempts: {config.retry_policy.max_attempts}")
|
||||
print(f" retry_policy.jitter: {config.retry_policy.jitter}")
|
||||
print(f" fallback_chain tools: {config.fallback_chain.tools}")
|
||||
print(f" timeout_ms: {config.timeout_ms}")
|
||||
|
||||
|
||||
def example_validation():
|
||||
"""Demonstrate validation of RetryPolicy parameters."""
|
||||
print("\n" + "=" * 50)
|
||||
print("Validation Examples")
|
||||
print("=" * 50)
|
||||
|
||||
invalid_configs = [
|
||||
("max_attempts=0", {"max_attempts": 0}),
|
||||
("backoff_factor=0.5", {"backoff_factor": 0.5}),
|
||||
("initial_delay_ms=-1", {"initial_delay_ms": -1}),
|
||||
("max_delay < initial", {"initial_delay_ms": 1000, "max_delay_ms": 500}),
|
||||
("jitter_factor=1.5", {"jitter_factor": 1.5}),
|
||||
]
|
||||
|
||||
for name, kwargs in invalid_configs:
|
||||
try:
|
||||
RetryPolicy(**kwargs)
|
||||
print(f"\n {name}: No error (unexpected!)")
|
||||
except ValueError as e:
|
||||
print(f"\n {name}:")
|
||||
print(f" → ValueError: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
example_retry_policy()
|
||||
example_fallback_chain()
|
||||
example_tool_execution_config()
|
||||
example_validation()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All examples completed!")
|
||||
print("=" * 50)
|
||||
@@ -0,0 +1,163 @@
|
||||
"""Example: Tool Validation with Custom Validators
|
||||
|
||||
This example demonstrates how to use ValidationResult and implement
|
||||
custom validators using the ToolValidatorProtocol.
|
||||
"""
|
||||
from praisonaiagents.tools import (
|
||||
ValidationResult,
|
||||
ToolValidatorProtocol,
|
||||
PassthroughValidator,
|
||||
)
|
||||
|
||||
|
||||
def example_validation_result():
|
||||
"""Demonstrate ValidationResult usage."""
|
||||
print("=" * 50)
|
||||
print("ValidationResult Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. Success factory
|
||||
success = ValidationResult.success()
|
||||
print(f"\n1. Success: valid={success.valid}, bool={bool(success)}")
|
||||
|
||||
# 2. Failure factory
|
||||
failure = ValidationResult.failure(
|
||||
errors=["Query cannot be empty", "Missing required field"],
|
||||
remediation="Provide a non-empty query string"
|
||||
)
|
||||
print(f"\n2. Failure: valid={failure.valid}")
|
||||
print(f" Errors: {failure.errors}")
|
||||
print(f" Remediation: {failure.remediation}")
|
||||
|
||||
# 3. Fluent API - building incrementally
|
||||
result = ValidationResult(valid=True)
|
||||
result.add_warning("Query is very long, may be slow")
|
||||
result.add_warning("Consider using pagination")
|
||||
print(f"\n3. With warnings: valid={result.valid}, warnings={result.warnings}")
|
||||
|
||||
# 4. Adding errors changes validity
|
||||
result.add_error("Rate limit exceeded")
|
||||
print(f"\n4. After error: valid={result.valid}, errors={result.errors}")
|
||||
|
||||
|
||||
class InputLengthValidator:
|
||||
"""Custom validator that checks string input lengths."""
|
||||
|
||||
def __init__(self, max_length: int = 100):
|
||||
self.max_length = max_length
|
||||
|
||||
def validate_args(self, tool_name, args, context=None):
|
||||
"""Validate that string arguments don't exceed max length."""
|
||||
for key, value in args.items():
|
||||
if isinstance(value, str) and len(value) > self.max_length:
|
||||
return ValidationResult.failure(
|
||||
errors=[f"Argument '{key}' ({len(value)} chars) exceeds limit ({self.max_length})"],
|
||||
remediation=f"Truncate '{key}' to {self.max_length} characters or less"
|
||||
)
|
||||
return ValidationResult.success()
|
||||
|
||||
def validate_result(self, tool_name, result, context=None):
|
||||
"""Validate tool results."""
|
||||
return ValidationResult.success()
|
||||
|
||||
|
||||
class TypeValidator:
|
||||
"""Custom validator that checks argument types."""
|
||||
|
||||
def __init__(self, type_specs: dict):
|
||||
"""
|
||||
Args:
|
||||
type_specs: Dict mapping arg names to expected types
|
||||
e.g., {"query": str, "limit": int}
|
||||
"""
|
||||
self.type_specs = type_specs
|
||||
|
||||
def validate_args(self, tool_name, args, context=None):
|
||||
result = ValidationResult(valid=True)
|
||||
|
||||
for arg_name, expected_type in self.type_specs.items():
|
||||
if arg_name in args:
|
||||
actual_value = args[arg_name]
|
||||
if not isinstance(actual_value, expected_type):
|
||||
result.add_error(
|
||||
f"Argument '{arg_name}' must be {expected_type.__name__}, "
|
||||
f"got {type(actual_value).__name__}"
|
||||
)
|
||||
|
||||
if not result.valid:
|
||||
result.remediation = "Check argument types and try again"
|
||||
|
||||
return result
|
||||
|
||||
def validate_result(self, tool_name, result, context=None):
|
||||
return ValidationResult.success()
|
||||
|
||||
|
||||
def example_custom_validators():
|
||||
"""Demonstrate custom validator implementations."""
|
||||
print("\n" + "=" * 50)
|
||||
print("Custom Validator Examples")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. InputLengthValidator
|
||||
print("\n1. InputLengthValidator:")
|
||||
validator = InputLengthValidator(max_length=50)
|
||||
|
||||
# Valid input
|
||||
result = validator.validate_args("search", {"query": "short query"})
|
||||
print(f" Short query: valid={result.valid}")
|
||||
|
||||
# Invalid input
|
||||
long_query = "x" * 100
|
||||
result = validator.validate_args("search", {"query": long_query})
|
||||
print(f" Long query: valid={result.valid}")
|
||||
print(f" Error: {result.errors[0]}")
|
||||
print(f" Fix: {result.remediation}")
|
||||
|
||||
# 2. TypeValidator
|
||||
print("\n2. TypeValidator:")
|
||||
validator = TypeValidator({"query": str, "limit": int, "offset": int})
|
||||
|
||||
# Valid types
|
||||
result = validator.validate_args("search", {"query": "test", "limit": 10})
|
||||
print(f" Correct types: valid={result.valid}")
|
||||
|
||||
# Invalid types
|
||||
result = validator.validate_args("search", {"query": 123, "limit": "ten"})
|
||||
print(f" Wrong types: valid={result.valid}")
|
||||
for error in result.errors:
|
||||
print(f" - {error}")
|
||||
|
||||
# 3. Protocol compliance check
|
||||
print("\n3. Protocol Compliance:")
|
||||
print(f" InputLengthValidator: {isinstance(InputLengthValidator(), ToolValidatorProtocol)}")
|
||||
print(f" TypeValidator: {isinstance(TypeValidator({}), ToolValidatorProtocol)}")
|
||||
print(f" PassthroughValidator: {isinstance(PassthroughValidator(), ToolValidatorProtocol)}")
|
||||
|
||||
|
||||
def example_passthrough_validator():
|
||||
"""Demonstrate the default PassthroughValidator."""
|
||||
print("\n" + "=" * 50)
|
||||
print("PassthroughValidator Example")
|
||||
print("=" * 50)
|
||||
|
||||
validator = PassthroughValidator()
|
||||
|
||||
# Always returns valid
|
||||
result = validator.validate_args("any_tool", {"any": "args"})
|
||||
print(f"\nvalidate_args: valid={result.valid}")
|
||||
|
||||
result = validator.validate_result("any_tool", {"any": "result"})
|
||||
print(f"validate_result: valid={result.valid}")
|
||||
|
||||
print("\nPassthroughValidator is useful as a default when no validation is needed.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
example_validation_result()
|
||||
example_custom_validators()
|
||||
example_passthrough_validator()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("All examples completed!")
|
||||
print("=" * 50)
|
||||
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Model Failover Example - Automatic Provider Switching
|
||||
|
||||
This example demonstrates how to configure model failover
|
||||
for reliability and cost optimization.
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import (
|
||||
AuthProfile,
|
||||
FailoverConfig,
|
||||
FailoverManager,
|
||||
)
|
||||
|
||||
# Create auth profiles for different providers
|
||||
openai_profile = AuthProfile(
|
||||
name="openai-primary",
|
||||
provider="openai",
|
||||
api_key=os.environ.get("OPENAI_API_KEY", "sk-..."),
|
||||
priority=1, # Highest priority
|
||||
rate_limit=100,
|
||||
)
|
||||
|
||||
anthropic_profile = AuthProfile(
|
||||
name="anthropic-backup",
|
||||
provider="anthropic",
|
||||
api_key=os.environ.get("ANTHROPIC_API_KEY", "sk-ant-..."),
|
||||
priority=2, # Second priority
|
||||
rate_limit=50,
|
||||
)
|
||||
|
||||
groq_profile = AuthProfile(
|
||||
name="groq-fallback",
|
||||
provider="groq",
|
||||
api_key=os.environ.get("GROQ_API_KEY", "gsk-..."),
|
||||
priority=3, # Third priority (fast but limited)
|
||||
rate_limit=30,
|
||||
)
|
||||
|
||||
# Configure failover behavior
|
||||
failover_config = FailoverConfig(
|
||||
max_retries=3,
|
||||
retry_delay=1.0,
|
||||
exponential_backoff=True,
|
||||
max_retry_delay=60.0,
|
||||
failover_on_rate_limit=True,
|
||||
failover_on_timeout=True,
|
||||
failover_on_error=True,
|
||||
)
|
||||
|
||||
# Create failover manager
|
||||
manager = FailoverManager(failover_config)
|
||||
manager.add_profile(openai_profile)
|
||||
manager.add_profile(anthropic_profile)
|
||||
manager.add_profile(groq_profile)
|
||||
|
||||
def demonstrate_failover():
|
||||
"""Demonstrate failover functionality."""
|
||||
|
||||
print("Failover Manager Status:")
|
||||
print()
|
||||
|
||||
# Get current status
|
||||
status = manager.status()
|
||||
for name, info in status.items():
|
||||
print(f" {name}:")
|
||||
print(f" Status: {info.get('status', 'unknown')}")
|
||||
print(f" Priority: {info.get('priority', 'N/A')}")
|
||||
print(f" Failures: {info.get('failure_count', 0)}")
|
||||
print()
|
||||
|
||||
# Get next available profile
|
||||
next_profile = manager.get_next_profile()
|
||||
if next_profile:
|
||||
print(f"Next profile to use: {next_profile.name}")
|
||||
print(f" Provider: {next_profile.provider}")
|
||||
print(f" Priority: {next_profile.priority}")
|
||||
print()
|
||||
|
||||
# Simulate marking a failure
|
||||
print("Simulating failure on primary provider...")
|
||||
manager.mark_failure("openai-primary", "Rate limit exceeded")
|
||||
|
||||
# Get next profile after failure
|
||||
next_profile = manager.get_next_profile()
|
||||
if next_profile:
|
||||
print(f"After failure, next profile: {next_profile.name}")
|
||||
print()
|
||||
|
||||
# Get retry delay
|
||||
delay = manager.get_retry_delay("openai-primary")
|
||||
print(f"Retry delay for openai-primary: {delay}s")
|
||||
print()
|
||||
|
||||
# Reset all providers
|
||||
print("Resetting all providers...")
|
||||
manager.reset_all()
|
||||
|
||||
next_profile = manager.get_next_profile()
|
||||
if next_profile:
|
||||
print(f"After reset, next profile: {next_profile.name}")
|
||||
|
||||
def cost_optimization_example():
|
||||
"""Example of cost-optimized failover."""
|
||||
|
||||
print("\nCost Optimization Example:")
|
||||
print("-" * 40)
|
||||
|
||||
# Create cost-optimized manager
|
||||
cost_manager = FailoverManager()
|
||||
|
||||
# Cheaper model first
|
||||
cost_manager.add_profile(AuthProfile(
|
||||
name="gpt-4o-mini",
|
||||
provider="openai",
|
||||
api_key=os.environ.get("OPENAI_API_KEY", ""),
|
||||
priority=1,
|
||||
metadata={"model": "gpt-4o-mini", "cost_per_1k": 0.00015}
|
||||
))
|
||||
|
||||
# Premium model as fallback
|
||||
cost_manager.add_profile(AuthProfile(
|
||||
name="gpt-4o",
|
||||
provider="openai",
|
||||
api_key=os.environ.get("OPENAI_API_KEY", ""),
|
||||
priority=2,
|
||||
metadata={"model": "gpt-4o", "cost_per_1k": 0.005}
|
||||
))
|
||||
|
||||
# Most expensive as last resort
|
||||
cost_manager.add_profile(AuthProfile(
|
||||
name="claude-opus",
|
||||
provider="anthropic",
|
||||
api_key=os.environ.get("ANTHROPIC_API_KEY", ""),
|
||||
priority=3,
|
||||
metadata={"model": "claude-3-opus", "cost_per_1k": 0.015}
|
||||
))
|
||||
|
||||
print("Cost-optimized failover chain:")
|
||||
for profile in [
|
||||
cost_manager.get_profile("gpt-4o-mini"),
|
||||
cost_manager.get_profile("gpt-4o"),
|
||||
cost_manager.get_profile("claude-opus"),
|
||||
]:
|
||||
if profile:
|
||||
cost = profile.metadata.get("cost_per_1k", "N/A")
|
||||
print(f" {profile.priority}. {profile.name} (${cost}/1K tokens)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Model Failover Configuration")
|
||||
print("=" * 40)
|
||||
print()
|
||||
|
||||
print("Failover Config:")
|
||||
print(f" Max retries: {failover_config.max_retries}")
|
||||
print(f" Initial delay: {failover_config.retry_delay}s")
|
||||
print(f" Exponential backoff: {failover_config.exponential_backoff}")
|
||||
print(f" Max delay: {failover_config.max_retry_delay}s")
|
||||
print()
|
||||
|
||||
print("Auth Profiles:")
|
||||
for profile in [openai_profile, anthropic_profile, groq_profile]:
|
||||
print(f" {profile.name}:")
|
||||
print(f" Provider: {profile.provider}")
|
||||
print(f" Priority: {profile.priority}")
|
||||
print(f" Rate limit: {profile.rate_limit} req/min")
|
||||
print()
|
||||
|
||||
demonstrate_failover()
|
||||
cost_optimization_example()
|
||||
File renamed without changes.
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Gateway Example - Multi-Agent Coordination
|
||||
|
||||
This example demonstrates how to use the Gateway for coordinating
|
||||
multiple agents and managing real-time communication.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, GatewayConfig, SessionConfig
|
||||
|
||||
# Configure the gateway
|
||||
gateway_config = GatewayConfig(
|
||||
host="127.0.0.1",
|
||||
port=8765,
|
||||
max_connections=100,
|
||||
heartbeat_interval=30,
|
||||
session_config=SessionConfig(
|
||||
timeout=3600, # 1 hour session timeout
|
||||
max_messages=500,
|
||||
)
|
||||
)
|
||||
|
||||
# Create specialized agents
|
||||
researcher = Agent(
|
||||
name="researcher",
|
||||
instructions="You research topics thoroughly and provide detailed information.",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="writer",
|
||||
instructions="You write clear, engaging content based on research.",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Example: Simple agent interaction
|
||||
if __name__ == "__main__":
|
||||
print("Gateway Configuration:")
|
||||
print(f" Host: {gateway_config.host}")
|
||||
print(f" Port: {gateway_config.port}")
|
||||
print(f" WebSocket URL: {gateway_config.ws_url}")
|
||||
print(f" Max Connections: {gateway_config.max_connections}")
|
||||
print()
|
||||
|
||||
# Test agent
|
||||
response = researcher.start("What are the key benefits of multi-agent systems?")
|
||||
print("Researcher Response:")
|
||||
print(response)
|
||||
@@ -5,7 +5,7 @@ This example demonstrates callback systems using PraisonAI's built-in
|
||||
callback functionality for monitoring agent interactions and tool usage.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, Agents
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.display_callback import register_display_callback
|
||||
from praisonaiagents.tools import internet_search
|
||||
|
||||
|
||||
Loaded 100 of 1101 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user