Compare commits

..
Author SHA1 Message Date
MervinPraison 3929d4fbce Release v4.5.127 2026-04-09 10:34:44 +01:00
MervinPraison c9e5023832 feat: PraisonAI PR Reviewer Integration setup 2026-04-09 10:33:35 +01:00
1175 changed files with 20797 additions and 143944 deletions

No files matched your search

-51
View File
@@ -1,51 +0,0 @@
# PraisonAI Environment Variables
# Copy this file to .env and update with your actual values
# === Required ===
OPENAI_API_KEY=sk-... # OpenAI key for LLM calls. Get yours at https://platform.openai.com
# === Required for Claw web-search tool ===
TAVILY_API_KEY=tvly-... # Get yours at https://app.tavily.com (free tier available)
# === Optional: Claw dashboard security ===
PRAISONAI_ALLOW_LOCAL_TOOLS=0 # Set to 1 to enable local shell tools (security risk)
# === Optional: Alternative LLM Providers ===
ANTHROPIC_API_KEY=sk-ant-... # Anthropic Claude models
GOOGLE_API_KEY=... # Google Gemini models
GROQ_API_KEY=gsk_... # Groq (fast inference)
COHERE_API_KEY=... # Cohere models
AZURE_OPENAI_API_KEY=... # Azure OpenAI
AZURE_OPENAI_ENDPOINT=... # Azure OpenAI endpoint
# === Optional: Memory backends ===
MEM0_API_KEY=... # Mem0 memory service
REDIS_URL=redis://localhost:6379 # Redis for state management
# === Optional: Observability ===
LANGFUSE_SECRET_KEY=sk-lf-... # Langfuse tracing
LANGFUSE_PUBLIC_KEY=pk-lf-... # Langfuse public key
# === Optional: Bot platforms ===
TELEGRAM_BOT_TOKEN=... # Telegram bot token
DISCORD_BOT_TOKEN=... # Discord bot token
SLACK_BOT_TOKEN=xoxb-... # Slack bot token
SLACK_APP_TOKEN=xapp-... # Slack app token
WHATSAPP_ACCESS_TOKEN=... # WhatsApp bot access token
WHATSAPP_PHONE_NUMBER_ID=... # WhatsApp phone number ID
# === Optional: Database ===
DATABASE_URL=sqlite:///~/.praison/database.sqlite # Database connection string
# === Optional: Tool Security ===
# ALLOWED_TOOLS=search,send_message # Whitelist specific tools to prevent name collisions
# Useful in multi-environment setups with overlapping tool names
# Leave unset to use all tools (with collision warnings)
# Do NOT set to empty string: that's treated as configuration error
# In CI, unknown tool names in the whitelist cause strict startup failure
# HERMES_ONLY_TOOLS=search,send_message # (Deprecated) Backward compatibility alias for ALLOWED_TOOLS
# === Optional: Development ===
LOGLEVEL=INFO # Logging level (DEBUG, INFO, WARNING, ERROR)
CHAINLIT_AUTH_SECRET=... # Chainlit authentication secret
DEBUG=false # Enable debug mode
@@ -19,7 +19,6 @@ runs:
- name: Checkout repository code
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Create prompt
Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

-72
View File
@@ -1,72 +0,0 @@
framework: "praisonai"
process: workflow
topic: "Autonomous Issue Resolution"
planning: false
roles:
issue_analyst:
role: "GitHub Issue Analyst"
goal: "Read the GitHub issue, understand the required changes, and map the problem to the local codebase"
backstory: "You are an expert software analyst who reads bug reports and feature requests. You excel at taking a problem description and finding exactly which files in the codebase need to be modified. Your primary job is discovery and planning."
tools:
- "execute_command"
software_engineer:
role: "Autonomous Software Engineer"
goal: "Implement required code changes on a branch, run tests, commit, and push"
backstory: "You are a senior software engineer. You always branch first, implement changes using shell commands, verify with git diff, run tests, then commit and push. You never create a Pull Request unless the issue body explicitly requests one."
tools:
- "execute_command"
- "github_create_branch"
- "github_commit_and_push"
- "github_create_pull_request"
steps:
- name: Analyze Issue
agent: issue_analyst
action: >
Thoroughly analyze the issue and the repository. Follow these steps in order.
First, set up git identity: execute_command 'git config user.name PraisonAI' then
execute_command 'git config user.email [email protected]'. Do NOT pass cwd.
Second, read the issue: execute_command 'cat .github/triage-context.md'. Do NOT pass cwd.
Third, read architecture guidelines: execute_command 'cat src/praisonai-agents/AGENTS.md'.
Fourth, decide if this issue requires code changes:
- If YES: identify the exact files and lines that must change. Output a concrete file-level plan.
- If NO (question, discussion, duplicate, invalid): say so clearly. No code changes needed.
Output: routing decision (Core SDK / Wrapper / Tools / No-change), validation result, and exact files/lines to change.
expected_output: A decision on whether code changes are needed, and if so a concrete file-level plan.
- name: Implement Fix
agent: software_engineer
action: >
You are working inside the repository at /Users/praison/praisonai-package.
Do NOT ask for confirmation. Do NOT use placeholder paths. Act immediately.
Read the analyst's output first. If the analyst said NO code changes are needed, stop here and
output a brief explanation — do NOT create a branch or PR.
If code changes ARE needed, follow these steps:
Step 1 — Create branch: github_create_branch with branch_name=praisonai/issue-{{ISSUE_NUMBER}}.
Step 2 — Implement changes using execute_command (no cwd argument).
To insert a line: python3 -c "lines=open('FILE').readlines(); lines.insert(N,'LINE\n'); open('FILE','w').writelines(lines)"
To replace text: python3 -c "t=open('FILE').read(); open('FILE','w').write(t.replace('OLD','NEW'))"
After each edit verify: execute_command 'git diff'
Step 3 — Run tests: execute_command 'python3 -m pytest src/praisonai-agents/tests/ -x -q --timeout=30 -p no:warnings'
Step 4 — Commit and push: github_commit_and_push with a descriptive commit_message.
Step 5 — Check if the issue body EXPLICITLY contains the phrase "create pull request" or "create PR"
(case-insensitive). ONLY in that case create a PR using github_create_pull_request with
title='fix: resolve issue #{{ISSUE_NUMBER}}',
body='Fixes #{{ISSUE_NUMBER}}.\n\nAutonomously resolved by PraisonAI.',
head_branch=praisonai/issue-{{ISSUE_NUMBER}}, base_branch=main.
Otherwise do NOT create a PR — just confirm the branch was pushed successfully.
expected_output: Either "No code changes needed", or "Branch pushed, no PR requested", or "Branch pushed and PR created at URL".
+9 -114
View File
@@ -1,116 +1,11 @@
framework: "praisonai"
topic: "Pull Request Code Review Analysis"
framework: praisonai
topic: Pull Request Review
roles:
security_reviewer:
role: "Security Code Reviewer"
goal: "Identify security vulnerabilities, authentication issues, input validation problems, and unsafe code practices in the PR changes"
backstory: "You are a cybersecurity expert specializing in code analysis. You have extensive experience in identifying common security flaws like injection attacks, authentication bypasses, exposed secrets, and unsafe data handling patterns."
tools:
- "execute_command"
performance_reviewer:
role: "Performance Code Reviewer"
goal: "Analyze code changes for performance implications, identify bottlenecks, inefficient algorithms, and resource usage issues"
backstory: "You are a performance optimization specialist with deep knowledge of algorithm efficiency, memory management, and system performance. You excel at spotting code that could cause performance degradation."
tools:
- "execute_command"
maintainability_reviewer:
role: "Code Quality & Maintainability Reviewer"
goal: "Evaluate code structure, readability, documentation, naming conventions, and adherence to best practices"
backstory: "You are a software engineering expert focused on code quality and maintainability. You ensure code follows established patterns, is well-documented, and will be easy for future developers to understand and modify."
tools:
- "execute_command"
lead_reviewer:
role: "Lead Technical Reviewer"
goal: "Synthesize all review feedback, make final recommendations, and post comprehensive review comments to the GitHub PR"
backstory: "You are a senior technical lead responsible for final review decisions. You coordinate input from security, performance, and maintainability reviewers to provide balanced, actionable feedback to the development team."
tools:
- "execute_command"
steps:
- name: security_analysis
agent: security_reviewer
action: |
Analyze the PR diff for security issues:
1. Extract PR details: `echo "$PR_DATA"`
2. Get changed files: `echo "$CHANGED_FILES"`
3. Review full diff: `gh pr diff ${PR_NUMBER}`
4. Look for:
- Hardcoded secrets, API keys, passwords
- SQL injection vulnerabilities
- XSS vulnerabilities
- Authentication/authorization bypasses
- Unsafe file operations
- Command injection risks
- Missing input validation
- Exposed sensitive data
5. Document findings with file paths and line numbers
expected_output: "Detailed security analysis report with specific vulnerabilities found, severity levels, and remediation suggestions"
- name: performance_analysis
agent: performance_reviewer
action: |
Analyze the PR diff for performance issues:
1. Review algorithm complexity changes
2. Check for inefficient database queries
3. Identify memory leaks or excessive allocations
4. Look for hot-path regressions
5. Check for blocking I/O operations
6. Analyze module-level imports that could slow startup
7. Review caching implementations
8. Check for recursive operations without termination
9. Document performance concerns with impact estimates
expected_output: "Performance analysis report highlighting bottlenecks, efficiency concerns, and optimization recommendations"
- name: maintainability_analysis
agent: maintainability_reviewer
action: |
Analyze the PR diff for code quality and maintainability:
1. Check naming conventions consistency
2. Evaluate code structure and organization
3. Review documentation completeness
4. Check for DRY violations
5. Analyze error handling patterns
6. Review test coverage for changes
7. Check adherence to repository coding standards
8. Evaluate API design consistency
9. Look for technical debt introduction
expected_output: "Code quality analysis with specific recommendations for improving maintainability and readability"
- name: final_review
agent: lead_reviewer
action: |
Synthesize all reviews and post final comprehensive feedback:
1. Collect input from security, performance, and maintainability reviews
2. Prioritize findings by severity (Critical, High, Medium, Low)
3. Create structured review comment using this format:
## 📋 Review Summary
[2-3 sentence overview and assessment]
## 🔍 General Feedback
[Overall patterns and observations]
## 🎯 Specific Feedback
### 🔴 Critical (if any)
[Security vulnerabilities, breaking changes, major bugs]
### 🟡 High (if any)
[Performance issues, design flaws, significant bugs]
### 🟢 Medium (if any)
[Code quality improvements, minor optimizations]
### 🔵 Low (if any)
[Documentation, naming suggestions, minor refactoring]
## ✅ Highlights (if any)
[Positive aspects worth mentioning]
4. Post comprehensive review: `echo "[REVIEW_CONTENT]" | gh pr comment ${PR_NUMBER} -F -`
5. If critical issues found, suggest not merging until resolved
expected_output: "Final review comment posted to GitHub PR with comprehensive analysis from all reviewers"
dependencies: [security_analysis, performance_analysis, maintainability_analysis]
role: Lead Code Reviewer
goal: Provide a comprehensive and constructive review of the pull request
backstory: You are an expert software engineer with deep knowledge of best practices, security, and performance. You meticulously review code for bugs and maintainability.
tasks:
code_review:
description: Review the code changes in the pull request. Look for logic errors, formatting issues, security flaws, and performance bottlenecks. Output the review as a detailed markdown comment.
expected_output: A detailed PR review in markdown format.
-3
View File
@@ -1,3 +0,0 @@
# GitHub Issue #1350: Add debug log on successful branch checkout
The github_create_branch function in src/praisonai-agents/praisonaiagents/tools/github_tools.py should call logger.debug after a successful checkout.
+11 -91
View File
@@ -31,7 +31,7 @@ jobs:
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.comment.user.login == 'coderabbitai[bot]' &&
contains(github.event.comment.body, 'summarize by coderabbit')
contains(github.event.comment.body, 'summary by coderabbit')
runs-on: ubuntu-latest
permissions:
pull-requests: write
@@ -112,86 +112,6 @@ jobs:
core.setOutput('triggered', 'true');
core.setOutput('pr_number', context.issue.number.toString());
# ================================================================
# FALLBACK: Claude trigger for cases where Copilot chain fails/skips
# Ensures Claude always runs after sufficient review time
# ================================================================
claude-fallback-timeout:
if: |
github.event_name == 'pull_request' &&
github.event.action == 'opened'
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
issues: write
pull-requests: write
steps:
- name: Wait for review window (15 min)
run: sleep 900
- name: Check if Claude already triggered
id: check_claude
uses: actions/github-script@v7
env:
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
with:
github-token: ${{ secrets.GH_TOKEN }}
script: |
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
const comments = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});
const alreadyPosted = comments.data.some(c =>
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
);
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
core.setOutput('comments_count', comments.data.length.toString());
// Collect all bot reviewer comments for context
const botReviews = comments.data.filter(c =>
['coderabbitai[bot]', 'qodo-code-review[bot]', 'gemini-code-assist[bot]',
'copilot-swe-agent', 'Copilot', 'greptile-apps[bot]'].some(bot =>
c.user.login.toLowerCase().includes(bot.toLowerCase())
)
);
core.setOutput('review_count', botReviews.length.toString());
- name: Aggregate reviews and trigger Claude
if: steps.check_claude.outputs.already_triggered != 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_TOKEN }}
script: |
const comments = await github.rest.issues.listComments({issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, per_page: 100});
const reviews = {coderabbit: [], qodo: [], gemini: [], copilot: [], greptile: [], others: []};
comments.data.forEach(c => {
const login = c.user.login.toLowerCase();
const body = c.body.substring(0, 500);
if (login.includes('coderabbit')) reviews.coderabbit.push(body);
else if (login.includes('qodo')) reviews.qodo.push(body);
else if (login.includes('gemini')) reviews.gemini.push(body);
else if (login.includes('copilot')) reviews.copilot.push(body);
else if (login.includes('greptile')) reviews.greptile.push(body);
else if (c.user.type === 'Bot') reviews.others.push(body);
});
const summaryParts = [];
if (reviews.coderabbit.length) summaryParts.push('CodeRabbit: ' + reviews.coderabbit.length + ' comments');
if (reviews.qodo.length) summaryParts.push('Qodo: ' + reviews.qodo.length + ' comments');
if (reviews.gemini.length) summaryParts.push('Gemini: ' + reviews.gemini.length + ' comments');
if (reviews.copilot.length) summaryParts.push('Copilot: ' + reviews.copilot.length + ' comments');
if (reviews.greptile.length) summaryParts.push('Greptile: ' + reviews.greptile.length + ' comments');
const reviewSummary = summaryParts.length ? summaryParts.join(' | ') : 'No bot reviews detected';
const claudeBody = '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. ' + reviewSummary + '.\n\n' +
'Review Context: ' + comments.data.length + ' total comments, ' +
(reviews.coderabbit.length + reviews.qodo.length + reviews.gemini.length + reviews.copilot.length + reviews.greptile.length) + ' bot reviews.\n\n' +
'Phase 1: Review per AGENTS.md principles (protocol-driven, backward compatible, no perf impact).\n' +
'Phase 2: FIX valid issues found by prior reviewers.\n' +
'Phase 3: Provide final verdict (approve or request changes).';
await github.rest.issues.createComment({issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: claudeBody});
console.log('Claude fallback triggered on PR #' + context.issue.number + ' with ' + reviewSummary);
# ================================================================
# Claude FINAL review — polls for Copilot's response, then triggers
# This runs IN-BAND under the PAT context (MervinPraison), avoiding
@@ -313,7 +233,7 @@ jobs:
issue_number: pr,
owner: context.repo.owner,
repo: context.repo.repo,
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
body: '@claude You are the FINAL architecture reviewer. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
});
console.log(`Claude triggered on PR #${pr}`);
@@ -417,7 +337,7 @@ jobs:
issue_number: pr,
owner: context.repo.owner,
repo: context.repo.repo,
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
body: '@claude You are the FINAL architecture reviewer. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
});
console.log(`Claude triggered on PR #${pr}`);
@@ -468,7 +388,7 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '@claude You are the Lead Engineer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL analysis and reviews above carefully (Gemini, CodeRabbit, Qodo, Copilot, etc).\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
body: '@claude You are the Lead Engineer. Read ALL analysis and reviews above carefully (Gemini, CodeRabbit, Qodo, Copilot, etc).\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
});
# ================================================================
@@ -489,7 +409,7 @@ jobs:
permissions:
pull-requests: write
steps:
- name: Trigger CodeRabbit and Qodo reviews
- name: Trigger CodeRabbit, Qodo, and Gemini reviews
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_TOKEN }}
@@ -512,12 +432,12 @@ jobs:
});
console.log(`Triggered Qodo for bot PR #${pr}`);
// 3. Trigger Gemini review (commented out)
// await github.rest.issues.createComment({
// issue_number: pr, owner, repo,
// body: '@gemini review this PR'
// });
// console.log(`Triggered Gemini for bot PR #${pr}`);
// 3. Trigger Gemini review
await github.rest.issues.createComment({
issue_number: pr, owner, repo,
body: '@gemini review this PR'
});
console.log(`Triggered Gemini for bot PR #${pr}`);
// NOTE: @copilot is NOT triggered here.
// It will be triggered by copilot-after-coderabbit or copilot-after-qodo
-2
View File
@@ -20,8 +20,6 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@v5
-2
View File
@@ -18,8 +18,6 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
+4 -14
View File
@@ -175,21 +175,17 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Fetch PR branch and Setup Remote
if: github.event.issue.pull_request
env:
PR_BRANCH: ${{ steps.check_fork.outputs.pr_branch }}
IS_FORK: ${{ steps.check_fork.outputs.is_fork }}
run: |
# Fetch PR head from base repo and put it into local branch
git fetch origin pull/${{ github.event.issue.number }}/head:"${PR_BRANCH}"
git fetch origin pull/${{ github.event.issue.number }}/head:${{ steps.check_fork.outputs.pr_branch }}
# If it's a fork, make origin point to local to trick claude-code-action's `git fetch origin <branch>`
if [ "${IS_FORK}" = "true" ]; then
if [ "${{ steps.check_fork.outputs.is_fork }}" == "true" ]; then
git remote set-url origin file://$(pwd)
fi
@@ -204,7 +200,7 @@ jobs:
label_trigger: "claude"
direct_prompt: |
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK. YOU DO NOT HAVE PUSH PERMISSIONS TO THIS REPOSITORY. ONLY READ FILES, VALIDATE CODE, AND PROVIDE YOUR COMMENTS/FEEDBACK DIRECTLY IN THIS PR VIA COMMENTS. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || 'NOTE: The branch is under MervinPraison/PraisonAI (not a fork). You are able to make modifications and push directly to this branch.\n\n' }}
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK. YOU DO NOT HAVE PUSH PERMISSIONS TO THIS REPOSITORY. ONLY READ FILES, VALIDATE CODE, AND PROVIDE YOUR COMMENTS/FEEDBACK DIRECTLY IN THIS PR VIA COMMENTS. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || '' }}
You are working on the PraisonAI SDK. Follow AGENTS.md strictly.
STEP 0 — SETUP GIT IDENTITY & AUTH (GLOBAL — required for all repos):
@@ -215,7 +211,6 @@ jobs:
STEP 1 — READ GUIDELINES:
Read AGENTS.md to understand the architecture rules.
SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify or touch praisonai-rust or praisonai-ts packages.
STEP 2 — ARCHITECTURE VALIDATION & ROUTING (MANDATORY before writing code):
Before implementing anything, answer these questions:
@@ -234,14 +229,9 @@ jobs:
If ANY of these conceptual checks fail (excluding routing), add a comment to the issue explaining why and close it. Do NOT create a PR.
STEP 3 — IMPLEMENT (for changes in THIS repo — PraisonAI):
- Create a fix branch: git checkout -b claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
- Implement a minimal, focused fix
- Create a fix branch and implement a minimal, focused fix
- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper
- Keep changes small and backward-compatible
- Commit: git add -A && git commit -m "fix: <description> (fixes #$ISSUE_NUMBER)"
- Push: git push origin claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
CRITICAL: After pushing, you MUST immediately run: gh pr create --title "fix: <title>" --body "Fixes #$ISSUE_NUMBER" --head claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
Do NOT stop after pushing. Do NOT say "manually create a PR". Always create the PR automatically.
STEP 3-ALT — IMPLEMENT IN EXTERNAL REPO (for PraisonAI-Tools, PraisonAIDocs, PraisonAI-Plugins):
When work must happen in a different repository, follow these steps EXACTLY. Do NOT attempt to use `cd`, as directory state is not preserved. Use `git -C` for all commands.
-4
View File
@@ -20,10 +20,6 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -34,10 +34,6 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
@@ -35,10 +35,6 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
-4
View File
@@ -46,10 +46,6 @@ jobs:
- name: Checkout PR code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
ref: ${{ steps.checkout_ref.outputs.ref }}
@@ -1,158 +0,0 @@
name: Cross-Repo Integration Tests
on:
push:
branches: [ main ]
paths:
- 'src/praisonai/praisonai/integration/**'
- 'src/praisonai/tests/integration/test_aiui_*'
- 'src/praisonai/pyproject.toml'
pull_request:
branches: [ main ]
paths:
- 'src/praisonai/praisonai/integration/**'
- 'src/praisonai/tests/integration/test_aiui_*'
- 'src/praisonai/pyproject.toml'
workflow_dispatch:
jobs:
cross-repo-integration:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout PraisonAI
uses: actions/checkout@v4
with:
path: PraisonAI
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Checkout PraisonAIUI
uses: actions/checkout@v4
with:
repository: MervinPraison/PraisonAIUI
path: PraisonAIUI
ref: main
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Install PraisonAI packages in development mode
cd PraisonAI/src/praisonai-agents
pip install -e .
cd ../praisonai
pip install -e ".[ui,dev]"
# Install PraisonAIUI in development mode
cd ../../../PraisonAIUI
pip install -e .
- name: Verify versions
run: |
python -c "import praisonaiagents; print(f'praisonaiagents: {praisonaiagents.__version__}')"
python -c "import praisonai; print(f'praisonai: {praisonai.__version__}')"
python -c "import praisonaiui; print(f'praisonaiui: {praisonaiui.__version__}')"
- name: Run PraisonAI integration tests
run: |
cd PraisonAI/src/praisonai
python -m pytest tests/integration/test_aiui_* -v --timeout=300
- name: Run PraisonAIUI integration tests
run: |
cd PraisonAIUI
if [ -f tests/integration/test_agentic_roundtrip.py ]; then
python -m pytest tests/integration/test_agentic_roundtrip.py -v --timeout=300
fi
if [ -f tests/test_feature_sdk_backends.py ]; then
python -m pytest tests/test_feature_sdk_backends.py -v --timeout=300
fi
- name: Test Pattern C CLI
run: |
cd PraisonAI/src/praisonai
# Test that ui-gateway command exists
python -m praisonai serve --help | grep ui-gateway
- name: Test public API exports
run: |
cd PraisonAI/src/praisonai
python -c "from praisonai import run_integrated_gateway; print('✓ run_integrated_gateway imported')"
python -c "from praisonai import configure_host; print('✓ configure_host imported')"
- name: Integration smoke test
env:
PRAISONAI_TEST_MODE: "1"
PYTHONPATH: ${{ github.workspace }}/PraisonAI/src/praisonai:${{ github.workspace }}/PraisonAI/src/praisonai-agents:${{ github.workspace }}/PraisonAIUI
run: |
cd PraisonAI/src/praisonai
python -c "
import sys
sys.path.insert(0, '.')
from praisonai.integration.host_app import configure_host
from praisonai import run_integrated_gateway
print('✓ Integration imports successful')
# Test configure_host with new parameters
try:
configure_host(
style='dashboard',
context_paths=['AGENTS.md'],
title='Test App'
)
print('✓ configure_host with new parameters works')
except Exception as e:
print(f'× configure_host failed: {e}')
sys.exit(1)
"
# Optional job with API key for agentic tests (if secret is available)
agentic-integration:
runs-on: ubuntu-latest
timeout-minutes: 30
if: ${{ vars.RUN_AGENTIC_TESTS == 'true' }}
needs: cross-repo-integration
steps:
- name: Checkout PraisonAI
uses: actions/checkout@v4
with:
path: PraisonAI
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Checkout PraisonAIUI
uses: actions/checkout@v4
with:
repository: MervinPraison/PraisonAIUI
path: PraisonAIUI
ref: main
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
cd PraisonAI/src/praisonai-agents
pip install -e .
cd ../praisonai
pip install -e ".[ui,dev]"
cd ../../../PraisonAIUI
pip install -e .
- name: Run agentic integration test
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
cd PraisonAI/src/praisonai
if [ -n "$OPENAI_API_KEY" ]; then
python -m pytest tests/integration/test_aiui_host_agentic.py -v --timeout=600
else
echo "OPENAI_API_KEY not available, skipping agentic tests"
fi
@@ -1,71 +0,0 @@
name: PraisonAI Issue Triage
on:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
issue_number:
description: 'Issue Number'
required: true
type: string
jobs:
triage:
runs-on: ubuntu-latest
if: >
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issue_comment' &&
!github.event.issue.pull_request &&
contains(github.event.comment.body, '@praisonai') &&
github.actor != 'github-actions[bot]' &&
github.event.comment.user.type != 'Bot' &&
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'))
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Generate GitHub App Token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.PRAISONAI_APP_ID }}
private_key: ${{ secrets.PRAISONAI_APP_PRIVATE_KEY }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Git
run: |
git config --local user.email "[email protected]"
git config --local user.name "PraisonAI Bot"
- name: Install PraisonAI
run: |
pip install uv
cd src/praisonai-agents && uv pip install --system -e ".[all]"
cd ../praisonai && uv pip install --system -e ".[all]"
- name: Run PraisonAI Issue Triage
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
PRAISONAI_AUTO_APPROVE: "true"
ISSUE_NUMBER_INPUT: ${{ inputs.issue_number }}
ISSUE_NUMBER_EVENT: ${{ github.event.issue.number }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
export ISSUE_NUMBER="$ISSUE_NUMBER_INPUT"
else
export ISSUE_NUMBER="$ISSUE_NUMBER_EVENT"
fi
praisonai github triage --issue $ISSUE_NUMBER --agent-file .github/praisonai-issue-triage.yaml
+5 -38
View File
@@ -5,57 +5,24 @@ on:
types: [opened, synchronize, reopened, ready_for_review]
issue_comment:
types: [created]
workflow_dispatch:
inputs:
pr_number:
description: 'Pull Request Number'
required: true
type: string
jobs:
review:
runs-on: ubuntu-latest
if: >
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '@praisonai') &&
github.actor != 'github-actions[bot]' &&
github.event.comment.user.type != 'Bot' &&
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'))
if: github.event.pull_request.draft == false && (github.event_name == 'pull_request' || contains(github.event.comment.body, '@praisonai'))
steps:
- name: Determine checkout ref
id: dest
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
PR_NUMBER_INPUT: ${{ inputs.pr_number }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if [ "${{ github.event_name }}" = "issue_comment" ]; then
echo "ref=refs/pull/$ISSUE_NUMBER/head" >> "$GITHUB_OUTPUT"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "ref=refs/pull/$PR_NUMBER_INPUT/head" >> "$GITHUB_OUTPUT"
else
echo "ref=$PR_HEAD_SHA" >> "$GITHUB_OUTPUT"
fi
- name: Checkout Repository
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ steps.dest.outputs.ref }}
fetch-depth: 0
- name: Generate GitHub App Token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.PRAISONAI_APP_ID }}
private_key: ${{ secrets.PRAISONAI_APP_PRIVATE_KEY }}
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- name: Set up Python
uses: actions/setup-python@v5
@@ -63,11 +30,11 @@ jobs:
python-version: '3.11'
- name: Install PraisonAI
run: pip install "praisonai[all]"
run: pip install praisonaiagents[all]
- name: Run PraisonAI PR Review
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
praisonai workflow run .github/praisonai-reviewer.yaml
praisonai agents --file .github/praisonai-reviewer.yaml
+1 -2
View File
@@ -15,8 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
-2
View File
@@ -22,8 +22,6 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
-12
View File
@@ -26,10 +26,6 @@ jobs:
# First checkout the upstream repo at the specified tag
- name: Checkout upstream repo
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
repository: MervinPraison/PraisonAI
ref: ${{ github.event.inputs.tag }}
@@ -48,10 +44,6 @@ jobs:
# Now checkout the fork at main branch
- name: Checkout fork
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
ref: main
@@ -449,10 +441,6 @@ jobs:
# First checkout the fork's main branch
- name: Checkout fork repo
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
-4
View File
@@ -37,10 +37,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+1 -2
View File
@@ -22,8 +22,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
-8
View File
@@ -13,10 +13,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
@@ -81,10 +77,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
-4
View File
@@ -33,10 +33,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+2 -16
View File
@@ -45,8 +45,7 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@@ -105,8 +104,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
@@ -187,8 +185,6 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -259,8 +255,6 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -318,8 +312,6 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -377,8 +369,6 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -436,8 +426,6 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -495,8 +483,6 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
-4
View File
@@ -45,10 +45,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+1 -14
View File
@@ -26,8 +26,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@@ -176,18 +175,6 @@ jobs:
run: praisonai queue list
continue-on-error: true
# ==================== PACKAGING TESTS ====================
- name: Test packaging/daemon entry points
run: |
echo "Testing both entry point methods..."
python -m praisonai --version
praisonai --version
- name: Test doctor packaging command
run: |
echo "Testing doctor packaging diagnostics..."
praisonai doctor packaging --json
# ==================== REAL API TESTS ====================
- name: Real API test - Agent run
if: env.OPENAI_API_KEY != ''
+1 -2
View File
@@ -94,8 +94,7 @@ Cargo.lock
**/*.rs.bk
*.pdb
.windsurf
.agent
src/praisonai/build*.egg-info/
src/praisonai/PraisonAI.egg-info/
jobs/*
*.egg-info
-14
View File
@@ -1,14 +0,0 @@
# Contributing to PraisonAI
Thank you for your interest! Here's how to get started:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
## Guidelines
- Follow existing code style
- Add tests for new features
- Update documentation as needed
+25
View File
@@ -0,0 +1,25 @@
# PraisonAI PR Reviewer Setup Guide
This guide explains how to set up PraisonAI as an automated pull request reviewer using GitHub Actions and GitHub Apps.
## Prerequisites
1. A GitHub App created within your organization or account.
2. The App must have the following permissions:
- Pull Requests: Read & Write
- Issues: Read & Write
- Contents: Read
3. Generate a Private Key for the GitHub App.
## Setup Steps
1. Configure GitHub Secrets for your repository:
- `APP_ID`: The App ID of your GitHub App.
- `PRIVATE_KEY`: The generated Private Key (PEM format).
- `OPENAI_API_KEY` (or other LLM key) for PraisonAI to use.
2. Ensure `.github/workflows/praisonai-pr-review.yml` is present in your default branch.
3. Customize `.github/praisonai-reviewer.yaml` to configure the reviewing agents with specific roles.
## Triggering the Review
The review will run automatically upon PR creation and synchronization. You can also trigger it manually by commenting `@praisonai` on any pull request or issue.
+4 -59
View File
@@ -23,11 +23,7 @@
</div>
PraisonAI 🦞 — **Hire a 24/7 AI Workforce.** Stop writing boilerplate and start shipping autonomous, self-improving agents that research, plan, and execute tasks across your apps. From one agent to an entire organization, deployed in 5 lines of code.
```bash
curl -fsSL https://praison.ai/install.sh | bash
```
PraisonAI 🦞 — **Hire a 24/7 AI Workforce.** Stop writing boilerplate and start shipping autonomous agents that research, plan, and execute tasks across your apps. From one agent to an entire organization, deployed in 5 lines of code.
<div align="center">
<br>
@@ -294,35 +290,8 @@ def search(query: str) -> str:
@tool
def calculate(expression: str) -> float:
"""Safely evaluate a numeric arithmetic expression."""
import ast
import operator
# Define allowed operations
_OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
def _safe_eval(node):
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
elif isinstance(node, ast.BinOp) and type(node.op) in _OPS:
return _OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
elif isinstance(node, ast.UnaryOp) and type(node.op) in _OPS:
return _OPS[type(node.op)](_safe_eval(node.operand))
else:
raise ValueError("Unsupported expression")
try:
return _safe_eval(ast.parse(expression, mode="eval").body)
except (ValueError, SyntaxError, TypeError, ZeroDivisionError, OverflowError):
raise ValueError("Invalid arithmetic expression")
"""Evaluate a math expression."""
return eval(expression)
agent = Agent(
instructions="You are a helpful assistant",
@@ -331,7 +300,6 @@ agent = Agent(
agent.start("Search for AI news and calculate 15*4")
```
> ⚠️ **Security Note:** Never use `eval()`, `exec()`, or `subprocess` in tool functions that process LLM-generated or user-supplied input. Always validate and sanitize inputs to prevent code injection attacks.
> 📖 [Full tools docs](https://docs.praison.ai/docs/tools/tools) — BaseTool, tool packages, 100+ built-in tools
### 5. Persistence (Databases)
@@ -358,15 +326,6 @@ pip install "praisonai[claw]"
praisonai claw
```
#### Required Environment Variables
Copy `.env.example` to `.env` and configure the following variables:
| Variable | Required | Description |
|----------|----------|-------------|
| `OPENAI_API_KEY` | Yes | OpenAI API key for all LLM calls |
| `TAVILY_API_KEY` | Yes (Claw) | Tavily key for the built-in web-search tool. Get one free at https://app.tavily.com |
Open **http://localhost:8082** — the dashboard comes with 13 built-in pages: Chat, Agents, Memory, Knowledge, Channels, Guardrails, Cron, and more. Add messaging channels directly from the UI.
> 📖 [Full Claw docs](https://docs.praison.ai/docs/concepts/claw) — platform tokens, CLI options, Docker, and YAML agent mode
@@ -382,7 +341,7 @@ praisonai flow
Open **http://localhost:7861** — use the **Agent** and **Agent Team** components to create sequential or parallel workflows. Connect Chat Input → Agent Team → Chat Output for instant multi-agent pipelines.
> 📖 [Full Flow docs](https://docs.praison.ai/docs/concepts/agentflow) — visual agent building, component reference, and deployment
> 📖 [Full Flow docs](https://docs.praison.ai/docs/concepts/flow) — visual agent building, component reference, and deployment
### 8. PraisonAI UI 🤖 (Clean Chat)
@@ -691,7 +650,6 @@ praisonai agents.yaml
| Auto-Save Sessions | [Docs](https://docs.praison.ai/docs/cli/session) | [📖](https://docs.praison.ai/docs/cli/session) |
| History in Context | [Docs](https://docs.praison.ai/docs/cli/session) | [📖](https://docs.praison.ai/docs/cli/session) |
| Telemetry | [Example](examples/python/telemetry/production-telemetry-example.py) | [📖](https://docs.praison.ai/docs/features/telemetry) |
| Langfuse Tracing | [Docs](https://docs.praison.ai/docs/observability/langfuse) | [📖](https://docs.praison.ai/docs/observability/langfuse) |
| Project Docs (.praison/docs/) | [Docs](https://docs.praison.ai/docs/cli/docs) | [📖](https://docs.praison.ai/docs/cli/docs) |
| AI Commit Messages | [Docs](https://docs.praison.ai/docs/cli/commit) | [📖](https://docs.praison.ai/docs/cli/commit) |
| @Mentions in Prompts | [Docs](https://docs.praison.ai/docs/cli/mentions) | [📖](https://docs.praison.ai/docs/cli/mentions) |
@@ -788,19 +746,6 @@ PraisonAI is built for speed, with agent instantiation in under 4μs. This reduc
---
## 🔍 Langfuse Tracing
```bash
pip install "praisonai[langfuse]"
praisonai langfuse
```
<p align="center">
<img src=".github/images/langfuse.png" alt="PraisonAI Langfuse Tracing" width="800" />
</p>
---
## 🎓 Video Tutorials
Learn PraisonAI through our comprehensive video series:
+7 -128
View File
@@ -15,7 +15,6 @@ Methods:
* <code title="class GuardrailResult">GuardrailResult.<a href="./src/praisonai-agents/praisonaiagents/guardrails/guardrail_result.py">from_tuple</a>(result: Tuple[bool, Any]) -> 'GuardrailResult'</code>
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_dict</a>(data: Dict[str, Any]) -> 'HandoffConfig'</code>
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class HandoffResult">HandoffResult.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_outcome</a>(outcome: AgentRunOutcome, target_agent: Optional[str] = None, source_agent: Optional[str] = None, handoff_depth: int = 0) -> 'HandoffResult'</code>
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">json</a>() -> Optional[str]</code>
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">to_dict</a>() -> dict</code>
* <code title="class ToolResult">ToolResult.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -29,20 +28,15 @@ from praisonaiagents import Agent, AutoAgents, AutoRagAgent, ContextAgent, DeepR
Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">aclose</a>() -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">agent_id</a>() -> str</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">analyze_prompt</a>(prompt: str) -> set</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">auto_memory</a>() -> Optional[bool]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">auto_memory</a>(value: Optional[bool]) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">background</a>() -> Optional[bool]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">background</a>(value: Optional[bool]) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">chat_history</a>()</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">chat_history</a>(value)</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">chat_with_context</a>(message: str, context: 'ContextPack', **kwargs) -> str</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">checkpoints</a>() -> Optional[bool]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">checkpoints</a>(value: Optional[bool]) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">clone_for_channel</a>() -> 'Agent'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">close</a>() -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">console</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">context_manager</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">context_manager</a>(value)</code>
@@ -58,7 +52,6 @@ Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">get_skills_prompt</a>() -> str</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to_async</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">is_closed</a>() -> bool</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_model</a>() -> Optional[str]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>() -> Optional[str]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>(value: Optional[str]) -> None</code>
@@ -76,7 +69,7 @@ Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until_async</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">skill_manager</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', action: str = 'add', **kwargs: Any) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', **kwargs: Any) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>(value: Optional[Any]) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">thinking_budget</a>() -> Optional[int]</code>
@@ -173,10 +166,7 @@ Methods:
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">safe_run</a>(**kwargs) -> ToolResult</code>
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate</a>() -> bool</code>
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_class</a>() -> bool</code>
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_schema_roundtrip</a>() -> bool</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">__call__</a>(*args, **kwargs) -> Any</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">check_availability</a>() -> tuple[bool, str]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">get_schema</a>() -> Dict[str, Any]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">injected_params</a>() -> Dict[str, Any]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">run</a>(**kwargs) -> Any</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">clear</a>() -> None</code>
@@ -184,18 +174,14 @@ Methods:
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">discover_single_file_plugins</a>() -> int</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_all</a>() -> Dict[str, Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool_definitions</a>() -> List[Dict[str, Any]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_trust_level</a>(name: str) -> Optional[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None, ttl_seconds: Optional[float] = None) -> List[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_base_tools</a>() -> List[BaseTool]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools</a>() -> List[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools_with_allowed_filter</a>(context: Optional[Dict[str, Any]] = None) -> List[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False) -> None</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">unregister</a>(name: str) -> bool</code>
* <code title="class Tools">Tools.<a href="./src/praisonai-agents/praisonaiagents/tools/tools.py">internet_search</a>(*args, **kwargs)</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_registry</a>() -> ToolRegistry</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None) -> None</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">tool</a>(func: Optional[Callable] = None) -> Union[FunctionTool, Callable[[Callable], FunctionTool]]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_tool</a>(tool: Any) -> bool</code>
@@ -215,12 +201,10 @@ Methods:
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">execute_callback_sync</a>(task_output: TaskOutput) -> None</code>
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">get_next_task</a>(context: Dict[str, Any]) -> Optional[str]</code>
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">initialize_memory</a>()</code>
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">initialize_memory_async</a>()</code>
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">set_status</a>(new_status: str) -> None</code>
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">store_in_memory</a>(content: str, agent_name: str = None, task_id: str = None)</code>
* <code title="class Task">Task.<a href="./src/praisonai-agents/praisonaiagents/task/task.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">loop</a>(step: Any = None, steps: Optional[List[Any]] = None, over: Optional[str] = None, from_csv: Optional[str] = None, from_file: Optional[str] = None, var_name: str = 'item', parallel: bool = False, max_workers: Optional[int] = None, output_variable: Optional[str] = None) -> Loop</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">parallel</a>(steps: List, max_workers: Optional[int] = None, on_failure: str = 'partial_ok') -> Parallel</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">parallel</a>(steps: List) -> Parallel</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">repeat</a>(step: Any, until: Optional[Callable[[WorkflowContext], bool]] = None, max_iterations: int = 10) -> Repeat</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">route</a>(routes: Dict[str, List], default: Optional[List] = None) -> Route</code>
@@ -357,7 +341,7 @@ Methods:
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_tool_function</a>(source_agent: 'Agent') -> Callable</code>
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_description</a>() -> str</code>
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_name</a>() -> str</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None, tool_policy_mode: Optional[Literal['intersect', 'passthrough']] = None, blocked_tools: Optional[List[str]] = None) -> Handoff</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None) -> Handoff</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">compress_history</a>(data: HandoffInputData) -> HandoffInputData</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">keep_last_n_messages</a>(n: int) -> Callable[[HandoffInputData], HandoffInputData]</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">remove_all_tools</a>(data: HandoffInputData) -> HandoffInputData</code>
@@ -375,9 +359,6 @@ Methods:
* <code title="class GuardrailConfig">GuardrailConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class LLMGuardrail">LLMGuardrail.<a href="./src/praisonai-agents/praisonaiagents/guardrails/llm_guardrail.py">__call__</a>(task_output) -> Tuple[bool, Union[str, 'TaskOutput']]</code>
* <code title="class LLMGuardrail">LLMGuardrail.<a href="./src/praisonai-agents/praisonaiagents/guardrails/llm_guardrail.py">validate_input</a>(content: str, **kwargs) -> Tuple[bool, str]</code>
* <code title="class LLMGuardrail">LLMGuardrail.<a href="./src/praisonai-agents/praisonaiagents/guardrails/llm_guardrail.py">validate_output</a>(content: str, **kwargs) -> Tuple[bool, str]</code>
* <code title="class LLMGuardrail">LLMGuardrail.<a href="./src/praisonai-agents/praisonaiagents/guardrails/llm_guardrail.py">validate_tool_call</a>(tool_name: str, arguments: Dict[str, Any], **kwargs) -> Tuple[bool, Dict[str, Any]]</code>
# Planning
@@ -477,26 +458,14 @@ Methods:
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">activate_by_name</a>(name: str) -> bool</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">add_skill</a>(skill_path: str) -> Optional[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">clear</a>() -> None</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">create_skill</a>(name: str, content: str, category: str = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">delete_skill</a>(name: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">discover</a>(skill_dirs: Optional[List[str]] = None, include_defaults: bool = True) -> int</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">edit_skill</a>(name: str, content: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_allowed_tools</a>(name: str) -> List[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_available_skills</a>() -> List[SkillMetadata]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_available_skills_by_state</a>(state: SkillState) -> List[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_instructions</a>(name: str) -> Optional[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_skill</a>(name: str) -> Optional[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_skills_diagnostics</a>() -> Dict[str, ValidationResult]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_user_invocable_skills</a>() -> List[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">invoke</a>(name: str, raw_args: str = '', session_id: Optional[str] = None, shell_exec: bool = False) -> Optional[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">load_resources</a>(name: str) -> bool</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">patch_skill</a>(name: str, old_string: str, new_string: str, file_path: str = None, replace_all: bool = False) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">remove_skill_file</a>(name: str, file_path: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">skill_names</a>() -> List[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">skills</a>() -> List[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">to_prompt</a>() -> str</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">validate_skill_capabilities</a>(skill_name: str, force_refresh: bool = False) -> ValidationResult</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">write_skill_file</a>(name: str, file_path: str, file_content: str) -> dict</code>
* <code title="class SkillMetadata">SkillMetadata.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">from_properties</a>(props: SkillProperties) -> 'SkillMetadata'</code>
* <code title="class SkillProperties">SkillProperties.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">to_dict</a>() -> dict</code>
* <code title="class SkillsConfig">SkillsConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -578,7 +547,7 @@ Methods:
Types:
```python
from praisonaiagents import FlowDisplay, track_workflow
from praisonaiagents import FlowDisplay, obs, track_workflow
```
Methods:
@@ -611,13 +580,6 @@ Methods:
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">set_tool_budget</a>(tool_name: str, max_tokens: int, protected: bool = False) -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">truncate_tool_output</a>(tool_name: str, output: str) -> str</code>
# UI
Types:
```python
from praisonaiagents import A2UI
```
# Config
Types:
@@ -630,7 +592,6 @@ Methods:
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">effective_track_changes</a>() -> bool</code>
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">from_dict</a>(data: Dict[str, Any]) -> 'AutonomyConfig'</code>
* <code title="class CachingConfig">CachingConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">from_dict</a>(data: Dict[str, Any]) -> 'ExecutionConfig'</code>
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class GuardrailConfig">GuardrailConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class HooksConfig">HooksConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -723,19 +684,14 @@ Methods:
* <code title="class AgentFlow">AgentFlow.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">verbose</a>(value: bool)</code>
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">get_app</a>() -> Any</code>
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">serve</a>(host: Optional[str] = None, port: Optional[int] = None, reload: bool = False, **kwargs: Any) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aannounce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">add_task</a>(task)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aexecute_task</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">announce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">append_to_state</a>(key: str, value: Any, max_length: Optional[int] = None) -> List[Any]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_all_tasks</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_task</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aspawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">astart</a>(content = None, return_dict = False, **kwargs)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">await_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clean_json_output</a>(output: str) -> str</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clear_state</a>() -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">close</a>() -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">context_manager</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">current_plan</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">default_completion_checker</a>(task, agent_output)</code>
@@ -747,7 +703,6 @@ Methods:
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_all_tasks_status</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_detailed_token_report</a>() -> Dict[str, Any]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_plan_markdown</a>() -> str</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_spawned_agents</a>() -> List[SpawnedSubAgent]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_state</a>(key: str, default: Any = None) -> Any</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_details</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_result</a>(task_id)</code>
@@ -764,12 +719,10 @@ Methods:
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_output_to_file</a>(task, task_output)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_session_state</a>(session_id: str, include_memory: bool = True) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">set_state</a>(key: str, value: Any) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">spawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">start</a>(content = None, return_dict = False, output = None, **kwargs)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">todo_list</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_plan_step_status</a>(step_id: str, status: str) -> bool</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_state</a>(updates: Dict) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">wait_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval</a>(request: ApprovalRequest) -> ApprovalDecision</code>
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval_sync</a>(request: ApprovalRequest) -> ApprovalDecision</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/embedding/embed.py">aembedding</a>(input: Union[str, List[str]], model: str = 'text-embedding-3-small', dimensions: Optional[int] = None, encoding_format: str = 'float', timeout: float = 600.0, api_key: Optional[str] = None, api_base: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs) -> EmbeddingResult</code>
@@ -780,7 +733,7 @@ Methods:
Types:
```python
from praisonai import Agent, AgentApp, AgentOS, AnthropicManagedAgent, CloudProvider, DB, Deploy, DeployConfig, DeployType, HostedAgent, HostedAgentConfig, LocalAgent, LocalAgentConfig, LocalManagedAgent, LocalManagedConfig, ManagedAgent, ManagedConfig, PraisonAI, __version__, arun, run, run_integrated_gateway
from praisonai import AgentApp, AgentOS, CloudProvider, Deploy, DeployConfig, DeployType, PraisonAI, __version__
```
# CLI
@@ -810,11 +763,6 @@ Methods:
* <code title="cli">praisonai bot bot-callback <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot discord <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot email <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot install-daemon <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot linear <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot list <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot purge <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot replay <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot slack <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot start <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
* <code title="cli">praisonai bot telegram <a href="./src/praisonai/praisonai/cli/commands/bot.py">--help</a></code>
@@ -855,7 +803,6 @@ Methods:
* <code title="cli">praisonai context show <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
* <code title="cli">praisonai context stats <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
* <code title="cli">praisonai context tail <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
* <code title="cli">praisonai dashboard unified <a href="./src/praisonai/praisonai/cli/commands/dashboard.py">--help</a></code>
* <code title="cli">praisonai debug acp <a href="./src/praisonai/praisonai/cli/commands/debug.py">--help</a></code>
* <code title="cli">praisonai debug debug-callback <a href="./src/praisonai/praisonai/cli/commands/debug.py">--help</a></code>
* <code title="cli">praisonai debug interactive <a href="./src/praisonai/praisonai/cli/commands/debug.py">--help</a></code>
@@ -882,10 +829,8 @@ Methods:
* <code title="cli">praisonai doctor env <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor mcp <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor network <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor packaging <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor performance <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor selftest <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor skills <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor tools <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor troubleshoot <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai endpoints list <a href="./src/praisonai/praisonai/cli/commands/endpoints.py">--help</a></code>
@@ -897,60 +842,32 @@ Methods:
* <code title="cli">praisonai eval judge <a href="./src/praisonai/praisonai/cli/commands/eval.py">--help</a></code>
* <code title="cli">praisonai eval list-judges <a href="./src/praisonai/praisonai/cli/commands/eval.py">--help</a></code>
* <code title="cli">praisonai eval performance <a href="./src/praisonai/praisonai/cli/commands/eval.py">--help</a></code>
* <code title="cli">praisonai examples find <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai examples info <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai examples list <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai examples report <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai examples run <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai examples run-all <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai examples stats <a href="./src/praisonai/praisonai/cli/commands/examples.py">--help</a></code>
* <code title="cli">praisonai flow export <a href="./src/praisonai/praisonai/cli/commands/flow.py">--help</a></code>
* <code title="cli">praisonai flow flow-start <a href="./src/praisonai/praisonai/cli/commands/flow.py">--help</a></code>
* <code title="cli">praisonai flow import <a href="./src/praisonai/praisonai/cli/commands/flow.py">--help</a></code>
* <code title="cli">praisonai flow list <a href="./src/praisonai/praisonai/cli/commands/flow.py">--help</a></code>
* <code title="cli">praisonai flow version <a href="./src/praisonai/praisonai/cli/commands/flow.py">--help</a></code>
* <code title="cli">praisonai gateway channels <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway gateway-callback <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway install <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway logs <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway mint-link <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway send <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway start <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway status <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway stop <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway uninstall <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai github triage <a href="./src/praisonai/praisonai/cli/commands/github.py">--help</a></code>
* <code title="cli">praisonai hooks add <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
* <code title="cli">praisonai hooks list <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
* <code title="cli">praisonai hooks remove <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
* <code title="cli">praisonai kanban block <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban boards <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban comment <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban complete <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban create <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban dispatch <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban link <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban list <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban move <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban reclaim <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban show <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban unblock <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai knowledge add <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
* <code title="cli">praisonai knowledge index <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
* <code title="cli">praisonai knowledge list <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
* <code title="cli">praisonai knowledge search <a href="./src/praisonai/praisonai/cli/commands/knowledge.py">--help</a></code>
* <code title="cli">praisonai langextract render <a href="./src/praisonai/praisonai/cli/commands/langextract.py">--help</a></code>
* <code title="cli">praisonai langextract view <a href="./src/praisonai/praisonai/cli/commands/langextract.py">--help</a></code>
* <code title="cli">praisonai langfuse config <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse connect <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse init <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse langfuse-start <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse sessions <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse show <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse status <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse stop <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse test <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse traces <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai langfuse version <a href="./src/praisonai/praisonai/cli/commands/langfuse.py">--help</a></code>
* <code title="cli">praisonai loop help <a href="./src/praisonai/praisonai/cli/commands/loop.py">--help</a></code>
* <code title="cli">praisonai loop loop-main <a href="./src/praisonai/praisonai/cli/commands/loop.py">--help</a></code>
@@ -959,17 +876,6 @@ Methods:
* <code title="cli">praisonai lsp start <a href="./src/praisonai/praisonai/cli/commands/lsp.py">--help</a></code>
* <code title="cli">praisonai lsp status <a href="./src/praisonai/praisonai/cli/commands/lsp.py">--help</a></code>
* <code title="cli">praisonai lsp stop <a href="./src/praisonai/praisonai/cli/commands/lsp.py">--help</a></code>
* <code title="cli">praisonai managed delete <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed get <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed list <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed managed-callback <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed multi <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed restore <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed resume <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed run <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed save <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed show <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai managed update <a href="./src/praisonai/praisonai/cli/commands/managed.py">--help</a></code>
* <code title="cli">praisonai mcp add <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
* <code title="cli">praisonai mcp auth <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
* <code title="cli">praisonai mcp describe <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
@@ -987,22 +893,10 @@ Methods:
* <code title="cli">praisonai memory search <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
* <code title="cli">praisonai memory show <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
* <code title="cli">praisonai memory status <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
* <code title="cli">praisonai n8n export <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n import <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n list <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n preview <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n pull <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n push <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n test <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai obs obs-fallback <a href="./src/praisonai/praisonai/cli/commands/obs.py">--help</a></code>
* <code title="cli">praisonai onboard onboard-callback <a href="./src/praisonai/praisonai/cli/commands/onboard.py">--help</a></code>
* <code title="cli">praisonai package install <a href="./src/praisonai/praisonai/cli/commands/package.py">--help</a></code>
* <code title="cli">praisonai package list <a href="./src/praisonai/praisonai/cli/commands/package.py">--help</a></code>
* <code title="cli">praisonai package uninstall <a href="./src/praisonai/praisonai/cli/commands/package.py">--help</a></code>
* <code title="cli">praisonai pairing approve <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai pairing clear <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai pairing list <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai pairing revoke <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai paths show <a href="./src/praisonai/praisonai/cli/commands/paths.py">--help</a></code>
* <code title="cli">praisonai plugins create <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins disable <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
@@ -1014,10 +908,6 @@ Methods:
* <code title="cli">praisonai plugins list <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins plugins-callback <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins remove <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai port check <a href="./src/praisonai/praisonai/cli/commands/port.py">--help</a></code>
* <code title="cli">praisonai port kill <a href="./src/praisonai/praisonai/cli/commands/port.py">--help</a></code>
* <code title="cli">praisonai port list <a href="./src/praisonai/praisonai/cli/commands/port.py">--help</a></code>
* <code title="cli">praisonai port port-callback <a href="./src/praisonai/praisonai/cli/commands/port.py">--help</a></code>
* <code title="cli">praisonai profile imports <a href="./src/praisonai/praisonai/cli/commands/profile.py">--help</a></code>
* <code title="cli">praisonai profile optimize <a href="./src/praisonai/praisonai/cli/commands/profile.py">--help</a></code>
* <code title="cli">praisonai profile profile-callback <a href="./src/praisonai/praisonai/cli/commands/profile.py">--help</a></code>
@@ -1092,7 +982,6 @@ Methods:
* <code title="cli">praisonai serve status <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve stop <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve ui <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve ui-gateway <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve unified <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai session delete <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
* <code title="cli">praisonai session export <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
@@ -1100,10 +989,6 @@ Methods:
* <code title="cli">praisonai session list <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
* <code title="cli">praisonai session resume <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
* <code title="cli">praisonai session show <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
* <code title="cli">praisonai setup config <a href="./src/praisonai/praisonai/cli/commands/setup.py">--help</a></code>
* <code title="cli">praisonai setup reset <a href="./src/praisonai/praisonai/cli/commands/setup.py">--help</a></code>
* <code title="cli">praisonai setup setup-callback <a href="./src/praisonai/praisonai/cli/commands/setup.py">--help</a></code>
* <code title="cli">praisonai setup wizard <a href="./src/praisonai/praisonai/cli/commands/setup.py">--help</a></code>
* <code title="cli">praisonai skills check <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
* <code title="cli">praisonai skills create <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
* <code title="cli">praisonai skills eligible <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
@@ -1143,13 +1028,7 @@ Methods:
* <code title="cli">praisonai train llm <a href="./src/praisonai/praisonai/cli/commands/train.py">--help</a></code>
* <code title="cli">praisonai train show <a href="./src/praisonai/praisonai/cli/commands/train.py">--help</a></code>
* <code title="cli">praisonai train train-callback <a href="./src/praisonai/praisonai/cli/commands/train.py">--help</a></code>
* <code title="cli">praisonai ui agents <a href="./src/praisonai/praisonai/cli/commands/ui.py">--help</a></code>
* <code title="cli">praisonai ui bot <a href="./src/praisonai/praisonai/cli/commands/ui.py">--help</a></code>
* <code title="cli">praisonai ui realtime <a href="./src/praisonai/praisonai/cli/commands/ui.py">--help</a></code>
* <code title="cli">praisonai ui ui <a href="./src/praisonai/praisonai/cli/commands/ui.py">--help</a></code>
* <code title="cli">praisonai up logs <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai up status <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai up up-start <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai version check <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
* <code title="cli">praisonai version show <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
* <code title="cli">praisonai version version-callback <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=4.6.55" \
"praisonai>=4.5.127" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=4.6.55" \
"praisonai>=4.5.127" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=4.6.55" \
"praisonai>=4.5.127" \
"praisonai[ui]" \
"praisonai[crewai]"
+1 -6
View File
@@ -12,13 +12,8 @@ from praisonaiagents import Agent
from praisonaiagents.approval import AgentApproval
from praisonaiagents.tools.shell_tools import execute_command
reviewer_agent = Agent(
name="CommandReviewer",
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
)
reviewer = AgentApproval(
approver_agent=reviewer_agent,
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
)
agent = Agent(
+3 -3
View File
@@ -35,7 +35,7 @@ def run_quick_comparison():
prompt="What is 2+2?",
iterations=2,
paths=["openai_sdk", "praisonai_agent"],
verbose=True
output="verbose"
)
# Print comparison table
@@ -56,7 +56,7 @@ def run_full_benchmark():
report = handler.run_full_benchmark(
prompt="Hi",
iterations=3,
verbose=True
output="verbose"
)
# Print full report
@@ -78,7 +78,7 @@ def run_agent_benchmark():
prompt="Explain Python in one sentence",
iterations=3,
paths=["openai_sdk", "praisonai_agent"],
verbose=True
output="verbose"
)
# Show timeline diagrams
+1 -1
View File
@@ -22,7 +22,7 @@ custom_modes = {
}
# Create handler with verbose output
handler = CompareHandler(verbose=True)
handler = CompareHandler(output="verbose")
# Example: Manually create comparison results for analysis
# (useful for testing without API calls)
+1 -1
View File
@@ -22,7 +22,7 @@ modes = parse_modes("basic,tools,planning")
print(f"\nParsed modes: {modes}")
# Create compare handler
handler = CompareHandler(verbose=True)
handler = CompareHandler(output="verbose")
# Compare modes (this will run actual agent calls)
# Uncomment to run:
@@ -15,7 +15,7 @@ agent = Agent(
# With array [preset, overrides]
agent_override = Agent(
instructions="You are a helpful assistant.",
memory=MemoryConfig(backend="redis", user_id="custom_user"),
memory=["redis", {"user_id": "custom_user"}],
)
if __name__ == "__main__":
@@ -2,7 +2,7 @@
from praisonaiagents import Agent
from praisonaiagents import AgentFlow, Task
from praisonaiagents.workflows.workflow_configs import (
WorkflowPlanningConfig, WorkflowMemoryConfig,
WorkflowOutputConfig, WorkflowPlanningConfig, WorkflowMemoryConfig,
)
# Full-featured workflow with all consolidated params
@@ -32,7 +32,7 @@ workflow = AgentFlow(
),
],
# Workflow-level consolidated params
output="verbose",
output=WorkflowOutputConfig(output="verbose", stream=True),
planning=WorkflowPlanningConfig(enabled=True, reasoning=True),
memory=WorkflowMemoryConfig(backend="file"),
# Agent-like params
+1 -1
View File
@@ -10,7 +10,7 @@ from praisonaiagents import Agent
# Basic: Enable web search with preset
agent = Agent(
instructions="You are a research assistant with web access.",
web="search_only", # Compatible across models without web_fetch
web="duckduckgo", # Presets: duckduckgo, tavily, google, bing, serper
)
if __name__ == "__main__":
+1 -13
View File
@@ -15,20 +15,8 @@ workflow = AgentFlow(
],
output="verbose",
planning=True,
variables={"topic": "AI agents"},
)
if __name__ == "__main__":
import os
import sys
if not os.environ.get("OPENAI_API_KEY"):
print("Set OPENAI_API_KEY to run this example.")
sys.exit(0)
if sys.platform == "win32":
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
result = workflow.run("AI agents")
result = workflow.run(variables={"topic": "AI agents"})
print(result)
+2 -2
View File
@@ -24,7 +24,7 @@ if has_api_key:
input_text="What is 2 + 2?",
expected_output="4",
num_iterations=3, # Run 3 times for statistical significance
verbose=True
output="verbose"
)
# Run evaluation
@@ -42,7 +42,7 @@ else:
input_text="What is 2 + 2?",
expected_output="4",
num_iterations=1,
verbose=True
output="verbose"
)
# Test with mock output
+2 -2
View File
@@ -27,7 +27,7 @@ if has_api_key:
scoring_type="numeric", # Score 1-10
threshold=7.0, # Pass if score >= 7
num_iterations=2,
verbose=True
output="verbose"
)
# Run evaluation
@@ -45,7 +45,7 @@ if has_api_key:
agent=agent,
input_text="Tell me a joke",
scoring_type="binary", # Pass or Fail
verbose=True
output="verbose"
)
binary_result = binary_evaluator.run(print_summary=True)
+1 -1
View File
@@ -26,7 +26,7 @@ if has_api_key:
num_iterations=10, # Run 10 benchmark iterations
warmup_runs=2, # 2 warmup runs before measurement
track_memory=True, # Track memory usage
verbose=True
output="verbose"
)
# Run evaluation
+4 -15
View File
@@ -16,19 +16,8 @@ def search_web(query: str) -> str:
def calculate(expression: str) -> str:
"""Calculate a math expression safely."""
import ast, operator
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod, ast.Pow: operator.pow,
ast.USub: operator.neg, ast.UAdd: operator.pos}
def _ev(n):
if isinstance(n, ast.Expression): return _ev(n.body)
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
raise ValueError(f"Unsupported: {ast.dump(n)}")
return str(_ev(ast.parse(expression, mode="eval")))
"""Calculate a math expression."""
return str(eval(expression))
# Check if we have an API key
@@ -48,7 +37,7 @@ if has_api_key:
input_text="Search for the weather in Paris and calculate 25 * 4",
expected_tools=["search_web", "calculate"], # Tools that should be called
forbidden_tools=["delete_file"], # Tools that should NOT be called
verbose=True
output="verbose"
)
# Run evaluation
@@ -70,7 +59,7 @@ evaluator = ReliabilityEvaluator(
agent=mock_agent,
expected_tools=["search_web", "calculate"], # Tools that should be called
forbidden_tools=["delete_file"], # Tools that should NOT be called
verbose=True
output="verbose"
)
result2 = evaluator.evaluate_tool_calls(
+1 -1
View File
@@ -184,7 +184,7 @@ Answer questions based ONLY on the provided knowledge context.
When asked about codes, provide the EXACT code from the documents.
Be concise but accurate.""",
knowledge=[temp_dir],
memory={"user_id": "compression_demo_user"},
user_id="compression_demo_user",
output="verbose",
)
@@ -80,7 +80,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
print(f"\nAnswer: {response}\n")
# Verify the answer contains the unique code
if "ZEBRA-71" in str(response or "").upper():
if "ZEBRA-71" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the unique code from context!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
@@ -104,7 +104,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
response = agent.chat("What is the security incident code?")
print(f"\nAnswer: {response}\n")
if "HAWK-88" in str(response or "").upper():
if "HAWK-88" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the security code!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
@@ -164,7 +164,7 @@ Answer questions based ONLY on the provided knowledge context.
When asked about department codes, provide the EXACT code from the documents.
Be specific about which department you're referring to.""",
knowledge=[temp_dir],
memory={"user_id": "hierarchy_demo_user"},
user_id="hierarchy_demo_user",
output="verbose",
)
@@ -124,7 +124,7 @@ def main():
# Show strategy selection for different corpus sizes
print(f"\nStrategy selection by corpus size:")
for size in [100, 1000, 10000, 50000, 100000]:
strategy = select_strategy(CorpusStats(file_count=max(1, size // 1000), total_tokens=size))
strategy = select_strategy(corpus_tokens=size)
print(f" {size:>6} tokens -> {strategy.value}")
except ImportError as e:
@@ -142,7 +142,7 @@ Answer questions based ONLY on the provided knowledge context.
When asked about verification codes, provide the EXACT code from the documents.
If information is not in the context, say 'Information not found in documents.'""",
knowledge=[temp_dir],
memory={"user_id": "strategy_demo_user"},
user_id="strategy_demo_user",
output="verbose", # Use new consolidated param
)
+1 -1
View File
@@ -65,7 +65,7 @@ def main():
name="PolicyExpert",
instructions="You are a company policy expert. Answer questions based ONLY on the provided knowledge. Quote exact values when asked about codes or numbers.",
knowledge=[temp_dir], # Add directory with documents
memory={"user_id": "example_user"},
user_id="example_user", # Required for mem0 backend
)
print("\n1. Testing Agent.chat() with context-required question:")
@@ -50,7 +50,7 @@ def main():
name="HR_Assistant_User1",
instructions="You are an HR assistant.",
knowledge=[temp_dir],
memory={"user_id": "user_alice"},
user_id="user_alice", # Knowledge scoped to Alice
)
response = user1_agent.chat("What is the policy on remote work?")
@@ -80,7 +80,7 @@ def main():
name="Personal_HR_Bot",
instructions="You are a personal HR assistant.",
knowledge=[temp_dir],
memory={"user_id": "user_bob"},
user_id="user_bob",
agent_id="hr_bot_v2",
)
@@ -49,7 +49,7 @@ Only authorized personnel with code WRENCH-992 may access this project.
name="AgentA_Ingester",
instructions="You ingest and store knowledge.",
knowledge=[temp_dir],
memory={"user_id": "user_alice"},
user_id="user_alice", # Scoped to Alice
output="silent",
)
@@ -103,7 +103,7 @@ Only authorized personnel with code WRENCH-992 may access this project.
name="AgentB_Querier",
instructions="Answer questions based on the provided knowledge. If you don't have the information, say so.",
knowledge=[temp_dir],
memory={"user_id": "user_alice"},
user_id="user_alice", # Same scope as Agent A
output="silent",
)
@@ -1,115 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + ClickHouse Persistence — Real conversation with session resume.
ClickHouse stores analytics/state; SQLite stores conversation history.
Prerequisites:
pip install praisonai praisonaiagents clickhouse-connect
export OPENAI_API_KEY="sk-..."
# ClickHouse running on localhost:8123
"""
import json
import os
import sys
import tempfile
import clickhouse_connect
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig, DB
from praisonaiagents import Agent
CH_HOST = os.getenv("CH_HOST", "localhost")
CH_PORT = int(os.getenv("CH_PORT", "8123"))
CH_USER = os.getenv("CH_USER", "clickhouse")
CH_PASSWORD = os.getenv("CH_PASSWORD", "clickhouse")
SQLITE_PATH = os.path.join(tempfile.gettempdir(), "managed_ch_conv.db")
print(f"ManagedAgent + ClickHouse Persistence\nCH: {CH_HOST}:{CH_PORT}\nSQLite: {SQLITE_PATH}\n")
# Setup ClickHouse table
client = clickhouse_connect.get_client(host=CH_HOST, port=CH_PORT, username=CH_USER, password=CH_PASSWORD)
TABLE = "managed_agent_sessions"
client.command(f"""
CREATE TABLE IF NOT EXISTS {TABLE} (
session_id String, agent_id String,
input_tokens UInt64, output_tokens UInt64,
state_json String, created_at DateTime DEFAULT now()
) ENGINE = MergeTree() ORDER BY (session_id, created_at)
""")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
db = DB(database_url=SQLITE_PATH)
managed = ManagedAgent(
provider="local", db=db,
config=LocalManagedConfig(
model="gpt-4o-mini", name="ClickHouse Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: My favourite movie is Interstellar by Christopher Nolan, 2014. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: The Hans Zimmer soundtrack is my favourite film score. Confirm.")
print(f"Agent: {result2[:200]}...")
# Store to ClickHouse
session_id = managed.session_id
client.insert(TABLE, [[
session_id, managed.agent_id or "",
managed.total_input_tokens, managed.total_output_tokens,
json.dumps({"environment_id": managed.environment_id}),
]], column_names=["session_id", "agent_id", "input_tokens", "output_tokens", "state_json"])
# ── Phase 2: ClickHouse Verification ──
print("\n=== Phase 2: ClickHouse Verification ===")
rows = client.query(f"SELECT session_id, agent_id FROM {TABLE} WHERE session_id=%(sid)s LIMIT 1", parameters={"sid": session_id})
for row in rows.result_rows:
print(f" session_id: {row[0]}, agent_id: {row[1]}")
# ── Phase 3: Destroy instance ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
print(f"Saved IDs: {saved_ids}")
del agent, managed, db
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed ──
print("=== Phase 4: Resume Session ===")
db2 = DB(database_url=SQLITE_PATH)
managed2 = ManagedAgent(provider="local", db=db2)
managed2.resume_session(session_id)
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("What is my favourite movie, director, year, and soundtrack composer?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Interstellar'": "interstellar" in r,
"Remembers 'Nolan'": "nolan" in r,
"Remembers '2014'": "2014" in r,
"Remembers 'Zimmer'": "zimmer" in r,
"Session ID continuity": managed2.session_id == session_id,
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
client.command(f"ALTER TABLE {TABLE} DELETE WHERE session_id = '{session_id}'")
client.close()
os.unlink(SQLITE_PATH)
print(f"\n{'PASS' if all_passed else 'FAIL'}: ClickHouse ManagedAgent persistence test")
@@ -1,27 +0,0 @@
"""
CockroachDB Serverless Persistence Example.
CockroachDB Serverless provides distributed SQL with scale-to-zero.
Auto-handles serialization retries (error 40001) and SSL.
Setup:
1. Create free CockroachDB Serverless cluster at https://cockroachlabs.cloud
2. Copy connection string from dashboard
3. Set env var: export COCKROACHDB_URL="postgresql://user:[email protected]:26257/mydb?sslmode=verify-full"
4. pip install praisonai[cockroachdb]
"""
import os
from praisonaiagents import Agent
agent = Agent(
name="CockroachDB Agent",
instructions="You are a helpful assistant.",
memory={
"session_id": "crdb-demo-session",
"db": {"database_url": os.getenv("COCKROACHDB_URL")},
},
)
result = agent.start("What is distributed SQL? Explain in 2 sentences.")
print(result)
@@ -1,92 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + JSON File Persistence — Real conversation with session resume.
Uses DefaultSessionStore (JSON files on disk). No external database required.
Prerequisites:
pip install praisonai praisonaiagents
export OPENAI_API_KEY="sk-..."
"""
import json
import os
import shutil
import sys
import tempfile
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig
from praisonaiagents import Agent
from praisonaiagents.session.store import DefaultSessionStore
SESSION_DIR = os.path.join(tempfile.gettempdir(), "managed_json_sessions")
print(f"ManagedAgent + JSON File Persistence\nSession dir: {SESSION_DIR}\n")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
store = DefaultSessionStore(session_dir=SESSION_DIR)
managed = ManagedAgent(
provider="local", session_store=store,
config=LocalManagedConfig(
model="gpt-4o-mini", name="JSON Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: My favourite city is Barcelona, Spain. I love the Sagrada Familia. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: I always stay at Hotel Arts in Barcelona. Confirm.")
print(f"Agent: {result2[:200]}...")
# ── Phase 2: Verify JSON files ──
print("\n=== Phase 2: JSON File Verification ===")
json_files = [f for f in os.listdir(SESSION_DIR) if f.endswith(".json")]
print(f"Files: {json_files}")
for fname in json_files:
with open(os.path.join(SESSION_DIR, fname), "r") as f:
data = json.load(f)
print(f" {fname}: {len(data.get('messages', []))} messages")
# ── Phase 3: Destroy instance ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
session_id = saved_ids["session_id"]
print(f"Saved IDs: {saved_ids}")
del agent, managed, store
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed ──
print("=== Phase 4: Resume Session ===")
store2 = DefaultSessionStore(session_dir=SESSION_DIR)
managed2 = ManagedAgent(provider="local", session_store=store2)
managed2.resume_session(session_id)
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("What is my favourite city, landmark, and hotel?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Barcelona'": "barcelona" in r,
"Remembers 'Sagrada Familia'": "sagrada" in r,
"Remembers 'Hotel Arts'": "hotel arts" in r or "arts" in r,
"Session ID continuity": managed2.session_id == session_id,
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
shutil.rmtree(SESSION_DIR)
print(f"\n{'PASS' if all_passed else 'FAIL'}: JSON File ManagedAgent persistence test")
@@ -1,102 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + MongoDB Persistence — Real conversation with session resume.
MongoDB stores state metadata; SQLite stores conversation history.
Prerequisites:
pip install praisonai praisonaiagents pymongo
export OPENAI_API_KEY="sk-..."
# MongoDB running on localhost:27017
"""
import os
import sys
import tempfile
import pymongo
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig, DB
from praisonaiagents import Agent
MONGO_URL = os.getenv("MONGO_URL", "mongodb://localhost:27017")
SQLITE_PATH = os.path.join(tempfile.gettempdir(), "managed_mongo_conv.db")
print(f"ManagedAgent + MongoDB Persistence\nMongoDB: {MONGO_URL}\nSQLite: {SQLITE_PATH}\n")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
db = DB(database_url=SQLITE_PATH)
managed = ManagedAgent(
provider="local", db=db,
config=LocalManagedConfig(
model="gpt-4o-mini", name="MongoDB Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: My favourite book is 'Dune' by Frank Herbert, published 1965. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: 'Dune Messiah' is my second favourite. Confirm.")
print(f"Agent: {result2[:200]}...")
# Store session metadata into MongoDB for verification
mongo_client = pymongo.MongoClient(MONGO_URL)
mongo_col = mongo_client["praisonai_managed"]["sessions"]
state_doc = {
"_id": managed.session_id,
"agent_id": managed.agent_id,
"total_input_tokens": managed.total_input_tokens,
}
mongo_col.replace_one({"_id": managed.session_id}, state_doc, upsert=True)
# ── Phase 2: MongoDB Verification ──
print("\n=== Phase 2: MongoDB Verification ===")
doc = mongo_col.find_one({"_id": managed.session_id})
print(f" Session in MongoDB: {doc['_id']}" if doc else " WARNING: not found")
# ── Phase 3: Destroy instance ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
session_id = saved_ids["session_id"]
print(f"Saved IDs: {saved_ids}")
del agent, managed, db
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed ──
print("=== Phase 4: Resume Session ===")
db2 = DB(database_url=SQLITE_PATH)
managed2 = ManagedAgent(provider="local", db=db2)
managed2.resume_session(session_id)
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("What is my favourite book, author, year, and second favourite?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Dune'": "dune" in r,
"Remembers 'Herbert'": "herbert" in r,
"Remembers '1965'": "1965" in r,
"Remembers 'Messiah'": "messiah" in r,
"Session ID continuity": managed2.session_id == session_id,
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
mongo_col.delete_one({"_id": session_id})
mongo_client.close()
os.unlink(SQLITE_PATH)
print(f"\n{'PASS' if all_passed else 'FAIL'}: MongoDB ManagedAgent persistence test")
@@ -1,92 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + MySQL Persistence — Real conversation with session resume.
Prerequisites:
pip install praisonai praisonaiagents mysql-connector-python
export OPENAI_API_KEY="sk-..."
# MySQL running on localhost:3307
"""
import os
import sys
import mysql.connector
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig, DB
from praisonaiagents import Agent
MYSQL_URL = os.getenv("MYSQL_URL", "mysql://root:praisontest@localhost:3307/praisonai")
print(f"ManagedAgent + MySQL Persistence\nDB: {MYSQL_URL}\n")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
db = DB(database_url=MYSQL_URL)
managed = ManagedAgent(
provider="local", db=db,
config=LocalManagedConfig(
model="gpt-4o-mini", name="MySQL Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: I live in Tokyo, Japan and love ramen from Ichiran. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: I work as a machine learning engineer at a robotics company. Confirm.")
print(f"Agent: {result2[:200]}...")
# ── Phase 2: Direct MySQL Verification ──
print("\n=== Phase 2: MySQL Verification ===")
conn = mysql.connector.connect(host="localhost", port=3307, user="root", password="praisontest", database="praisonai")
cursor = conn.cursor()
cursor.execute("SELECT table_name FROM information_schema.tables WHERE table_schema='praisonai' AND table_name LIKE '%message%'")
msg_tables = [r[0] for r in cursor.fetchall()]
print(f"Message tables: {msg_tables}")
for t in msg_tables:
cursor.execute(f"SELECT COUNT(*) FROM `{t}`")
print(f" {t}: {cursor.fetchone()[0]} rows")
conn.close()
# ── Phase 3: Destroy instance ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
print(f"Saved IDs: {saved_ids}")
del agent, managed, db
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed ──
print("=== Phase 4: Resume Session ===")
db2 = DB(database_url=MYSQL_URL)
managed2 = ManagedAgent(provider="local", db=db2)
managed2.resume_session(saved_ids["session_id"])
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("Where do I live, what's my favourite food, and what do I do for work?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Tokyo'": "tokyo" in r,
"Remembers 'ramen'": "ramen" in r,
"Remembers 'Ichiran'": "ichiran" in r,
"Remembers 'machine learning'": "machine learning" in r or "ml" in r,
"Remembers 'robotics'": "robotic" in r,
"Session ID continuity": managed2.session_id == saved_ids["session_id"],
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
print(f"\n{'PASS' if all_passed else 'FAIL'}: MySQL ManagedAgent persistence test")
@@ -1,36 +0,0 @@
"""
Neon Serverless PostgreSQL Persistence Example.
Neon auto-idles after inactivity — you only pay when active.
The PraisonAI adapter auto-handles cold-start retries and SSL.
Setup:
1. Create free Neon project at https://neon.tech
2. Copy connection string from dashboard
3. Set env var: export NEON_DATABASE_URL="postgresql://user:[email protected]/dbname?sslmode=require"
4. pip install praisonai[neon]
"""
import os
from praisonaiagents import Agent
# Option 1: Use NEON_DATABASE_URL env var (recommended)
agent = Agent(
name="Neon Agent",
instructions="You are a helpful assistant.",
memory={
"session_id": "neon-demo-session",
"db": {"database_url": os.getenv("NEON_DATABASE_URL")},
},
)
# Option 2: Direct URL
# agent = Agent(
# name="Neon Agent",
# instructions="You are a helpful assistant.",
# db={"database_url": "postgresql://user:[email protected]/dbname?sslmode=require"},
# session_id="neon-demo-session",
# )
result = agent.start("What is serverless PostgreSQL? Explain in 2 sentences.")
print(result)
@@ -1,94 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + PostgreSQL Persistence — Real conversation with session resume.
Prerequisites:
pip install praisonai praisonaiagents psycopg2-binary
export OPENAI_API_KEY="sk-..."
# PostgreSQL running on localhost:5432
"""
import os
import sys
import psycopg2
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig, DB
from praisonaiagents import Agent
PG_URL = os.getenv("PG_URL", "postgresql://postgres:postgres@localhost:5432/postgres")
print(f"ManagedAgent + PostgreSQL Persistence\nDB: {PG_URL}\n")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
db = DB(database_url=PG_URL)
managed = ManagedAgent(
provider="local", db=db,
config=LocalManagedConfig(
model="gpt-4o-mini", name="PG Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: My favourite language is Rust, 3 years experience. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: I'm building a web crawler with the reqwest crate. Confirm.")
print(f"Agent: {result2[:200]}...")
# ── Phase 2: Direct PostgreSQL Verification ──
print("\n=== Phase 2: PostgreSQL Verification ===")
conn = psycopg2.connect(PG_URL)
cursor = conn.cursor()
cursor.execute("""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public' AND table_name LIKE '%message%'
""")
msg_tables = [r[0] for r in cursor.fetchall()]
print(f"Message tables: {msg_tables}")
for t in msg_tables:
cursor.execute(f"SELECT COUNT(*) FROM {t}")
print(f" {t}: {cursor.fetchone()[0]} rows")
conn.close()
# ── Phase 3: Destroy instance ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
print(f"Saved IDs: {saved_ids}")
del agent, managed, db
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed ──
print("=== Phase 4: Resume Session ===")
db2 = DB(database_url=PG_URL)
managed2 = ManagedAgent(provider="local", db=db2)
managed2.resume_session(saved_ids["session_id"])
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("What is my favourite language, experience, and what am I building?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Rust'": "rust" in r,
"Remembers '3 years'": "3" in r and "year" in r,
"Remembers 'crawler'": "crawler" in r or "crawl" in r,
"Remembers 'reqwest'": "reqwest" in r,
"Session ID continuity": managed2.session_id == saved_ids["session_id"],
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
print(f"\n{'PASS' if all_passed else 'FAIL'}: PostgreSQL ManagedAgent persistence test")
@@ -1,90 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + Redis Persistence — Real conversation with session resume.
Redis stores state/metadata; SQLite stores conversation history.
Prerequisites:
pip install praisonai praisonaiagents redis
export OPENAI_API_KEY="sk-..."
# Redis running on localhost:6379
"""
import os
import sys
import tempfile
import redis as redis_lib
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig, DB
from praisonaiagents import Agent
REDIS_URL = os.getenv("REDIS_URL", "redis://:myredissecret@localhost:6379/0")
SQLITE_PATH = os.path.join(tempfile.gettempdir(), "managed_redis_conv.db")
print(f"ManagedAgent + Redis Persistence\nRedis: {REDIS_URL}\nSQLite: {SQLITE_PATH}\n")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
db = DB(database_url=SQLITE_PATH, state_url=REDIS_URL)
managed = ManagedAgent(
provider="local", db=db,
config=LocalManagedConfig(
model="gpt-4o-mini", name="Redis Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: My cat Luna is a Siamese who loves tuna treats. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: Luna was adopted from a shelter in Portland, Oregon. Confirm.")
print(f"Agent: {result2[:200]}...")
# ── Phase 2: Direct Redis Verification ──
print("\n=== Phase 2: Redis Verification ===")
rc = redis_lib.Redis(host="localhost", port=6379, password="myredissecret", decode_responses=True)
all_keys = rc.keys("*")
print(f"Total Redis keys: {len(all_keys)}")
# ── Phase 3: Destroy instance ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
print(f"Saved IDs: {saved_ids}")
del agent, managed, db
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed ──
print("=== Phase 4: Resume Session ===")
db2 = DB(database_url=SQLITE_PATH, state_url=REDIS_URL)
managed2 = ManagedAgent(provider="local", db=db2)
managed2.resume_session(saved_ids["session_id"])
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("What is my cat's name, breed, favourite treat, and adoption city?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Luna'": "luna" in r,
"Remembers 'Siamese'": "siamese" in r,
"Remembers 'tuna'": "tuna" in r,
"Remembers 'Portland'": "portland" in r,
"Session ID continuity": managed2.session_id == saved_ids["session_id"],
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
os.unlink(SQLITE_PATH)
print(f"\n{'PASS' if all_passed else 'FAIL'}: Redis ManagedAgent persistence test")
@@ -1,92 +0,0 @@
#!/usr/bin/env python3
"""
ManagedAgent + SQLite Persistence — Real conversation with session resume.
Prerequisites:
pip install praisonai praisonaiagents
export OPENAI_API_KEY="sk-..."
"""
import os
import sqlite3
import sys
import tempfile
if not os.getenv("OPENAI_API_KEY"):
sys.exit("ERROR: OPENAI_API_KEY not set.")
from praisonai import ManagedAgent, LocalManagedConfig, DB
from praisonaiagents import Agent
DB_PATH = os.path.join(tempfile.gettempdir(), "managed_sqlite_test.db")
print(f"ManagedAgent + SQLite Persistence\nDB: {DB_PATH}\n")
# ── Phase 1: Create agent, teach facts ──
print("=== Phase 1: First Session ===")
db = DB(database_url=DB_PATH)
managed = ManagedAgent(
provider="local", db=db,
config=LocalManagedConfig(
model="gpt-4o-mini", name="SQLite Memory Agent",
system="You are a helpful assistant. Remember all facts the user tells you.",
),
)
agent = Agent(name="User", backend=managed)
result1 = agent.run("Remember: My dog's name is Biscuit, a golden retriever. Confirm.")
print(f"Agent: {result1[:200]}...")
print(f"Session ID: {managed.session_id}")
result2 = agent.run("Also remember: Biscuit's favourite toy is a blue squeaky ball. Confirm.")
print(f"Agent: {result2[:200]}...")
# ── Phase 2: Verify SQLite directly ──
print("\n=== Phase 2: SQLite Verification ===")
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = [r[0] for r in cursor.fetchall()]
print(f"Tables: {tables}")
for t in tables:
if "message" in t.lower():
cursor.execute(f"SELECT COUNT(*) FROM [{t}]")
print(f" {t}: {cursor.fetchone()[0]} rows")
conn.close()
# ── Phase 3: Destroy instance (simulate idle) ──
print("\n=== Phase 3: Instance Goes Idle ===")
saved_ids = managed.save_ids()
print(f"Saved IDs: {saved_ids}")
del agent, managed, db
print("Agent destroyed.\n")
# ── Phase 4: Resume — no config needed, restored from DB ──
print("=== Phase 4: Resume Session ===")
db2 = DB(database_url=DB_PATH)
managed2 = ManagedAgent(provider="local", db=db2)
managed2.resume_session(saved_ids["session_id"])
print(f"Resumed session: {managed2.session_id}")
agent2 = Agent(name="User", backend=managed2)
result3 = agent2.run("What is my dog's name, breed, and favourite toy?")
print(f"Agent: {result3[:300]}...")
# ── Phase 5: Validate ──
print("\n=== Phase 5: Validation ===")
r = result3.lower()
checks = {
"Remembers 'Biscuit'": "biscuit" in r,
"Remembers 'golden retriever'": "golden retriever" in r,
"Remembers toy": "blue" in r or "squeaky" in r,
"Session ID continuity": managed2.session_id == saved_ids["session_id"],
}
all_passed = True
for check, ok in checks.items():
print(f" [{'PASS' if ok else 'FAIL'}] {check}")
if not ok:
all_passed = False
os.unlink(DB_PATH)
print(f"\n{'PASS' if all_passed else 'FAIL'}: SQLite ManagedAgent persistence test")
@@ -1,30 +0,0 @@
"""
Supabase Direct PostgreSQL Persistence Example.
Supabase offers two connection modes:
1. REST API (via supabase-py) — set SUPABASE_URL + SUPABASE_KEY
2. Direct PostgreSQL (via psycopg2) — use Postgres connection string
This example uses direct PostgreSQL mode with auto-retry for paused projects.
Setup:
1. Create free Supabase project at https://supabase.com
2. Go to Settings > Database > Connection string (URI)
3. Set env var: export SUPABASE_DATABASE_URL="postgresql://postgres.xxx:[pass]@xxx.pooler.supabase.com:6543/postgres"
4. pip install praisonai[postgresql]
"""
import os
from praisonaiagents import Agent
agent = Agent(
name="Supabase Agent",
instructions="You are a helpful assistant.",
memory={
"session_id": "supabase-demo-session",
"db": {"database_url": os.getenv("SUPABASE_DATABASE_URL")},
},
)
result = agent.start("What is Supabase? Explain in 2 sentences.")
print(result)
@@ -1,31 +0,0 @@
"""
Turso/libSQL Persistence Example.
Turso provides SQLite-at-the-edge with automatic sync and scale-to-zero.
Setup:
1. Create free Turso database at https://turso.tech
2. Run: turso db tokens create <db-name>
3. Set env vars:
export TURSO_DATABASE_URL="libsql://mydb-user.turso.io"
export TURSO_AUTH_TOKEN="eyJ..."
4. pip install praisonai[turso]
"""
import os
from praisonaiagents import Agent
agent = Agent(
name="Turso Agent",
instructions="You are a helpful assistant.",
memory={
"session_id": "turso-demo-session",
"db": {
"database_url": os.getenv("TURSO_DATABASE_URL"),
"auth_token": os.getenv("TURSO_AUTH_TOKEN"),
},
},
)
result = agent.start("What is edge computing? Explain in 2 sentences.")
print(result)
+8 -31
View File
@@ -19,43 +19,20 @@ import os
def calculate(expression: str) -> str:
"""
A simple calculator tool that evaluates mathematical expressions safely.
A simple calculator tool that evaluates mathematical expressions.
Args:
expression: A mathematical expression to evaluate (e.g., "2 + 2")
Returns:
The result of the calculation
"""
import ast
import operator
_OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
def _safe_eval(node):
if isinstance(node, ast.Expression):
return _safe_eval(node.body)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.UnaryOp) and type(node.op) in _OPS:
return _OPS[type(node.op)](_safe_eval(node.operand))
if isinstance(node, ast.BinOp) and type(node.op) in _OPS:
return _OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
raise ValueError(f"Unsupported expression: {ast.dump(node)}")
try:
tree = ast.parse(expression, mode="eval")
result = _safe_eval(tree)
# Safe evaluation of mathematical expressions
allowed_chars = set("0123456789+-*/.() ")
if not all(c in allowed_chars for c in expression):
return "Error: Invalid characters in expression"
result = eval(expression)
return f"Result: {result}"
except Exception as e:
return f"Error: {str(e)}"
+6 -12
View File
@@ -24,18 +24,12 @@ def main():
def calculate(expression: str) -> str:
"""Safely evaluate a mathematical expression."""
try:
import ast, operator
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul,
ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod, ast.Pow: operator.pow,
ast.USub: operator.neg, ast.UAdd: operator.pos}
def _ev(n):
if isinstance(n, ast.Expression): return _ev(n.body)
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): return n.value
if isinstance(n, ast.UnaryOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.operand))
if isinstance(n, ast.BinOp) and type(n.op) in _OPS: return _OPS[type(n.op)](_ev(n.left), _ev(n.right))
raise ValueError(f"Unsupported: {ast.dump(n)}")
return f"Result: {_ev(ast.parse(expression, mode='eval'))}"
# Only allow safe math operations
allowed = set("0123456789+-*/.(). ")
if not all(c in allowed for c in expression):
return "Error: Invalid characters in expression"
result = eval(expression) # Safe due to character filtering
return f"Result: {result}"
except Exception as e:
return f"Error: {e}"
+2 -1
View File
@@ -10,7 +10,8 @@ from praisonaiagents import Agent
agent = Agent(
name="FlexBot",
instructions="You are a helpful assistant.",
llm={"model": "gpt-4o-mini", "configurable": True},
llm="gpt-4o-mini",
llm_config={"configurable": True}
)
if __name__ == "__main__":
+1 -1
View File
@@ -21,7 +21,7 @@ agent = Agent(
name="ContextBot",
instructions="You help show context information.",
tools=[show_context],
memory={"session_id": "my-session-123"},
session_id="my-session-123"
)
if __name__ == "__main__":
+30 -22
View File
@@ -1,40 +1,48 @@
"""
Langfuse Integration Example (Updated for TraceSinkProtocol)
Langfuse Integration Example
This example shows how to use Langfuse for LLM observability with PraisonAI's native trace infrastructure.
This example shows how to use Langfuse for LLM observability.
Setup:
pip install "praisonai[langfuse]"
export LANGFUSE_PUBLIC_KEY=pk-lf-xxx
export LANGFUSE_SECRET_KEY=sk-lf-xxx
1. Sign up at https://langfuse.com/
2. Get your API keys from the project settings
3. Set environment variables:
export LANGFUSE_PUBLIC_KEY=pk-lf-xxx
export LANGFUSE_SECRET_KEY=sk-lf-xxx
4. Install dependencies:
pip install opentelemetry-sdk opentelemetry-exporter-otlp
Usage:
python langfuse_example.py
"""
import os
from praisonai_tools.observability import obs
from praisonaiagents import Agent
from praisonai.observability import LangfuseSink
from praisonaiagents.trace.protocol import (
TraceEmitter, set_default_emitter
# Initialize Langfuse
success = obs.init(
provider="langfuse",
project_name="praisonai-demo",
)
# Initialize Langfuse observability
sink = LangfuseSink()
emitter = TraceEmitter(sink=sink, enabled=True)
set_default_emitter(emitter)
if not success:
print("Failed to initialize Langfuse. Check your API keys.")
print("Required: LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY")
exit(1)
# Create and run agent — all traces automatically captured
print("Langfuse initialized successfully!")
print(f"View traces at: https://cloud.langfuse.com/")
# Create agent
agent = Agent(
name="Coder",
instructions="You are a helpful coding assistant.",
llm="openai/gpt-4o-mini",
model="gpt-4o-mini",
)
try:
result = agent.start("Write a Python function to calculate fibonacci numbers")
print(result)
finally:
# Ensure traces are flushed and resources cleaned up
sink.flush()
sink.close()
# Run with tracing
with obs.trace("coding-session", user_id="developer-1"):
response = agent.chat("Write a Python function to calculate fibonacci numbers")
print(response)
print("\nCheck Langfuse dashboard for traces!")
+3 -13
View File
@@ -19,16 +19,6 @@ from praisonaiagents import Agent, AgentTeam
# Initialize observability
obs.init()
def set_span_attr(span, key, value):
"""Set span attribute safely across observability implementations."""
if hasattr(span, "set_attribute"):
span.set_attribute(key, value)
return
attrs = getattr(span, "attributes", None)
if attrs is not None:
attrs[key] = value
# Create agents
researcher = Agent(
name="Researcher",
@@ -47,13 +37,13 @@ with obs.trace("content-creation", metadata={"workflow": "research-write"}):
# Research phase
with obs.span("research-phase", kind=SpanKind.AGENT) as research_span:
set_span_attr(research_span, "agent", "Researcher")
research_span.attributes["agent"] = "Researcher"
research_result = researcher.chat("Research the benefits of AI in healthcare")
# Writing phase
with obs.span("writing-phase", kind=SpanKind.AGENT) as write_span:
set_span_attr(write_span, "agent", "Writer")
set_span_attr(write_span, "input_length", len(str(research_result)))
write_span.attributes["agent"] = "Writer"
write_span.attributes["input_length"] = len(str(research_result))
final_content = writer.chat(f"Write a blog post based on: {research_result}")
print("Final Content:")
@@ -1,156 +0,0 @@
"""
ClickHouse — Persistence Example
Demonstrates:
- Connecting to ClickHouse
- Creating tables for agent state
- Writing and reading agent metadata
- JSON state storage and retrieval
- Session resume after simulated restart
Requirements:
pip install clickhouse-connect
Docker: ClickHouse on localhost:8123 (user=clickhouse, password=clickhouse)
Run:
python clickhouse_persistence.py
"""
import json
import uuid
import clickhouse_connect
CH_HOST = "localhost"
CH_PORT = 8123
CH_USER = "clickhouse"
CH_PASSWORD = "clickhouse"
print(f"ClickHouse: {CH_HOST}:{CH_PORT}\n")
client = clickhouse_connect.get_client(
host=CH_HOST, port=CH_PORT, username=CH_USER, password=CH_PASSWORD,
)
TABLE_SESSIONS = f"praison_sessions_{uuid.uuid4().hex[:8]}"
TABLE_STATE = f"praison_state_{uuid.uuid4().hex[:8]}"
# --- Phase 1: Create tables and insert session data ---
print("=== Phase 1: Create Tables & Insert Sessions ===")
client.command(f"""
CREATE TABLE IF NOT EXISTS {TABLE_SESSIONS} (
session_id String,
agent_id String,
agent_version UInt32,
total_input_tokens UInt64,
total_output_tokens UInt64,
model String,
created_at DateTime DEFAULT now()
) ENGINE = MergeTree()
ORDER BY session_id
""")
sessions = [
["session_ch_001", "agent_demo_001", 3, 500, 200, "gpt-4o"],
["session_ch_002", "agent_demo_002", 1, 1000, 400, "gpt-4o-mini"],
["session_ch_003", "agent_demo_001", 3, 750, 300, "gpt-4o"],
]
client.insert(
TABLE_SESSIONS, sessions,
column_names=["session_id", "agent_id", "agent_version", "total_input_tokens", "total_output_tokens", "model"],
)
for s in sessions:
print(f" Inserted: {s[0]} (agent={s[1]}, tokens_in={s[3]})")
print()
# --- Phase 2: Query and verify ---
print("=== Phase 2: Query & Verify ===")
result = client.query(f"SELECT * FROM {TABLE_SESSIONS} ORDER BY session_id")
print(f" Total sessions: {len(result.result_rows)}")
for row in result.result_rows:
print(f" {row[0]}: agent={row[1]}, v{row[2]}, tokens_in={row[3]}, model={row[5]}")
assert len(result.result_rows) == 3
assert result.result_rows[0][0] == "session_ch_001"
# Aggregate query
agg = client.query(f"SELECT agent_id, SUM(total_input_tokens) as total_in FROM {TABLE_SESSIONS} GROUP BY agent_id ORDER BY agent_id")
print("\n Tokens by agent:")
for row in agg.result_rows:
print(f" {row[0]}: {row[1]} input tokens")
print()
# --- Phase 3: JSON state storage ---
print("=== Phase 3: JSON State Storage ===")
client.command(f"""
CREATE TABLE IF NOT EXISTS {TABLE_STATE} (
session_id String,
state_json String,
updated_at DateTime DEFAULT now()
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY session_id
""")
state = {
"agent_id": "agent_demo_001",
"agent_version": 3,
"environment_id": "env_ch_001",
"total_input_tokens": 2000,
"total_output_tokens": 800,
"compute_instance_id": "flyio_ch_001",
"session_history": [
{"id": "session_ch_001", "status": "completed"},
{"id": "session_ch_003", "status": "idle"},
],
}
client.insert(
TABLE_STATE,
[["session_ch_001", json.dumps(state)]],
column_names=["session_id", "state_json"],
)
print(" Stored JSON state for session_ch_001")
# Retrieve
result = client.query(f"SELECT state_json FROM {TABLE_STATE} WHERE session_id = 'session_ch_001'")
recovered = json.loads(result.result_rows[0][0])
print(f" Recovered agent_id: {recovered['agent_id']}")
print(f" Recovered tokens: in={recovered['total_input_tokens']}, out={recovered['total_output_tokens']}")
print(f" Recovered compute: {recovered['compute_instance_id']}")
assert recovered["total_input_tokens"] == 2000
print()
# --- Phase 4: Session Resume ---
print("=== Phase 4: Session Resume (Simulating Restart) ===")
client.close()
client2 = clickhouse_connect.get_client(
host=CH_HOST, port=CH_PORT, username=CH_USER, password=CH_PASSWORD,
)
result2 = client2.query(f"SELECT state_json FROM {TABLE_STATE} WHERE session_id = 'session_ch_001'")
assert len(result2.result_rows) > 0, "State not found after restart!"
recovered2 = json.loads(result2.result_rows[0][0])
print(f" Recovered after restart: agent={recovered2['agent_id']}, tokens_in={recovered2['total_input_tokens']}")
# Update state
recovered2["total_input_tokens"] += 500
client2.insert(
TABLE_STATE,
[["session_ch_001", json.dumps(recovered2)]],
column_names=["session_id", "state_json"],
)
print(f" Updated tokens_in: {recovered2['total_input_tokens']}") # noqa: F541
# --- Cleanup ---
print("\n=== Cleanup ===")
client2.command(f"DROP TABLE IF EXISTS {TABLE_SESSIONS}")
client2.command(f"DROP TABLE IF EXISTS {TABLE_STATE}")
client2.close()
print(" Tables dropped.")
print("\n✅ ClickHouse Persistence — All tests passed!")
@@ -1,128 +0,0 @@
"""
JSON File DefaultSessionStore — Full Persistence Example
Demonstrates:
- Creating a DefaultSessionStore (JSON file-backed)
- Adding messages and metadata
- Verifying data on disk
- Session resume after simulated restart
- Full managed agent state roundtrip (7 data categories)
Requirements:
pip install praisonaiagents
Run:
python json_file_session_store.py
"""
import json
import os
import shutil
import tempfile
import uuid
from praisonaiagents.session.store import DefaultSessionStore
# --- Setup ---
session_dir = os.path.join(tempfile.gettempdir(), f"praison_json_example_{uuid.uuid4().hex[:6]}")
os.makedirs(session_dir, exist_ok=True)
print(f"Session directory: {session_dir}\n")
store = DefaultSessionStore(session_dir=session_dir)
SESSION_ID = f"json-demo-{uuid.uuid4().hex[:8]}"
# --- Phase 1: Add messages ---
print("=== Phase 1: Add Messages ===")
messages = [
("user", "What is machine learning?"),
("assistant", "Machine learning is a subset of AI that enables systems to learn from data."),
("user", "Give me an example."),
("assistant", "Spam filters use ML to classify emails as spam or not spam."),
]
for role, content in messages:
store.add_message(SESSION_ID, role, content)
print(f" Added [{role}]: {content[:60]}")
print()
# --- Phase 2: Store metadata (simulating managed agent state) ---
print("=== Phase 2: Store Metadata ===")
session = store.get_session(SESSION_ID)
session.metadata = {
"agent_id": "json_demo_agent_001",
"agent_version": 2,
"environment_id": "env_local_json",
"total_input_tokens": 450,
"total_output_tokens": 180,
"compute_instance_id": "local_json_demo",
"session_history": [
{"id": SESSION_ID, "status": "idle", "title": "ML chat"},
],
}
store._save_session(session)
print(f" agent_id: {session.metadata['agent_id']}")
print(f" tokens: in={session.metadata['total_input_tokens']}, out={session.metadata['total_output_tokens']}")
print(f" compute: {session.metadata['compute_instance_id']}")
print()
# --- Phase 3: Verify on disk ---
print("=== Phase 3: Verify JSON File on Disk ===")
json_file = os.path.join(session_dir, f"{SESSION_ID}.json")
assert os.path.exists(json_file), f"JSON file not found: {json_file}"
with open(json_file) as f:
data = json.load(f)
print(f" File: {json_file}")
print(f" Session ID: {data['session_id']}")
print(f" Messages: {len(data.get('messages', []))}")
print(f" Metadata agent_id: {data['metadata']['agent_id']}")
print(f" Metadata tokens_in: {data['metadata']['total_input_tokens']}")
assert data["metadata"]["agent_id"] == "json_demo_agent_001"
assert data["metadata"]["total_input_tokens"] == 450
assert len(data["messages"]) == 4
print()
# --- Phase 4: Session Resume ---
print("=== Phase 4: Session Resume (Simulating Restart) ===")
del store
store2 = DefaultSessionStore(session_dir=session_dir)
# Verify session exists
assert store2.session_exists(SESSION_ID), "Session not found after restart!"
# Recover history
history = store2.get_chat_history(SESSION_ID)
print(f" Recovered messages: {len(history)}")
assert len(history) == 4
# Recover metadata
session2 = store2.get_session(SESSION_ID)
meta = session2.metadata
print(f" Recovered agent_id: {meta['agent_id']}")
print(f" Recovered tokens: in={meta['total_input_tokens']}, out={meta['total_output_tokens']}")
print(f" Recovered compute: {meta['compute_instance_id']}")
print(f" Recovered session_history: {len(meta['session_history'])} entries")
assert meta["agent_id"] == "json_demo_agent_001"
assert meta["total_input_tokens"] == 450
assert meta["compute_instance_id"] == "local_json_demo"
# Continue conversation
store2.add_message(SESSION_ID, "user", "What about deep learning?")
store2.add_message(SESSION_ID, "assistant", "Deep learning uses neural networks with many layers.")
final_history = store2.get_chat_history(SESSION_ID)
print(f" Messages after resume: {len(final_history)}")
assert len(final_history) == 6
# --- Cleanup ---
print("\n=== Cleanup ===")
shutil.rmtree(session_dir)
print(" Session directory removed.")
print("\n✅ JSON File DefaultSessionStore — All tests passed!")
+3 -5
View File
@@ -50,12 +50,10 @@ print("=== Qdrant Knowledge Store Demo (Agent-First) ===")
agent = Agent(
name="KnowledgeAssistant",
instructions="You are a helpful assistant with access to technical documentation.",
knowledge=[doc_path],
knowledge={
"sources": [doc_path],
"vector_store": {
"provider": "qdrant",
"url": "http://localhost:6333",
},
"vector_store": "qdrant",
"url": "http://localhost:6333"
}
)
+5 -8
View File
@@ -13,8 +13,7 @@ Expected output:
- Session can be resumed later with same session_id
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
# Create database connection (PostgreSQL + Redis)
my_db = db(
@@ -26,12 +25,10 @@ my_db = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. Be concise.",
memory={
"db": my_db,
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
# Set explicitly for session continuity across runs:
"session_id": "my-persistent-session",
},
db=my_db,
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
# Set explicitly for session continuity across runs:
session_id="my-persistent-session"
)
# Chat - messages are automatically persisted
-123
View File
@@ -1,123 +0,0 @@
"""
MongoDB StateStore — Full Persistence Example
Demonstrates:
- Creating a MongoDB StateStore
- Key-value operations (get/set/delete/exists)
- Complex state persistence (agent metadata, usage tokens)
- Direct MongoDB verification
- Session resume after simulated restart
Requirements:
pip install praisonai pymongo
Docker: MongoDB on localhost:27017 (no auth)
Run:
python mongodb_state_store.py
"""
import uuid
import pymongo
from praisonai.persistence.state.mongodb import MongoDBStateStore
MONGO_URL = "mongodb://localhost:27017"
DATABASE = "praisonai_example"
COLLECTION = f"state_{uuid.uuid4().hex[:8]}"
print(f"MongoDB: {MONGO_URL}")
print(f"Database: {DATABASE}, Collection: {COLLECTION}\n")
store = MongoDBStateStore(url=MONGO_URL, database=DATABASE, collection=COLLECTION)
# --- Phase 1: Basic key-value operations ---
print("=== Phase 1: Basic Key-Value Operations ===")
store.set("agent_id", "mongo_demo_agent_001")
store.set("model", "gpt-4o-mini")
store.set("counter", 42)
print(f" agent_id: {store.get('agent_id')}")
print(f" model: {store.get('model')}")
print(f" counter: {store.get('counter')}")
print(f" exists('agent_id'): {store.exists('agent_id')}")
print(f" exists('missing'): {store.exists('missing')}")
assert store.get("agent_id") == "mongo_demo_agent_001"
assert store.get("counter") == 42
print()
# --- Phase 2: Complex state persistence ---
print("=== Phase 2: Complex State Persistence ===")
managed_state = {
"agent_id": "mongo_demo_agent_001",
"agent_version": 4,
"environment_id": "env_mongo_001",
"total_input_tokens": 800,
"total_output_tokens": 300,
"compute_instance_id": "e2b_mongo_demo",
"session_history": [
{"id": "session_m1", "status": "completed"},
{"id": "session_m2", "status": "idle"},
],
}
store.set("managed_state", managed_state)
recovered = store.get("managed_state")
print(f" agent_id: {recovered['agent_id']}")
print(f" agent_version: {recovered['agent_version']}")
print(f" total_input_tokens: {recovered['total_input_tokens']}")
print(f" compute_instance_id: {recovered['compute_instance_id']}")
print(f" session_history count: {len(recovered['session_history'])}")
assert recovered["agent_id"] == "mongo_demo_agent_001"
assert recovered["total_input_tokens"] == 800
print()
# --- Phase 3: Direct MongoDB Verification ---
print("=== Phase 3: Direct MongoDB Verification ===")
client = pymongo.MongoClient(MONGO_URL)
doc = client[DATABASE][COLLECTION].find_one({"_id": "managed_state"})
print(f" Raw doc _id: {doc['_id']}")
print(f" Raw doc value.agent_id: {doc['value']['agent_id']}")
print(f" Raw doc value.total_input_tokens: {doc['value']['total_input_tokens']}")
assert doc["value"]["compute_instance_id"] == "e2b_mongo_demo"
client.close()
print()
# --- Phase 4: Session Resume ---
print("=== Phase 4: Session Resume (Simulating Restart) ===")
store.close()
store2 = MongoDBStateStore(url=MONGO_URL, database=DATABASE, collection=COLLECTION)
recovered2 = store2.get("managed_state")
assert recovered2 is not None, "State not found after restart!"
print(f" Recovered agent_id: {recovered2['agent_id']}")
print(f" Recovered tokens: in={recovered2['total_input_tokens']}, out={recovered2['total_output_tokens']}")
print(f" Recovered compute: {recovered2['compute_instance_id']}")
# Update
recovered2["total_input_tokens"] += 400
store2.set("managed_state", recovered2)
final = store2.get("managed_state")
print(f" Updated input_tokens: {final['total_input_tokens']}")
assert final["total_input_tokens"] == 1200
# Delete test
store2.delete("counter")
assert not store2.exists("counter")
print(" Delete verified: counter removed")
store2.close()
# --- Cleanup ---
print("\n=== Cleanup ===")
client = pymongo.MongoClient(MONGO_URL)
client[DATABASE].drop_collection(COLLECTION)
client.close()
print(" Collection dropped.")
print("\n✅ MongoDB StateStore — All tests passed!")
@@ -1,136 +0,0 @@
"""
MySQL ConversationStore — Full Persistence Example
Demonstrates:
- Creating a MySQL ConversationStore
- Creating sessions, adding messages
- Direct SQL verification that data is in MySQL
- Session resume after simulated restart
Requirements:
pip install praisonai mysql-connector-python
Docker: MySQL on localhost:3307 (user=root, password=praisontest, database=praisonai)
Run:
python mysql_conversation_store.py
"""
import uuid
import mysql.connector
from praisonai.persistence.conversation.mysql import MySQLConversationStore
from praisonai.persistence.conversation.base import ConversationSession, ConversationMessage
MYSQL_HOST = "localhost"
MYSQL_PORT = 3307
MYSQL_USER = "root"
MYSQL_PASSWORD = "praisontest"
MYSQL_DB = "praisonai"
TABLE_PREFIX = f"example_{uuid.uuid4().hex[:6]}_"
print(f"MySQL: {MYSQL_USER}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}")
print(f"Table prefix: {TABLE_PREFIX}\n")
store = MySQLConversationStore(
host=MYSQL_HOST, port=MYSQL_PORT, user=MYSQL_USER,
password=MYSQL_PASSWORD, database=MYSQL_DB, table_prefix=TABLE_PREFIX,
)
SESSION_ID = f"mysql-demo-{uuid.uuid4().hex[:8]}"
# --- Phase 1: Create session and add messages ---
print("=== Phase 1: Create Session & Add Messages ===")
session = ConversationSession(
session_id=SESSION_ID,
agent_id="mysql_demo_agent",
name="MySQL Demo",
metadata={"agent_version": 7, "model": "gpt-4o-mini"},
)
store.create_session(session)
messages = [
("user", "What makes MySQL popular?"),
("assistant", "MySQL is popular for its speed, reliability, and ease of use in web applications."),
("user", "What about scalability?"),
("assistant", "MySQL supports replication, sharding, and clustering for horizontal scalability."),
]
for role, content in messages:
msg = ConversationMessage(session_id=SESSION_ID, role=role, content=content)
store.add_message(SESSION_ID, msg)
print(f" Added [{role}]: {content[:60]}")
print()
# --- Phase 2: Verify data ---
print("=== Phase 2: Verify Data in MySQL ===")
retrieved = store.get_session(SESSION_ID)
print(f" Session: {retrieved.session_id}")
print(f" Agent ID: {retrieved.agent_id}")
print(f" Metadata: {retrieved.metadata}")
stored = store.get_messages(SESSION_ID)
print(f" Messages: {len(stored)}")
for msg in stored:
print(f" [{msg.role}] {msg.content[:70]}")
print()
# --- Phase 3: Direct SQL Verification ---
print("=== Phase 3: Direct SQL Verification ===")
conn = mysql.connector.connect(
host=MYSQL_HOST, port=MYSQL_PORT, user=MYSQL_USER,
password=MYSQL_PASSWORD, database=MYSQL_DB,
)
cur = conn.cursor()
cur.execute(f"SELECT COUNT(*) FROM {TABLE_PREFIX}messages WHERE session_id = %s", (SESSION_ID,))
count = cur.fetchone()[0]
print(f" Raw SQL message count: {count}")
assert count == 4, f"Expected 4, got {count}"
cur.execute(f"SELECT agent_id FROM {TABLE_PREFIX}sessions WHERE session_id = %s", (SESSION_ID,))
agent_id = cur.fetchone()[0]
print(f" Raw SQL agent_id: {agent_id}")
assert agent_id == "mysql_demo_agent"
conn.close()
print()
# --- Phase 4: Session Resume ---
print("=== Phase 4: Session Resume (Simulating Restart) ===")
store.close()
store2 = MySQLConversationStore(
host=MYSQL_HOST, port=MYSQL_PORT, user=MYSQL_USER,
password=MYSQL_PASSWORD, database=MYSQL_DB, table_prefix=TABLE_PREFIX,
)
resumed = store2.get_session(SESSION_ID)
assert resumed is not None, "Session not found after restart!"
print(f" Resumed session: {resumed.session_id}")
print(f" Metadata preserved: {resumed.metadata}")
msgs = store2.get_messages(SESSION_ID)
print(f" Messages recovered: {len(msgs)}")
assert len(msgs) == 4
# Continue
store2.add_message(SESSION_ID, ConversationMessage(session_id=SESSION_ID, role="user", content="Thanks!"))
store2.add_message(SESSION_ID, ConversationMessage(session_id=SESSION_ID, role="assistant", content="You're welcome!"))
final = store2.get_messages(SESSION_ID)
print(f" Messages after resume: {len(final)}")
assert len(final) == 6
store2.close()
# --- Cleanup ---
print("\n=== Cleanup ===")
conn = mysql.connector.connect(
host=MYSQL_HOST, port=MYSQL_PORT, user=MYSQL_USER,
password=MYSQL_PASSWORD, database=MYSQL_DB,
)
cur = conn.cursor()
cur.execute(f"DROP TABLE IF EXISTS {TABLE_PREFIX}messages")
cur.execute(f"DROP TABLE IF EXISTS {TABLE_PREFIX}sessions")
conn.commit()
conn.close()
print(" Tables dropped.")
print("\n✅ MySQL ConversationStore — All tests passed!")
@@ -1,120 +0,0 @@
"""
PostgreSQL ConversationStore — Full Persistence Example
Demonstrates:
- Creating a PostgreSQL ConversationStore
- Creating sessions, adding messages, storing metadata
- Direct SQL verification that data is in PostgreSQL
- Session resume after simulated restart
Requirements:
pip install praisonai psycopg2-binary
Docker: PostgreSQL on localhost:5432 (user=postgres, password=postgres)
Run:
python postgres_conversation_store.py
"""
import uuid
import psycopg2
from praisonai.persistence.conversation.postgres import PostgresConversationStore
from praisonai.persistence.conversation.base import ConversationSession, ConversationMessage
PG_URL = "postgresql://postgres:postgres@localhost:5432/postgres"
TABLE_PREFIX = f"example_{uuid.uuid4().hex[:6]}_"
print(f"PostgreSQL URL: {PG_URL}")
print(f"Table prefix: {TABLE_PREFIX}\n")
store = PostgresConversationStore(url=PG_URL, table_prefix=TABLE_PREFIX)
SESSION_ID = f"pg-demo-{uuid.uuid4().hex[:8]}"
# --- Phase 1: Create session and add messages ---
print("=== Phase 1: Create Session & Add Messages ===")
session = ConversationSession(
session_id=SESSION_ID,
agent_id="pg_demo_agent",
name="PostgreSQL Demo",
metadata={"agent_version": 5, "model": "gpt-4o"},
)
store.create_session(session)
messages = [
("user", "What are the benefits of PostgreSQL?"),
("assistant", "PostgreSQL offers ACID compliance, extensibility, JSON support, and excellent performance."),
("user", "How does it compare to MySQL?"),
("assistant", "PostgreSQL has better standards compliance and advanced features, while MySQL is simpler for basic use."),
]
for role, content in messages:
msg = ConversationMessage(session_id=SESSION_ID, role=role, content=content)
store.add_message(SESSION_ID, msg)
print(f" Added [{role}]: {content[:60]}")
print()
# --- Phase 2: Verify data in PostgreSQL ---
print("=== Phase 2: Verify Data in PostgreSQL ===")
retrieved = store.get_session(SESSION_ID)
print(f" Session: {retrieved.session_id}")
print(f" Agent ID: {retrieved.agent_id}")
print(f" Metadata: {retrieved.metadata}")
stored = store.get_messages(SESSION_ID)
print(f" Messages: {len(stored)}")
for msg in stored:
print(f" [{msg.role}] {msg.content[:70]}")
print()
# --- Phase 3: Direct SQL Verification ---
print("=== Phase 3: Direct SQL Verification ===")
conn = psycopg2.connect(PG_URL)
cur = conn.cursor()
cur.execute(f"SELECT COUNT(*) FROM public.{TABLE_PREFIX}messages WHERE session_id = %s", (SESSION_ID,))
count = cur.fetchone()[0]
print(f" Raw SQL message count: {count}")
assert count == 4, f"Expected 4, got {count}"
cur.execute(f"SELECT agent_id FROM public.{TABLE_PREFIX}sessions WHERE session_id = %s", (SESSION_ID,))
agent_id = cur.fetchone()[0]
print(f" Raw SQL agent_id: {agent_id}")
assert agent_id == "pg_demo_agent"
conn.close()
print()
# --- Phase 4: Session Resume ---
print("=== Phase 4: Session Resume (Simulating Restart) ===")
store.close()
store2 = PostgresConversationStore(url=PG_URL, table_prefix=TABLE_PREFIX)
resumed = store2.get_session(SESSION_ID)
assert resumed is not None, "Session not found after restart!"
print(f" Resumed session: {resumed.session_id}")
print(f" Metadata preserved: {resumed.metadata}")
msgs = store2.get_messages(SESSION_ID)
print(f" Messages recovered: {len(msgs)}")
assert len(msgs) == 4
# Continue
store2.add_message(SESSION_ID, ConversationMessage(session_id=SESSION_ID, role="user", content="Thanks!"))
store2.add_message(SESSION_ID, ConversationMessage(session_id=SESSION_ID, role="assistant", content="You're welcome!"))
final = store2.get_messages(SESSION_ID)
print(f" Messages after resume: {len(final)}")
assert len(final) == 6
store2.close()
# --- Cleanup ---
print("\n=== Cleanup ===")
conn = psycopg2.connect(PG_URL)
conn.autocommit = True
cur = conn.cursor()
cur.execute(f"DROP TABLE IF EXISTS public.{TABLE_PREFIX}messages CASCADE")
cur.execute(f"DROP TABLE IF EXISTS public.{TABLE_PREFIX}sessions CASCADE")
conn.close()
print(" Tables dropped.")
print("\n✅ PostgreSQL ConversationStore — All tests passed!")
+3 -6
View File
@@ -12,8 +12,7 @@ Expected output:
- Export shows full session data
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
def get_weather(city: str) -> str:
"""Get weather for a city."""
@@ -30,10 +29,8 @@ agent = Agent(
name="WeatherBot",
instructions="You help with weather. Use the get_weather tool when asked.",
tools=[get_weather],
memory={
"db": my_db,
"session_id": "weather-session-001",
},
db=my_db,
session_id="weather-session-001"
)
# First chat - triggers run tracking
+3 -6
View File
@@ -13,8 +13,7 @@ Expected output:
Agent responds with state persisted to Redis
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
print("=== Redis State Store (Agent-First) ===")
@@ -27,10 +26,8 @@ my_db = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
memory={
"db": my_db,
"session_id": "redis-state-session",
},
db=my_db,
session_id="redis-state-session"
)
# Chat - state is automatically managed via Redis
-129
View File
@@ -1,129 +0,0 @@
"""
Redis StateStore — Full Persistence Example
Demonstrates:
- Creating a Redis StateStore
- Key-value operations (get/set/delete/exists)
- JSON state persistence (agent metadata, usage tokens)
- Hash operations for structured data
- Session resume: simulate restart, recover state
Requirements:
pip install praisonai redis
Docker: Redis on localhost:6379 (password=myredissecret)
Run:
python redis_state_store.py
"""
import uuid
import redis as redis_lib
from praisonai.persistence.state.redis import RedisStateStore
REDIS_HOST = "localhost"
REDIS_PORT = 6379
REDIS_PASSWORD = "myredissecret"
PREFIX = f"example_{uuid.uuid4().hex[:6]}:"
print(f"Redis: {REDIS_HOST}:{REDIS_PORT}")
print(f"Key prefix: {PREFIX}\n")
store = RedisStateStore(
host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, prefix=PREFIX,
)
# --- Phase 1: Basic key-value operations ---
print("=== Phase 1: Basic Key-Value Operations ===")
store.set("agent_id", "redis_demo_agent_001")
store.set("model", "gpt-4o-mini")
store.set("counter", "42")
print(f" agent_id: {store.get('agent_id')}")
print(f" model: {store.get('model')}")
print(f" counter: {store.get('counter')}")
print(f" exists('agent_id'): {store.exists('agent_id')}")
print(f" exists('missing'): {store.exists('missing')}")
print()
# --- Phase 2: JSON state persistence ---
print("=== Phase 2: JSON State Persistence ===")
managed_state = {
"agent_id": "redis_demo_agent_001",
"agent_version": 3,
"environment_id": "env_local_001",
"total_input_tokens": 1500,
"total_output_tokens": 600,
"compute_instance_id": "docker_redis_demo",
"session_history": [
{"id": "session_001", "status": "completed", "title": "Weather chat"},
{"id": "session_002", "status": "idle", "title": "Code review"},
],
}
store.set_json("managed_state", managed_state)
recovered = store.get_json("managed_state")
print(f" agent_id: {recovered['agent_id']}")
print(f" agent_version: {recovered['agent_version']}")
print(f" total_input_tokens: {recovered['total_input_tokens']}")
print(f" total_output_tokens: {recovered['total_output_tokens']}")
print(f" compute_instance_id: {recovered['compute_instance_id']}")
print(f" session_history count: {len(recovered['session_history'])}")
assert recovered["agent_id"] == "redis_demo_agent_001"
assert recovered["total_input_tokens"] == 1500
assert recovered["compute_instance_id"] == "docker_redis_demo"
print()
# --- Phase 3: Hash operations ---
print("=== Phase 3: Hash Operations ===")
store.hset("agent_meta", "version", "5")
store.hset("agent_meta", "env_id", "env_abc")
store.hset("agent_meta", "model", "gpt-4o")
print(f" version: {store.hget('agent_meta', 'version')}")
all_meta = store.hgetall("agent_meta")
print(f" all fields: {all_meta}")
print()
# --- Phase 4: Session Resume (Simulating Restart) ---
print("=== Phase 4: Session Resume (Simulating Restart) ===")
store.close()
# New store instance (simulating process restart)
store2 = RedisStateStore(
host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, prefix=PREFIX,
)
# Recover state
recovered2 = store2.get_json("managed_state")
assert recovered2 is not None, "State not found after restart!"
print(f" Recovered agent_id: {recovered2['agent_id']}")
print(f" Recovered tokens: in={recovered2['total_input_tokens']}, out={recovered2['total_output_tokens']}")
print(f" Recovered compute: {recovered2['compute_instance_id']}")
print(f" Recovered sessions: {len(recovered2['session_history'])}")
# Update state (simulate more work)
recovered2["total_input_tokens"] += 500
recovered2["total_output_tokens"] += 200
store2.set_json("managed_state", recovered2)
final = store2.get_json("managed_state")
print(f" Updated tokens: in={final['total_input_tokens']}, out={final['total_output_tokens']}")
assert final["total_input_tokens"] == 2000
assert final["total_output_tokens"] == 800
store2.close()
# --- Cleanup ---
print("\n=== Cleanup ===")
r = redis_lib.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD)
for key in r.keys(f"{PREFIX}*"):
r.delete(key)
r.close()
print(" Keys deleted.")
print("\n✅ Redis StateStore — All tests passed!")
+3 -6
View File
@@ -11,8 +11,7 @@ Expected output:
- Second run: Resumes with history
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
# Create database
my_db = db(
@@ -26,10 +25,8 @@ SESSION_ID = "resume-demo-session"
agent = Agent(
name="MemoryBot",
instructions="You remember our conversation. Be helpful and concise.",
memory={
"db": my_db,
"session_id": SESSION_ID,
},
db=my_db,
session_id=SESSION_ID
)
# Check if this is a new or resumed session
+3 -6
View File
@@ -26,8 +26,7 @@ Expected Output:
Messages persisted: 2
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
# Create database adapter (simplified import)
db_instance = db(
@@ -38,10 +37,8 @@ db_instance = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. Keep responses brief.",
memory={
"db": db_instance,
"session_id": "demo-session-001", # Same session_id = resume conversation
},
db=db_instance,
session_id="demo-session-001", # Same session_id = resume conversation
output="silent"
)
@@ -1,112 +0,0 @@
"""
SQLite ConversationStore — Full Persistence Example
Demonstrates:
- Creating a SQLite ConversationStore
- Creating sessions and adding messages
- Verifying data is stored in the SQLite file
- Session resume: destroy agent, restore from DB, verify state
Requirements:
pip install praisonai
Run:
python sqlite_conversation_store.py
"""
import os
import sqlite3
import tempfile
from praisonai.persistence.conversation.sqlite import SQLiteConversationStore
from praisonai.persistence.conversation.base import ConversationSession, ConversationMessage
# --- Setup ---
db_path = os.path.join(tempfile.gettempdir(), "praison_sqlite_example.db")
print(f"SQLite DB path: {db_path}\n")
store = SQLiteConversationStore(path=db_path)
SESSION_ID = "sqlite-demo-session-001"
# --- Phase 1: Create session and add messages ---
print("=== Phase 1: Create Session & Add Messages ===")
session = ConversationSession(
session_id=SESSION_ID,
agent_id="sqlite_demo_agent",
name="SQLite Demo Session",
metadata={"agent_version": 3, "environment": "local"},
)
store.create_session(session)
messages = [
("user", "Hello! What is the capital of France?"),
("assistant", "The capital of France is Paris."),
("user", "What about Germany?"),
("assistant", "The capital of Germany is Berlin."),
]
for role, content in messages:
msg = ConversationMessage(session_id=SESSION_ID, role=role, content=content)
store.add_message(SESSION_ID, msg)
print(f" Added [{role}]: {content[:50]}")
print()
# --- Phase 2: Verify data in DB ---
print("=== Phase 2: Verify Data in SQLite ===")
retrieved_session = store.get_session(SESSION_ID)
print(f" Session ID: {retrieved_session.session_id}")
print(f" Agent ID: {retrieved_session.agent_id}")
print(f" Metadata: {retrieved_session.metadata}")
stored_messages = store.get_messages(SESSION_ID)
print(f" Messages stored: {len(stored_messages)}")
for msg in stored_messages:
print(f" [{msg.role}] {msg.content[:60]}")
print()
# --- Phase 3: Session Resume ---
print("=== Phase 3: Session Resume (Simulating Restart) ===")
store.close()
# Reopen store from same file (simulating process restart)
store2 = SQLiteConversationStore(path=db_path)
# Verify session still exists
resumed_session = store2.get_session(SESSION_ID)
assert resumed_session is not None, "Session not found after restart!"
print(f" Resumed session: {resumed_session.session_id}")
print(f" Agent ID: {resumed_session.agent_id}")
print(f" Metadata preserved: {resumed_session.metadata}")
# Verify messages
resumed_messages = store2.get_messages(SESSION_ID)
print(f" Messages recovered: {len(resumed_messages)}")
assert len(resumed_messages) == 4, f"Expected 4 messages, got {len(resumed_messages)}"
# Continue the conversation
new_msg = ConversationMessage(session_id=SESSION_ID, role="user", content="What about Spain?")
store2.add_message(SESSION_ID, new_msg)
new_msg2 = ConversationMessage(session_id=SESSION_ID, role="assistant", content="The capital of Spain is Madrid.")
store2.add_message(SESSION_ID, new_msg2)
final_messages = store2.get_messages(SESSION_ID)
print(f" Messages after resume: {len(final_messages)}")
assert len(final_messages) == 6
store2.close()
# --- Phase 4: Direct SQLite verification ---
print("\n=== Phase 4: Direct SQLite Verification ===")
conn = sqlite3.connect(db_path)
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM praison_messages WHERE session_id = ?", (SESSION_ID,))
count = cur.fetchone()[0]
print(f" Raw SQL message count: {count}")
assert count == 6
conn.close()
# Cleanup
os.remove(db_path)
print("\n✅ SQLite ConversationStore — All tests passed!")
+3 -6
View File
@@ -11,8 +11,7 @@ Expected output:
- Data persisted to local SQLite file
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
import tempfile
import os
@@ -26,10 +25,8 @@ print(f"Database: {db_path}")
agent = Agent(
name="LocalBot",
instructions="You are a helpful assistant.",
memory={
"db": my_db,
"session_id": "local-session",
},
db=my_db,
session_id="local-session"
)
# Chat
+1 -1
View File
@@ -57,8 +57,8 @@ def main():
agent = Agent(
name="SecureAgent",
instructions="You are a file management assistant.",
policy=engine
)
agent.policy = engine
print("\n--- Agent with Policy Enforcement Created ---")
print(f"Agent: {agent.name}")
+2 -29
View File
@@ -21,36 +21,9 @@ def search_web(query: str) -> str:
return f"Search results for: {query}"
def calculate(expression: str) -> str:
"""Calculate a mathematical expression safely."""
import ast
import operator
_OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
def _eval(node):
if isinstance(node, ast.Expression):
return _eval(node.body)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.UnaryOp) and type(node.op) in _OPS:
return _OPS[type(node.op)](_eval(node.operand))
if isinstance(node, ast.BinOp) and type(node.op) in _OPS:
return _OPS[type(node.op)](_eval(node.left), _eval(node.right))
raise ValueError(f"Unsupported expression: {ast.dump(node)}")
"""Calculate a mathematical expression."""
try:
tree = ast.parse(expression, mode="eval")
return f"Result: {_eval(tree)}"
return f"Result: {eval(expression)}"
except Exception:
return "Invalid expression"
-27
View File
@@ -1,27 +0,0 @@
#!/usr/bin/env python3
"""Minimal A2UI example — agent + send_a2ui_messages tool.
Install:
pip install praisonaiagents[a2ui]
Run:
python send_surface.py
For live rendering, use PraisonAIUI with AG-UI (/agui) — see integrate-a2ui-frontend docs.
"""
from praisonaiagents import Agent
from praisonaiagents.tools.a2ui_tools import send_a2ui_messages
agent = Agent(
name="ui-assistant",
instructions=(
"When asked for UI, call send_a2ui_messages with A2UI v0.9 JSON "
"(createSurface on surface id 'main')."
),
tools=[send_a2ui_messages],
)
if __name__ == "__main__":
result = agent.start("Show a simple welcome card on surface main")
print(result)
@@ -60,10 +60,9 @@ agents_system = AgentTeam(
process="sequential", output="verbose"
)
if __name__ == "__main__":
print("Starting hierarchical agent generation...")
result = agents_system.start()
print("Starting hierarchical agent generation...")
result = agents_system.start()
print(f"\nHierarchical Result: {str(result)[:200]}...")
print("\n✅ AutoAgents hierarchical generation complete!")
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
print(f"\nHierarchical Result: {result[:200]}...")
print("\n✅ AutoAgents hierarchical generation complete!")
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
+2 -3
View File
@@ -1,5 +1,4 @@
from praisonaiagents import Agent
if __name__ == "__main__":
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
agent.launch(port=8080, protocol="mcp", path="/")
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
agent.launch(port=8080, protocol="mcp")
+2 -11
View File
@@ -90,18 +90,9 @@ def main():
if result:
print("\n🔍 Analysis Results:")
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
for task_id, task_result in result["task_results"].items():
print(f"\nTask {task_id}:")
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
print(task_result.raw)
else:
print("❌ Failed to capture and analyze camera feed")
+1 -1
View File
@@ -75,7 +75,7 @@ class ContinuousCameraMonitor:
agents=[self.vision_agent],
tasks=[task],
process="sequential",
output="silent", # Reduced verbosity for continuous monitoring
verbose=0 # Reduced verbosity for continuous monitoring
)
return agents.start()
+2 -11
View File
@@ -148,19 +148,10 @@ def main():
"scene_analysis": "🌍 SCENE ANALYSIS"
}
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
for task_id, task_result in result["task_results"].items():
print(f"\n{agent_names.get(task_id, f'Task {task_id}')}:")
print("-" * 40)
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
print(task_result.raw)
print("\n" + "="*60)
print("✅ Multi-agent analysis complete!")
@@ -34,5 +34,5 @@ print(f"\nChanged to: {handler.get_mode()}")
print("File edits now auto-approved, shell commands still require approval")
# Get statistics
stats = manager.get_stats()
stats = handler.get_stats()
print(f"\nStats: {stats}")
+1 -1
View File
@@ -31,4 +31,4 @@ print(f"Total cost: ${summary['total_cost']:.4f}")
print(f"Avg cost/request: ${summary['avg_cost_per_request']:.4f}")
# Display formatted (uses Rich if available)
handler.print_summary()
handler.display_summary()
@@ -8,7 +8,7 @@ Docs: https://docs.praison.ai/cli/git-integration
from praisonai.cli.features import GitIntegrationHandler
# Initialize
handler = GitIntegrationHandler(verbose=True)
handler = GitIntegrationHandler(output="verbose")
git = handler.initialize(repo_path=".")
# Check if it's a git repo
+1 -1
View File
@@ -8,7 +8,7 @@ Docs: https://docs.praison.ai/cli/repo-map
from praisonai.cli.features import RepoMapHandler
# Initialize with current directory
handler = RepoMapHandler(verbose=True)
handler = RepoMapHandler(output="verbose")
repo_map = handler.initialize(root=".")
# Get the repository map
@@ -9,7 +9,7 @@ from praisonai.cli.features import SandboxExecutorHandler
from praisonai.cli.features.sandbox_executor import SandboxMode
# Initialize with basic sandbox
handler = SandboxExecutorHandler(verbose=True)
handler = SandboxExecutorHandler(output="verbose")
sandbox = handler.initialize(mode="basic")
print(f"Sandbox mode: {handler.get_mode()}")
@@ -41,9 +41,9 @@ def my_custom_handler(args, context):
custom_cmd = SlashCommand(
name="mycommand",
description="My custom command",
action=my_custom_handler,
kind=CommandKind.CUSTOM,
alt_names=["mc"]
handler=my_custom_handler,
kind=CommandKind.ACTION,
aliases=["mc"]
)
handler.register(custom_cmd)
+2 -1
View File
@@ -25,7 +25,8 @@ pdf_agent = Agent(
goal="Help users understand and extract information from PDF documents",
backstory="You are an expert at reading, analyzing, and answering questions about PDF documents. You provide accurate, detailed answers based on the document content.",
instructions="Read the provided PDF document and answer questions about its content. Be specific and cite relevant sections when possible.",
knowledge={**config, "sources": ["document.pdf"]}, # Replace with your PDF path
knowledge=["document.pdf"], # Replace with your PDF file path
knowledge=config,
reflection=True,
Loaded 100 of 1175 files, more files were not shown because too many files have changed in this diff. Show more