mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-08 08:11:07 -10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe87b9514e | ||
|
|
fec0b5fa32 | ||
|
|
c8a3881dda |
No files matched your search
@@ -70,8 +70,6 @@ runs:
|
||||
|
||||
- name: Install GitHub MCP Server
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
run: |
|
||||
claude mcp add-json github '{
|
||||
"command": "docker",
|
||||
@@ -84,7 +82,7 @@ runs:
|
||||
"ghcr.io/github/github-mcp-server:sha-ff3036d"
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "'"${INPUT_GITHUB_TOKEN}"'"
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ inputs.github_token }}"
|
||||
}
|
||||
}'
|
||||
|
||||
@@ -209,44 +207,21 @@ runs:
|
||||
User Request:
|
||||
$USER_REQUEST
|
||||
|
||||
Please analyze the request and provide helpful assistance.
|
||||
|
||||
Please analyze the request and provide helpful assistance. You have access to repository tools and can help with:
|
||||
- Code analysis and implementation
|
||||
- GitHub workflows and automation
|
||||
- Pull request reviews and feedback
|
||||
- Issue resolution and bug fixes
|
||||
- Documentation updates
|
||||
- Testing and deployment
|
||||
|
||||
CRITICAL INSTRUCTION: If your task involves modifying codebase files to add a feature or fix a bug:
|
||||
|
||||
STEP 0 — SETUP GIT IDENTITY:
|
||||
git config user.name "MervinPraison"
|
||||
git config user.email "[email protected]"
|
||||
|
||||
STEP 1 — READ GUIDELINES:
|
||||
MANDATORY: Before writing any code, you MUST carefully read "src/praisonai-agents/AGENTS.md" to understand this repository's extremely specific Architecture, Protocol-First design rules, and Agent-Centric Philosophy.
|
||||
|
||||
STEP 2 — ARCHITECTURE VALIDATION & ROUTING (MANDATORY before writing code):
|
||||
Before implementing anything, answer these questions:
|
||||
- CORE vs WRAPPER vs TOOLS ROUTING:
|
||||
1. Core SDK (praisonaiagents/): Only core protocols, base classes, decorators. No heavy implementations.
|
||||
2. Wrapper (praisonai/): CLI, heavy implementations, optional dependencies.
|
||||
3. Tools (PraisonAI-Tools): Specific integrations, external tools (e.g. SurrealDB, Slack), and community extensions MUST go to the `MervinPraison/PraisonAI-Tools` repository. If the feature is a tool, clone `https://github.com/MervinPraison/PraisonAI-Tools`, implement it there, and create the PR using `gh pr create -R MervinPraison/PraisonAI-Tools`.
|
||||
- Does it duplicate existing functionality? Check if Agent already supports this via existing params (reflection, planning, tools, hooks, memory).
|
||||
- Does it inherit from Agent properly? New agent types MUST inherit Agent, not wrap it with composition.
|
||||
- Does it add new dependencies? Only optional deps allowed, must be lazy-imported.
|
||||
- Will agent.py grow larger? If the change adds >50 lines to agent.py, find a way to extract instead.
|
||||
- Is there a name collision with existing exports in __init__.py?
|
||||
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:
|
||||
- Create a new git branch appropriately named (e.g. \`feature/issue-<number>\`)
|
||||
- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper
|
||||
- Keep changes small and backward-compatible
|
||||
|
||||
STEP 4 — TEST:
|
||||
- Run: cd src/praisonai-agents && PYTHONPATH=. python -m pytest tests/ -x -q --timeout=30
|
||||
- Ensure no regressions
|
||||
|
||||
STEP 5 — CREATE PR:
|
||||
- Commit with descriptive message, push the branch
|
||||
- Use the GitHub CLI (\`gh pr create --title "..." --body "Fixes #<number>"\`) to open a Pull Request
|
||||
CRITICAL: You MUST create the PR automatically using \`gh pr create\`. Do NOT just provide a link.
|
||||
|
||||
1. ALWAYS create a new git branch appropriately named (e.g. \`feature/issue-<number>\`).
|
||||
2. MANDATORY: Before writing any code, you MUST carefully read "src/praisonai-agents/AGENTS.md" to understand this repository's extremely specific Architecture, Protocol-First design rules, and Agent-Centric Philosophy.
|
||||
3. Make your necessary code changes honoring the rules in AGENTS.md.
|
||||
4. Commit your changes and push the branch to the remote repository.
|
||||
5. Use the GitHub CLI (\`gh pr create --title "..." --body "Fixes #<number>"\`) to open a Pull Request containing your work.
|
||||
|
||||
Respond naturally and helpfully to the user's request using the available tools.
|
||||
EOF
|
||||
|
||||
@@ -256,14 +231,6 @@ runs:
|
||||
- name: Run Claude Code
|
||||
if: env.SKIP_EXECUTION != 'true'
|
||||
shell: bash
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }}
|
||||
GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
INPUT_MAX_TURNS: ${{ inputs.max_turns }}
|
||||
INPUT_ALLOWED_TOOLS: ${{ inputs.allowed_tools }}
|
||||
INPUT_DISALLOWED_TOOLS: ${{ inputs.disallowed_tools }}
|
||||
INPUT_TIMEOUT_MINUTES: ${{ inputs.timeout_minutes }}
|
||||
run: |
|
||||
echo "🚀 Running Claude Code with GitHub context..."
|
||||
|
||||
@@ -271,26 +238,30 @@ runs:
|
||||
CMD_ARGS=("-p" "--verbose" "--output-format" "stream-json")
|
||||
|
||||
# Add max turns if specified
|
||||
if [[ -n "$INPUT_MAX_TURNS" ]]; then
|
||||
CMD_ARGS+=("--max-turns" "$INPUT_MAX_TURNS")
|
||||
if [[ -n "${{ inputs.max_turns }}" ]]; then
|
||||
CMD_ARGS+=("--max-turns" "${{ inputs.max_turns }}")
|
||||
fi
|
||||
|
||||
# Add allowed tools (include GitHub tools by default)
|
||||
TOOLS="mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues,mcp__github__create_comment,Read,Write,Edit,Bash"
|
||||
if [[ -n "$INPUT_ALLOWED_TOOLS" ]]; then
|
||||
TOOLS="$TOOLS,$INPUT_ALLOWED_TOOLS"
|
||||
if [[ -n "${{ inputs.allowed_tools }}" ]]; then
|
||||
TOOLS="$TOOLS,${{ inputs.allowed_tools }}"
|
||||
fi
|
||||
CMD_ARGS+=("--allowedTools" "$TOOLS")
|
||||
|
||||
# Add disallowed tools
|
||||
if [[ -n "$INPUT_DISALLOWED_TOOLS" ]]; then
|
||||
CMD_ARGS+=("--disallowedTools" "$INPUT_DISALLOWED_TOOLS")
|
||||
if [[ -n "${{ inputs.disallowed_tools }}" ]]; then
|
||||
CMD_ARGS+=("--disallowedTools" "${{ inputs.disallowed_tools }}")
|
||||
fi
|
||||
|
||||
echo "📝 Executing Claude Code with prompt from file..."
|
||||
|
||||
# Execute Claude Code with timeout, using stdin for the prompt
|
||||
TIMEOUT_SECONDS=$(($INPUT_TIMEOUT_MINUTES * 60))
|
||||
timeout $TIMEOUT_SECONDS claude "${CMD_ARGS[@]}" < "$PROMPT_FILE"
|
||||
TIMEOUT_SECONDS=$((${{ inputs.timeout_minutes }} * 60))
|
||||
timeout $TIMEOUT_SECONDS claude "${CMD_ARGS[@]}" < "${{ env.PROMPT_FILE }}"
|
||||
|
||||
echo "✅ Claude Code execution completed"
|
||||
echo "✅ Claude Code execution completed"
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }}
|
||||
GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
@@ -19,7 +19,6 @@ runs:
|
||||
- name: Checkout repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Create prompt
|
||||
|
||||
@@ -57,15 +57,13 @@ runs:
|
||||
|
||||
- name: Install Gemini CLI
|
||||
id: install
|
||||
env:
|
||||
INPUT_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
if [[ "$INPUT_VERSION" == "latest" || "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9\.-]+)?(\+[a-zA-Z0-9\.-]+)?$ ]]; then
|
||||
echo "Installing Gemini CLI from npm: @google/gemini-cli@$INPUT_VERSION"
|
||||
npm install -g @google/gemini-cli@$INPUT_VERSION
|
||||
if [[ "${{ inputs.version }}" == "latest" || "${{ inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9\.-]+)?(\+[a-zA-Z0-9\.-]+)?$ ]]; then
|
||||
echo "Installing Gemini CLI from npm: @google/gemini-cli@${{ inputs.version }}"
|
||||
npm install -g @google/gemini-cli@${{ inputs.version }}
|
||||
else
|
||||
echo "Installing Gemini CLI from GitHub: github:google-gemini/gemini-cli#$INPUT_VERSION"
|
||||
npm install -g github:google-gemini/gemini-cli#$INPUT_VERSION
|
||||
echo "Installing Gemini CLI from GitHub: github:google-gemini/gemini-cli#${{ inputs.version }}"
|
||||
npm install -g github:google-gemini/gemini-cli#${{ inputs.version }}
|
||||
fi
|
||||
echo "Verifying installation:"
|
||||
if command -v gemini >/dev/null 2>&1; then
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 270 KiB |
@@ -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".
|
||||
@@ -1,116 +0,0 @@
|
||||
framework: "praisonai"
|
||||
topic: "Pull Request Code Review Analysis"
|
||||
|
||||
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]
|
||||
@@ -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.
|
||||
@@ -1,14 +1,11 @@
|
||||
name: Auto PR Comment
|
||||
|
||||
# Review chain: CodeRabbit/Qodo/Gemini review first → Copilot → Claude (final)
|
||||
# For human PRs: CodeRabbit/Qodo auto-review → this workflow triggers Copilot → then Claude
|
||||
# Review chain: CodeRabbit/Qodo/Gemini review first → THEN trigger Copilot
|
||||
# For human PRs: CodeRabbit/Qodo auto-review → this workflow triggers Copilot
|
||||
# For bot PRs: CodeRabbit/Qodo skip bots, so we trigger them explicitly first
|
||||
#
|
||||
# IMPORTANT: @copilot ignores bot comments. All @copilot mentions must use
|
||||
# PAT_TOKEN so the comment posts as MervinPraison (a real user).
|
||||
#
|
||||
# Claude trigger runs IN-BAND (same workflow) to avoid GitHub Actions
|
||||
# blocking bot-triggered workflows with action_required approval gates.
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
@@ -17,11 +14,6 @@ on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
|
||||
env:
|
||||
MAX_POLL_ATTEMPTS: 20
|
||||
POLL_DELAY_MS: 30000
|
||||
CLAUDE_TRIGGER_LOGINS: '["MervinPraison","github-actions[bot]"]'
|
||||
|
||||
jobs:
|
||||
# ================================================================
|
||||
# HUMAN PRs: Trigger @copilot AFTER CodeRabbit posts its summary
|
||||
@@ -31,16 +23,12 @@ 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
|
||||
outputs:
|
||||
pr_number: ${{ steps.trigger.outputs.pr_number }}
|
||||
triggered: ${{ steps.trigger.outputs.triggered }}
|
||||
steps:
|
||||
- name: Post Copilot review request (as user via PAT)
|
||||
id: trigger
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
@@ -57,18 +45,14 @@ jobs:
|
||||
);
|
||||
if (alreadyPosted) {
|
||||
console.log('Copilot already triggered, skipping');
|
||||
core.setOutput('triggered', 'false');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above from Qodo, Coderabbit, and Gemini first — incorporate their findings.\n\nReview areas:\n1. **Bloat check**: Are changes minimal and focused? Any unnecessary code or scope creep?\n2. **Security**: Any hardcoded secrets, unsafe eval/exec, missing input validation?\n3. **Performance**: Any module-level heavy imports? Hot-path regressions?\n4. **Tests**: Are tests included? Do they cover the changes adequately?\n5. **Backward compat**: Any public API changes without deprecation?\n6. **Code quality**: DRY violations, naming conventions, error handling?\n7. **Address reviewer feedback**: If Qodo, Coderabbit, or Gemini flagged valid issues, include them in your review\n8. Suggest specific improvements with code examples where possible'
|
||||
});
|
||||
core.setOutput('triggered', 'true');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
})
|
||||
|
||||
# Fallback: Trigger @copilot after Qodo review (if Coderabbit doesn't fire)
|
||||
copilot-after-qodo:
|
||||
@@ -78,12 +62,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
outputs:
|
||||
pr_number: ${{ steps.trigger.outputs.pr_number }}
|
||||
triggered: ${{ steps.trigger.outputs.triggered }}
|
||||
steps:
|
||||
- name: Post Copilot review request (as user via PAT)
|
||||
id: trigger
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
@@ -99,377 +79,14 @@ jobs:
|
||||
);
|
||||
if (alreadyPosted) {
|
||||
console.log('Copilot already triggered, skipping');
|
||||
core.setOutput('triggered', 'false');
|
||||
core.setOutput('pr_number', context.issue.number.toString());
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above first.\n\nReview areas:\n1. **Bloat check**: Are changes minimal and focused?\n2. **Security**: Any hardcoded secrets, unsafe eval/exec, missing input validation?\n3. **Performance**: Any module-level heavy imports? Hot-path regressions?\n4. **Tests**: Are tests included? Do they cover the changes adequately?\n5. **Backward compat**: Any public API changes without deprecation?\n6. **Code quality**: DRY violations, naming conventions, error handling?\n7. Suggest specific improvements with code examples where possible'
|
||||
});
|
||||
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
|
||||
# the action_required approval gate that blocks bot-triggered workflows.
|
||||
# ================================================================
|
||||
claude-after-copilot:
|
||||
needs: [copilot-after-coderabbit]
|
||||
if: |
|
||||
always() &&
|
||||
needs.copilot-after-coderabbit.result == 'success' &&
|
||||
needs.copilot-after-coderabbit.outputs.triggered == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Wait for Copilot to respond (poll)
|
||||
id: poll
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
MAX_POLL_ATTEMPTS: ${{ env.MAX_POLL_ATTEMPTS }}
|
||||
POLL_DELAY_MS: ${{ env.POLL_DELAY_MS }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
// Poll for Copilot's response — check every 30s for up to 10 minutes
|
||||
const maxAttempts = parseInt(process.env.MAX_POLL_ATTEMPTS, 10);
|
||||
const delayMs = parseInt(process.env.POLL_DELAY_MS, 10);
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
console.log(`Poll attempt ${i + 1}/${maxAttempts}...`);
|
||||
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
owner,
|
||||
repo,
|
||||
per_page: 50,
|
||||
sort: 'created',
|
||||
direction: 'desc'
|
||||
});
|
||||
|
||||
// Look for Copilot's response (copilot-swe-agent or Copilot)
|
||||
const copilotResponse = comments.data.find(c =>
|
||||
(c.user.login === 'copilot-swe-agent' ||
|
||||
c.user.login === 'Copilot' ||
|
||||
c.user.login.toLowerCase().includes('copilot')) &&
|
||||
c.user.type === 'Bot'
|
||||
);
|
||||
|
||||
if (copilotResponse) {
|
||||
console.log(`Copilot responded at ${copilotResponse.created_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
// Also check PR reviews for Copilot
|
||||
const reviews = await github.rest.pulls.listReviews({
|
||||
pull_number: pr,
|
||||
owner,
|
||||
repo
|
||||
});
|
||||
|
||||
const copilotReview = reviews.data.find(r =>
|
||||
r.user.login.toLowerCase().includes('copilot')
|
||||
);
|
||||
|
||||
if (copilotReview) {
|
||||
console.log(`Copilot review submitted at ${copilotReview.submitted_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
if (i < maxAttempts - 1) {
|
||||
console.log(`Copilot hasn't responded yet, waiting ${delayMs/1000}s...`);
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Copilot did not respond within timeout, triggering Claude anyway');
|
||||
core.setOutput('copilot_responded', 'timeout');
|
||||
|
||||
- name: Check if Claude already triggered
|
||||
id: check_claude
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
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');
|
||||
|
||||
- name: Trigger Claude final review
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-coderabbit.outputs.pr_number }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
await github.rest.issues.createComment({
|
||||
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'
|
||||
});
|
||||
console.log(`Claude triggered on PR #${pr}`);
|
||||
|
||||
# ================================================================
|
||||
# Claude after Qodo fallback path (same polling approach)
|
||||
# ================================================================
|
||||
claude-after-copilot-qodo:
|
||||
needs: [copilot-after-qodo]
|
||||
if: |
|
||||
always() &&
|
||||
needs.copilot-after-qodo.result == 'success' &&
|
||||
needs.copilot-after-qodo.outputs.triggered == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Wait for Copilot to respond (poll)
|
||||
id: poll
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-qodo.outputs.pr_number }}
|
||||
MAX_POLL_ATTEMPTS: ${{ env.MAX_POLL_ATTEMPTS }}
|
||||
POLL_DELAY_MS: ${{ env.POLL_DELAY_MS }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const maxAttempts = parseInt(process.env.MAX_POLL_ATTEMPTS, 10);
|
||||
const delayMs = parseInt(process.env.POLL_DELAY_MS, 10);
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
console.log(`Poll attempt ${i + 1}/${maxAttempts}...`);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr, owner, repo,
|
||||
per_page: 50, sort: 'created', direction: 'desc'
|
||||
});
|
||||
const copilotResponse = comments.data.find(c =>
|
||||
(c.user.login === 'copilot-swe-agent' ||
|
||||
c.user.login === 'Copilot' ||
|
||||
c.user.login.toLowerCase().includes('copilot')) &&
|
||||
c.user.type === 'Bot'
|
||||
);
|
||||
if (copilotResponse) {
|
||||
console.log(`Copilot responded at ${copilotResponse.created_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
const reviews = await github.rest.pulls.listReviews({
|
||||
pull_number: pr, owner, repo
|
||||
});
|
||||
const copilotReview = reviews.data.find(r =>
|
||||
r.user.login.toLowerCase().includes('copilot')
|
||||
);
|
||||
if (copilotReview) {
|
||||
console.log(`Copilot review submitted at ${copilotReview.submitted_at}`);
|
||||
core.setOutput('copilot_responded', 'true');
|
||||
return;
|
||||
}
|
||||
if (i < maxAttempts - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
}
|
||||
}
|
||||
console.log('Copilot did not respond within timeout, triggering Claude anyway');
|
||||
core.setOutput('copilot_responded', 'timeout');
|
||||
|
||||
- name: Check if Claude already triggered
|
||||
id: check_claude
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-qodo.outputs.pr_number }}
|
||||
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
|
||||
const comments = await github.rest.issues.listComments({
|
||||
issue_number: pr,
|
||||
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');
|
||||
|
||||
- name: Trigger Claude final review
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ needs.copilot-after-qodo.outputs.pr_number }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const pr = parseInt(process.env.PR_NUMBER, 10);
|
||||
await github.rest.issues.createComment({
|
||||
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'
|
||||
});
|
||||
console.log(`Claude triggered on PR #${pr}`);
|
||||
|
||||
# ================================================================
|
||||
# Claude after Gemini — restores the trigger lost from the deleted
|
||||
# chain-claude-after-copilot.yml workflow.
|
||||
# Fires when Gemini posts a review comment on a PR or issue.
|
||||
# ================================================================
|
||||
claude-after-gemini:
|
||||
if: |
|
||||
github.event_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.user.login == 'gemini-code-assist[bot]' ||
|
||||
contains(github.event.comment.body, 'Review completed by Gemini CLI') ||
|
||||
contains(github.event.comment.body, 'The fix is ready for review. Please test')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- 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');
|
||||
|
||||
- name: Post Claude analysis request (Lead Engineer)
|
||||
if: steps.check_claude.outputs.already_triggered != 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
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'
|
||||
});
|
||||
})
|
||||
|
||||
# ================================================================
|
||||
# BOT PRs: CodeRabbit/Qodo skip bot-authored PRs by default.
|
||||
@@ -489,7 +106,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 +129,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
|
||||
|
||||
@@ -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
|
||||
@@ -37,8 +35,6 @@ jobs:
|
||||
|
||||
- name: Run Import Time Benchmark
|
||||
id: import_time
|
||||
env:
|
||||
FAIL_ON_REGRESSION: ${{ inputs.fail_on_regression }}
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/import_time.py 2>&1 | tee import_time_output.txt
|
||||
@@ -50,7 +46,7 @@ jobs:
|
||||
# Check threshold (200ms)
|
||||
if (( $(echo "$IMPORT_TIME > 200" | bc -l) )); then
|
||||
echo "::warning::Import time ${IMPORT_TIME}ms exceeds 200ms threshold"
|
||||
if [ "$FAIL_ON_REGRESSION" == "true" ]; then
|
||||
if [ "${{ inputs.fail_on_regression }}" == "true" ]; then
|
||||
echo "import_passed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
@@ -59,8 +55,6 @@ jobs:
|
||||
|
||||
- name: Run Memory Usage Benchmark
|
||||
id: memory
|
||||
env:
|
||||
FAIL_ON_REGRESSION: ${{ inputs.fail_on_regression }}
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/memory_usage.py 2>&1 | tee memory_output.txt
|
||||
@@ -72,7 +66,7 @@ jobs:
|
||||
# Check threshold (30MB)
|
||||
if (( $(echo "$PEAK_MEMORY > 30" | bc -l) )); then
|
||||
echo "::warning::Memory usage ${PEAK_MEMORY}MB exceeds 30MB threshold"
|
||||
if [ "$FAIL_ON_REGRESSION" == "true" ]; then
|
||||
if [ "${{ inputs.fail_on_regression }}" == "true" ]; then
|
||||
echo "memory_passed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
@@ -101,11 +95,9 @@ jobs:
|
||||
|
||||
- name: Run Parameter Impact Benchmark
|
||||
id: param_impact
|
||||
env:
|
||||
INPUT_ITERATIONS: ${{ inputs.iterations }}
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/param_impact_benchmark.py --no-save --iterations "$INPUT_ITERATIONS" 2>&1 | tee param_impact_output.txt
|
||||
python benchmarks/param_impact_benchmark.py --no-save --iterations ${{ inputs.iterations }} 2>&1 | tee param_impact_output.txt
|
||||
|
||||
- name: Check Lazy Imports
|
||||
id: lazy_imports
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Chain Claude After Reviewers
|
||||
|
||||
# Event-driven: triggers when Copilot finishes reviewing a PR
|
||||
on:
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
# Trigger Claude after Copilot finishes on a PR — Claude is the FINAL reviewer
|
||||
# Claude reads ALL previous reviews (Qodo, Coderabbit, Copilot) and fixes any valid issues
|
||||
claude-final-review:
|
||||
if: |
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.user.login, 'copilot')) ||
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.user.login, 'copilot'))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Post Claude final review + fix request
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
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'
|
||||
})
|
||||
|
||||
# Trigger Claude after Gemini finishes on an Issue or PR
|
||||
claude-after-gemini:
|
||||
if: |
|
||||
github.event_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.user.login == 'gemini-code-assist[bot]' ||
|
||||
contains(github.event.comment.body, 'Review completed by Gemini CLI') ||
|
||||
contains(github.event.comment.body, 'The fix is ready for review. Please test')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Post Claude analysis request (final reviewer)
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
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'
|
||||
})
|
||||
@@ -15,18 +15,15 @@ jobs:
|
||||
issues: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Run Claude Issue Triage
|
||||
uses: ./.github/actions/claude-issue-triage-action
|
||||
|
||||
+16
-200
@@ -11,116 +11,10 @@ on:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
# ================================================================
|
||||
# Job 1: Lightweight triage for ALL new issues (including external users)
|
||||
# Posts welcome comment + adds triage labels. No Claude code execution.
|
||||
# ================================================================
|
||||
issue-triage:
|
||||
if: |
|
||||
github.event_name == 'issues' &&
|
||||
github.event.action == 'opened' &&
|
||||
!contains(toJSON(github.event.issue.labels), 'claude')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Triage and acknowledge issue
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const title = (issue.title || '').toLowerCase();
|
||||
const body = (issue.body || '').toLowerCase();
|
||||
const content = title + ' ' + body;
|
||||
const isOwner = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(
|
||||
issue.author_association
|
||||
);
|
||||
|
||||
// --- Label triage ---
|
||||
const labels = [];
|
||||
|
||||
// Kind labels
|
||||
if (content.includes('feature request') || content.includes('[feature')) {
|
||||
labels.push('enhancement');
|
||||
} else if (content.includes('bug') || content.includes('error') || content.includes('crash') || content.includes('traceback')) {
|
||||
labels.push('bug');
|
||||
} else if (content.includes('question') || content.includes('how do i') || content.includes('how to')) {
|
||||
labels.push('question');
|
||||
}
|
||||
|
||||
// Area labels
|
||||
if (content.includes('security') || content.includes('cve') || content.includes('vulnerability')) {
|
||||
labels.push('security');
|
||||
}
|
||||
if (content.includes('performance') || content.includes('slow') || content.includes('memory leak')) {
|
||||
labels.push('performance');
|
||||
}
|
||||
if (content.includes('documentation') || content.includes('docs')) {
|
||||
labels.push('documentation');
|
||||
}
|
||||
if (content.includes('typescript') || content.includes('javascript') || content.includes('npm')) {
|
||||
labels.push('javascript');
|
||||
}
|
||||
|
||||
// Apply labels
|
||||
if (labels.length > 0) {
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels
|
||||
});
|
||||
}
|
||||
|
||||
// --- Acknowledgment comment (external users only) ---
|
||||
if (!isOwner) {
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: [
|
||||
`👋 Thanks for opening this issue, @${issue.user.login}!`,
|
||||
'',
|
||||
'A maintainer will review this shortly. In the meantime:',
|
||||
'- Make sure you\'ve included steps to reproduce (for bugs)',
|
||||
'- Check [existing issues](https://github.com/MervinPraison/PraisonAI/issues) for duplicates',
|
||||
'- Review the [documentation](https://docs.praison.ai) for related guides',
|
||||
'',
|
||||
'_A maintainer can trigger deeper analysis by commenting `@claude` on this issue._'
|
||||
].join('\n')
|
||||
});
|
||||
}
|
||||
|
||||
// --- For owner: add 'claude' label to trigger the code-fix job ---
|
||||
if (isOwner) {
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['claude']
|
||||
});
|
||||
}
|
||||
|
||||
# ================================================================
|
||||
# Job 2: Full Claude code-fix (owner/collaborator only)
|
||||
# Triggers on: labeled 'claude', @claude comments, assignments
|
||||
# ================================================================
|
||||
claude-response:
|
||||
# Allow: human users, github-actions[bot] (auto-comments/labels)
|
||||
# Block: dependabot, cursor, renovate, other bots
|
||||
# Skip 'opened' events — triage job handles those above
|
||||
if: |
|
||||
github.event.action != 'opened' &&
|
||||
(github.event.action != 'labeled' || github.event.label.name == 'claude') &&
|
||||
(github.event_name != 'issue_comment' || contains(github.event.comment.body, '@claude')) &&
|
||||
(
|
||||
@@ -139,136 +33,58 @@ jobs:
|
||||
actions: read
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CLAUDE_APP_ID }}
|
||||
private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- name: Check for Fork PR
|
||||
id: check_fork
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const isPR = context.eventName === 'pull_request' || (context.payload.issue && context.payload.issue.pull_request);
|
||||
if (isPR) {
|
||||
const prNumber = context.payload.pull_request ? context.payload.pull_request.number : context.payload.issue.number;
|
||||
const pr = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
core.setOutput('pr_branch', pr.data.head.ref);
|
||||
|
||||
if (pr.data.head.repo && pr.data.head.repo.full_name !== context.repo.full_name) {
|
||||
core.setOutput('is_fork', 'true');
|
||||
core.setOutput('clone_url', pr.data.head.repo.clone_url);
|
||||
core.setOutput('branch', pr.data.head.ref);
|
||||
return;
|
||||
}
|
||||
}
|
||||
core.setOutput('is_fork', 'false');
|
||||
|
||||
- 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
|
||||
run: |
|
||||
# Fetch PR head from base repo and put it into local 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 [ "${{ steps.check_fork.outputs.is_fork }}" == "true" ]; then
|
||||
git remote set-url origin file://$(pwd)
|
||||
fi
|
||||
|
||||
- uses: anthropics/claude-code-action@beta
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
with:
|
||||
allowed_bots: 'praisonai-triage-agent[bot]'
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ steps.app-token.outputs.token }}
|
||||
trigger_phrase: "@claude"
|
||||
label_trigger: "claude"
|
||||
allowed_non_write_users: "*"
|
||||
|
||||
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' }}
|
||||
You are working on the PraisonAI SDK. Follow AGENTS.md strictly.
|
||||
|
||||
STEP 0 — SETUP GIT IDENTITY & AUTH (GLOBAL — required for all repos):
|
||||
git config --global user.name "MervinPraison"
|
||||
git config --global user.email "[email protected]"
|
||||
gh auth setup-git
|
||||
|
||||
STEP 0 — SETUP GIT IDENTITY:
|
||||
git config user.name "MervinPraison"
|
||||
git config user.email "[email protected]"
|
||||
|
||||
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):
|
||||
STEP 2 — ARCHITECTURE VALIDATION (MANDATORY before writing code):
|
||||
Before implementing anything, answer these questions:
|
||||
- CORE vs WRAPPER vs TOOLS vs DOCS vs PLUGINS ROUTING:
|
||||
1. Core SDK (praisonaiagents/): Only core protocols, base classes, decorators. No heavy implementations.
|
||||
2. Wrapper (praisonai/): CLI, heavy implementations, optional dependencies.
|
||||
3. Tools (PraisonAI-Tools): Capabilities Agents actively call during execution (e.g. SurrealDB tool, Slack tool).
|
||||
4. Documentation (PraisonAIDocs): Documentation pages, guides, API references.
|
||||
5. Plugins (PraisonAI-Plugins): Framework lifecycle extensions replacing systemic behavior (e.g. tracing, logging, metrics, hooks, guardrails).
|
||||
For items routed to an EXTERNAL repository (Tools, Docs, Plugins), follow STEP 3-ALT below.
|
||||
- Does this add heavy implementations to the core SDK (praisonaiagents)? If YES → move it to the wrapper (praisonai) or make it a plugin.
|
||||
- Does it duplicate existing functionality? Check if Agent already supports this via existing params (reflection, planning, tools, hooks, memory).
|
||||
- Does it inherit from Agent properly? New agent types MUST inherit Agent, not wrap it with composition.
|
||||
- Does it add new dependencies? Only optional deps allowed, must be lazy-imported.
|
||||
- Will agent.py grow larger? If the change adds >50 lines to agent.py, find a way to extract instead.
|
||||
- Is there a name collision with existing exports in __init__.py?
|
||||
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.
|
||||
If ANY of these checks fail, 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
|
||||
STEP 3 — IMPLEMENT:
|
||||
- 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.
|
||||
a) Clone the repository:
|
||||
gh repo clone MervinPraison/<REPO_NAME> /tmp/<REPO_NAME>
|
||||
b) Copy GitHub authentication from the main repository so push works seamlessly:
|
||||
git -C /tmp/<REPO_NAME> config http."https://github.com/".extraheader "$(git config --get http."https://github.com/".extraheader)"
|
||||
c) Create a feature branch (NEVER commit to main):
|
||||
git -C /tmp/<REPO_NAME> checkout -b claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
|
||||
d) Make your changes using absolute paths (e.g. edit /tmp/<REPO_NAME>/docs.json)
|
||||
e) Commit:
|
||||
git -C /tmp/<REPO_NAME> add -A
|
||||
git -C /tmp/<REPO_NAME> commit -m "feat: <description> (fixes #$ISSUE_NUMBER)"
|
||||
f) Push the branch:
|
||||
git -C /tmp/<REPO_NAME> push origin claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
|
||||
g) Create PR in the EXTERNAL repo:
|
||||
gh pr create -R MervinPraison/<REPO_NAME> --head claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d) --title "feat: <title>" --body "Fixes MervinPraison/PraisonAI#$ISSUE_NUMBER
|
||||
|
||||
<body>"
|
||||
|
||||
STEP 4 — TEST:
|
||||
- For SDK changes: cd src/praisonai-agents && PYTHONPATH=. python -m pytest tests/ -x -q --timeout=30
|
||||
- For docs changes: verify files exist and are valid markdown/MDX
|
||||
- Run: cd src/praisonai-agents && PYTHONPATH=. python -m pytest tests/ -x -q --timeout=30
|
||||
- Ensure no regressions
|
||||
|
||||
STEP 5 — CREATE PR:
|
||||
- Commit with descriptive message, push, and create PR using `gh pr create`
|
||||
- For external repos, use `gh pr create -R MervinPraison/<REPO_NAME>`
|
||||
CRITICAL: You MUST create the PR automatically using `gh pr create`. Do NOT just provide a link or say "manual push required".
|
||||
NOTE: If you worked on an external repo, the wrapper action will report "No commits" for the main repo at the end. This is expected, do not worry about it.
|
||||
CRITICAL: You MUST create the PR automatically using `gh pr create`. Do NOT just provide a link.
|
||||
allowed_tools: |
|
||||
Bash(git:*)
|
||||
Bash(python:*)
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
|
||||
@@ -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,72 +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
|
||||
with:
|
||||
persist-credentials: false
|
||||
with:
|
||||
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"
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
export ISSUE_NUMBER="${{ inputs.issue_number }}"
|
||||
else
|
||||
export ISSUE_NUMBER="${{ github.event.issue.number }}"
|
||||
fi
|
||||
praisonai github triage --issue $ISSUE_NUMBER --agent-file .github/praisonai-issue-triage.yaml
|
||||
@@ -1,72 +0,0 @@
|
||||
name: PraisonAI PR Reviewer
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
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'))
|
||||
|
||||
steps:
|
||||
- name: Determine checkout ref
|
||||
id: dest
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
||||
echo "ref=refs/pull/${{ github.event.issue.number }}/head" >> "$GITHUB_OUTPUT"
|
||||
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "ref=refs/pull/${{ inputs.pr_number }}/head" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "ref=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
with:
|
||||
persist-credentials: false
|
||||
with:
|
||||
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 }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install PraisonAI
|
||||
run: pip install "praisonai[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
|
||||
@@ -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:
|
||||
|
||||
@@ -22,8 +22,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
|
||||
@@ -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 }}
|
||||
@@ -38,20 +34,14 @@ jobs:
|
||||
# Get the commit hash from the tag
|
||||
- name: Get commit hash
|
||||
id: get_commit
|
||||
env:
|
||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse HEAD)
|
||||
echo "commit_hash=$COMMIT_HASH" >> $GITHUB_OUTPUT
|
||||
echo "Found commit: $COMMIT_HASH for tag $INPUT_TAG"
|
||||
echo "Found commit: $COMMIT_HASH for tag ${{ github.event.inputs.tag }}"
|
||||
|
||||
# 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
|
||||
@@ -70,11 +60,8 @@ jobs:
|
||||
# Fetch from upstream
|
||||
git fetch upstream --tags --force
|
||||
|
||||
- name: Hard reset to tag commit
|
||||
env:
|
||||
COMMIT_HASH: ${{ steps.get_commit.outputs.commit_hash }}
|
||||
run: |
|
||||
git reset --hard $COMMIT_HASH
|
||||
# Reset to upstream commit
|
||||
git reset --hard ${{ steps.get_commit.outputs.commit_hash }}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
@@ -449,10 +436,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 }}
|
||||
@@ -462,64 +445,55 @@ jobs:
|
||||
# Add upstream and get commit from tag
|
||||
- name: Get upstream tag commit
|
||||
id: get_upstream_commit
|
||||
env:
|
||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
# Add upstream remote
|
||||
git remote add upstream https://github.com/MervinPraison/PraisonAI.git
|
||||
|
||||
# Delete local tag if it exists
|
||||
git tag -d "$INPUT_TAG" || true
|
||||
git tag -d ${{ github.event.inputs.tag }} || true
|
||||
|
||||
# Fetch from upstream including tags with force
|
||||
git fetch upstream --tags --force
|
||||
|
||||
# Verify tag exists in upstream
|
||||
if ! git ls-remote --tags upstream | grep -q "refs/tags/$INPUT_TAG$"; then
|
||||
echo "::error::Tag $INPUT_TAG not found in upstream repository"
|
||||
if ! git ls-remote --tags upstream | grep -q "refs/tags/${{ github.event.inputs.tag }}$"; then
|
||||
echo "::error::Tag ${{ github.event.inputs.tag }} not found in upstream repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the commit hash from the upstream tag
|
||||
UPSTREAM_COMMIT=$(git rev-list -n 1 refs/tags/"$INPUT_TAG")
|
||||
UPSTREAM_COMMIT=$(git rev-list -n 1 refs/tags/${{ github.event.inputs.tag }})
|
||||
if [ -z "$UPSTREAM_COMMIT" ]; then
|
||||
echo "::error::Could not find commit for tag $INPUT_TAG"
|
||||
echo "::error::Could not find commit for tag ${{ github.event.inputs.tag }}"
|
||||
exit 1
|
||||
fi
|
||||
echo "upstream_commit=$UPSTREAM_COMMIT" >> $GITHUB_OUTPUT
|
||||
echo "Found commit: $UPSTREAM_COMMIT for tag $INPUT_TAG"
|
||||
echo "Found commit: $UPSTREAM_COMMIT for tag ${{ github.event.inputs.tag }}"
|
||||
|
||||
# Create tag at the same commit
|
||||
- name: Create tag at commit
|
||||
env:
|
||||
GIT_ASKPASS: /bin/echo
|
||||
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
INPUT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
git config --global user.name "MervinPraison"
|
||||
git config --global user.email "[email protected]"
|
||||
|
||||
# Remove tag if it exists locally
|
||||
git tag -d "$INPUT_TAG" || true
|
||||
git tag -d ${{ github.event.inputs.tag }} || true
|
||||
|
||||
# Remove tag if it exists remotely
|
||||
git push origin :refs/tags/"$INPUT_TAG" || true
|
||||
git push origin :refs/tags/${{ github.event.inputs.tag }} || true
|
||||
|
||||
- name: Fetch tag from upstream
|
||||
env:
|
||||
UPSTREAM_COMMIT: ${{ steps.get_upstream_commit.outputs.upstream_commit }}
|
||||
run: |
|
||||
git fetch upstream $UPSTREAM_COMMIT --force
|
||||
# Fetch the specific commit from upstream
|
||||
git fetch upstream ${{ steps.get_upstream_commit.outputs.upstream_commit }} --force
|
||||
|
||||
- name: Reset to upstream commit
|
||||
env:
|
||||
UPSTREAM_COMMIT: ${{ steps.get_upstream_commit.outputs.upstream_commit }}
|
||||
run: |
|
||||
git reset --hard $UPSTREAM_COMMIT
|
||||
# Reset to upstream commit
|
||||
git reset --hard ${{ steps.get_upstream_commit.outputs.upstream_commit }}
|
||||
|
||||
# Create and push tag
|
||||
git tag -a "$INPUT_TAG" -m "Release $INPUT_TAG (synced from upstream MervinPraison/PraisonAI@$INPUT_TAG)"
|
||||
git push origin "$INPUT_TAG"
|
||||
git tag -a ${{ github.event.inputs.tag }} -m "Release ${{ github.event.inputs.tag }} (synced from upstream MervinPraison/PraisonAI@${{ github.event.inputs.tag }})"
|
||||
git push origin ${{ github.event.inputs.tag }}
|
||||
env:
|
||||
GIT_ASKPASS: /bin/echo
|
||||
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
|
||||
# Create the release
|
||||
- name: Create Release
|
||||
|
||||
@@ -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
|
||||
@@ -71,11 +67,9 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Run Comprehensive Test Suite
|
||||
env:
|
||||
INPUT_TEST_TYPE: ${{ github.event.inputs.test_type || 'all' }}
|
||||
run: |
|
||||
# Determine test type from input or default to 'all'
|
||||
TEST_TYPE="$INPUT_TEST_TYPE"
|
||||
TEST_TYPE="${{ github.event.inputs.test_type || 'all' }}"
|
||||
|
||||
echo "🧪 Running comprehensive test suite (type: $TEST_TYPE)"
|
||||
|
||||
@@ -108,16 +102,13 @@ jobs:
|
||||
|
||||
- name: Generate Comprehensive Test Report
|
||||
if: always()
|
||||
env:
|
||||
INPUT_TEST_TYPE: ${{ github.event.inputs.test_type || 'all' }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
run: |
|
||||
echo "# 📋 Comprehensive Test Report" > comprehensive_report.md
|
||||
echo "" >> comprehensive_report.md
|
||||
echo "**Python Version:** ${{ matrix.python-version }}" >> comprehensive_report.md
|
||||
echo "**Test Type:** $INPUT_TEST_TYPE" >> comprehensive_report.md
|
||||
echo "**Trigger:** $EVENT_NAME" >> comprehensive_report.md
|
||||
echo "**Status:** 🟢 All Tests Passed" >> comprehensive_report.md
|
||||
echo "**Test Type:** ${{ github.event.inputs.test_type || 'all' }}" >> comprehensive_report.md
|
||||
echo "**Trigger:** ${{ github.event_name }}" >> comprehensive_report.md
|
||||
echo "**Date:** $(date -u)" >> comprehensive_report.md
|
||||
echo "" >> comprehensive_report.md
|
||||
|
||||
echo "## 🧪 Test Categories Covered:" >> comprehensive_report.md
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,21 +34,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🚨 Cost Warning
|
||||
env:
|
||||
INPUT_FRAMEWORK: ${{ github.event.inputs.framework }}
|
||||
INPUT_CONFIRM_COSTS: ${{ github.event.inputs.confirm_costs }}
|
||||
run: |
|
||||
echo "⚠️ WARNING: This workflow will make real API calls!"
|
||||
echo "💰 This may incur charges on your API accounts"
|
||||
echo "🎯 Framework: $INPUT_FRAMEWORK"
|
||||
echo "✅ Cost confirmation: $INPUT_CONFIRM_COSTS"
|
||||
echo "🎯 Framework: ${{ github.event.inputs.framework }}"
|
||||
echo "✅ Cost confirmation: ${{ github.event.inputs.confirm_costs }}"
|
||||
|
||||
- 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
|
||||
@@ -100,23 +93,21 @@ jobs:
|
||||
|
||||
- name: Generate Real Test Report
|
||||
if: always()
|
||||
env:
|
||||
INPUT_FRAMEWORK: ${{ github.event.inputs.framework }}
|
||||
run: |
|
||||
echo "# 🔥 Real End-to-End Test Report" > real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
echo "⚠️ **WARNING: This report represents tests that made real API calls**" >> real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
echo "**Framework Tested:** $INPUT_FRAMEWORK" >> real_test_report.md
|
||||
echo "**Framework Tested:** ${{ github.event.inputs.framework }}" >> real_test_report.md
|
||||
echo "**Python Version:** ${{ matrix.python-version }}" >> real_test_report.md
|
||||
echo "**Date:** $(date -u)" >> real_test_report.md
|
||||
echo "**Triggered by:** $GITHUB_ACTOR" >> real_test_report.md
|
||||
echo "**Triggered by:** ${{ github.actor }}" >> real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
|
||||
echo "## 🧪 Test Results" >> real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
|
||||
if [ "$INPUT_FRAMEWORK" == "autogen" ] || [ "$INPUT_FRAMEWORK" == "all" ]; then
|
||||
if [ "${{ github.event.inputs.framework }}" == "autogen" ] || [ "${{ github.event.inputs.framework }}" == "all" ]; then
|
||||
echo "### AutoGen Real Tests:" >> real_test_report.md
|
||||
echo "- Environment verification" >> real_test_report.md
|
||||
echo "- Agent creation with real API calls" >> real_test_report.md
|
||||
@@ -124,7 +115,7 @@ jobs:
|
||||
echo "" >> real_test_report.md
|
||||
fi
|
||||
|
||||
if [ "$INPUT_FRAMEWORK" == "crewai" ] || [ "$INPUT_FRAMEWORK" == "all" ]; then
|
||||
if [ "${{ github.event.inputs.framework }}" == "crewai" ] || [ "${{ github.event.inputs.framework }}" == "all" ]; then
|
||||
echo "### CrewAI Real Tests:" >> real_test_report.md
|
||||
echo "- Environment verification" >> real_test_report.md
|
||||
echo "- Crew creation with real API calls" >> real_test_report.md
|
||||
@@ -159,14 +150,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🛡️ Safety Check Failed
|
||||
env:
|
||||
INPUT_FRAMEWORK: ${{ github.event.inputs.framework }}
|
||||
INPUT_CONFIRM_COSTS: ${{ github.event.inputs.confirm_costs }}
|
||||
run: |
|
||||
echo "🚨 Real tests not executed due to safety checks:"
|
||||
echo ""
|
||||
echo "✅ Costs confirmed: $INPUT_CONFIRM_COSTS"
|
||||
echo "✅ Framework selected: $INPUT_FRAMEWORK"
|
||||
echo "✅ Costs confirmed: ${{ github.event.inputs.confirm_costs }}"
|
||||
echo "✅ Framework selected: ${{ github.event.inputs.framework }}"
|
||||
echo ""
|
||||
echo "To run real tests:"
|
||||
echo "1. Select a framework (autogen, crewai, or all)"
|
||||
|
||||
@@ -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:
|
||||
|
||||
+1
-2
@@ -94,8 +94,7 @@ Cargo.lock
|
||||
**/*.rs.bk
|
||||
*.pdb
|
||||
.windsurf
|
||||
.agent
|
||||
|
||||
src/praisonai/build*.egg-info/
|
||||
src/praisonai/PraisonAI.egg-info/
|
||||
jobs/*
|
||||
*.egg-info
|
||||
@@ -1,29 +0,0 @@
|
||||
# Contributing to PraisonAI
|
||||
|
||||
Thank you for your interest! Here's how to get started:
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Fork the repository
|
||||
2. Clone your fork and install dependencies:
|
||||
```bash
|
||||
git clone https://github.com/your-username/PraisonAI.git
|
||||
cd PraisonAI
|
||||
pip install -e src/praisonai-agents/
|
||||
pip install -e src/praisonai/
|
||||
```
|
||||
3. Create a feature branch: `git checkout -b feature/your-feature`
|
||||
4. Make your changes
|
||||
5. Submit a pull request
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Follow existing code style (PEP 8)
|
||||
- Add tests for new features and run them locally using pytest:
|
||||
```bash
|
||||
cd src/praisonai-agents && pytest tests/
|
||||
```
|
||||
- Update documentation as needed
|
||||
- Check existing issues or open a new one to discuss your idea before implementing large changes
|
||||
|
||||
For questions or support, please visit our [GitHub Issues](https://github.com/MervinPraison/PraisonAI/issues).
|
||||
@@ -23,17 +23,14 @@
|
||||
|
||||
</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 🦞 — Automate and solve complex challenges with AI agent teams that plan, research, code, and deliver results to Telegram, Discord, and WhatsApp — running 24/7. A low-code, production-ready multi-agent framework with handoffs, guardrails, memory, RAG, and 100+ LLM providers, built around simplicity, customisation, and effective human-agent collaboration.
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<a href="https://x.com/elonmusk/status/1893870468249141688" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Highlighted_by_Elon_Musk-000000?style=for-the-badge&logo=x&logoColor=white" alt="Highlighted by Elon Musk" />
|
||||
</a>
|
||||
<p><em>"Grok 3 customer support" — <a href="https://x.com/elonmusk/status/1893870468249141688">Elon Musk quoting PraisonAI's tutorial</a></em></p>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
@@ -72,6 +69,26 @@ curl -fsSL https://praison.ai/install.sh | bash
|
||||
|
||||
---
|
||||
|
||||
> **Quick Paths:**
|
||||
> - 🆕 **New here?** → [Quick Start](#-quick-start) *(1 minute to first agent)*
|
||||
> - 📦 **Installing?** → [Installation](#-installation)
|
||||
> - 🐍 **Python SDK?** → [Python Examples](#-using-python-code)
|
||||
> - 📄 **YAML/No-Code?** → [YAML Examples](#-using-yaml-no-code)
|
||||
> - 🎯 **CLI user?** → [CLI Quick Reference](#-cli-quick-reference)
|
||||
> - 🤝 **Contributing?** → [Contributing](#-contributing)
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Performance
|
||||
|
||||
PraisonAI is built for speed, with agent instantiation in under 4μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
|
||||
|
||||
| Performance Metric | PraisonAI |
|
||||
|--------------------|-----------|
|
||||
| Avg Instantiation Time | **3.77 μs** |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Use Cases
|
||||
|
||||
AI agents solving real-world problems across industries:
|
||||
@@ -87,44 +104,9 @@ AI agents solving real-world problems across industries:
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Meet your first Agent (Under 1 Minute)
|
||||
## Supported Providers
|
||||
|
||||
1. Install the lightweight core SDK:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
2. Run your first autonomous agent:
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Give your agent a goal, and watch it work.
|
||||
agent = Agent(instructions="You are a senior data analyst.")
|
||||
agent.start("Analyze the top 3 tech trends of 2026 and format as a markdown table.")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌌 The PraisonAI Ecosystem
|
||||
|
||||
Start simple with the core SDK, or expand to full visual builders and dashboards when you're ready.
|
||||
|
||||
* **Core SDK (`praisonaiagents`)**: For pure Python development. `pip install praisonaiagents`
|
||||
* 💻 **PraisonAI CLI (`praisonai`)**: For terminal-based developers. `pip install praisonai`
|
||||
* 🦞 **Claw Dashboard**: Connect agents directly to Telegram, Slack, or Discord. `pip install "praisonai[claw]"`
|
||||
* 🔗 **Flow Visual Builder**: Drag-and-drop workflow creation. `pip install "praisonai[flow]"`
|
||||
* 🤖 **PraisonAI UI**: Clean chat interface. `pip install "praisonai[ui]"`
|
||||
|
||||
### JavaScript SDK
|
||||
|
||||
```bash
|
||||
npm install praisonai
|
||||
```
|
||||
|
||||
## 🧠 Supported Providers & Features
|
||||
|
||||
Powered by 100+ LLMs (OpenAI, Anthropic, Gemini & local models).
|
||||
PraisonAI supports 100+ LLM providers through seamless integration:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/OpenAI-412991?style=flat&logo=openai&logoColor=white" alt="OpenAI" />
|
||||
@@ -182,14 +164,6 @@ Powered by 100+ LLMs (OpenAI, Anthropic, Gemini & local models).
|
||||
|
||||
</details>
|
||||
|
||||
<div align="center">
|
||||
<a href="https://x.com/elonmusk/status/1893870468249141688" target="_blank">
|
||||
<img src=".github/images/elon_musk_praisonai.png" alt="Highlighted by Elon Musk" width="600" />
|
||||
</a>
|
||||
<p><em>"Grok 3 customer support" — <a href="https://x.com/elonmusk/status/1893870468249141688">Elon Musk quoting PraisonAI's tutorial</a></em></p>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## 🌟 Why PraisonAI?
|
||||
@@ -231,6 +205,78 @@ Powered by 100+ LLMs (OpenAI, Anthropic, Gemini & local models).
|
||||
</details>
|
||||
|
||||
|
||||
<div align="center">
|
||||
<a href="https://x.com/elonmusk/status/1893870468249141688" target="_blank">
|
||||
<img src=".github/images/elon_musk_praisonai.png" alt="Highlighted by Elon Musk" width="600" />
|
||||
</a>
|
||||
<p><em>"Grok 3 customer support" — <a href="https://x.com/elonmusk/status/1893870468249141688">Elon Musk quoting PraisonAI's tutorial</a></em></p>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
Get started with PraisonAI in under 1 minute:
|
||||
|
||||
```bash
|
||||
# Install
|
||||
pip install praisonaiagents
|
||||
|
||||
# Set API key
|
||||
export OPENAI_API_KEY=your_key_here
|
||||
|
||||
# Create a simple agent
|
||||
python -c "from praisonaiagents import Agent; Agent(instructions='You are a helpful AI assistant').start('Write a haiku about AI')"
|
||||
```
|
||||
|
||||
> **Next Steps:** [Single Agent Example](#1-single-agent) | [Multi Agents](#2-multi-agents) | [Full Docs](https://docs.praison.ai)
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
### Python SDK
|
||||
|
||||
Lightweight package dedicated for coding:
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
|
||||
For the full framework with CLI support:
|
||||
|
||||
```bash
|
||||
pip install praisonai
|
||||
```
|
||||
|
||||
🦞 **PraisonAI Claw** — full UI with bots, memory, knowledge, and gateway:
|
||||
|
||||
```bash
|
||||
pip install "praisonai[claw]"
|
||||
praisonai claw
|
||||
```
|
||||
|
||||
🔗 **PraisonAI Flow** — Langflow Visual Flow Builder:
|
||||
|
||||
```bash
|
||||
pip install "praisonai[flow]"
|
||||
praisonai flow
|
||||
```
|
||||
|
||||
🤖 **PraisonAI UI** — Clean chat interface:
|
||||
|
||||
```bash
|
||||
pip install "praisonai[ui]"
|
||||
praisonai ui
|
||||
```
|
||||
|
||||
### JavaScript SDK
|
||||
|
||||
```bash
|
||||
npm install praisonai
|
||||
```
|
||||
|
||||
|
||||
|
||||
---
|
||||
@@ -654,7 +700,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) |
|
||||
@@ -729,20 +774,6 @@ const agent = new Agent({ instructions: 'You are a helpful AI assistant' });
|
||||
agent.start('Write a movie script about a robot in Mars');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Performance
|
||||
|
||||
PraisonAI is built for speed, with agent instantiation in under 4μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
|
||||
|
||||
| Performance Metric | PraisonAI |
|
||||
|--------------------|-----------|
|
||||
| Avg Instantiation Time | **3.77 μs** |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## ⭐ Star History
|
||||
@@ -751,19 +782,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:
|
||||
|
||||
@@ -28,19 +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">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>
|
||||
@@ -56,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>
|
||||
@@ -172,7 +167,6 @@ Methods:
|
||||
* <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 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">injected_params</a>() -> Dict[str, Any]</code>
|
||||
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">run</a>(**kwargs) -> Any</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">clear</a>() -> None</code>
|
||||
@@ -180,7 +174,6 @@ Methods:
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">discover_single_file_plugins</a>() -> int</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_all</a>() -> Dict[str, Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None) -> List[Union[BaseTool, Callable]]</code>
|
||||
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">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">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False) -> None</code>
|
||||
@@ -208,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>
|
||||
|
||||
@@ -368,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
|
||||
|
||||
@@ -470,23 +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_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_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">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>
|
||||
@@ -568,7 +547,7 @@ Methods:
|
||||
|
||||
Types:
|
||||
```python
|
||||
from praisonaiagents import FlowDisplay, track_workflow
|
||||
from praisonaiagents import FlowDisplay, obs, track_workflow
|
||||
```
|
||||
|
||||
Methods:
|
||||
@@ -713,7 +692,6 @@ Methods:
|
||||
* <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">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>
|
||||
@@ -755,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__
|
||||
from praisonai import AgentApp, AgentOS, CloudProvider, Deploy, DeployConfig, DeployType, PraisonAI, __version__
|
||||
```
|
||||
|
||||
# CLI
|
||||
@@ -785,8 +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 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>
|
||||
@@ -827,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>
|
||||
@@ -867,28 +842,19 @@ 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 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>
|
||||
@@ -896,19 +862,6 @@ Methods:
|
||||
* <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>
|
||||
* <code title="cli">praisonai lsp logs <a href="./src/praisonai/praisonai/cli/commands/lsp.py">--help</a></code>
|
||||
@@ -916,17 +869,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>
|
||||
@@ -944,22 +886,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>
|
||||
@@ -971,10 +901,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>
|
||||
@@ -1056,10 +982,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>
|
||||
@@ -1099,13 +1021,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>
|
||||
|
||||
@@ -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.37" \
|
||||
"praisonai>=4.5.112" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.37" \
|
||||
"praisonai>=4.5.112" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.37" \
|
||||
"praisonai>=4.5.112" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -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,25 +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.",
|
||||
db={"database_url": os.getenv("COCKROACHDB_URL")},
|
||||
session_id="crdb-demo-session",
|
||||
)
|
||||
|
||||
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,34 +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.",
|
||||
db={"database_url": os.getenv("NEON_DATABASE_URL")},
|
||||
session_id="neon-demo-session",
|
||||
)
|
||||
|
||||
# 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,28 +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.",
|
||||
db={"database_url": os.getenv("SUPABASE_DATABASE_URL")},
|
||||
session_id="supabase-demo-session",
|
||||
)
|
||||
|
||||
result = agent.start("What is Supabase? Explain in 2 sentences.")
|
||||
print(result)
|
||||
@@ -1,29 +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.",
|
||||
db={
|
||||
"database_url": os.getenv("TURSO_DATABASE_URL"),
|
||||
"auth_token": os.getenv("TURSO_AUTH_TOKEN"),
|
||||
},
|
||||
session_id="turso-demo-session",
|
||||
)
|
||||
|
||||
result = agent.start("What is edge computing? Explain in 2 sentences.")
|
||||
print(result)
|
||||
@@ -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!")
|
||||
@@ -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!")
|
||||
@@ -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!")
|
||||
@@ -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!")
|
||||
@@ -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!")
|
||||
@@ -43,34 +43,14 @@ app = FastAPI(
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# Add CORS middleware with secure configuration
|
||||
cors_origins = os.getenv("API_CORS_ORIGINS", "").split(",")
|
||||
cors_origins = [origin.strip() for origin in cors_origins if origin.strip() and origin.strip() != "*"]
|
||||
|
||||
# Default secure origins if none specified
|
||||
if not cors_origins:
|
||||
# Secure defaults for different environments
|
||||
if os.getenv("ENVIRONMENT") == "production":
|
||||
# In production, require explicit configuration
|
||||
cors_origins = []
|
||||
else:
|
||||
# Development defaults - restrict to local origins
|
||||
cors_origins = [
|
||||
"http://localhost:3000", # Development frontend
|
||||
"http://localhost:8000", # Local development
|
||||
"http://127.0.0.1:3000", # Local development
|
||||
"http://127.0.0.1:8000", # Local development
|
||||
]
|
||||
|
||||
# Only add CORS middleware if origins are specified
|
||||
if cors_origins:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=cors_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["Authorization", "Content-Type", "Origin", "Accept"],
|
||||
)
|
||||
# Add CORS middleware
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Create directories for storing reports
|
||||
REPORTS_DIR = Path("generated_reports")
|
||||
|
||||
@@ -14,17 +14,14 @@ import subprocess
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_cli(cmd: str, cwd: str = None) -> tuple[int, str, str]:
|
||||
"""Run a CLI command and return (exit_code, stdout, stderr)."""
|
||||
# Use shell=False with shlex.split for safer execution
|
||||
args = shlex.split(cmd)
|
||||
result = subprocess.run(
|
||||
args,
|
||||
shell=False, # Use shell=False for security
|
||||
cmd,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=cwd,
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
"""
|
||||
Linear Agent Example for PraisonAI.
|
||||
|
||||
Demonstrates how to create a Linear bot that responds to issue mentions
|
||||
and assignments with full coding capabilities.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from praisonaiagents import Agent
|
||||
from praisonai.bots import LinearBot
|
||||
|
||||
# Create an agent with Linear tools and coding capabilities
|
||||
agent = Agent(
|
||||
name="PraisonAI Coder",
|
||||
instructions="""
|
||||
You are an autonomous coding agent integrated with Linear.
|
||||
|
||||
When you are mentioned or assigned an issue:
|
||||
1. Analyze the issue description and requirements
|
||||
2. Break down complex tasks into smaller steps
|
||||
3. Use available tools to implement solutions
|
||||
4. Update the issue with progress comments
|
||||
5. Create GitHub pull requests when code changes are needed
|
||||
|
||||
Focus on being helpful, thorough, and providing clear updates.
|
||||
""",
|
||||
llm="gpt-4o-mini",
|
||||
tools=[
|
||||
"linear_search_issues",
|
||||
"linear_get_issue",
|
||||
"linear_add_comment",
|
||||
"linear_update_issue",
|
||||
"linear_list_teams",
|
||||
"linear_list_issue_states",
|
||||
"github_create_branch",
|
||||
"github_commit_and_push",
|
||||
"github_create_pull_request",
|
||||
"read_file",
|
||||
"write_file",
|
||||
"execute_command"
|
||||
],
|
||||
memory=True,
|
||||
web_search=True,
|
||||
auto_approve_tools=True,
|
||||
)
|
||||
|
||||
# Create Linear bot
|
||||
bot = LinearBot(
|
||||
token="your-linear-oauth-token", # or set LINEAR_OAUTH_TOKEN env var
|
||||
signing_secret="your-webhook-secret", # or set LINEAR_WEBHOOK_SECRET env var
|
||||
agent=agent,
|
||||
webhook_port=8080,
|
||||
)
|
||||
|
||||
async def main():
|
||||
"""Start the Linear bot."""
|
||||
print("Starting Linear bot...")
|
||||
print("Set up your Linear webhook to point to: http://your-host:8080/webhook")
|
||||
print("Configure AgentSession and Comment events in Linear webhook settings")
|
||||
|
||||
try:
|
||||
await bot.start()
|
||||
except KeyboardInterrupt:
|
||||
print("\nStopping bot...")
|
||||
await bot.stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,9 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent
|
||||
|
||||
# Zero config — defaults: name="Agent", model="claude-haiku-4-5"
|
||||
managed = ManagedAgent()
|
||||
agent = Agent(name="coder", backend=managed)
|
||||
result = agent.start("Say hello")
|
||||
|
||||
print(f"Agent ID: {managed.agent_id}")
|
||||
print(f"Version: {managed.agent_version}")
|
||||
@@ -1,17 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Environment is created automatically — just specify packages/networking in config
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Coding Assistant",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant. Write clean, well-documented code.",
|
||||
networking={"type": "unrestricted"},
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="coder", backend=managed)
|
||||
result = agent.start("Say hello")
|
||||
|
||||
print(f"Agent ID: {managed.agent_id}")
|
||||
print(f"Environment ID: {managed.environment_id}")
|
||||
@@ -1,18 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Agent, environment, and session are all created automatically on first use
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Coding Assistant",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant. Write clean, well-documented code.",
|
||||
session_title="Quickstart session",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="coder", backend=managed)
|
||||
result = agent.start("Say hello")
|
||||
|
||||
print(f"Agent ID: {managed.agent_id}")
|
||||
print(f"Environment ID: {managed.environment_id}")
|
||||
print(f"Session ID: {managed.session_id}")
|
||||
@@ -1,18 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Coding Assistant",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant. Write clean, well-documented code.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="coder", backend=managed)
|
||||
|
||||
result = agent.start(
|
||||
"Create a Python script that generates the first 20 Fibonacci numbers and saves them to fibonacci.txt",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
print("\nAgent finished.")
|
||||
@@ -1,27 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Select specific tools: only bash, read, write — everything else disabled
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Bash Only Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant that can only use bash commands.",
|
||||
tools=[
|
||||
{
|
||||
"type": "agent_toolset_20260401",
|
||||
"default_config": {"enabled": False},
|
||||
"configs": [
|
||||
{"name": "bash", "enabled": True},
|
||||
{"name": "read", "enabled": True},
|
||||
{"name": "write", "enabled": True},
|
||||
],
|
||||
},
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="bash-agent", backend=managed)
|
||||
result = agent.start("List the current directory contents and show system info using uname -a")
|
||||
|
||||
print(result)
|
||||
print("\nAgent finished.")
|
||||
@@ -1,25 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Disable specific tools: web access disabled, everything else stays on
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="No Web Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a coding assistant. You cannot access the web.",
|
||||
tools=[
|
||||
{
|
||||
"type": "agent_toolset_20260401",
|
||||
"configs": [
|
||||
{"name": "web_fetch", "enabled": False},
|
||||
{"name": "web_search", "enabled": False},
|
||||
],
|
||||
},
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="no-web-agent", backend=managed)
|
||||
result = agent.start("Write a hello world Python script and run it")
|
||||
|
||||
print(result)
|
||||
print("\nAgent finished.")
|
||||
@@ -1,41 +0,0 @@
|
||||
import json
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
|
||||
def handle_weather(tool_name, tool_input):
|
||||
"""Custom tool callback — PraisonAI calls this when the agent uses get_weather."""
|
||||
location = tool_input.get("location", "Unknown")
|
||||
print(f"\n[Custom tool call: {tool_name}]")
|
||||
print(f"[Input: {json.dumps(tool_input)}]")
|
||||
return f"Weather in {location}: 15°C, partly cloudy, humidity 72%"
|
||||
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Weather Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a weather assistant. Use the get_weather tool to check weather.",
|
||||
tools=[
|
||||
{"type": "agent_toolset_20260401"},
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather for a location",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string", "description": "City name"},
|
||||
},
|
||||
"required": ["location"],
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
on_custom_tool=handle_weather,
|
||||
)
|
||||
|
||||
agent = Agent(name="weather-agent", backend=managed)
|
||||
result = agent.start("What is the weather in London?")
|
||||
|
||||
print(result)
|
||||
print("\nAgent finished.")
|
||||
@@ -1,22 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="My Agent v1",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="updatable", backend=managed)
|
||||
|
||||
# First call creates the agent
|
||||
result = agent.start("Say hello briefly")
|
||||
print(f"Created: {managed.agent_id}, version: {managed.agent_version}")
|
||||
|
||||
# Update the agent's system prompt (no need to recreate)
|
||||
managed.update_agent(
|
||||
name="My Agent v2",
|
||||
system="You are a senior Python developer. Write production-quality code.",
|
||||
)
|
||||
print(f"Updated: {managed.agent_id}, version: {managed.agent_version}")
|
||||
@@ -1,23 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Session Demo Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="session-demo", backend=managed)
|
||||
|
||||
# Run a task to create agent + session
|
||||
agent.start("Say hello briefly")
|
||||
|
||||
# List sessions for this agent
|
||||
sessions = managed.list_sessions()
|
||||
print(f"Total sessions: {len(sessions)}")
|
||||
for s in sessions[:5]:
|
||||
print(f" ID: {s['id']}")
|
||||
print(f" Status: {s['status']}")
|
||||
print(f" Title: {s['title']}")
|
||||
print()
|
||||
@@ -1,17 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Research Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a research assistant. Search the web for information.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="researcher", backend=managed)
|
||||
result = agent.start(
|
||||
"Search the web for the latest Python 3.13 features and summarize them in 3 bullet points"
|
||||
)
|
||||
|
||||
print(result)
|
||||
print("\nAgent finished.")
|
||||
@@ -1,25 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Multi Turn Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="multi-turn", backend=managed)
|
||||
print(f"Session ID: {managed.session_id}")
|
||||
|
||||
# Multi-turn: each call reuses the same session — agent remembers context
|
||||
messages = [
|
||||
"Create a file called greeting.py that prints 'Hello World'",
|
||||
"Now modify greeting.py to accept a name as a command line argument",
|
||||
"Run greeting.py with the argument 'Claude'",
|
||||
]
|
||||
|
||||
for i, message in enumerate(messages):
|
||||
print(f"\n--- Turn {i + 1}: {message} ---\n")
|
||||
result = agent.start(message)
|
||||
print(result)
|
||||
print("\nAgent finished turn.")
|
||||
@@ -1,22 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Just add packages to the config — environment is created automatically
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Data Science Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a data science assistant.",
|
||||
packages={"pip": ["pandas", "numpy"]},
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="data-scientist", backend=managed)
|
||||
|
||||
print(f"Environment ID: {managed.environment_id}")
|
||||
|
||||
result = agent.start(
|
||||
"Create a Python script that generates random sales data with pandas and saves a summary to sales_summary.txt"
|
||||
)
|
||||
|
||||
print(result)
|
||||
print("\nAgent finished.")
|
||||
@@ -1,20 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Interruptable Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="interruptable", backend=managed)
|
||||
|
||||
# Start a task
|
||||
result = agent.start("Write a Python script that prints numbers 1 to 5")
|
||||
print(result)
|
||||
|
||||
# Interrupt the agent mid-work
|
||||
print("\n[Sending interrupt...]")
|
||||
managed.interrupt()
|
||||
print("Agent stopped.")
|
||||
@@ -1,28 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Usage Tracker Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant.",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="tracker", backend=managed)
|
||||
result = agent.start("Write a one-line Python script that prints the current date and run it")
|
||||
|
||||
print(result)
|
||||
print("\nAgent finished.")
|
||||
|
||||
# Usage tracked automatically
|
||||
print("\n--- Usage Report ---")
|
||||
print(f"Input tokens: {managed.total_input_tokens}")
|
||||
print(f"Output tokens: {managed.total_output_tokens}")
|
||||
|
||||
# Or retrieve detailed session info from the API
|
||||
info = managed.retrieve_session()
|
||||
print(f"\nSession ID: {info.get('id')}")
|
||||
print(f"Status: {info.get('status')}")
|
||||
if "usage" in info:
|
||||
print(f"API Input tokens: {info['usage']['input_tokens']}")
|
||||
print(f"API Output tokens: {info['usage']['output_tokens']}")
|
||||
@@ -1,35 +0,0 @@
|
||||
import json
|
||||
import pathlib
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
IDS_FILE = pathlib.Path("managed_ids.json")
|
||||
|
||||
if IDS_FILE.exists():
|
||||
saved = json.loads(IDS_FILE.read_text())
|
||||
print(f"Resuming session: {saved['session_id']}\n")
|
||||
|
||||
managed = ManagedAgent()
|
||||
managed.resume_session(saved["session_id"])
|
||||
|
||||
agent = Agent(name="coder", backend=managed)
|
||||
result = agent.start("What is my favourite number?", stream=True)
|
||||
|
||||
else:
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Persistent Coder",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant.",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="coder", backend=managed)
|
||||
result = agent.start("Remember this: my favourite number is 42.", stream=True)
|
||||
|
||||
ids = managed.save_ids()
|
||||
IDS_FILE.write_text(json.dumps(ids, indent=2))
|
||||
|
||||
print(f"\nSaved IDs to {IDS_FILE}")
|
||||
print(f" agent_id : {managed.agent_id}")
|
||||
print(f" environment_id: {managed.environment_id}")
|
||||
print(f" session_id : {managed.session_id}")
|
||||
print("\nRun this script again to resume the session.")
|
||||
@@ -1,21 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Session Demo",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant.",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="demo", backend=managed)
|
||||
|
||||
agent.start("Say hello and confirm you are ready.", stream=True)
|
||||
|
||||
print("\n--- IDs ---")
|
||||
print(f"agent_id : {managed.agent_id}")
|
||||
print(f"environment_id: {managed.environment_id}")
|
||||
print(f"session_id : {managed.session_id}")
|
||||
print(f"chat_history : {len(agent.chat_history)} messages")
|
||||
|
||||
ids = managed.save_ids()
|
||||
print(f"\nsave_ids(): {ids}")
|
||||
@@ -1,22 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Multiple package managers — pip + npm installed before agent starts
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Full Stack Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a full stack developer.",
|
||||
packages={
|
||||
"pip": ["pandas", "numpy", "scikit-learn"],
|
||||
"npm": ["express"],
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="fullstack", backend=managed)
|
||||
result = agent.start(
|
||||
"Verify pandas and express are installed: run 'python3 -c \"import pandas; print(pandas.__version__)\"' and 'node -e \"console.log(require.resolve('express'))\"'",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
print("\nAgent finished.")
|
||||
@@ -1,24 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Limited networking: restrict container to specific hosts only
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Restricted Network Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant with restricted network access.",
|
||||
networking={
|
||||
"type": "limited",
|
||||
"allowed_hosts": ["api.github.com"],
|
||||
"allow_mcp_servers": False,
|
||||
"allow_package_managers": True,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="restricted", backend=managed)
|
||||
result = agent.start(
|
||||
"Try to fetch https://api.github.com and report what you get. Then try https://example.com — it should fail.",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
print("\nAgent finished.")
|
||||
@@ -1,43 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# Environment management: list, retrieve, archive, delete
|
||||
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Env Management Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant.",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="env-mgmt", backend=managed)
|
||||
|
||||
# First call creates agent + environment + session
|
||||
agent.start("Say hello briefly")
|
||||
|
||||
# Access the underlying Anthropic client for environment management
|
||||
client = managed._get_client()
|
||||
env_id = managed.environment_id
|
||||
print(f"Environment ID: {env_id}")
|
||||
|
||||
# List all environments
|
||||
environments = client.beta.environments.list()
|
||||
print(f"\nTotal environments: {len(environments.data)}")
|
||||
for env in environments.data[:5]:
|
||||
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
|
||||
|
||||
# Retrieve a specific environment
|
||||
env = client.beta.environments.retrieve(env_id)
|
||||
print(f"\nRetrieved: {env.id} | {env.name}")
|
||||
|
||||
# Archive (read-only, existing sessions continue)
|
||||
archived = client.beta.environments.archive(env_id)
|
||||
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
|
||||
|
||||
# Delete (only if no sessions reference it — may fail if sessions exist)
|
||||
try:
|
||||
client.beta.environments.delete(env_id)
|
||||
print(f"Deleted: {env_id}")
|
||||
except Exception as e:
|
||||
print(f"Delete skipped (expected if sessions exist): {e}")
|
||||
|
||||
print("\nEnvironment management complete.")
|
||||
@@ -1,35 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# MCP servers: configure agent with remote MCP tool servers
|
||||
# Note: Anthropic's managed agents only support URL-based (SSE) MCP servers
|
||||
managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="MCP Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant with access to MCP servers.",
|
||||
tools=[
|
||||
{"type": "agent_toolset_20260401"},
|
||||
{"type": "mcp_toolset", "mcp_server_name": "deepwiki"},
|
||||
],
|
||||
mcp_servers=[
|
||||
{
|
||||
"type": "url",
|
||||
"url": "https://mcp.deepwiki.com/sse",
|
||||
"name": "deepwiki",
|
||||
},
|
||||
],
|
||||
networking={
|
||||
"type": "limited",
|
||||
"allow_mcp_servers": True,
|
||||
"allow_package_managers": True,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="mcp-agent", backend=managed)
|
||||
result = agent.start(
|
||||
"Use the deepwiki MCP to read the wiki page for the anthropics/anthropic-cookbook github repo and give a one sentence summary",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
print("\nAgent finished.")
|
||||
@@ -1,340 +0,0 @@
|
||||
import json
|
||||
import pathlib
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
|
||||
# 1. Create an agent
|
||||
|
||||
managed = ManagedAgent()
|
||||
agent = Agent(name="teacher", backend=managed)
|
||||
result = agent.start("Say hello briefly", stream=True)
|
||||
|
||||
print(f"[1] Agent created: {managed.agent_id} (v{managed.agent_version})")
|
||||
|
||||
# 2. Update the agent
|
||||
|
||||
managed.update_agent(
|
||||
name="Teaching Agent v2",
|
||||
system="You are a senior Python developer. Write clean, production-quality code.",
|
||||
)
|
||||
|
||||
print(f"[2] Agent updated: Teaching Agent v2 (v{managed.agent_version})")
|
||||
|
||||
# 3-4. Environment + Session are created automatically (already done in step 1)
|
||||
|
||||
print(f"[3] Environment created: {managed.environment_id}")
|
||||
print(f"[4] Session created: {managed.session_id}")
|
||||
|
||||
# 5. Stream a response
|
||||
|
||||
print("\n[5] Streaming response...")
|
||||
|
||||
result = agent.start("Write a Python script that prints 'Hello from Managed Agents!' and run it", stream=True)
|
||||
|
||||
# 6. Multi-turn conversation (same session remembers context)
|
||||
|
||||
print("\n[6] Multi-turn: sending follow-up...")
|
||||
|
||||
result = agent.start("Now modify that script to accept a name argument and greet that person", stream=True)
|
||||
|
||||
# 7. Track usage
|
||||
|
||||
info = managed.retrieve_session()
|
||||
print("\n[7] Usage report:")
|
||||
if info.get("usage"):
|
||||
print(f" Input tokens: {info['usage']['input_tokens']}")
|
||||
print(f" Output tokens: {info['usage']['output_tokens']}")
|
||||
else:
|
||||
print(f" Input tokens: {managed.total_input_tokens}")
|
||||
print(f" Output tokens: {managed.total_output_tokens}")
|
||||
|
||||
# 8. List sessions
|
||||
|
||||
sessions = managed.list_sessions()
|
||||
print(f"\n[8] Total sessions: {len(sessions)}")
|
||||
for s in sessions[:3]:
|
||||
print(f" {s['id']} | {s['status']} | {s['title']}")
|
||||
|
||||
# 9. Selective tools (only bash + read + write)
|
||||
|
||||
bash_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Bash Only Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You can only use bash, read, and write tools.",
|
||||
tools=[
|
||||
{
|
||||
"type": "agent_toolset_20260401",
|
||||
"default_config": {"enabled": False},
|
||||
"configs": [
|
||||
{"name": "bash", "enabled": True},
|
||||
{"name": "read", "enabled": True},
|
||||
{"name": "write", "enabled": True},
|
||||
],
|
||||
},
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
bash_agent = Agent(name="bash-only", backend=bash_managed)
|
||||
|
||||
print("\n[9] Bash-only agent streaming...")
|
||||
result = bash_agent.start("Show the current date and Python version using bash", stream=True)
|
||||
|
||||
# 10. Disable specific tools (web disabled, everything else on)
|
||||
|
||||
no_web_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="No Web Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a coding assistant. You cannot access the web.",
|
||||
tools=[
|
||||
{
|
||||
"type": "agent_toolset_20260401",
|
||||
"configs": [
|
||||
{"name": "web_fetch", "enabled": False},
|
||||
{"name": "web_search", "enabled": False},
|
||||
],
|
||||
},
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
no_web_agent = Agent(name="no-web", backend=no_web_managed)
|
||||
|
||||
print("\n[10] No-web agent streaming...")
|
||||
result = no_web_agent.start("Write a Python one-liner that calculates 2**100 and print the result", stream=True)
|
||||
|
||||
# 11. Custom tools (you define the tool, PraisonAI calls your callback)
|
||||
|
||||
|
||||
def handle_weather(tool_name, tool_input):
|
||||
print(f"\n [Custom tool: {tool_name} | Input: {json.dumps(tool_input)}]")
|
||||
return "Tokyo: 22°C, sunny, humidity 55%"
|
||||
|
||||
|
||||
custom_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Weather Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a weather assistant. Use the get_weather tool to check weather.",
|
||||
tools=[
|
||||
{"type": "agent_toolset_20260401"},
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather for a location",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string", "description": "City name"},
|
||||
},
|
||||
"required": ["location"],
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
on_custom_tool=handle_weather,
|
||||
)
|
||||
|
||||
custom_agent = Agent(name="weather", backend=custom_managed)
|
||||
|
||||
print("\n[11] Custom tool agent streaming...")
|
||||
result = custom_agent.start("What is the weather in Tokyo?", stream=True)
|
||||
|
||||
# 12. Web search agent
|
||||
|
||||
search_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Search Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a research assistant. Search the web and summarize.",
|
||||
),
|
||||
)
|
||||
|
||||
search_agent = Agent(name="searcher", backend=search_managed)
|
||||
|
||||
print("\n[12] Web search agent streaming...")
|
||||
result = search_agent.start("Search the web for Python 3.13 new features and give me 3 bullet points", stream=True)
|
||||
|
||||
# 13. Environment with pre-installed packages
|
||||
|
||||
data_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Data Science Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a data science assistant.",
|
||||
packages={"pip": ["pandas", "numpy"]},
|
||||
),
|
||||
)
|
||||
|
||||
data_agent = Agent(name="data-scientist", backend=data_managed)
|
||||
|
||||
print("\n[13] Data science environment streaming...")
|
||||
result = data_agent.start("Use pandas to create a small DataFrame with 3 rows of sample data and print it", stream=True)
|
||||
|
||||
# 14. Interrupt a session
|
||||
|
||||
interrupt_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Interruptable Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful coding assistant.",
|
||||
),
|
||||
)
|
||||
|
||||
interrupt_agent = Agent(name="interruptable", backend=interrupt_managed)
|
||||
|
||||
print("\n[14] Interrupt demo...")
|
||||
result = interrupt_agent.start("Write a Python script that prints numbers 1 to 10", stream=True)
|
||||
interrupt_managed.interrupt()
|
||||
print(" [Interrupt sent]")
|
||||
|
||||
# 15. Session resume — save IDs, create a fresh ManagedAgent, resume, and verify context
|
||||
|
||||
print("\n[15] Session resume demo...")
|
||||
|
||||
# Tell the original agent a memorable fact
|
||||
result = agent.start("Remember this: my favourite number is 42", stream=True)
|
||||
|
||||
# Save IDs to disk
|
||||
ids = managed.save_ids()
|
||||
ids_file = pathlib.Path("managed_ids.json")
|
||||
ids_file.write_text(json.dumps(ids, indent=2))
|
||||
print(f" Saved IDs to {ids_file}: {ids}")
|
||||
|
||||
# Create a completely new ManagedAgent and resume the saved session
|
||||
resume_managed = ManagedAgent()
|
||||
resume_managed.resume_session(ids["session_id"])
|
||||
resume_agent = Agent(name="resumed", backend=resume_managed)
|
||||
|
||||
# Ask about the fact — proves the session memory was preserved
|
||||
result = resume_agent.start("What is my favourite number?", stream=True)
|
||||
print(f" Resumed session: {resume_managed.session_id}")
|
||||
|
||||
# Clean up
|
||||
ids_file.unlink(missing_ok=True)
|
||||
|
||||
# 16. Multi-package managers — pip + npm installed before agent starts
|
||||
|
||||
print("\n[16] Multi-package managers...")
|
||||
|
||||
multi_pkg_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Full Stack Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a full stack developer.",
|
||||
packages={
|
||||
"pip": ["pandas", "numpy", "scikit-learn"],
|
||||
"npm": ["express"],
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
multi_pkg_agent = Agent(name="fullstack", backend=multi_pkg_managed)
|
||||
result = multi_pkg_agent.start(
|
||||
"Verify pandas and express are installed: run python3 -c 'import pandas; print(pandas.__version__)' and node -e 'console.log(require.resolve(\"express\"))'",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
# 17. Limited networking — restrict container to specific hosts
|
||||
|
||||
print("\n[17] Limited networking...")
|
||||
|
||||
limited_net_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="Restricted Network Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant with restricted network access.",
|
||||
networking={
|
||||
"type": "limited",
|
||||
"allowed_hosts": ["api.github.com"],
|
||||
"allow_mcp_servers": False,
|
||||
"allow_package_managers": True,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
limited_net_agent = Agent(name="restricted", backend=limited_net_managed)
|
||||
result = limited_net_agent.start("Fetch https://api.github.com and report the status", stream=True)
|
||||
|
||||
# 18. Environment management — list, retrieve, archive
|
||||
|
||||
print("\n[18] Environment management...")
|
||||
|
||||
env_client = managed._get_client()
|
||||
|
||||
# List all environments
|
||||
environments = env_client.beta.environments.list()
|
||||
print(f" Total environments: {len(environments.data)}")
|
||||
for env in environments.data[:3]:
|
||||
print(f" {env.id} | {env.name}")
|
||||
|
||||
# Retrieve a specific environment
|
||||
env = env_client.beta.environments.retrieve(managed.environment_id)
|
||||
print(f" Retrieved: {env.id} | {env.name}")
|
||||
|
||||
# 19. MCP servers — configure agent with remote MCP tool servers
|
||||
|
||||
print("\n[19] MCP servers...")
|
||||
|
||||
mcp_managed = ManagedAgent(
|
||||
config=ManagedConfig(
|
||||
name="MCP Agent",
|
||||
model="claude-haiku-4-5",
|
||||
system="You are a helpful assistant with access to MCP servers.",
|
||||
tools=[
|
||||
{"type": "agent_toolset_20260401"},
|
||||
{"type": "mcp_toolset", "mcp_server_name": "deepwiki"},
|
||||
],
|
||||
mcp_servers=[
|
||||
{
|
||||
"type": "url",
|
||||
"url": "https://mcp.deepwiki.com/sse",
|
||||
"name": "deepwiki",
|
||||
},
|
||||
],
|
||||
networking={
|
||||
"type": "limited",
|
||||
"allow_mcp_servers": True,
|
||||
"allow_package_managers": True,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
mcp_agent = Agent(name="mcp-agent", backend=mcp_managed)
|
||||
result = mcp_agent.start("Use the deepwiki MCP to read the wiki page for the anthropics/anthropic-cookbook github repo and give a one sentence summary", stream=True)
|
||||
|
||||
# Final usage summary
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("FINAL USAGE SUMMARY")
|
||||
print("=" * 60)
|
||||
|
||||
all_backends = [
|
||||
("Teaching Agent v2", managed),
|
||||
("Bash Only Agent", bash_managed),
|
||||
("No Web Agent", no_web_managed),
|
||||
("Weather Agent", custom_managed),
|
||||
("Search Agent", search_managed),
|
||||
("Data Science Agent", data_managed),
|
||||
("Interruptable Agent", interrupt_managed),
|
||||
("Resumed Session", resume_managed),
|
||||
("Full Stack Agent", multi_pkg_managed),
|
||||
("Restricted Network", limited_net_managed),
|
||||
("MCP Agent", mcp_managed),
|
||||
]
|
||||
|
||||
total_input = 0
|
||||
total_output = 0
|
||||
|
||||
for name, backend in all_backends:
|
||||
info = backend.retrieve_session()
|
||||
usage = info.get("usage", {})
|
||||
inp = usage.get("input_tokens", backend.total_input_tokens)
|
||||
out = usage.get("output_tokens", backend.total_output_tokens)
|
||||
total_input += inp
|
||||
total_output += out
|
||||
print(f" {name:30s} | in: {inp:6d} | out: {out:6d}")
|
||||
|
||||
print(f" {'TOTAL':30s} | in: {total_input:6d} | out: {total_output:6d}")
|
||||
print("=" * 60)
|
||||
@@ -1,5 +0,0 @@
|
||||
from praisonai import Agent, ManagedAgent
|
||||
|
||||
agent = Agent(name="teacher", backend=ManagedAgent())
|
||||
result = agent.start("Write a Python script that prints 'Hello from Managed Agents!' and run it")
|
||||
print(result)
|
||||
@@ -1,117 +0,0 @@
|
||||
"""All compute providers — comprehensive test across Local, Docker, E2B, and Modal.
|
||||
|
||||
This example demonstrates local agent loops with various compute backends for tool sandboxing.
|
||||
Uses the new LocalAgent class to clearly indicate local execution with optional cloud compute.
|
||||
|
||||
Requires:
|
||||
- Docker running locally
|
||||
- E2B_API_KEY set
|
||||
- modal CLI configured (modal token set)
|
||||
"""
|
||||
import asyncio
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import LocalAgent, LocalAgentConfig
|
||||
|
||||
|
||||
async def test_provider(name, compute, extra_provision_kwargs=None):
|
||||
"""Test a single compute provider end-to-end."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" PROVIDER: {name}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
managed = LocalAgent(
|
||||
compute=compute,
|
||||
config=LocalAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful assistant. Be concise.",
|
||||
name=f"{name}Agent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name=f"{name}-test", backend=managed)
|
||||
|
||||
# 1. Provision
|
||||
print(f"\n [1] Provisioning {name}...")
|
||||
provision_kwargs = extra_provision_kwargs or {}
|
||||
info = await managed.provision_compute(**provision_kwargs)
|
||||
print(f" Instance: {info.instance_id}")
|
||||
print(f" Status: {info.status}")
|
||||
|
||||
# 2. Execute command
|
||||
print(f"\n [2] Execute in {name}...")
|
||||
result = await managed.execute_in_compute("python3 -c 'print(42 * 13)'")
|
||||
stdout = result["stdout"].strip()
|
||||
assert "546" in stdout, f"Expected 546, got: {stdout}"
|
||||
print(f" 42 * 13 = {stdout} ✓")
|
||||
|
||||
# 3. Execute echo
|
||||
result = await managed.execute_in_compute(f"echo 'Hello from {name}'")
|
||||
print(f" Echo: {result['stdout'].strip()}")
|
||||
|
||||
# 4. Agent LLM
|
||||
print(f"\n [3] Agent LLM via {name}...")
|
||||
llm_result = agent.start("What is 9 * 8? Just the number.", stream=True)
|
||||
print(f" LLM result: {llm_result}")
|
||||
|
||||
# 5. Multi-turn
|
||||
print("\n [4] Multi-turn...")
|
||||
llm_result = agent.start("Add 10 to that. Just the number.", stream=True)
|
||||
print(f" Follow-up: {llm_result}")
|
||||
|
||||
# 6. Usage
|
||||
session = managed.retrieve_session()
|
||||
usage = session["usage"]
|
||||
print(f"\n [5] Usage: in={usage['input_tokens']}, out={usage['output_tokens']}")
|
||||
|
||||
# 7. Shutdown
|
||||
print(f"\n [6] Shutdown {name}...")
|
||||
await managed.shutdown_compute()
|
||||
print(" Done ✓")
|
||||
|
||||
return {
|
||||
"name": name,
|
||||
"input_tokens": usage["input_tokens"],
|
||||
"output_tokens": usage["output_tokens"],
|
||||
}
|
||||
|
||||
|
||||
async def main():
|
||||
results = []
|
||||
|
||||
# Docker
|
||||
try:
|
||||
r = await test_provider("Docker", "docker", {"image": "python:3.12-slim"})
|
||||
results.append(r)
|
||||
except Exception as e:
|
||||
print(f"\n Docker FAILED: {e}")
|
||||
|
||||
# E2B
|
||||
try:
|
||||
r = await test_provider("E2B", "e2b", {"idle_timeout_s": 120})
|
||||
results.append(r)
|
||||
except Exception as e:
|
||||
print(f"\n E2B FAILED: {e}")
|
||||
|
||||
# Modal
|
||||
try:
|
||||
r = await test_provider("Modal", "modal", {"idle_timeout_s": 120})
|
||||
results.append(r)
|
||||
except Exception as e:
|
||||
print(f"\n Modal FAILED: {e}")
|
||||
|
||||
# Summary
|
||||
print(f"\n\n{'='*60}")
|
||||
print(" SUMMARY")
|
||||
print(f"{'='*60}")
|
||||
total_in = 0
|
||||
total_out = 0
|
||||
for r in results:
|
||||
total_in += r["input_tokens"]
|
||||
total_out += r["output_tokens"]
|
||||
print(f" {r['name']:15s} | in: {r['input_tokens']:6d} | out: {r['output_tokens']:6d}")
|
||||
print(f" {'TOTAL':15s} | in: {total_in:6d} | out: {total_out:6d}")
|
||||
print(f"{'='*60}")
|
||||
print(f" {len(results)}/{3} providers passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,47 +0,0 @@
|
||||
"""Run every managed-runtime example and report a summary.
|
||||
|
||||
Executes the runtime_*.py files in this directory as subprocesses with a
|
||||
30s per-example timeout. Exits non-zero if any example fails.
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
EXAMPLES = [
|
||||
# ── LLM providers (where the LLM reasoning happens) ──
|
||||
"runtime_anthropic.py", # full loop in Anthropic cloud
|
||||
"runtime_openai.py", # LLM in OpenAI cloud
|
||||
"runtime_gemini.py", # LLM in Google cloud
|
||||
"runtime_ollama.py", # LLM on local/self-hosted Ollama
|
||||
# ── Cloud/compute providers (where tool execution happens) ──
|
||||
"runtime_e2b.py", # tools sandboxed in E2B cloud VM
|
||||
"runtime_modal.py", # tools sandboxed in Modal serverless cloud
|
||||
"runtime_fly.py", # tools sandboxed on Fly.io Machines
|
||||
"runtime_daytona.py", # tools sandboxed in Daytona cloud workspace
|
||||
"runtime_docker.py", # tools sandboxed in local Docker container
|
||||
]
|
||||
|
||||
results = []
|
||||
for name in EXAMPLES:
|
||||
path = os.path.join(HERE, name)
|
||||
start = time.time()
|
||||
proc = subprocess.run(
|
||||
[sys.executable, path],
|
||||
capture_output=True, text=True, timeout=120,
|
||||
env=os.environ.copy(), # propagate API keys and PYTHONPATH
|
||||
)
|
||||
dur = time.time() - start
|
||||
ok = proc.returncode == 0
|
||||
results.append((name, ok, dur, proc.stdout.strip().splitlines()[-1] if proc.stdout.strip() else ""))
|
||||
print(f"[{'OK' if ok else 'FAIL'}] {name:30s} {dur:.2f}s")
|
||||
if not ok:
|
||||
print(proc.stdout)
|
||||
print(proc.stderr)
|
||||
|
||||
print("\nSummary:")
|
||||
for name, ok, dur, tail in results:
|
||||
print(f" {'✓' if ok else '✗'} {name:30s} {dur:5.2f}s {tail[:60]}")
|
||||
|
||||
sys.exit(0 if all(r[1] for r in results) else 1)
|
||||
@@ -1,55 +0,0 @@
|
||||
"""Daytona compute provider — run agent tools inside a Daytona cloud sandbox.
|
||||
|
||||
Requires: DAYTONA_API_KEY environment variable set.
|
||||
Organization must have a default region configured in Daytona Dashboard.
|
||||
Install: pip install daytona-sdk
|
||||
"""
|
||||
import asyncio
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
|
||||
# ── 1. Create agent with Daytona compute ──
|
||||
managed = ManagedAgent(
|
||||
provider="local",
|
||||
compute="daytona",
|
||||
config=LocalManagedConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="DaytonaAgent",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="daytona-agent", backend=managed)
|
||||
|
||||
print("[1] Agent created with Daytona compute")
|
||||
print(f" Compute: {managed.compute_provider.provider_name}")
|
||||
|
||||
# ── 2. Provision Daytona sandbox ──
|
||||
print("\n[2] Provisioning Daytona sandbox...")
|
||||
info = asyncio.run(managed.provision_compute(idle_timeout_s=120))
|
||||
print(f" Instance: {info.instance_id}")
|
||||
print(f" Status: {info.status}")
|
||||
|
||||
# ── 3. Execute commands ──
|
||||
print("\n[3] Executing commands in Daytona...")
|
||||
result = asyncio.run(managed.execute_in_compute("python3 -c 'import sys; print(sys.version)'"))
|
||||
print(f" Python version: {result['stdout'].strip()}")
|
||||
print(f" Exit code: {result['exit_code']}")
|
||||
|
||||
result = asyncio.run(managed.execute_in_compute("echo 'Hello from Daytona!'"))
|
||||
print(f" Echo: {result['stdout'].strip()}")
|
||||
|
||||
# ── 4. Agent LLM execution ──
|
||||
print("\n[4] Agent LLM execution...")
|
||||
result = agent.start("What is 11 * 13? Just the number.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 5. Usage ──
|
||||
info = managed.retrieve_session()
|
||||
print(f"\n[5] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# ── 6. Shutdown ──
|
||||
print("\n[6] Shutting down Daytona sandbox...")
|
||||
asyncio.run(managed.shutdown_compute())
|
||||
print(" Shutdown complete.")
|
||||
|
||||
print("\nDone!")
|
||||
@@ -1,67 +0,0 @@
|
||||
"""Docker compute provider — run agent tools inside a Docker container.
|
||||
|
||||
Requires: Docker running locally.
|
||||
"""
|
||||
import asyncio
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
|
||||
# ── 1. Basic agent with Docker compute ──
|
||||
managed = ManagedAgent(
|
||||
provider="local",
|
||||
compute="docker",
|
||||
config=LocalManagedConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="DockerAgent",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="docker-agent", backend=managed)
|
||||
|
||||
print("[1] Agent created with Docker compute")
|
||||
print(f" Agent ID: {managed.agent_id or '(lazy — created on first call)'}")
|
||||
print(f" Compute: {managed.compute_provider.provider_name}")
|
||||
|
||||
# ── 2. Provision Docker container ──
|
||||
print("\n[2] Provisioning Docker container...")
|
||||
info = asyncio.run(managed.provision_compute(image="python:3.12-slim"))
|
||||
print(f" Instance: {info.instance_id}")
|
||||
print(f" Status: {info.status}")
|
||||
|
||||
# ── 3. Execute commands inside the container ──
|
||||
print("\n[3] Executing commands in Docker...")
|
||||
result = asyncio.run(managed.execute_in_compute("python3 -c 'import sys; print(sys.version)'"))
|
||||
print(f" Python version: {result['stdout'].strip()}")
|
||||
print(f" Exit code: {result['exit_code']}")
|
||||
|
||||
result = asyncio.run(managed.execute_in_compute("echo 'Hello from Docker!'"))
|
||||
print(f" Echo: {result['stdout'].strip()}")
|
||||
|
||||
# ── 4. Install packages in the container ──
|
||||
print("\n[4] Installing packages...")
|
||||
result = asyncio.run(managed.execute_in_compute("pip install requests -q"))
|
||||
print(f" pip exit code: {result['exit_code']}")
|
||||
|
||||
result = asyncio.run(managed.execute_in_compute("python3 -c 'import requests; print(requests.__version__)'"))
|
||||
print(f" requests version: {result['stdout'].strip()}")
|
||||
|
||||
# ── 5. Use agent with LLM (runs locally, compute is for tool sandboxing) ──
|
||||
print("\n[5] Agent LLM execution...")
|
||||
result = agent.start("What is 15 * 23? Just the number.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 6. Multi-turn ──
|
||||
print("\n[6] Multi-turn...")
|
||||
result = agent.start("Double that number.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 7. Usage tracking ──
|
||||
info = managed.retrieve_session()
|
||||
print(f"\n[7] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# ── 8. Shutdown Docker container ──
|
||||
print("\n[8] Shutting down Docker container...")
|
||||
asyncio.run(managed.shutdown_compute())
|
||||
print(" Shutdown complete.")
|
||||
|
||||
print("\nDone!")
|
||||
@@ -1,66 +0,0 @@
|
||||
"""E2B compute provider — run agent tools inside an E2B cloud sandbox.
|
||||
|
||||
Requires: E2B_API_KEY environment variable set.
|
||||
Install: pip install e2b
|
||||
"""
|
||||
import asyncio
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
|
||||
# ── 1. Create agent with E2B compute ──
|
||||
managed = ManagedAgent(
|
||||
provider="local",
|
||||
compute="e2b",
|
||||
config=LocalManagedConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="E2BAgent",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="e2b-agent", backend=managed)
|
||||
|
||||
print("[1] Agent created with E2B compute")
|
||||
print(f" Compute: {managed.compute_provider.provider_name}")
|
||||
|
||||
# ── 2. Provision E2B sandbox ──
|
||||
print("\n[2] Provisioning E2B sandbox...")
|
||||
info = asyncio.run(managed.provision_compute(idle_timeout_s=120))
|
||||
print(f" Instance: {info.instance_id}")
|
||||
print(f" Status: {info.status}")
|
||||
|
||||
# ── 3. Execute commands in the sandbox ──
|
||||
print("\n[3] Executing commands in E2B...")
|
||||
result = asyncio.run(managed.execute_in_compute("python3 -c 'import sys; print(sys.version)'"))
|
||||
print(f" Python version: {result['stdout'].strip()}")
|
||||
print(f" Exit code: {result['exit_code']}")
|
||||
|
||||
result = asyncio.run(managed.execute_in_compute("echo 'Hello from E2B!'"))
|
||||
print(f" Echo: {result['stdout'].strip()}")
|
||||
|
||||
# ── 4. Run a Python script ──
|
||||
print("\n[4] Running Python script...")
|
||||
result = asyncio.run(managed.execute_in_compute(
|
||||
"python3 -c 'for i in range(5): print(f\"Line {i}\")'"
|
||||
))
|
||||
print(f" Output:\n{result['stdout']}")
|
||||
|
||||
# ── 5. Agent LLM execution ──
|
||||
print("[5] Agent LLM execution...")
|
||||
result = agent.start("What is the square root of 144? Just the number.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 6. Multi-turn ──
|
||||
print("\n[6] Multi-turn...")
|
||||
result = agent.start("Now cube that number.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 7. Usage ──
|
||||
info = managed.retrieve_session()
|
||||
print(f"\n[7] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# ── 8. Shutdown ──
|
||||
print("\n[8] Shutting down E2B sandbox...")
|
||||
asyncio.run(managed.shutdown_compute())
|
||||
print(" Shutdown complete.")
|
||||
|
||||
print("\nDone!")
|
||||
@@ -1,96 +0,0 @@
|
||||
"""Local provider — advanced features: update agent, custom tools, session resume, packages."""
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
|
||||
# ── 1. Create agent ──
|
||||
managed = ManagedAgent(
|
||||
provider="local",
|
||||
config=LocalManagedConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful math tutor.",
|
||||
name="MathTutor",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="tutor", backend=managed)
|
||||
result = agent.start("What is 17 * 23?", stream=True)
|
||||
print(f"[1] Created agent: {managed.agent_id} (v{managed.agent_version})")
|
||||
|
||||
# ── 2. Update agent ──
|
||||
managed.update_agent(
|
||||
name="Senior Math Tutor",
|
||||
system="You are a senior math tutor. Always show your work step by step.",
|
||||
)
|
||||
print(f"[2] Updated to v{managed.agent_version}")
|
||||
result = agent.start("What is 123 + 456?", stream=True)
|
||||
|
||||
# ── 3. Custom tool ──
|
||||
def handle_calculator(tool_name, tool_input):
|
||||
expr = tool_input.get("expression", "0")
|
||||
try:
|
||||
val = eval(expr, {"__builtins__": {}})
|
||||
except Exception:
|
||||
val = "error"
|
||||
print(f" [Calculator: {expr} = {val}]")
|
||||
return str(val)
|
||||
|
||||
custom_managed = ManagedAgent(
|
||||
provider="local",
|
||||
config=LocalManagedConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are an assistant with a calculator tool. Use it for math.",
|
||||
name="CalcAgent",
|
||||
tools=[
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "calculator",
|
||||
"description": "Evaluate a math expression",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {"type": "string", "description": "Math expression"},
|
||||
},
|
||||
"required": ["expression"],
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
on_custom_tool=handle_calculator,
|
||||
)
|
||||
custom_agent = Agent(name="calc", backend=custom_managed)
|
||||
print("\n[3] Custom tool agent...")
|
||||
result = custom_agent.start("Use the calculator to compute 99 * 77", stream=True)
|
||||
|
||||
# ── 4. Session resume ──
|
||||
print("\n[4] Session resume...")
|
||||
result = agent.start("Remember: my lucky number is 7", stream=True)
|
||||
ids = managed.save_ids()
|
||||
print(f" Saved IDs: {ids}")
|
||||
|
||||
resume_managed = ManagedAgent(provider="local", config=LocalManagedConfig(model="gpt-4o-mini"))
|
||||
resume_managed.restore_ids(ids)
|
||||
resume_agent = Agent(name="resumed", backend=resume_managed)
|
||||
result = resume_agent.start("What is my lucky number?", stream=True)
|
||||
print(f" Resumed session: {resume_managed.session_id}")
|
||||
|
||||
# ── 5. Interrupt ──
|
||||
print("\n[5] Interrupt demo...")
|
||||
int_managed = ManagedAgent(
|
||||
provider="local",
|
||||
config=LocalManagedConfig(model="gpt-4o-mini", system="Be helpful.", name="IntAgent"),
|
||||
)
|
||||
int_agent = Agent(name="interruptable", backend=int_managed)
|
||||
result = int_agent.start("Count from 1 to 5", stream=True)
|
||||
int_managed.interrupt()
|
||||
print(" [Interrupt sent]")
|
||||
|
||||
# ── 6. Reset session ──
|
||||
print("\n[6] Reset session...")
|
||||
managed.reset_session()
|
||||
result = agent.start("What was my lucky number?", stream=True)
|
||||
print(" (new session — context lost, expected)")
|
||||
|
||||
# ── Usage summary ──
|
||||
print("\n" + "=" * 50)
|
||||
info = managed.retrieve_session()
|
||||
print(f"Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
print("=" * 50)
|
||||
print("Done!")
|
||||
@@ -1,46 +0,0 @@
|
||||
"""Local agent — basic local execution with gpt-4o-mini.
|
||||
|
||||
No external infrastructure needed. Runs the agent loop locally.
|
||||
Uses the new canonical LocalAgent class for clarity.
|
||||
"""
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import LocalAgent, LocalAgentConfig
|
||||
|
||||
# Create a local agent (runs locally, no managed runtime)
|
||||
managed = LocalAgent(
|
||||
config=LocalAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful assistant. Be concise.",
|
||||
name="LocalAgent",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="local-basic", backend=managed)
|
||||
|
||||
# 1. Basic execution
|
||||
print("[1] Basic execution...")
|
||||
result = agent.start("What is the capital of France? One word.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Agent metadata
|
||||
print(f"\n[2] Agent ID: {managed.agent_id}")
|
||||
print(f" Version: {managed.agent_version}")
|
||||
print(f" Env ID: {managed.environment_id}")
|
||||
print(f" Session: {managed.session_id}")
|
||||
|
||||
# 3. Multi-turn (same session keeps context)
|
||||
print("\n[3] Multi-turn...")
|
||||
result = agent.start("What country is that city in?", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Usage tracking
|
||||
info = managed.retrieve_session()
|
||||
print(f"\n[4] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# 5. List sessions
|
||||
sessions = managed.list_sessions()
|
||||
print(f"\n[5] Sessions: {len(sessions)}")
|
||||
for s in sessions:
|
||||
print(f" {s['id']} | {s['status']}")
|
||||
|
||||
print("\nDone!")
|
||||
@@ -1,66 +0,0 @@
|
||||
"""Modal compute provider — run agent tools inside a Modal cloud sandbox.
|
||||
|
||||
Requires: modal CLI configured (modal token set) or MODAL_TOKEN_ID + MODAL_TOKEN_SECRET.
|
||||
Install: pip install modal
|
||||
"""
|
||||
import asyncio
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
|
||||
# ── 1. Create agent with Modal compute ──
|
||||
managed = ManagedAgent(
|
||||
provider="local",
|
||||
compute="modal",
|
||||
config=LocalManagedConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="ModalAgent",
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="modal-agent", backend=managed)
|
||||
|
||||
print("[1] Agent created with Modal compute")
|
||||
print(f" Compute: {managed.compute_provider.provider_name}")
|
||||
|
||||
# ── 2. Provision Modal sandbox ──
|
||||
print("\n[2] Provisioning Modal sandbox...")
|
||||
info = asyncio.run(managed.provision_compute(idle_timeout_s=120))
|
||||
print(f" Instance: {info.instance_id}")
|
||||
print(f" Status: {info.status}")
|
||||
|
||||
# ── 3. Execute commands in the sandbox ──
|
||||
print("\n[3] Executing commands in Modal...")
|
||||
result = asyncio.run(managed.execute_in_compute("python3 -c 'import sys; print(sys.version)'"))
|
||||
print(f" Python version: {result['stdout'].strip()}")
|
||||
print(f" Exit code: {result['exit_code']}")
|
||||
|
||||
result = asyncio.run(managed.execute_in_compute("echo 'Hello from Modal!'"))
|
||||
print(f" Echo: {result['stdout'].strip()}")
|
||||
|
||||
# ── 4. Run a computation ──
|
||||
print("\n[4] Running computation...")
|
||||
result = asyncio.run(managed.execute_in_compute(
|
||||
"python3 -c 'print(sum(range(1, 101)))'"
|
||||
))
|
||||
print(f" Sum 1..100 = {result['stdout'].strip()}")
|
||||
|
||||
# ── 5. Agent LLM execution ──
|
||||
print("\n[5] Agent LLM execution...")
|
||||
result = agent.start("What is 7 factorial? Just the number.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 6. Multi-turn ──
|
||||
print("\n[6] Multi-turn...")
|
||||
result = agent.start("Is that number even or odd?", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# ── 7. Usage ──
|
||||
info = managed.retrieve_session()
|
||||
print(f"\n[7] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# ── 8. Shutdown ──
|
||||
print("\n[8] Shutting down Modal sandbox...")
|
||||
asyncio.run(managed.shutdown_compute())
|
||||
print(" Shutdown complete.")
|
||||
|
||||
print("\nDone!")
|
||||
@@ -1,47 +0,0 @@
|
||||
"""Anthropic managed runtime — full agent loop runs in Anthropic's cloud.
|
||||
|
||||
The ENTIRE loop (LLM calls, tool execution, memory, sessions) runs remotely
|
||||
on Anthropic's managed infrastructure. This is the only provider today where
|
||||
"the whole thing runs in that cloud".
|
||||
|
||||
Requires: ANTHROPIC_API_KEY environment variable set.
|
||||
"""
|
||||
import os
|
||||
|
||||
if not os.getenv("ANTHROPIC_API_KEY"):
|
||||
print("[skip] ANTHROPIC_API_KEY not set — set it to run this example.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
import anthropic # noqa: F401 (required by AnthropicManagedAgent)
|
||||
except ImportError:
|
||||
print("[skip] 'anthropic' SDK not installed — pip install 'anthropic>=0.94.0'")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
try:
|
||||
from praisonai import Agent, ManagedAgent, ManagedConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
managed = ManagedAgent(
|
||||
provider="anthropic",
|
||||
config=ManagedConfig(
|
||||
model="claude-3-5-sonnet-latest",
|
||||
system="You are a concise coding assistant. One-sentence answers.",
|
||||
name="AnthropicRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="anthropic-runtime", backend=managed)
|
||||
|
||||
# 1. Agent work entirely in the cloud
|
||||
print("[1] Agent:", agent.start("Write a Python one-liner that sums 1..10. Just the code."))
|
||||
|
||||
# 2. Multi-turn reuses the same cloud session
|
||||
print("[2] Agent:", agent.start("Now change it to factorial of 5."))
|
||||
|
||||
# 3. Cloud-side session metadata
|
||||
info = managed.retrieve_session()
|
||||
print(f"[3] session={managed.session_id} status={info.get('status')} "
|
||||
f"in={info.get('usage', {}).get('input_tokens', 0)} "
|
||||
f"out={info.get('usage', {}).get('output_tokens', 0)}")
|
||||
@@ -1,52 +0,0 @@
|
||||
"""Agent runtime with tools sandboxed in a Daytona cloud workspace.
|
||||
|
||||
The agent's LLM loop runs locally; tool / code execution runs in a Daytona-
|
||||
managed workspace (disposable cloud dev environment, pre-configured images).
|
||||
|
||||
Requires:
|
||||
- ``OPENAI_API_KEY`` for the LLM
|
||||
- ``DAYTONA_API_KEY`` for the Daytona API
|
||||
- ``pip install daytona-sdk``
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print("[skip] OPENAI_API_KEY not set.")
|
||||
raise SystemExit(0)
|
||||
if not os.getenv("DAYTONA_API_KEY"):
|
||||
print("[skip] DAYTONA_API_KEY not set.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import SandboxedAgent, SandboxedAgentConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
|
||||
async def main():
|
||||
sandboxed = SandboxedAgent(
|
||||
compute="daytona", # tool execution runs in a Daytona workspace
|
||||
config=SandboxedAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a concise coding assistant.",
|
||||
name="DaytonaRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="daytona-runtime", backend=sandboxed)
|
||||
|
||||
info = await sandboxed.provision_compute(idle_timeout_s=120)
|
||||
print(f"[1] Daytona workspace: {info.instance_id} ({info.status})")
|
||||
|
||||
result = await sandboxed.execute_in_compute("python3 -c 'print(len([1,2,3,4,5]) ** 3)'")
|
||||
print(f"[2] Cloud compute result: {result['stdout'].strip()}")
|
||||
|
||||
print("[3] Agent:", agent.start("What is 5 cubed? Just the number."))
|
||||
|
||||
await sandboxed.shutdown_compute()
|
||||
print("[4] Daytona workspace shut down.")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -1,59 +0,0 @@
|
||||
"""Agent runtime with tools sandboxed in a local Docker container.
|
||||
|
||||
The agent's LLM loop runs locally; tool / code execution is isolated in a
|
||||
Docker container. Not a cloud provider, but the same pattern — useful for
|
||||
local development and CI.
|
||||
|
||||
Requires:
|
||||
- ``OPENAI_API_KEY`` for the LLM
|
||||
- Docker daemon running (``docker info`` must succeed)
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print("[skip] OPENAI_API_KEY not set.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
["docker", "info"],
|
||||
check=True, capture_output=True, timeout=5,
|
||||
)
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
|
||||
print("[skip] Docker daemon not available — start Docker Desktop.")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
try:
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import SandboxedAgent, SandboxedAgentConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
|
||||
async def main():
|
||||
sandboxed = SandboxedAgent(
|
||||
compute="docker", # tool execution runs in a local Docker container
|
||||
config=SandboxedAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a concise coding assistant.",
|
||||
name="DockerRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="docker-runtime", backend=sandboxed)
|
||||
|
||||
info = await sandboxed.provision_compute(image="python:3.12-slim")
|
||||
print(f"[1] Docker container: {info.instance_id} ({info.status})")
|
||||
|
||||
result = await sandboxed.execute_in_compute("python3 -c 'print(sum(i*i for i in range(1, 11)))'")
|
||||
print(f"[2] Container compute result: {result['stdout'].strip()}")
|
||||
|
||||
print("[3] Agent:", agent.start("What is the sum of squares from 1 to 10? Just the number."))
|
||||
|
||||
await sandboxed.shutdown_compute()
|
||||
print("[4] Docker container shut down.")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -1,56 +0,0 @@
|
||||
"""Agent runtime with tools sandboxed in an E2B cloud VM.
|
||||
|
||||
The agent's LLM loop runs locally; any tool / code execution is sandboxed
|
||||
in an ephemeral E2B cloud instance. Uses ``SandboxedAgent`` — the honest
|
||||
name for the "local loop + cloud tools" pattern.
|
||||
|
||||
Requires:
|
||||
- ``OPENAI_API_KEY`` for the LLM
|
||||
- ``E2B_API_KEY`` for the cloud sandbox
|
||||
- ``pip install e2b``
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print("[skip] OPENAI_API_KEY not set.")
|
||||
raise SystemExit(0)
|
||||
if not os.getenv("E2B_API_KEY"):
|
||||
print("[skip] E2B_API_KEY not set — set it to run in the E2B cloud sandbox.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import SandboxedAgent, SandboxedAgentConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
|
||||
async def main():
|
||||
sandboxed = SandboxedAgent(
|
||||
compute="e2b", # tool execution runs in E2B cloud
|
||||
config=SandboxedAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a concise coding assistant.",
|
||||
name="E2BRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="e2b-runtime", backend=sandboxed)
|
||||
|
||||
# 1. Provision the cloud sandbox
|
||||
info = await sandboxed.provision_compute(idle_timeout_s=120)
|
||||
print(f"[1] E2B sandbox: {info.instance_id} ({info.status})")
|
||||
|
||||
# 2. Execute code IN the cloud sandbox
|
||||
result = await sandboxed.execute_in_compute("python3 -c 'print(sum(range(1, 11)))'")
|
||||
print(f"[2] Cloud compute result: {result['stdout'].strip()}")
|
||||
|
||||
# 3. Agent loop (LLM local, any tool calls auto-sandboxed in E2B)
|
||||
print("[3] Agent:", agent.start("What is 13 * 17? Just the number."))
|
||||
|
||||
await sandboxed.shutdown_compute()
|
||||
print("[4] E2B sandbox shut down.")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -1,57 +0,0 @@
|
||||
"""Agent runtime with tools sandboxed on Fly.io Machines.
|
||||
|
||||
The agent's LLM loop runs locally; tool / code execution runs in a Fly.io
|
||||
Machine (fast-booting micro-VM with per-second billing and global regions).
|
||||
|
||||
Requires:
|
||||
- ``OPENAI_API_KEY`` for the LLM
|
||||
- ``FLY_API_TOKEN`` for the Fly.io Machines API
|
||||
- ``FLY_APP_NAME`` (optional) existing Fly app; auto-created otherwise
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print("[skip] OPENAI_API_KEY not set.")
|
||||
raise SystemExit(0)
|
||||
if not os.getenv("FLY_API_TOKEN"):
|
||||
print("[skip] FLY_API_TOKEN not set — get one via `fly auth token`.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import SandboxedAgent, SandboxedAgentConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
|
||||
async def main():
|
||||
sandboxed = SandboxedAgent(
|
||||
compute="flyio", # tool execution runs on Fly.io Machines
|
||||
config=SandboxedAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a concise coding assistant.",
|
||||
name="FlyioRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="fly-runtime", backend=sandboxed)
|
||||
|
||||
info = await sandboxed.provision_compute(
|
||||
image="python:3.12-slim",
|
||||
cpu=1,
|
||||
memory_mb=512,
|
||||
idle_timeout_s=120,
|
||||
)
|
||||
print(f"[1] Fly Machine: {info.instance_id} ({info.status})")
|
||||
|
||||
result = await sandboxed.execute_in_compute("python3 -c 'print(2 ** 20)'")
|
||||
print(f"[2] Cloud compute result: {result['stdout'].strip()}")
|
||||
|
||||
print("[3] Agent:", agent.start("What is 2 raised to the 20th power? Just the number."))
|
||||
|
||||
await sandboxed.shutdown_compute()
|
||||
print("[4] Fly Machine shut down.")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -1,40 +0,0 @@
|
||||
"""Gemini managed runtime — agent loop local, LLM calls go to Google's cloud.
|
||||
|
||||
``ManagedAgent(provider="gemini")`` wires a local managed loop to Gemini's
|
||||
hosted models via litellm.
|
||||
|
||||
Requires: GEMINI_API_KEY (or GOOGLE_API_KEY) environment variable set.
|
||||
"""
|
||||
import os
|
||||
|
||||
if not (os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")):
|
||||
print("[skip] GEMINI_API_KEY / GOOGLE_API_KEY not set — set one to run this example.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
managed = ManagedAgent(
|
||||
provider="gemini",
|
||||
config=LocalManagedConfig(
|
||||
# Use explicit litellm prefix to guarantee routing to Google
|
||||
model="gemini/gemini-2.0-flash-exp",
|
||||
system="You are a concise assistant. One-sentence answers.",
|
||||
name="GeminiRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="gemini-runtime", backend=managed)
|
||||
|
||||
# 1. Agent work (LLM call goes to Google cloud)
|
||||
print("[1] Agent:", agent.start("What does REST stand for? One sentence."))
|
||||
|
||||
# 2. Multi-turn reuses the session
|
||||
print("[2] Agent:", agent.start("How is it different from GraphQL?"))
|
||||
|
||||
info = managed.retrieve_session()
|
||||
print(f"[3] session={managed.session_id} "
|
||||
f"in={info.get('usage', {}).get('input_tokens', 0)} "
|
||||
f"out={info.get('usage', {}).get('output_tokens', 0)}")
|
||||
@@ -1,63 +0,0 @@
|
||||
"""Anthropic hosted runtime — entire agent runs on Anthropic's managed infrastructure.
|
||||
|
||||
Uses the new canonical HostedAgent class which clearly communicates that the entire
|
||||
agent loop runs in Anthropic's cloud, not locally.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Skip guards - exit cleanly if prerequisites not met
|
||||
if not (os.getenv("ANTHROPIC_API_KEY") or os.getenv("CLAUDE_API_KEY")):
|
||||
print("[skip] ANTHROPIC_API_KEY or CLAUDE_API_KEY not set")
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
import anthropic
|
||||
except ImportError:
|
||||
print("[skip] anthropic SDK not installed")
|
||||
sys.exit(0)
|
||||
|
||||
# Heavy imports only after skip-guards pass
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import HostedAgent, HostedAgentConfig
|
||||
|
||||
# Create a hosted agent running entirely on Anthropic's managed runtime
|
||||
hosted = HostedAgent(
|
||||
provider="anthropic",
|
||||
config=HostedAgentConfig(
|
||||
model="claude-3-5-sonnet-latest",
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="AnthropicHostedAgent",
|
||||
tools=[{"type": "agent_toolset_20260401"}],
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="anthropic-hosted", backend=hosted)
|
||||
|
||||
# 1. Basic execution - runs entirely in Anthropic's cloud
|
||||
print("[1] Hosted execution on Anthropic infrastructure...")
|
||||
result = agent.start("What is the capital of France? One word.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Agent metadata from Anthropic's API
|
||||
print(f"\n[2] Agent ID: {hosted.agent_id}")
|
||||
print(f" Version: {hosted.agent_version}")
|
||||
print(f" Env ID: {hosted.environment_id}")
|
||||
print(f" Session: {hosted.session_id}")
|
||||
|
||||
# 3. Multi-turn (same session keeps context in Anthropic's cloud)
|
||||
print("\n[3] Multi-turn conversation...")
|
||||
result = agent.start("What country is that city in?", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Usage tracking from Anthropic's usage API
|
||||
info = hosted.retrieve_session()
|
||||
print(f"\n[4] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# 5. List all sessions for this agent
|
||||
sessions = hosted.list_sessions()
|
||||
print(f"\n[5] Sessions: {len(sessions)}")
|
||||
for s in sessions[:3]: # Show first 3
|
||||
print(f" {s['id']} | {s['status']}")
|
||||
|
||||
print("\nDone! Agent loop ran entirely on Anthropic's managed infrastructure.")
|
||||
@@ -1,61 +0,0 @@
|
||||
"""Local agent loop with Gemini LLM — runs locally, not in a managed runtime.
|
||||
|
||||
Uses the new canonical LocalAgent class which clearly communicates that only the
|
||||
agent loop runs locally. The LLM calls go to Google's Gemini API, but there's no managed runtime involved.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Skip guards - exit cleanly if prerequisites not met
|
||||
if not (os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")):
|
||||
print("[skip] GEMINI_API_KEY or GOOGLE_API_KEY not set")
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
import google.generativeai
|
||||
except ImportError:
|
||||
print("[skip] google-generativeai SDK not installed")
|
||||
sys.exit(0)
|
||||
|
||||
# Heavy imports only after skip-guards pass
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import LocalAgent, LocalAgentConfig
|
||||
|
||||
# Create a local agent using Gemini LLM
|
||||
local = LocalAgent(
|
||||
config=LocalAgentConfig(
|
||||
model="gemini/gemini-2.0-flash", # Use Gemini with litellm routing prefix
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="LocalGeminiAgent",
|
||||
tools=["execute_command", "read_file", "write_file"],
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="local-gemini", backend=local)
|
||||
|
||||
# 1. Basic execution - agent loop runs locally, LLM calls go to Gemini
|
||||
print("[1] Local execution with Gemini LLM...")
|
||||
result = agent.start("What is the capital of France? One word.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Agent metadata (locally generated UUIDs)
|
||||
print(f"\n[2] Agent ID: {local.agent_id}")
|
||||
print(f" Version: {local.agent_version}")
|
||||
print(f" Env ID: {local.environment_id}")
|
||||
print(f" Session: {local.session_id}")
|
||||
|
||||
# 3. Multi-turn conversation (session state maintained locally)
|
||||
print("\n[3] Multi-turn conversation...")
|
||||
result = agent.start("What country is that city in?", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Usage tracking (accumulated locally)
|
||||
info = local.retrieve_session()
|
||||
print(f"\n[4] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# 5. Tool execution (runs in local subprocess)
|
||||
print("\n[5] Tool execution example...")
|
||||
result = agent.start("Create a file called hello_gemini.txt with 'Hello from Gemini agent!'", stream=True)
|
||||
print(f" Tool result: {result}")
|
||||
|
||||
print("\nDone! Agent loop ran locally with Gemini LLM calls.")
|
||||
@@ -1,61 +0,0 @@
|
||||
"""Local agent loop with Ollama LLM — runs locally, not in a managed runtime.
|
||||
|
||||
Uses the new canonical LocalAgent class which clearly communicates that only the
|
||||
agent loop runs locally. The LLM calls go to a local Ollama instance, no managed runtime involved.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
|
||||
# Skip guards - exit cleanly if prerequisites not met
|
||||
# Check if Ollama daemon is running
|
||||
try:
|
||||
response = requests.get("http://localhost:11434/api/tags", timeout=2)
|
||||
if response.status_code != 200:
|
||||
raise Exception("Ollama not responding")
|
||||
except Exception:
|
||||
print("[skip] Ollama daemon not running at localhost:11434")
|
||||
sys.exit(0)
|
||||
|
||||
# Heavy imports only after skip-guards pass
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import LocalAgent, LocalAgentConfig
|
||||
|
||||
# Create a local agent using Ollama LLM
|
||||
local = LocalAgent(
|
||||
config=LocalAgentConfig(
|
||||
model="ollama/llama3.2", # Use Ollama with litellm routing prefix
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="LocalOllamaAgent",
|
||||
tools=["execute_command", "read_file", "write_file"],
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="local-ollama", backend=local)
|
||||
|
||||
# 1. Basic execution - agent loop runs locally, LLM calls go to local Ollama
|
||||
print("[1] Local execution with Ollama LLM...")
|
||||
result = agent.start("What is the capital of France? One word.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Agent metadata (locally generated UUIDs)
|
||||
print(f"\n[2] Agent ID: {local.agent_id}")
|
||||
print(f" Version: {local.agent_version}")
|
||||
print(f" Env ID: {local.environment_id}")
|
||||
print(f" Session: {local.session_id}")
|
||||
|
||||
# 3. Multi-turn conversation (session state maintained locally)
|
||||
print("\n[3] Multi-turn conversation...")
|
||||
result = agent.start("What country is that city in?", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Usage tracking (accumulated locally)
|
||||
info = local.retrieve_session()
|
||||
print(f"\n[4] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# 5. Tool execution (runs in local subprocess)
|
||||
print("\n[5] Tool execution example...")
|
||||
result = agent.start("Create a file called hello_ollama.txt with 'Hello from Ollama agent!'", stream=True)
|
||||
print(f" Tool result: {result}")
|
||||
|
||||
print("\nDone! Agent loop ran locally with Ollama LLM calls.")
|
||||
@@ -1,61 +0,0 @@
|
||||
"""Local agent loop with OpenAI LLM — runs locally, not in a managed runtime.
|
||||
|
||||
Uses the new canonical LocalAgent class which clearly communicates that only the
|
||||
agent loop runs locally. The LLM calls go to OpenAI, but there's no managed runtime involved.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Skip guards - exit cleanly if prerequisites not met
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print("[skip] OPENAI_API_KEY not set")
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
import openai
|
||||
except ImportError:
|
||||
print("[skip] openai SDK not installed")
|
||||
sys.exit(0)
|
||||
|
||||
# Heavy imports only after skip-guards pass
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import LocalAgent, LocalAgentConfig
|
||||
|
||||
# Create a local agent using OpenAI's LLM
|
||||
local = LocalAgent(
|
||||
config=LocalAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a helpful coding assistant. Be concise.",
|
||||
name="LocalOpenAIAgent",
|
||||
tools=["execute_command", "read_file", "write_file"],
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(name="local-openai", backend=local)
|
||||
|
||||
# 1. Basic execution - agent loop runs locally, LLM calls go to OpenAI
|
||||
print("[1] Local execution with OpenAI LLM...")
|
||||
result = agent.start("What is the capital of France? One word.", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 2. Agent metadata (locally generated UUIDs)
|
||||
print(f"\n[2] Agent ID: {local.agent_id}")
|
||||
print(f" Version: {local.agent_version}")
|
||||
print(f" Env ID: {local.environment_id}")
|
||||
print(f" Session: {local.session_id}")
|
||||
|
||||
# 3. Multi-turn conversation (session state maintained locally)
|
||||
print("\n[3] Multi-turn conversation...")
|
||||
result = agent.start("What country is that city in?", stream=True)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# 4. Usage tracking (accumulated locally)
|
||||
info = local.retrieve_session()
|
||||
print(f"\n[4] Usage: in={info['usage']['input_tokens']}, out={info['usage']['output_tokens']}")
|
||||
|
||||
# 5. Tool execution (runs in local subprocess)
|
||||
print("\n[5] Tool execution example...")
|
||||
result = agent.start("Create a file called hello.txt with 'Hello from local agent!'", stream=True)
|
||||
print(f" Tool result: {result}")
|
||||
|
||||
print("\nDone! Agent loop ran locally with OpenAI LLM calls.")
|
||||
@@ -1,61 +0,0 @@
|
||||
"""Agent runtime with tools sandboxed in Modal cloud functions.
|
||||
|
||||
The agent's LLM loop runs locally; tool / code execution is sandboxed in
|
||||
Modal's serverless cloud. Modal gives fast cold-starts and per-second billing.
|
||||
|
||||
Requires:
|
||||
- ``OPENAI_API_KEY`` for the LLM
|
||||
- Modal CLI configured: ``modal token set --token-id ... --token-secret ...``
|
||||
- ``pip install modal``
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print("[skip] OPENAI_API_KEY not set.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
import modal # noqa: F401
|
||||
except ImportError:
|
||||
print("[skip] 'modal' package not installed — pip install modal")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
# Modal auth: presence of ~/.modal.toml or MODAL_TOKEN_ID + MODAL_TOKEN_SECRET
|
||||
if not (os.path.exists(os.path.expanduser("~/.modal.toml"))
|
||||
or (os.getenv("MODAL_TOKEN_ID") and os.getenv("MODAL_TOKEN_SECRET"))):
|
||||
print("[skip] Modal not configured — run `modal token set ...`")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
from praisonai import Agent
|
||||
from praisonai.integrations import SandboxedAgent, SandboxedAgentConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
|
||||
async def main():
|
||||
sandboxed = SandboxedAgent(
|
||||
compute="modal", # tool execution runs in Modal's cloud
|
||||
config=SandboxedAgentConfig(
|
||||
model="gpt-4o-mini",
|
||||
system="You are a concise coding assistant.",
|
||||
name="ModalRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="modal-runtime", backend=sandboxed)
|
||||
|
||||
info = await sandboxed.provision_compute(idle_timeout_s=120)
|
||||
print(f"[1] Modal instance: {info.instance_id} ({info.status})")
|
||||
|
||||
result = await sandboxed.execute_in_compute("python3 -c 'import math; print(math.factorial(10))'")
|
||||
print(f"[2] Cloud compute result: {result['stdout'].strip()}")
|
||||
|
||||
print("[3] Agent:", agent.start("What is the 10th Fibonacci number? Just the number."))
|
||||
|
||||
await sandboxed.shutdown_compute()
|
||||
print("[4] Modal instance shut down.")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -1,45 +0,0 @@
|
||||
"""Ollama managed runtime — agent runs against a local/self-hosted LLM.
|
||||
|
||||
``ManagedAgent(provider="ollama")`` wires the managed loop to an Ollama
|
||||
server. Model runs locally (or on a self-hosted box) instead of in a public
|
||||
cloud — useful when you want the "managed agent" developer experience with
|
||||
no external API dependency.
|
||||
|
||||
Requires: An Ollama server reachable (default ``http://localhost:11434``)
|
||||
with at least one model pulled (e.g., ``ollama pull llama3.2``).
|
||||
"""
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
base = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
||||
try:
|
||||
urllib.request.urlopen(f"{base}/api/tags", timeout=2).read()
|
||||
except (urllib.error.URLError, OSError) as e:
|
||||
print(f"[skip] Ollama not reachable at {base} ({e}). Start it with `ollama serve`.")
|
||||
raise SystemExit(0)
|
||||
|
||||
try:
|
||||
from praisonai import Agent, ManagedAgent, LocalManagedConfig
|
||||
except ImportError as e:
|
||||
print(f"[skip] praisonai wrapper not importable ({e}).")
|
||||
raise SystemExit(0) from None
|
||||
|
||||
managed = ManagedAgent(
|
||||
provider="ollama",
|
||||
config=LocalManagedConfig(
|
||||
model="llama3.2",
|
||||
system="You are a concise assistant. One-sentence answers.",
|
||||
name="OllamaRuntimeAgent",
|
||||
),
|
||||
)
|
||||
agent = Agent(name="ollama-runtime", backend=managed)
|
||||
|
||||
# 1. Agent work (LLM call goes to your Ollama server)
|
||||
print("[1] Agent:", agent.start("What is the capital of Japan? One word."))
|
||||
|
||||
# 2. Multi-turn reuses the session
|
||||
print("[2] Agent:", agent.start("Name its most famous mountain."))
|
||||
|
||||
info = managed.retrieve_session()
|
||||
print(f"[3] session={managed.session_id}")
|
||||
Loaded 100 of 841 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user