mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 22:52:58 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9790ad44ea | ||
|
|
f23556a04a |
No files matched your search
@@ -1,2 +0,0 @@
|
||||
patreon: MervinPraison
|
||||
ko_fi: MervinPraison
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: '[BUG] '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Environment
|
||||
- Provider (select one):
|
||||
- [ ] Anthropic
|
||||
- [ ] OpenAI
|
||||
- [ ] Google Vertex AI
|
||||
- [ ] AWS Bedrock
|
||||
- [ ] Other: <!-- specify -->
|
||||
- PraisonAI version: <!-- if known -->
|
||||
- Operating System: <!-- e.g. macOS 14.3, Windows 11, Ubuntu 22.04 -->
|
||||
|
||||
## Full Code
|
||||
<!-- Full code that reproduces the bug -->
|
||||
|
||||
## Steps to Reproduce
|
||||
1. <!-- First step -->
|
||||
2. <!-- Second step -->
|
||||
3. <!-- And so on... -->
|
||||
|
||||
## Expected Behavior
|
||||
<!-- What you expected to happen -->
|
||||
|
||||
## Actual Behavior
|
||||
<!-- What actually happened -->
|
||||
|
||||
## Additional Context
|
||||
<!-- Add any other context about the problem here, such as screenshots, logs, etc. -->
|
||||
@@ -1,256 +0,0 @@
|
||||
name: "Claude Code Action"
|
||||
description: "Run Claude Code in GitHub Actions workflows"
|
||||
|
||||
inputs:
|
||||
anthropic_api_key:
|
||||
description: "Anthropic API key"
|
||||
required: false
|
||||
github_token:
|
||||
description: "GitHub token for Claude to operate with"
|
||||
required: false
|
||||
default: ${{ github.token }}
|
||||
trigger_phrase:
|
||||
description: "The trigger phrase to look for in comments, issue/PR bodies, and issue titles"
|
||||
required: false
|
||||
default: "@claude"
|
||||
assignee_trigger:
|
||||
description: "The assignee username that triggers the action (e.g. @claude). Only used for issue assignment"
|
||||
required: false
|
||||
max_turns:
|
||||
description: "Maximum number of conversation turns Claude can take"
|
||||
required: false
|
||||
timeout_minutes:
|
||||
description: "Timeout in minutes for execution"
|
||||
required: false
|
||||
default: "30"
|
||||
model:
|
||||
description: "Model to use (provider-specific format required for Bedrock/Vertex)"
|
||||
required: false
|
||||
use_bedrock:
|
||||
description: "Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API"
|
||||
required: false
|
||||
default: "false"
|
||||
use_vertex:
|
||||
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
||||
required: false
|
||||
default: "false"
|
||||
allowed_tools:
|
||||
description: "Additional tools for Claude to use (the base GitHub tools will always be included)"
|
||||
required: false
|
||||
default: ""
|
||||
disallowed_tools:
|
||||
description: "Tools that Claude should never use"
|
||||
required: false
|
||||
default: ""
|
||||
custom_instructions:
|
||||
description: "Additional custom instructions to include in the prompt for Claude"
|
||||
required: false
|
||||
default: ""
|
||||
mcp_config:
|
||||
description: "Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers"
|
||||
required: false
|
||||
default: ""
|
||||
claude_env:
|
||||
description: "Custom environment variables to pass to Claude Code execution (YAML format)"
|
||||
required: false
|
||||
default: ""
|
||||
direct_prompt:
|
||||
description: "Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows)"
|
||||
required: false
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Install Claude Code
|
||||
shell: bash
|
||||
run: npm install -g @anthropic-ai/claude-code
|
||||
|
||||
- name: Install GitHub MCP Server
|
||||
shell: bash
|
||||
run: |
|
||||
claude mcp add-json github '{
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run",
|
||||
"-i",
|
||||
"--rm",
|
||||
"-e",
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
"ghcr.io/github/github-mcp-server:sha-ff3036d"
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ inputs.github_token }}"
|
||||
}
|
||||
}'
|
||||
|
||||
- name: Extract GitHub Context and Create Prompt
|
||||
shell: bash
|
||||
id: prepare_context
|
||||
env:
|
||||
# Pass user-controlled inputs via environment variables to prevent script injection (GHSL-2025-093)
|
||||
EVENT_COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
EVENT_ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
EVENT_ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
EVENT_PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
EVENT_REVIEW_BODY: ${{ github.event.review.body }}
|
||||
EVENT_ASSIGNEE_LOGIN: ${{ github.event.assignee.login }}
|
||||
INPUT_TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
|
||||
INPUT_ASSIGNEE_TRIGGER: ${{ inputs.assignee_trigger }}
|
||||
INPUT_DIRECT_PROMPT: ${{ inputs.direct_prompt }}
|
||||
EVENT_ACTION: ${{ github.event.action }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
run: |
|
||||
echo "🔍 Extracting GitHub context from event: $EVENT_NAME"
|
||||
|
||||
# Function to check for trigger phrase
|
||||
check_trigger() {
|
||||
local text="$1"
|
||||
local trigger="$INPUT_TRIGGER_PHRASE"
|
||||
if [[ "$text" == *"$trigger"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Extract context based on event type
|
||||
TRIGGER_FOUND="false"
|
||||
USER_REQUEST=""
|
||||
CONTEXT_INFO=""
|
||||
|
||||
case "$EVENT_NAME" in
|
||||
"issue_comment")
|
||||
COMMENT_BODY="$EVENT_COMMENT_BODY"
|
||||
ISSUE_TITLE="$EVENT_ISSUE_TITLE"
|
||||
ISSUE_NUMBER="$EVENT_ISSUE_NUMBER"
|
||||
|
||||
if check_trigger "$COMMENT_BODY"; then
|
||||
TRIGGER_FOUND="true"
|
||||
USER_REQUEST="$COMMENT_BODY"
|
||||
CONTEXT_INFO="Issue Comment on #$ISSUE_NUMBER: $ISSUE_TITLE"
|
||||
fi
|
||||
;;
|
||||
|
||||
"pull_request_review_comment")
|
||||
COMMENT_BODY="$EVENT_COMMENT_BODY"
|
||||
PR_TITLE="$EVENT_PR_TITLE"
|
||||
PR_NUMBER="$EVENT_PR_NUMBER"
|
||||
|
||||
if check_trigger "$COMMENT_BODY"; then
|
||||
TRIGGER_FOUND="true"
|
||||
USER_REQUEST="$COMMENT_BODY"
|
||||
CONTEXT_INFO="PR Comment on #$PR_NUMBER: $PR_TITLE"
|
||||
fi
|
||||
;;
|
||||
|
||||
"pull_request_review")
|
||||
REVIEW_BODY="$EVENT_REVIEW_BODY"
|
||||
PR_TITLE="$EVENT_PR_TITLE"
|
||||
PR_NUMBER="$EVENT_PR_NUMBER"
|
||||
|
||||
if check_trigger "$REVIEW_BODY"; then
|
||||
TRIGGER_FOUND="true"
|
||||
USER_REQUEST="$REVIEW_BODY"
|
||||
CONTEXT_INFO="PR Review on #$PR_NUMBER: $PR_TITLE"
|
||||
fi
|
||||
;;
|
||||
|
||||
"issues")
|
||||
ISSUE_BODY="$EVENT_ISSUE_BODY"
|
||||
ISSUE_TITLE="$EVENT_ISSUE_TITLE"
|
||||
ISSUE_NUMBER="$EVENT_ISSUE_NUMBER"
|
||||
|
||||
if check_trigger "$ISSUE_TITLE" || check_trigger "$ISSUE_BODY"; then
|
||||
TRIGGER_FOUND="true"
|
||||
USER_REQUEST="$ISSUE_BODY"
|
||||
CONTEXT_INFO="Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
|
||||
elif [[ "$EVENT_ACTION" == "assigned" && -n "$INPUT_ASSIGNEE_TRIGGER" ]]; then
|
||||
ASSIGNEE="$EVENT_ASSIGNEE_LOGIN"
|
||||
if [[ "$ASSIGNEE" == "$INPUT_ASSIGNEE_TRIGGER" ]]; then
|
||||
TRIGGER_FOUND="true"
|
||||
USER_REQUEST="$ISSUE_BODY"
|
||||
CONTEXT_INFO="Issue #$ISSUE_NUMBER assigned to $ASSIGNEE: $ISSUE_TITLE"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check for direct prompt override
|
||||
if [[ -n "$INPUT_DIRECT_PROMPT" ]]; then
|
||||
TRIGGER_FOUND="true"
|
||||
USER_REQUEST="$INPUT_DIRECT_PROMPT"
|
||||
CONTEXT_INFO="Automated GitHub workflow"
|
||||
fi
|
||||
|
||||
if [[ "$TRIGGER_FOUND" != "true" ]]; then
|
||||
echo "❌ No trigger phrase found or direct prompt provided. Exiting gracefully."
|
||||
echo "SKIP_EXECUTION=true" >> $GITHUB_ENV
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✅ Trigger found! Context: $CONTEXT_INFO"
|
||||
|
||||
# Create comprehensive prompt
|
||||
mkdir -p /tmp/claude-action
|
||||
cat > /tmp/claude-action/github-context-prompt.txt << EOF
|
||||
You are Claude Code, an AI assistant helping with GitHub workflows and code.
|
||||
|
||||
Repository: $REPO_NAME
|
||||
Context: $CONTEXT_INFO
|
||||
Event: $EVENT_NAME
|
||||
|
||||
User Request:
|
||||
$USER_REQUEST
|
||||
|
||||
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
|
||||
|
||||
Respond naturally and helpfully to the user's request using the available tools.
|
||||
EOF
|
||||
|
||||
echo "PROMPT_FILE=/tmp/claude-action/github-context-prompt.txt" >> $GITHUB_ENV
|
||||
echo "SKIP_EXECUTION=false" >> $GITHUB_ENV
|
||||
|
||||
- name: Run Claude Code
|
||||
if: env.SKIP_EXECUTION != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "🚀 Running Claude Code with GitHub context..."
|
||||
|
||||
# Build command arguments
|
||||
CMD_ARGS=("-p" "--verbose" "--output-format" "stream-json")
|
||||
|
||||
# Add max turns if specified
|
||||
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 "${{ inputs.allowed_tools }}" ]]; then
|
||||
TOOLS="$TOOLS,${{ inputs.allowed_tools }}"
|
||||
fi
|
||||
CMD_ARGS+=("--allowedTools" "$TOOLS")
|
||||
|
||||
# Add 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=$((${{ inputs.timeout_minutes }} * 60))
|
||||
timeout $TIMEOUT_SECONDS claude "${CMD_ARGS[@]}" < "${{ env.PROMPT_FILE }}"
|
||||
|
||||
echo "✅ Claude Code execution completed"
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||
GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
@@ -1,87 +0,0 @@
|
||||
name: "Claude Issue Triage Action"
|
||||
description: "Automatically triage GitHub issues using Claude Code"
|
||||
|
||||
inputs:
|
||||
timeout_minutes:
|
||||
description: "Timeout in minutes for execution"
|
||||
required: false
|
||||
default: "5"
|
||||
anthropic_api_key:
|
||||
description: "Anthropic API key"
|
||||
required: true
|
||||
github_token:
|
||||
description: "GitHub token with repo and issues permissions"
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Checkout repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Create prompt file
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/claude-prompts
|
||||
cat > /tmp/claude-prompts/claude-issue-triage-prompt.txt << 'EOF'
|
||||
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
|
||||
|
||||
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
|
||||
|
||||
Issue Information:
|
||||
- REPO: ${{ github.repository }}
|
||||
- ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
|
||||
TASK OVERVIEW:
|
||||
|
||||
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
|
||||
|
||||
2. Next, use the GitHub tools to get context about the issue:
|
||||
- You have access to these tools:
|
||||
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
|
||||
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
|
||||
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
|
||||
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
|
||||
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
|
||||
- Start by using mcp__github__get_issue to get the issue details
|
||||
|
||||
3. Analyze the issue content, considering:
|
||||
- The issue title and description
|
||||
- The type of issue (bug report, feature request, question, etc.)
|
||||
- Technical areas mentioned
|
||||
- Severity or priority indicators
|
||||
- User impact
|
||||
- Components affected
|
||||
|
||||
4. Select appropriate labels from the available labels list provided above:
|
||||
- Choose labels that accurately reflect the issue's nature
|
||||
- Be specific but comprehensive
|
||||
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
|
||||
- Consider platform labels (android, ios) if applicable
|
||||
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
|
||||
|
||||
5. Apply the selected labels:
|
||||
- Use mcp__github__update_issue to apply your selected labels
|
||||
- DO NOT post any comments explaining your decision
|
||||
- DO NOT communicate directly with users
|
||||
- If no labels are clearly applicable, do not apply any labels
|
||||
|
||||
IMPORTANT GUIDELINES:
|
||||
- Be thorough in your analysis
|
||||
- Only select labels from the provided list above
|
||||
- DO NOT post any comments to the issue
|
||||
- Your ONLY action should be to apply labels using mcp__github__update_issue
|
||||
- It's okay to not add any labels if none are clearly applicable
|
||||
EOF
|
||||
|
||||
- name: Run Claude Code
|
||||
uses: ./.github/actions/claude-code-action
|
||||
with:
|
||||
prompt_file: /tmp/claude-prompts/claude-issue-triage-prompt.txt
|
||||
allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
|
||||
install_github_mcp: "true"
|
||||
timeout_minutes: ${{ inputs.timeout_minutes }}
|
||||
anthropic_api_key: ${{ inputs.anthropic_api_key }}
|
||||
github_token: ${{ inputs.github_token }}
|
||||
@@ -1,81 +0,0 @@
|
||||
name: 'Gemini CLI Action'
|
||||
description: 'Uses the Gemini CLI to automate software development tasks.'
|
||||
author: 'Gemini'
|
||||
|
||||
inputs:
|
||||
prompt:
|
||||
description: 'A specific prompt to guide Gemini.'
|
||||
required: false
|
||||
default: 'You are a helpful assistant.'
|
||||
GEMINI_API_KEY:
|
||||
description: 'Your Gemini API key.'
|
||||
required: true
|
||||
OTLP_GCP_WIF_PROVIDER:
|
||||
description: 'The workload identity provider for GCP authentication.'
|
||||
required: false
|
||||
OTLP_GOOGLE_CLOUD_PROJECT:
|
||||
description: 'The Google Cloud project for telemetry.'
|
||||
required: false
|
||||
settings_json:
|
||||
description: |
|
||||
A JSON string to configure the Gemini CLI. This will be written to .gemini/settings.json.
|
||||
To enable observability with Google Cloud, include:
|
||||
{"telemetry": {"enabled": true, "target": "gcp"}, "sandbox": false}
|
||||
required: false
|
||||
version:
|
||||
description: |
|
||||
The version of @google/gemini-cli to execute.
|
||||
Can be a specific version from npm (e.g., '0.1.0', 'latest'), a branch name (e.g., 'main'), or a commit hash.
|
||||
required: false
|
||||
default: 'latest'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Configure Gemini CLI
|
||||
if: inputs.settings_json != ''
|
||||
run: |
|
||||
mkdir -p .gemini
|
||||
echo "$SETTINGS_JSON" > .gemini/settings.json
|
||||
shell: bash
|
||||
env:
|
||||
SETTINGS_JSON: ${{ inputs.settings_json }}
|
||||
|
||||
- name: Authenticate to Google Cloud for Telemetry
|
||||
if: inputs.OTLP_GCP_WIF_PROVIDER != ''
|
||||
id: auth
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
with:
|
||||
project_id: ${{ inputs.OTLP_GOOGLE_CLOUD_PROJECT }}
|
||||
workload_identity_provider: ${{ inputs.OTLP_GCP_WIF_PROVIDER }}
|
||||
|
||||
- name: Install Gemini CLI
|
||||
id: install
|
||||
run: |
|
||||
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#${{ 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
|
||||
gemini --version || echo "Gemini CLI installed successfully (version command not available)"
|
||||
else
|
||||
echo "Error: Gemini CLI not found in PATH"
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Run Gemini CLI
|
||||
run: |
|
||||
gemini --yolo --prompt "$PROMPT"
|
||||
shell: bash
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ inputs.GEMINI_API_KEY }}
|
||||
PROMPT: ${{ inputs.prompt }}
|
||||
|
||||
branding:
|
||||
icon: 'terminal'
|
||||
color: 'blue'
|
||||
@@ -1,26 +0,0 @@
|
||||
name: Auto Issue Comment with PR Creation
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
add-comment:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Add automated comment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude review this issue and do a detailed analysis and fix this if the existing code doesn\'t have the solution implemented. Making sure it has backward compatibility, no existing features removed. After making those changes, again review the applied changes. Use @web to search if you dont know any information or to find the latest documentation or to find the latest version. Run the code if you think you need to run it to test it. Minimal code change to start with if required any changes. MAINLY IT SHOULD NOT IMPACT ON THE CURRENT SPEED OF EXECUTION of existing features, not increasing the overhead. **Please create a PR using gh tool with your changes**.'
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Auto PR Comment
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
add-comment:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Add automated comment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude review this pull request and do a detailed analysis and fix this if the existing code doesn\'t have the solution implemented. Making sure it has backward compatibility, no existing features removed. After making those changes, again review the applied changes. Use @web to search if you dont know any information or to find the latest documentation or to find the latest version. Run the code if you think you need to run it to test it. Minimal code change to start with if required any changes. MAINLY IT SHOULD NOT IMPACT ON THE CURRENT SPEED OF EXECUTION of existing features, not increasing the overhead.'
|
||||
})
|
||||
@@ -1,145 +0,0 @@
|
||||
name: Performance Benchmarks
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
iterations:
|
||||
description: 'Number of benchmark iterations'
|
||||
required: false
|
||||
default: '100'
|
||||
fail_on_regression:
|
||||
description: 'Fail if performance regresses'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e src/praisonai-agents[all]
|
||||
pip install agno crewai langgraph
|
||||
|
||||
- name: Run Import Time Benchmark
|
||||
id: import_time
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/import_time.py 2>&1 | tee import_time_output.txt
|
||||
|
||||
# Extract import time
|
||||
IMPORT_TIME=$(grep -oP 'Median:\s+\K[\d.]+' import_time_output.txt || echo "0")
|
||||
echo "import_time=$IMPORT_TIME" >> $GITHUB_OUTPUT
|
||||
|
||||
# Check threshold (200ms)
|
||||
if (( $(echo "$IMPORT_TIME > 200" | bc -l) )); then
|
||||
echo "::warning::Import time ${IMPORT_TIME}ms exceeds 200ms threshold"
|
||||
if [ "${{ inputs.fail_on_regression }}" == "true" ]; then
|
||||
echo "import_passed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "import_passed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run Memory Usage Benchmark
|
||||
id: memory
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/memory_usage.py 2>&1 | tee memory_output.txt
|
||||
|
||||
# Extract peak memory
|
||||
PEAK_MEMORY=$(grep -oP 'Peak:\s+\K[\d.]+' memory_output.txt || echo "0")
|
||||
echo "peak_memory=$PEAK_MEMORY" >> $GITHUB_OUTPUT
|
||||
|
||||
# Check threshold (30MB)
|
||||
if (( $(echo "$PEAK_MEMORY > 30" | bc -l) )); then
|
||||
echo "::warning::Memory usage ${PEAK_MEMORY}MB exceeds 30MB threshold"
|
||||
if [ "${{ inputs.fail_on_regression }}" == "true" ]; then
|
||||
echo "memory_passed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "memory_passed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Run Simple Benchmark
|
||||
id: simple
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/simple_benchmark.py 2>&1 | tee simple_output.txt
|
||||
|
||||
# Extract PraisonAI time
|
||||
PRAISON_TIME=$(grep -oP 'PraisonAI\s+\K[\d.]+' simple_output.txt | head -1 || echo "0")
|
||||
echo "praison_time=$PRAISON_TIME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Tools Benchmark
|
||||
id: tools
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/tools_benchmark.py 2>&1 | tee tools_output.txt
|
||||
|
||||
# Extract PraisonAI time with tools
|
||||
PRAISON_TOOLS_TIME=$(grep -oP 'PraisonAI\s+\K[\d.]+' tools_output.txt | head -1 || echo "0")
|
||||
echo "praison_tools_time=$PRAISON_TOOLS_TIME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Parameter Impact Benchmark
|
||||
id: param_impact
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python benchmarks/param_impact_benchmark.py --no-save --iterations ${{ inputs.iterations }} 2>&1 | tee param_impact_output.txt
|
||||
|
||||
- name: Check Lazy Imports
|
||||
id: lazy_imports
|
||||
run: |
|
||||
cd src/praisonai-agents
|
||||
python -c "
|
||||
import sys
|
||||
from praisonaiagents import Agent
|
||||
agent = Agent(name='Test', output='silent')
|
||||
|
||||
# Check litellm is NOT loaded
|
||||
if 'litellm' in sys.modules:
|
||||
print('FAIL: litellm loaded during silent agent creation')
|
||||
exit(1)
|
||||
else:
|
||||
print('PASS: litellm not loaded (lazy import working)')
|
||||
"
|
||||
|
||||
- name: Generate Benchmark Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Performance Benchmark Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Metric | Value | Threshold | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|--------|-------|-----------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Import Time | ${{ steps.import_time.outputs.import_time }}ms | <200ms | ${{ steps.import_time.outputs.import_passed == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Peak Memory | ${{ steps.memory.outputs.peak_memory }}MB | <30MB | ${{ steps.memory.outputs.memory_passed == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Agent Instantiation | ${{ steps.simple.outputs.praison_time }}μs | - | ℹ️ |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Agent with Tools | ${{ steps.tools.outputs.praison_tools_time }}μs | - | ℹ️ |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Lazy Import Check" >> $GITHUB_STEP_SUMMARY
|
||||
echo "✅ litellm not loaded during silent agent creation" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Fail on Regression
|
||||
if: inputs.fail_on_regression == 'true'
|
||||
run: |
|
||||
if [ "${{ steps.import_time.outputs.import_passed }}" == "false" ]; then
|
||||
echo "::error::Import time regression detected"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ steps.memory.outputs.memory_passed }}" == "false" ]; then
|
||||
echo "::error::Memory usage regression detected"
|
||||
exit 1
|
||||
fi
|
||||
echo "All performance checks passed!"
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Build Claude Code Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Image version tag'
|
||||
required: true
|
||||
default: 'latest'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./.github/actions/claude-code-action
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
tags: ghcr.io/mervinpraison/praisonai-claudecode:${{ inputs.version }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: false
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Claude Issue Triage
|
||||
description: "Automatically triage GitHub issues using Claude Code"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
triage-issue:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Run Claude Issue Triage
|
||||
uses: ./.github/actions/claude-issue-triage-action
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,21 +0,0 @@
|
||||
name: Claude OAuth
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
code:
|
||||
description: 'Authorization code (leave empty for step 1)'
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
actions: write # Required for cache management
|
||||
contents: read # Required for basic repository access
|
||||
|
||||
jobs:
|
||||
auth:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: grll/claude-code-login@v1
|
||||
with:
|
||||
code: ${{ inputs.code }}
|
||||
secrets_admin_pat: ${{ secrets.SECRETS_ADMIN_PAT }}
|
||||
@@ -1,160 +0,0 @@
|
||||
name: Claude Assistant
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened, assigned, labeled]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
claude-response:
|
||||
# Prevent certain bots from triggering this action (but allow github-actions[bot] for auto-comments)
|
||||
if: |
|
||||
github.actor != 'dependabot[bot]' &&
|
||||
github.actor != 'cursor[bot]' &&
|
||||
github.actor != 'renovate[bot]' &&
|
||||
!(contains(github.actor, '[bot]') && github.actor != 'github-actions[bot]')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_ACCESS_TOKEN }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
trigger_phrase: "@claude"
|
||||
allowed_tools: |
|
||||
Bash(git:*)
|
||||
Bash(python:*)
|
||||
Bash(pip:*)
|
||||
Bash(conda:*)
|
||||
Bash(pytest:*)
|
||||
Bash(gh:*)
|
||||
Bash(python -m pytest:*)
|
||||
Bash(python -m pip:*)
|
||||
Bash(poetry:*)
|
||||
View
|
||||
GlobTool
|
||||
GrepTool
|
||||
BatchTool
|
||||
Edit
|
||||
Replace
|
||||
mcp__github__get_issue
|
||||
mcp__github__get_issue_comments
|
||||
mcp__github__update_issue
|
||||
timeout_minutes: 30
|
||||
|
||||
post-review-comment:
|
||||
needs: claude-response
|
||||
if: success() && needs.claude-response.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Check for existing review comment
|
||||
id: check-comment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const reviewText = '@claude review if the changes made in the above branch are as per the requirements and make changes to the above branch if issues still exist.';
|
||||
let comments = [];
|
||||
let issueNumber;
|
||||
|
||||
try {
|
||||
// Determine issue/PR number based on event type
|
||||
if (context.eventName === 'pull_request_review_comment' || context.eventName === 'pull_request_review') {
|
||||
issueNumber = context.payload.pull_request?.number || context.issue.number;
|
||||
} else if (context.eventName === 'issue_comment' && context.payload.issue.pull_request) {
|
||||
// This is a comment on a PR (not an issue)
|
||||
issueNumber = context.payload.issue.number;
|
||||
} else {
|
||||
// This is a regular issue
|
||||
issueNumber = context.issue.number;
|
||||
}
|
||||
|
||||
comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber
|
||||
});
|
||||
|
||||
// Look for Claude's completed review comments
|
||||
const claudeReviewComments = comments.data.filter(comment =>
|
||||
(comment.user.login === 'claude[bot]' || comment.body.includes('✅') || comment.body.includes('## Summary')) &&
|
||||
!comment.body.includes('⏳') &&
|
||||
!comment.body.includes('🔄') &&
|
||||
!comment.body.includes('Loading...')
|
||||
);
|
||||
|
||||
// Check if our review request comment already exists
|
||||
const existingReviewRequest = comments.data.find(comment =>
|
||||
comment.body.includes(reviewText)
|
||||
);
|
||||
|
||||
// Post if Claude has completed a review AND we haven't posted the review request yet
|
||||
const shouldPost = claudeReviewComments.length > 0 && !existingReviewRequest;
|
||||
core.setOutput('should-post', shouldPost);
|
||||
console.log(`Claude review completed: ${claudeReviewComments.length > 0}`);
|
||||
console.log(`Review request exists: ${!!existingReviewRequest}`);
|
||||
console.log(`Should post comment: ${shouldPost}`);
|
||||
|
||||
} catch (error) {
|
||||
console.log('Error checking comments:', error);
|
||||
core.setOutput('should-post', false); // If we can't check, don't post
|
||||
}
|
||||
|
||||
- name: Wait 30 seconds before posting comment
|
||||
if: steps.check-comment.outputs.should-post == 'true'
|
||||
run: sleep 30
|
||||
|
||||
- name: Post review comment
|
||||
if: steps.check-comment.outputs.should-post == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
let issueNumber;
|
||||
let isPR = false;
|
||||
|
||||
// Determine issue/PR number and type based on event type
|
||||
if (context.eventName === 'pull_request_review_comment' || context.eventName === 'pull_request_review') {
|
||||
issueNumber = context.payload.pull_request?.number || context.issue.number;
|
||||
isPR = true;
|
||||
} else if (context.eventName === 'issue_comment' && context.payload.issue.pull_request) {
|
||||
// This is a comment on a PR (not an issue)
|
||||
issueNumber = context.payload.issue.number;
|
||||
isPR = true;
|
||||
} else {
|
||||
// This is a regular issue - don't post review comment
|
||||
issueNumber = context.issue.number;
|
||||
isPR = false;
|
||||
}
|
||||
|
||||
// Only post review comment on PRs, not regular issues
|
||||
if (isPR) {
|
||||
console.log(`Posting review comment on PR #${issueNumber}`);
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '@claude review if the changes made in the above branch are as per the requirements and make changes to the above branch if issues still exist. MAINLY IT SHOULD NOT IMPACT ON THE CURRENT SPEED OF EXECUTION of existing features, not increasing the overhead.'
|
||||
});
|
||||
} else {
|
||||
console.log(`Skipping review comment for regular issue #${issueNumber}`);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
name: Run tests and upload coverage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Run tests and collect coverage
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-cov pytest-asyncio
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV
|
||||
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd src/praisonai
|
||||
# Run tests - allow some failures for now while we fix remaining issues
|
||||
pytest --cov=praisonai --cov-branch --cov-report=xml tests/unit/ --tb=no -q || echo "Some tests failed, but coverage was collected"
|
||||
|
||||
- name: Upload results to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: src/praisonai/coverage.xml
|
||||
flags: unit-tests
|
||||
name: unit-tests-coverage
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
@@ -1,54 +0,0 @@
|
||||
name: Publish Docker
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Docker image tag (default: latest)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/mervinpraison/praisonai
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||
type=semver,pattern={{version}},enable=${{ github.event_name == 'release' }}
|
||||
type=semver,pattern={{major}}.{{minor}},enable=${{ github.event_name == 'release' }}
|
||||
type=raw,value=${{ github.event.inputs.tag || 'latest' }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -1,76 +0,0 @@
|
||||
name: Gemini Automated Issue Triage
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
triage-issue:
|
||||
if: >
|
||||
github.event_name == 'issue_comment' &&
|
||||
!github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '@gemini triage') &&
|
||||
(github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR')
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
id-token: write
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
||||
cancel-in-progress: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: generate_token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
|
||||
- name: Run Gemini Issue Triage
|
||||
uses: google-gemini/gemini-cli-action@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
with:
|
||||
version: 0.1.8-rc.0
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
OTLP_GCP_WIF_PROVIDER: ${{ secrets.OTLP_GCP_WIF_PROVIDER }}
|
||||
OTLP_GOOGLE_CLOUD_PROJECT: ${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}
|
||||
settings_json: |
|
||||
{
|
||||
"coreTools": [
|
||||
"run_shell_command(gh label list)",
|
||||
"run_shell_command(gh issue edit)"
|
||||
],
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"target": "gcp"
|
||||
},
|
||||
"sandbox": false
|
||||
}
|
||||
prompt: |
|
||||
You are an issue triage assistant. Analyze the current GitHub issue and apply the most appropriate existing labels.
|
||||
|
||||
Steps:
|
||||
1. Run: `gh label list --limit 100` to get all available labels.
|
||||
2. Review the issue title and body provided in the environment variables.
|
||||
3. Select the most relevant labels from the existing labels, focusing on kind/*, area/*, and priority/*.
|
||||
4. Apply the selected labels to this issue using: `gh issue edit ISSUE_NUMBER --add-label "label1,label2"`
|
||||
|
||||
Guidelines:
|
||||
- Only use labels that already exist in the repository.
|
||||
- Do not add comments or modify the issue content.
|
||||
- Triage only the current issue.
|
||||
- Assign all applicable kind/*, area/*, and priority/* labels based on the issue content.
|
||||
@@ -1,202 +0,0 @@
|
||||
name: Gemini Issue Review
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
review-issue:
|
||||
if: >
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'issue_comment' &&
|
||||
!github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '@gemini') &&
|
||||
!contains(github.event.comment.body, '@gemini triage') &&
|
||||
(github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR'))
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
steps:
|
||||
- name: Generate token
|
||||
id: generate_token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
|
||||
- name: Set up Git
|
||||
run: |
|
||||
git config --local user.email "[email protected]"
|
||||
git config --local user.name "Gemini Bot"
|
||||
|
||||
- name: Get issue data
|
||||
id: issue_data
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
# Pass user-controlled inputs via environment variables to prevent script injection (GHSL-2025-093)
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
EVENT_COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
run: |
|
||||
# Get issue number from event or context
|
||||
if [ "$EVENT_NAME" = "issue_comment" ]; then
|
||||
ISSUE_NUMBER="$EVENT_ISSUE_NUMBER"
|
||||
elif [ "$EVENT_NAME" = "issues" ]; then
|
||||
ISSUE_NUMBER="$EVENT_ISSUE_NUMBER"
|
||||
else
|
||||
echo "Unable to determine issue number"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get issue details
|
||||
gh issue view $ISSUE_NUMBER --json title,body,labels,assignees > issue_details.json
|
||||
|
||||
# Extract additional instructions from comment if triggered by comment
|
||||
ADDITIONAL_INSTRUCTIONS=""
|
||||
if [ "$EVENT_NAME" = "issue_comment" ]; then
|
||||
COMMENT_BODY="$EVENT_COMMENT_BODY"
|
||||
ADDITIONAL_INSTRUCTIONS=$(echo "$COMMENT_BODY" | sed -n 's/.*@gemini[[:space:]]*\(.*\)/\1/p' | head -1)
|
||||
fi
|
||||
|
||||
echo "ISSUE_NUMBER=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "ADDITIONAL_INSTRUCTIONS=$ADDITIONAL_INSTRUCTIONS" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Gemini CLI
|
||||
uses: ./.github/actions/gemini
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
ISSUE_NUMBER: ${{ steps.issue_data.outputs.ISSUE_NUMBER }}
|
||||
ADDITIONAL_INSTRUCTIONS: ${{ steps.issue_data.outputs.ADDITIONAL_INSTRUCTIONS }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
with:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
OTLP_GCP_WIF_PROVIDER: ${{ secrets.OTLP_GCP_WIF_PROVIDER }}
|
||||
OTLP_GOOGLE_CLOUD_PROJECT: ${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}
|
||||
prompt: |
|
||||
You are a helpful GitHub issue assistant. Your task is to analyze and help with GitHub issues.
|
||||
|
||||
**Issue Details:**
|
||||
- Issue Number: #${ISSUE_NUMBER}
|
||||
- Repository: ${REPOSITORY}
|
||||
- Issue URL: ${{ github.event.issue.html_url }}
|
||||
|
||||
**Additional Instructions:** ${ADDITIONAL_INSTRUCTIONS}
|
||||
|
||||
**CRITICAL: When making code modifications, follow this EXACT workflow:**
|
||||
|
||||
**STEP 1: Analyze the Issue**
|
||||
- Use: `gh issue view ${ISSUE_NUMBER} --json title,body,labels,assignees`
|
||||
- Analyze issue content thoroughly
|
||||
- Search codebase for related files using `glob`, `search_file_content`, `list_directory`
|
||||
|
||||
**STEP 2: If Code Changes Are Needed**
|
||||
Create a new branch with 'gemini/' prefix:
|
||||
- Use: `git checkout -b gemini/issue-${ISSUE_NUMBER}-description`
|
||||
- Example: `git checkout -b gemini/issue-123-fix-sequential-tools`
|
||||
|
||||
**STEP 3: Make Code Modifications**
|
||||
- Use `read_file` to examine existing code
|
||||
- Use `write_file` or `replace` to make changes
|
||||
- Use `search_file_content` to find related code
|
||||
- Make focused, targeted changes to fix the issue
|
||||
|
||||
**STEP 4: Test Changes (if applicable)**
|
||||
- Run relevant tests if test files exist
|
||||
- Use `run_shell_command` to execute test commands
|
||||
|
||||
**STEP 5: Commit and Push Changes**
|
||||
- Use: `git add .` (or specific files)
|
||||
- Use: `git commit -m "Gemini: [Issue #${ISSUE_NUMBER}] Brief description of fix"`
|
||||
- Use: `git push -u origin gemini/issue-${ISSUE_NUMBER}-description`
|
||||
|
||||
**STEP 6: Create Pull Request**
|
||||
- Use: `gh pr create --title "Fix #${ISSUE_NUMBER}: Issue description" --body "Fixes #${ISSUE_NUMBER}\n\nSummary of changes:\n- Change 1\n- Change 2" --head gemini/issue-${ISSUE_NUMBER}-description --base main`
|
||||
- This will return a PR URL like: https://github.com/repo/pull/123
|
||||
|
||||
**STEP 7: Comment on Issue with Results**
|
||||
Use: `gh issue comment ${ISSUE_NUMBER} -b "MESSAGE"`
|
||||
|
||||
**Your final comment MUST include:**
|
||||
- Summary of what was analyzed
|
||||
- What changes were made (if any)
|
||||
- Link to the created PR (if applicable)
|
||||
- Brief explanation of the solution
|
||||
|
||||
**Example final comment:**
|
||||
```
|
||||
Hi! I've analyzed and fixed this issue.
|
||||
|
||||
**Problem:** Sequential tool calling was getting stuck after first tool call.
|
||||
|
||||
**Solution:** Added proper prompt handling after each tool call in the Ollama model integration.
|
||||
|
||||
**Changes Made:**
|
||||
- Modified `src/praisonai-agents/ollama-sequential.py`
|
||||
- Added continuation prompts for multi-step tool execution
|
||||
|
||||
**Pull Request:** https://github.com/MervinPraison/PraisonAI/pull/856
|
||||
|
||||
The fix is ready for review. Please test and let me know if you encounter any issues!
|
||||
```
|
||||
|
||||
**IMPORTANT NOTES:**
|
||||
- Always create branches with 'gemini/' prefix
|
||||
- Always include issue number in branch name and commit message
|
||||
- Always create PR that references the issue
|
||||
- Always provide PR link in your final response
|
||||
- Use the GITHUB_TOKEN environment variable for authentication
|
||||
|
||||
Please proceed with helping resolve this issue following this exact workflow.
|
||||
|
||||
settings_json: |
|
||||
{
|
||||
"coreTools": [
|
||||
"run_shell_command(echo)",
|
||||
"run_shell_command(gh issue view)",
|
||||
"run_shell_command(gh issue comment)",
|
||||
"run_shell_command(gh issue list)",
|
||||
"run_shell_command(gh issue edit)",
|
||||
"run_shell_command(gh pr create)",
|
||||
"run_shell_command(gh pr view)",
|
||||
"run_shell_command(gh pr list)",
|
||||
"run_shell_command(cat)",
|
||||
"run_shell_command(head)",
|
||||
"run_shell_command(tail)",
|
||||
"run_shell_command(grep)",
|
||||
"run_shell_command(find)",
|
||||
"run_shell_command(git checkout)",
|
||||
"run_shell_command(git add)",
|
||||
"run_shell_command(git commit)",
|
||||
"run_shell_command(git push)",
|
||||
"run_shell_command(git status)",
|
||||
"run_shell_command(git branch)",
|
||||
"list_directory",
|
||||
"read_file",
|
||||
"read_many_files",
|
||||
"write_file",
|
||||
"replace",
|
||||
"glob",
|
||||
"search_file_content",
|
||||
"web_fetch",
|
||||
"google_web_search",
|
||||
"save_memory"
|
||||
],
|
||||
"telemetry": {
|
||||
"enabled": false,
|
||||
"otlpGcpWifProvider": "${{ secrets.OTLP_GCP_WIF_PROVIDER }}",
|
||||
"googleCloudProject": "${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}"
|
||||
}
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
name: Gemini PR Review
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to review'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
review-pr:
|
||||
if: >
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '@gemini') &&
|
||||
(github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR'))
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: generate_token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.PRIVATE_KEY }}
|
||||
|
||||
- name: Determine checkout ref
|
||||
id: checkout_ref
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
||||
echo "ref=refs/pull/${{ github.event.issue.number }}/head" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "ref=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Checkout PR code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
ref: ${{ steps.checkout_ref.outputs.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get PR details
|
||||
id: get_pr
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
# Pass user-controlled inputs via environment variables to prevent script injection (GHSL-2025-093)
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
EVENT_INPUTS_PR_NUMBER: ${{ github.event.inputs.pr_number }}
|
||||
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
EVENT_COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
run: |
|
||||
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
|
||||
PR_NUMBER=$EVENT_INPUTS_PR_NUMBER
|
||||
elif [ "$EVENT_NAME" = "issue_comment" ]; then
|
||||
PR_NUMBER=$EVENT_ISSUE_NUMBER
|
||||
else
|
||||
PR_NUMBER=$EVENT_PR_NUMBER
|
||||
fi
|
||||
|
||||
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Extract additional instructions from comment (if triggered by comment)
|
||||
ADDITIONAL_INSTRUCTIONS=""
|
||||
if [ "$EVENT_NAME" = "issue_comment" ]; then
|
||||
COMMENT_BODY="$EVENT_COMMENT_BODY"
|
||||
ADDITIONAL_INSTRUCTIONS=$(echo "$COMMENT_BODY" | sed 's/.*@gemini//' | xargs)
|
||||
fi
|
||||
echo "additional_instructions=$ADDITIONAL_INSTRUCTIONS" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Get PR details
|
||||
PR_DATA=$(gh pr view $PR_NUMBER --json title,body,additions,deletions,changedFiles,baseRefName,headRefName)
|
||||
echo "pr_data=$PR_DATA" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Get file changes
|
||||
CHANGED_FILES=$(gh pr diff $PR_NUMBER --name-only)
|
||||
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run Gemini PR Review
|
||||
uses: ./.github/actions/gemini
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
|
||||
PR_DATA: ${{ steps.get_pr.outputs.pr_data }}
|
||||
CHANGED_FILES: ${{ steps.get_pr.outputs.changed_files }}
|
||||
ADDITIONAL_INSTRUCTIONS: ${{ steps.get_pr.outputs.additional_instructions }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
with:
|
||||
version: 0.1.8-rc.0
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
OTLP_GCP_WIF_PROVIDER: ${{ secrets.OTLP_GCP_WIF_PROVIDER }}
|
||||
OTLP_GOOGLE_CLOUD_PROJECT: ${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}
|
||||
settings_json: |
|
||||
{
|
||||
"coreTools": [
|
||||
"run_shell_command(echo)",
|
||||
"run_shell_command(gh pr view)",
|
||||
"run_shell_command(gh pr diff)",
|
||||
"run_shell_command(gh pr comment)",
|
||||
"run_shell_command(gh pr list)",
|
||||
"run_shell_command(cat)",
|
||||
"run_shell_command(head)",
|
||||
"run_shell_command(tail)",
|
||||
"run_shell_command(grep)",
|
||||
"run_shell_command(find)",
|
||||
"run_shell_command(git checkout)",
|
||||
"run_shell_command(git add)",
|
||||
"run_shell_command(git commit)",
|
||||
"run_shell_command(git push)",
|
||||
"run_shell_command(git status)",
|
||||
"run_shell_command(git branch)",
|
||||
"run_shell_command(git fetch)",
|
||||
"list_directory",
|
||||
"read_file",
|
||||
"read_many_files",
|
||||
"write_file",
|
||||
"replace",
|
||||
"glob",
|
||||
"search_file_content",
|
||||
"web_fetch",
|
||||
"google_web_search",
|
||||
"save_memory"
|
||||
],
|
||||
"telemetry": {
|
||||
"enabled": false,
|
||||
"target": "gcp"
|
||||
},
|
||||
"sandbox": false
|
||||
}
|
||||
prompt: |
|
||||
You are an expert code reviewer and AI assistant for Pull Requests. You can review code AND make modifications to improve the PR.
|
||||
|
||||
**PR Details:**
|
||||
- PR Number: #$PR_NUMBER
|
||||
- Repository: $REPOSITORY
|
||||
- Additional Instructions: $ADDITIONAL_INSTRUCTIONS
|
||||
|
||||
**CRITICAL: When making code modifications, follow this EXACT workflow:**
|
||||
|
||||
**STEP 1: Analyze the PR**
|
||||
- Use: `echo "$PR_DATA"` to get PR details (JSON format)
|
||||
- Use: `echo "$CHANGED_FILES"` to get changed files list
|
||||
- Use: `gh pr diff ${PR_NUMBER}` to see the full diff
|
||||
- Use: `gh pr view ${PR_NUMBER} --json title,body,headRefName` to get PR info
|
||||
- Analyze the code changes thoroughly
|
||||
|
||||
**STEP 2: If Code Improvements Are Needed**
|
||||
**IMPORTANT:** DO NOT create a new branch! Work on the EXISTING PR branch.
|
||||
- Use: `git fetch origin` to get latest changes
|
||||
- Get PR branch name: `gh pr view ${PR_NUMBER} --json headRefName`
|
||||
- Switch to PR branch: `git checkout BRANCH_NAME` (use actual branch name from PR)
|
||||
- Use: `git pull origin BRANCH_NAME` to get latest PR changes
|
||||
|
||||
**STEP 3: Make Code Modifications**
|
||||
- Use `read_file` to examine existing code
|
||||
- Use `write_file` or `replace` to make improvements
|
||||
- Use `search_file_content` to find related code
|
||||
- Make focused, targeted improvements to the PR
|
||||
|
||||
**STEP 4: Test Changes (if applicable)**
|
||||
- Run relevant tests if test files exist
|
||||
- Use `run_shell_command` to execute test commands
|
||||
|
||||
**STEP 5: Commit and Push Changes to EXISTING PR Branch**
|
||||
- Use: `git add .` (or specific files)
|
||||
- Use: `git commit -m "Gemini: [PR #${PR_NUMBER}] Brief description of improvements"`
|
||||
- Use: `git push origin BRANCH_NAME` (push to EXISTING PR branch)
|
||||
|
||||
**STEP 6: Review and Comment on PR**
|
||||
Write comprehensive review and post comment with your analysis and any improvements made.
|
||||
Use: `gh pr comment ${PR_NUMBER} -b "MESSAGE"`
|
||||
|
||||
Review Guidelines:
|
||||
- Focus on code quality, security, performance, and maintainability
|
||||
- Check for common issues: potential bugs, security vulnerabilities, performance bottlenecks
|
||||
- Verify error handling and edge cases
|
||||
- Look for code style and best practices
|
||||
- Comment on architecture and design decisions if significant
|
||||
- Be constructive and specific in feedback
|
||||
- Highlight both issues and positive aspects
|
||||
- Suggest improvements with examples when possible
|
||||
|
||||
Review Areas:
|
||||
- **Security**: Authentication, authorization, input validation, data sanitization
|
||||
- **Performance**: Algorithms, database queries, caching, resource usage
|
||||
- **Reliability**: Error handling, logging, testing coverage, edge cases
|
||||
- **Maintainability**: Code structure, documentation, naming conventions
|
||||
- **Functionality**: Logic correctness, requirements fulfillment
|
||||
|
||||
Output Format:
|
||||
Structure your review using this exact format with markdown:
|
||||
|
||||
## 📋 Review Summary
|
||||
Provide a brief 2-3 sentence overview of the PR and overall assessment.
|
||||
|
||||
## 🔍 General Feedback
|
||||
- List general observations about code quality
|
||||
- Mention overall patterns or architectural decisions
|
||||
- Highlight positive aspects of the implementation
|
||||
- Note any recurring themes across files
|
||||
|
||||
## 🎯 Specific Feedback
|
||||
Only include sections below that have actual issues. If there are no issues in a priority category, omit that entire section.
|
||||
|
||||
### 🔴 Critical
|
||||
(Only include this section if there are critical issues)
|
||||
Issues that must be addressed before merging (security vulnerabilities, breaking changes, major bugs):
|
||||
- **File: `filename:line`** - Description of critical issue with specific recommendation
|
||||
|
||||
### 🟡 High
|
||||
(Only include this section if there are high priority issues)
|
||||
Important issues that should be addressed (performance problems, design flaws, significant bugs):
|
||||
- **File: `filename:line`** - Description of high priority issue with suggested fix
|
||||
|
||||
### 🟢 Medium
|
||||
(Only include this section if there are medium priority issues)
|
||||
Improvements that would enhance code quality (style issues, minor optimizations, better practices):
|
||||
- **File: `filename:line`** - Description of medium priority improvement
|
||||
|
||||
### 🔵 Low
|
||||
(Only include this section if there are suggestions)
|
||||
Nice-to-have improvements and suggestions (documentation, naming, minor refactoring):
|
||||
- **File: `filename:line`** - Description of suggestion or enhancement
|
||||
|
||||
**Note**: If no specific issues are found in any category, simply state "No specific issues identified in this review."
|
||||
|
||||
## ✅ Highlights
|
||||
(Only include this section if there are positive aspects to highlight)
|
||||
- Mention specific good practices or implementations
|
||||
- Acknowledge well-written code sections
|
||||
- Note improvements from previous versions
|
||||
|
||||
---
|
||||
*Review completed by Gemini CLI*
|
||||
@@ -1,68 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Python package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "develop" ]
|
||||
pull_request:
|
||||
branches: [ "develop" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install flake8 pytest
|
||||
python -m pip install ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
python -m pip install duckduckgo_search
|
||||
python -m pip install pytest-asyncio pytest-cov
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
python -m pip install "praisonaiagents[knowledge]"
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
|
||||
- name: Debug API Key Status
|
||||
run: |
|
||||
echo "🔍 Checking API key availability..."
|
||||
if [ -n "${{ secrets.OPENAI_API_KEY }}" ]; then
|
||||
echo "✅ GitHub secret OPENAI_API_KEY is available"
|
||||
echo "🔑 API key starts with: $(echo "$OPENAI_API_KEY" | cut -c1-7)..."
|
||||
else
|
||||
echo "⚠️ GitHub secret OPENAI_API_KEY is NOT set - using fallback"
|
||||
echo "🔑 Using fallback key: sk-test-key..."
|
||||
fi
|
||||
echo "🌐 API Base: $OPENAI_API_BASE"
|
||||
echo "🤖 Model: $OPENAI_MODEL_NAME"
|
||||
|
||||
# - name: Lint with flake8
|
||||
# run: |
|
||||
# # stop the build if there are Python syntax errors or undefined names
|
||||
# flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
# flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
cd src/praisonai && python tests/test_runner.py --pattern fast
|
||||
@@ -1,40 +0,0 @@
|
||||
# This workflow will upload a Python Package using Twine when a release is created
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install build
|
||||
- name: Build package
|
||||
run: python -m build src/praisonai
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/praisonai/dist/
|
||||
@@ -1,524 +0,0 @@
|
||||
name: Release Workflow
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to create release with (e.g., v2.0.10)'
|
||||
required: true
|
||||
fork_owner:
|
||||
description: 'GitHub username of the fork owner'
|
||||
required: true
|
||||
default: 'tosin2013'
|
||||
|
||||
jobs:
|
||||
test-ubuntu:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-24.04]
|
||||
framework: ['crewai', 'autogen']
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 90
|
||||
|
||||
steps:
|
||||
# First checkout the upstream repo at the specified tag
|
||||
- name: Checkout upstream repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: MervinPraison/PraisonAI
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
fetch-depth: 0
|
||||
|
||||
# Get the commit hash from the tag
|
||||
- name: Get commit hash
|
||||
id: get_commit
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse HEAD)
|
||||
echo "commit_hash=$COMMIT_HASH" >> $GITHUB_OUTPUT
|
||||
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:
|
||||
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
clean: true
|
||||
|
||||
# Sync with upstream commit
|
||||
- name: Sync with upstream
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions"
|
||||
git config --global user.email "[email protected]"
|
||||
|
||||
# Add upstream remote
|
||||
git remote add upstream https://github.com/MervinPraison/PraisonAI.git
|
||||
|
||||
# Fetch from upstream
|
||||
git fetch upstream --tags --force
|
||||
|
||||
# Reset to upstream commit
|
||||
git reset --hard ${{ steps.get_commit.outputs.commit_hash }}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
poetry config virtualenvs.in-project true
|
||||
poetry config virtualenvs.create true
|
||||
poetry --version
|
||||
poetry config --list
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
poetry lock #--no-update
|
||||
poetry install --all-extras
|
||||
poetry env info
|
||||
# Verify venv exists
|
||||
test -d .venv || { echo "Virtual environment not created!"; exit 1; }
|
||||
test -f .venv/bin/activate || { echo "Activation script not found!"; exit 1; }
|
||||
|
||||
- name: Run setup script
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
set -e # Exit on any error
|
||||
curl -OL https://raw.githubusercontent.com/tosin2013/PraisonAI/refs/heads/main/setup.sh
|
||||
chmod +x setup.sh
|
||||
./setup.sh --all --cicd || { echo "Setup script failed"; exit 1; }
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Test Basic Functionality
|
||||
run: |
|
||||
set -e # Exit on any error
|
||||
|
||||
# Random sleep between 60-120 seconds to avoid API rate limits
|
||||
SLEEP_TIME=$((RANDOM % 61 + 60))
|
||||
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
|
||||
sleep $SLEEP_TIME
|
||||
|
||||
lsof -ti:8084 | xargs -r kill -9 || true
|
||||
source .venv/bin/activate || { echo "Failed to activate virtual environment"; exit 1; }
|
||||
mkdir -p test_output || { echo "Failed to create test_output directory"; exit 1; }
|
||||
|
||||
echo "Testing basic CLI functionality..."
|
||||
|
||||
# Test auto generation with explicit framework
|
||||
praisonai --framework ${{ matrix.framework }} --auto "create a short test script" 2>&1 | tee test_output/auto_test.log
|
||||
|
||||
# Verify the output file exists and has content
|
||||
if [[ ! -s test_output/auto_test.log ]]; then
|
||||
echo "Error: No output generated from auto test"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test auto generation without framework
|
||||
echo "Basic functionality tests passed!"
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
LOGLEVEL: "debug"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Test Framework-specific Features
|
||||
continue-on-error: false
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
|
||||
# Random sleep between 60-120 seconds to avoid API rate limits
|
||||
SLEEP_TIME=$((RANDOM % 61 + 60))
|
||||
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
|
||||
sleep $SLEEP_TIME
|
||||
|
||||
lsof -ti:8084 | xargs -r kill -9 || true
|
||||
praisonai --framework ${{ matrix.framework }} --auto "create a movie script about Robots in Mars" 2>&1 | tee test_output/framework_output.txt || echo "Framework-specific test failed" >> test_output/errors.log
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Test Core Functionality
|
||||
continue-on-error: false
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
|
||||
# Random sleep between 60-120 seconds to avoid API rate limits
|
||||
SLEEP_TIME=$((RANDOM % 61 + 60))
|
||||
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
|
||||
sleep $SLEEP_TIME
|
||||
|
||||
echo "Testing core functionality..."
|
||||
|
||||
# Test package imports
|
||||
python3 -c "from praisonai.cli import PraisonAI; from praisonai.auto import AutoGenerator; from praisonai.agents_generator import AgentsGenerator" || exit 1
|
||||
|
||||
# Test configuration loading
|
||||
python3 -c "from praisonai.inc.config import generate_config; print(generate_config())" || exit 1
|
||||
|
||||
# Test tools loading
|
||||
python3 -c "from praisonai.inbuilt_tools import *" || exit 1
|
||||
|
||||
echo "Core functionality tests passed!"
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
LOGLEVEL: "debug"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Test Agent Generation
|
||||
continue-on-error: false
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
|
||||
# Random sleep between 60-120 seconds to avoid API rate limits
|
||||
SLEEP_TIME=$((RANDOM % 61 + 60))
|
||||
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
|
||||
sleep $SLEEP_TIME
|
||||
|
||||
echo "Testing agent generation..."
|
||||
|
||||
# Create a test YAML file
|
||||
cat > test_agents.yaml <<EOF
|
||||
topic: "Test automation task"
|
||||
framework: "${{ matrix.framework }} "
|
||||
roles:
|
||||
- name: "Tester"
|
||||
goal: "Test the system"
|
||||
backstory: "Expert system tester"
|
||||
tasks:
|
||||
- description: "Verify system functionality"
|
||||
expected_output: "Test results"
|
||||
EOF
|
||||
|
||||
# Test YAML loading and agent generation
|
||||
python3 -c '
|
||||
from praisonai.agents_generator import AgentsGenerator
|
||||
config = [{"model": "llama-3.3-70b-versatile", "base_url": "https://api.groq.com/openai/v1/"}]
|
||||
generator = AgentsGenerator("test_agents.yaml", "${{ matrix.framework }} ", config)
|
||||
print("Agent generation test passed!")
|
||||
' || exit 1
|
||||
|
||||
echo "Agent generation tests passed!"
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
LOGLEVEL: "debug"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Test Custom Tools Integration
|
||||
continue-on-error: false
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
|
||||
# Random sleep between 60-120 seconds to avoid API rate limits
|
||||
SLEEP_TIME=$((RANDOM % 61 + 60))
|
||||
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
|
||||
sleep $SLEEP_TIME
|
||||
|
||||
echo "Testing custom tools integration..."
|
||||
|
||||
# Create a test tool
|
||||
cat > test_tool.py <<EOF
|
||||
from duckduckgo_search import DDGS
|
||||
from praisonai_tools import BaseTool
|
||||
|
||||
class InternetSearchTool(BaseTool):
|
||||
name: str = "Internet Search Tool"
|
||||
description: str = "Search Internet for relevant information"
|
||||
|
||||
def _run(self, query: str):
|
||||
return {"status": "success", "query": query}
|
||||
|
||||
def test(self):
|
||||
"""Test method to verify tool setup"""
|
||||
test_query = "test query"
|
||||
result = self._run(test_query)
|
||||
assert result["status"] == "success"
|
||||
assert result["query"] == test_query
|
||||
return True
|
||||
EOF
|
||||
|
||||
# Test tool loading and initialization
|
||||
python3 -c '
|
||||
import importlib.util
|
||||
import sys
|
||||
|
||||
# Load the test tool
|
||||
spec = importlib.util.spec_from_file_location("test_tool", "test_tool.py")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules["test_tool"] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Initialize and test the tool
|
||||
tool = module.InternetSearchTool()
|
||||
assert tool.test(), "Tool test failed"
|
||||
print("Custom tool test passed!")
|
||||
' || exit 1
|
||||
|
||||
echo "Custom tools integration test passed!"
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
LOGLEVEL: "debug"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Test PraisonAI Agents Configuration
|
||||
continue-on-error: false
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
|
||||
# Random sleep between 60-120 seconds to avoid API rate limits
|
||||
SLEEP_TIME=$((RANDOM % 61 + 60))
|
||||
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
|
||||
sleep $SLEEP_TIME
|
||||
|
||||
echo "Testing agents configuration..."
|
||||
mkdir -p test_output || { echo "Failed to create test_output directory"; exit 1; }
|
||||
|
||||
# Create test script
|
||||
cat > test_agents.py <<EOF
|
||||
from langchain_openai import ChatOpenAI
|
||||
from crewai import Agent, Task, Crew
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
import random
|
||||
|
||||
logging.basicConfig(level="DEBUG")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def random_sleep(min_seconds=15, max_seconds=30):
|
||||
"""Sleep for a random duration to prevent API rate limiting"""
|
||||
duration = random.uniform(min_seconds, max_seconds)
|
||||
logger.info(f"Rate limit protection: Sleeping for {duration:.2f} seconds...")
|
||||
time.sleep(duration)
|
||||
|
||||
class RateLimitedChatOpenAI(ChatOpenAI):
|
||||
"""ChatOpenAI with built-in rate limiting"""
|
||||
def __call__(self, *args, **kwargs):
|
||||
random_sleep()
|
||||
return super().__call__(*args, **kwargs)
|
||||
|
||||
def run_test():
|
||||
logger.info("Initializing test components...")
|
||||
|
||||
# Initialize the LLM with rate limiting
|
||||
llm = RateLimitedChatOpenAI(
|
||||
model=os.getenv('OPENAI_MODEL_NAME', 'llama-3.3-70b-versatile'),
|
||||
base_url=os.getenv('OPENAI_API_BASE', 'https://api.groq.com/openai/v1/'),
|
||||
api_key=os.getenv('OPENAI_API_KEY'),
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
random_sleep()
|
||||
logger.info("Creating researcher agent...")
|
||||
researcher = Agent(
|
||||
role="Test Automation Expert",
|
||||
goal="Verify system functionality and performance",
|
||||
backstory="""You are an expert in test automation and system verification,
|
||||
skilled in identifying potential issues and validating functionality.""",
|
||||
verbose=True,
|
||||
allow_delegation=False,
|
||||
llm=llm
|
||||
)
|
||||
|
||||
random_sleep()
|
||||
logger.info("Creating validator agent...")
|
||||
validator = Agent(
|
||||
role="Quality Assurance Specialist",
|
||||
goal="Validate test results and ensure system reliability",
|
||||
backstory="""You are a QA specialist focused on ensuring
|
||||
system reliability and proper functionality.""",
|
||||
verbose=True,
|
||||
allow_delegation=False,
|
||||
llm=llm
|
||||
)
|
||||
|
||||
random_sleep()
|
||||
logger.info("Creating test tasks...")
|
||||
test_task = Task(
|
||||
description="Run a basic system verification test",
|
||||
expected_output="A report on system functionality verification",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
random_sleep()
|
||||
validation_task = Task(
|
||||
description="Validate the test results and compile findings",
|
||||
expected_output="A validation report of test findings",
|
||||
agent=validator
|
||||
)
|
||||
|
||||
random_sleep()
|
||||
logger.info("Initializing crew...")
|
||||
crew = Crew(
|
||||
agents=[researcher, validator],
|
||||
tasks=[test_task, validation_task],
|
||||
verbose=2
|
||||
)
|
||||
|
||||
logger.info("Starting crew execution...")
|
||||
result = crew.kickoff()
|
||||
logger.info("Test execution completed.")
|
||||
logger.info(f"Result: {result}")
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
run_test()
|
||||
except Exception as e:
|
||||
logger.error(f"Test failed with error: {str(e)}", exc_info=True)
|
||||
raise
|
||||
EOF
|
||||
|
||||
# Run the test script with increased timeout
|
||||
echo "Testing with Python API..."
|
||||
timeout 1800 python3 test_agents.py 2>&1 | tee test_output/agents_test.log
|
||||
|
||||
# Verify the output
|
||||
if [[ ! -s test_output/agents_test.log ]]; then
|
||||
echo "Error: No output generated from agents test"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Agents configuration tests passed!"
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
|
||||
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
|
||||
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
|
||||
LOGLEVEL: "debug"
|
||||
PRAISONAI_NON_INTERACTIVE: "true"
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: praisonai-output-${{ matrix.os }}-${{ matrix.framework }}
|
||||
path: test_output/
|
||||
retention-days: 14
|
||||
|
||||
create-release:
|
||||
needs: [test-ubuntu]
|
||||
runs-on: ubuntu-latest
|
||||
if: success()
|
||||
steps:
|
||||
# First checkout the fork's main branch
|
||||
- name: Checkout fork repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PAT_TOKEN }}
|
||||
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
|
||||
ref: main
|
||||
|
||||
# Add upstream and get commit from tag
|
||||
- name: Get upstream tag commit
|
||||
id: get_upstream_commit
|
||||
run: |
|
||||
# Add upstream remote
|
||||
git remote add upstream https://github.com/MervinPraison/PraisonAI.git
|
||||
|
||||
# Delete local tag if it exists
|
||||
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/${{ 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/${{ github.event.inputs.tag }})
|
||||
if [ -z "$UPSTREAM_COMMIT" ]; then
|
||||
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 ${{ github.event.inputs.tag }}"
|
||||
|
||||
# Create tag at the same commit
|
||||
- name: Create tag at commit
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions"
|
||||
git config --global user.email "[email protected]"
|
||||
|
||||
# Remove tag if it exists locally
|
||||
git tag -d ${{ github.event.inputs.tag }} || true
|
||||
|
||||
# Remove tag if it exists remotely
|
||||
git push origin :refs/tags/${{ github.event.inputs.tag }} || true
|
||||
|
||||
# Fetch the specific commit from upstream
|
||||
git fetch upstream ${{ steps.get_upstream_commit.outputs.upstream_commit }} --force
|
||||
|
||||
# Reset to upstream commit
|
||||
git reset --hard ${{ steps.get_upstream_commit.outputs.upstream_commit }}
|
||||
|
||||
# Create and push 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
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.event.inputs.tag }}
|
||||
release_name: Release ${{ github.event.inputs.tag }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: |
|
||||
Release ${{ github.event.inputs.tag }} (synced from upstream)
|
||||
|
||||
This release is synchronized with MervinPraison/PraisonAI@${{ github.event.inputs.tag }}
|
||||
Commit: ${{ steps.get_upstream_commit.outputs.upstream_commit }}
|
||||
|
||||
For installation:
|
||||
```bash
|
||||
pip install praisonai
|
||||
```
|
||||
|
||||
Changes from upstream v2.0.10:
|
||||
- Updated agent configuration tests
|
||||
- Added rate limiting protection
|
||||
- Improved test reliability
|
||||
|
||||
Forked from: MervinPraison/PraisonAI
|
||||
@@ -1,196 +0,0 @@
|
||||
name: Comprehensive Test Suite
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
inputs:
|
||||
test_type:
|
||||
description: 'Type of tests to run'
|
||||
required: true
|
||||
default: 'all'
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- unit
|
||||
- integration
|
||||
- fast
|
||||
- performance
|
||||
- frameworks
|
||||
- autogen
|
||||
- crewai
|
||||
release:
|
||||
types: [published, prereleased]
|
||||
schedule:
|
||||
# Run comprehensive tests weekly on Sundays at 3 AM UTC
|
||||
- cron: '0 3 * * 0'
|
||||
|
||||
jobs:
|
||||
comprehensive-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_MODEL_NAME: gpt-4o-mini
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.11"]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov pytest-benchmark pytest-timeout
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Verify environment variables
|
||||
run: |
|
||||
if [ -n "$OPENAI_API_KEY" ]; then
|
||||
echo "✅ OPENAI_API_KEY is set (length: ${#OPENAI_API_KEY})"
|
||||
else
|
||||
echo "⚠️ OPENAI_API_KEY is NOT set"
|
||||
fi
|
||||
|
||||
- name: Run Comprehensive Test Suite
|
||||
run: |
|
||||
# Determine test type from input or default to 'all'
|
||||
TEST_TYPE="${{ github.event.inputs.test_type || 'all' }}"
|
||||
|
||||
echo "🧪 Running comprehensive test suite (type: $TEST_TYPE)"
|
||||
|
||||
case $TEST_TYPE in
|
||||
"unit")
|
||||
cd src/praisonai && python -m pytest tests/unit/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=xml --cov-branch || echo "Some unit tests failed"
|
||||
;;
|
||||
"integration")
|
||||
cd src/praisonai && python -m pytest tests/integration/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=xml --cov-branch || echo "Some integration tests failed"
|
||||
;;
|
||||
"fast")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern fast || echo "Some fast tests failed"
|
||||
;;
|
||||
"performance")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern "performance" || echo "Some performance tests failed"
|
||||
;;
|
||||
"frameworks")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern frameworks || echo "Some framework tests failed"
|
||||
;;
|
||||
"autogen")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern autogen || echo "Some autogen tests failed"
|
||||
;;
|
||||
"crewai")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern crewai || echo "Some crewai tests failed"
|
||||
;;
|
||||
"all"|*)
|
||||
cd src/praisonai && python tests/test_runner.py --pattern all || echo "Some tests failed"
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Generate Comprehensive Test Report
|
||||
if: always()
|
||||
run: |
|
||||
echo "# 📋 Comprehensive Test Report" > comprehensive_report.md
|
||||
echo "" >> comprehensive_report.md
|
||||
echo "**Python Version:** ${{ matrix.python-version }}" >> 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
|
||||
echo "" >> comprehensive_report.md
|
||||
echo "### Unit Tests:" >> comprehensive_report.md
|
||||
echo "- ✅ Core agent functionality" >> comprehensive_report.md
|
||||
echo "- ✅ Async operations" >> comprehensive_report.md
|
||||
echo "- ✅ Tool integrations" >> comprehensive_report.md
|
||||
echo "- ✅ UI components" >> comprehensive_report.md
|
||||
echo "" >> comprehensive_report.md
|
||||
|
||||
echo "### Integration Tests:" >> comprehensive_report.md
|
||||
echo "- ✅ MCP (Model Context Protocol)" >> comprehensive_report.md
|
||||
echo "- ✅ RAG (Retrieval Augmented Generation)" >> comprehensive_report.md
|
||||
echo "- ✅ Base URL API mapping" >> comprehensive_report.md
|
||||
echo "- ✅ Multi-agent workflows" >> comprehensive_report.md
|
||||
echo "- ✅ AutoGen framework integration" >> comprehensive_report.md
|
||||
echo "- ✅ CrewAI framework integration" >> comprehensive_report.md
|
||||
echo "- 💬 LLM integrations (OpenAI, Anthropic, etc.)" >> comprehensive_report.md
|
||||
echo "- 🖥️ UI frameworks (Gradio, Streamlit)" >> comprehensive_report.md
|
||||
echo "- 📊 Memory and persistence" >> comprehensive_report.md
|
||||
echo "- 🌐 Multi-modal capabilities" >> comprehensive_report.md
|
||||
|
||||
echo "- ✅ AutoGen framework integration" >> comprehensive_report.md
|
||||
echo "- ✅ CrewAI framework integration" >> comprehensive_report.md
|
||||
echo "" >> comprehensive_report.md
|
||||
|
||||
echo "### Key Features Tested:" >> comprehensive_report.md
|
||||
echo "- 🤖 Agent creation and configuration" >> comprehensive_report.md
|
||||
echo "- 📋 Task management and execution" >> comprehensive_report.md
|
||||
echo "- 🔄 Sync/async workflows" >> comprehensive_report.md
|
||||
echo "- 🛠️ Custom tools and error handling" >> comprehensive_report.md
|
||||
echo "- 🧠 Knowledge bases and RAG" >> comprehensive_report.md
|
||||
echo "- 🔌 MCP server connections" >> comprehensive_report.md
|
||||
echo "- 💬 LLM integrations (OpenAI, Anthropic, etc.)" >> comprehensive_report.md
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: src/praisonai/coverage.xml
|
||||
flags: comprehensive-tests
|
||||
name: comprehensive-tests-coverage
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
|
||||
- name: Upload Comprehensive Test Results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: comprehensive-test-results-python-${{ matrix.python-version }}
|
||||
path: |
|
||||
comprehensive_report.md
|
||||
src/praisonai/htmlcov/
|
||||
src/praisonai/coverage.xml
|
||||
src/praisonai/.coverage
|
||||
retention-days: 30
|
||||
|
||||
test-matrix-summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: comprehensive-test
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Generate Matrix Summary
|
||||
run: |
|
||||
echo "# 🎯 Test Matrix Summary" > matrix_summary.md
|
||||
echo "" >> matrix_summary.md
|
||||
echo "## Python Version Results:" >> matrix_summary.md
|
||||
echo "- Python 3.10: ${{ needs.comprehensive-test.result }}" >> matrix_summary.md
|
||||
echo "- Python 3.11: ${{ needs.comprehensive-test.result }}" >> matrix_summary.md
|
||||
echo "" >> matrix_summary.md
|
||||
echo "## Overall Status:" >> matrix_summary.md
|
||||
if [ "${{ needs.comprehensive-test.result }}" == "success" ]; then
|
||||
echo "✅ **All tests passed across all Python versions!**" >> matrix_summary.md
|
||||
else
|
||||
echo "❌ **Some tests failed. Check individual job logs for details.**" >> matrix_summary.md
|
||||
fi
|
||||
|
||||
- name: Upload Matrix Summary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-matrix-summary
|
||||
path: matrix_summary.md
|
||||
retention-days: 30
|
||||
@@ -1,288 +0,0 @@
|
||||
name: Core Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
test-core:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov pytest-timeout
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV
|
||||
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV
|
||||
# Also export to current shell session for immediate availability
|
||||
export OPENAI_API_KEY="${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}"
|
||||
export OPENAI_API_BASE="${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}"
|
||||
export OPENAI_MODEL_NAME="${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}"
|
||||
export LOGLEVEL=DEBUG
|
||||
# Verify immediate availability
|
||||
echo "🔧 Immediate verification in same step:"
|
||||
echo " OPENAI_API_KEY length in current session: ${#OPENAI_API_KEY}"
|
||||
echo " OPENAI_API_KEY starts with sk-: $(echo "$OPENAI_API_KEY" | grep -q '^sk-' && echo 'YES' || echo 'NO')"
|
||||
|
||||
- name: Debug API Key Status
|
||||
run: |
|
||||
echo "🔍 Checking API key availability..."
|
||||
if [ -n "${{ secrets.OPENAI_API_KEY }}" ]; then
|
||||
echo "✅ GitHub secret OPENAI_API_KEY is available"
|
||||
echo "🔑 API key starts with: $(echo "$OPENAI_API_KEY" | cut -c1-7)..."
|
||||
else
|
||||
echo "⚠️ GitHub secret OPENAI_API_KEY is NOT set - using fallback"
|
||||
echo "🔑 Using fallback key: sk-test-key..."
|
||||
fi
|
||||
echo "🌐 API Base: $OPENAI_API_BASE"
|
||||
echo "🤖 Model: $OPENAI_MODEL_NAME"
|
||||
echo "🐛 Log Level: $LOGLEVEL"
|
||||
echo "📊 Environment Check:"
|
||||
echo " - OPENAI_API_KEY length: ${#OPENAI_API_KEY}"
|
||||
echo " - OPENAI_API_BASE: $OPENAI_API_BASE"
|
||||
echo " - OPENAI_MODEL_NAME: $OPENAI_MODEL_NAME"
|
||||
echo " - LOGLEVEL: $LOGLEVEL"
|
||||
echo "🔍 Is API key actually set?"
|
||||
echo " - API key starts with sk-: $(echo "$OPENAI_API_KEY" | grep -q '^sk-' && echo 'YES' || echo 'NO')"
|
||||
echo " - API key is not test key: $([ "$OPENAI_API_KEY" != 'sk-test-key-for-github-actions-testing-only-not-real' ] && echo 'YES' || echo 'NO')"
|
||||
|
||||
- name: Debug Environment Variables Raw
|
||||
run: |
|
||||
echo "🔧 Raw environment variable check:"
|
||||
echo "OPENAI_API_KEY set: $(if [ -n "$OPENAI_API_KEY" ]; then echo 'YES'; else echo 'NO'; fi)"
|
||||
echo "OPENAI_API_KEY length: ${#OPENAI_API_KEY}"
|
||||
echo "OPENAI_API_KEY first 10 chars: ${OPENAI_API_KEY:0:10}"
|
||||
echo "OPENAI_API_KEY last 5 chars: ${OPENAI_API_KEY: -5}"
|
||||
printenv | grep OPENAI || echo "No OPENAI env vars found"
|
||||
|
||||
- name: Debug Python Environment Variables
|
||||
run: |
|
||||
python -c "
|
||||
import os
|
||||
print('🐍 Python Environment Variable Check:')
|
||||
api_key = os.environ.get('OPENAI_API_KEY', 'NOT_SET')
|
||||
if api_key != 'NOT_SET':
|
||||
print(f' ✅ OPENAI_API_KEY: {api_key[:7]}... (length: {len(api_key)})')
|
||||
else:
|
||||
print(' ❌ OPENAI_API_KEY: NOT_SET')
|
||||
print(f' 🌐 OPENAI_API_BASE: {os.environ.get(\"OPENAI_API_BASE\", \"NOT_SET\")}')
|
||||
print(f' 🤖 OPENAI_MODEL_NAME: {os.environ.get(\"OPENAI_MODEL_NAME\", \"NOT_SET\")}')
|
||||
print(f' 📋 All OPENAI env vars:')
|
||||
for key, value in os.environ.items():
|
||||
if key.startswith('OPENAI'):
|
||||
print(f' {key}: {value[:10] if len(value) > 10 else value}...')
|
||||
"
|
||||
|
||||
- name: Validate API Key
|
||||
run: |
|
||||
echo "🔑 Testing API key validity with minimal OpenAI call..."
|
||||
python -c "
|
||||
import os
|
||||
try:
|
||||
from openai import OpenAI
|
||||
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))
|
||||
response = client.models.list()
|
||||
print('✅ API Key is VALID - OpenAI responded successfully')
|
||||
print(f'📊 Available models: {len(list(response.data))} models found')
|
||||
except Exception as e:
|
||||
print(f'❌ API Key is INVALID - Error: {e}')
|
||||
print('🔍 This explains why all API-dependent tests are failing')
|
||||
print('💡 The GitHub secret OPENAI_API_KEY needs to be updated with a valid key')
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Debug PraisonAI API Key Usage
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}
|
||||
OPENAI_API_BASE: ${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}
|
||||
OPENAI_MODEL_NAME: ${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}
|
||||
LOGLEVEL: DEBUG
|
||||
run: |
|
||||
echo "🔍 Testing PraisonAI API key usage directly..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
# Attempt to import SecretStr, otherwise use a dummy class
|
||||
try:
|
||||
from pydantic.types import SecretStr
|
||||
except ImportError:
|
||||
class SecretStr: # Dummy class if pydantic is not available in this minimal context
|
||||
def __init__(self, value): self._value = value
|
||||
def get_secret_value(self): return self._value
|
||||
|
||||
def get_key_display_value(key_value):
|
||||
if isinstance(key_value, SecretStr):
|
||||
return key_value.get_secret_value()[:10] if key_value.get_secret_value() else 'EMPTY_SECRET'
|
||||
elif isinstance(key_value, str):
|
||||
return key_value[:10] if key_value != 'nokey' and key_value != 'NOT_SET' else key_value
|
||||
return 'INVALID_TYPE'
|
||||
|
||||
print('🔧 Direct PraisonAI API Key Check:')
|
||||
env_api_key = os.environ.get(\"OPENAI_API_KEY\", \"NOT_SET\")
|
||||
print(f'Environment OPENAI_API_KEY: {get_key_display_value(env_api_key)}...')
|
||||
|
||||
from praisonai import PraisonAI
|
||||
praisonai = PraisonAI()
|
||||
|
||||
print(f'PraisonAI config_list: {praisonai.config_list}')
|
||||
api_key_from_config = praisonai.config_list[0].get('api_key', 'NOT_SET')
|
||||
print(f'API key from PraisonAI config: {get_key_display_value(api_key_from_config)}...')
|
||||
|
||||
from praisonai.inc.models import PraisonAIModel
|
||||
|
||||
print('\\n🧪 Testing PraisonAIModel with explicit API key (CrewAI method):')
|
||||
model_with_explicit_key = PraisonAIModel(
|
||||
model='openai/gpt-5-nano',
|
||||
base_url=praisonai.config_list[0].get('base_url'),
|
||||
api_key=api_key_from_config # This will be a string from praisonai.config_list
|
||||
)
|
||||
print(f' Model: {model_with_explicit_key.model}')
|
||||
print(f' Model name: {model_with_explicit_key.model_name}')
|
||||
print(f' API key var: {model_with_explicit_key.api_key_var}')
|
||||
# model_with_explicit_key.api_key is now a string, or 'nokey'
|
||||
print(f' API key (explicitly passed to PraisonAIModel): {get_key_display_value(model_with_explicit_key.api_key)}...')
|
||||
print(f' Base URL: {model_with_explicit_key.base_url}')
|
||||
|
||||
try:
|
||||
llm_instance = model_with_explicit_key.get_model()
|
||||
print(f' ✅ LLM instance created successfully: {type(llm_instance).__name__}')
|
||||
|
||||
# langchain_openai.ChatOpenAI stores the key in openai_api_key as SecretStr
|
||||
llm_api_key_attr = getattr(llm_instance, 'openai_api_key', 'NOT_FOUND')
|
||||
if llm_api_key_attr != 'NOT_FOUND':
|
||||
print(f' LLM instance API key: {get_key_display_value(llm_api_key_attr)}...')
|
||||
else:
|
||||
print(f' LLM instance API key attribute not found.')
|
||||
except Exception as e:
|
||||
print(f' ❌ Failed to create LLM instance: {e}')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
cd src/praisonai && python -m pytest tests/unit/ -v --tb=short --disable-warnings --cov=praisonai --cov-report=term-missing --cov-report=xml --cov-branch || echo "Some tests failed - see output above"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Integration Tests
|
||||
run: |
|
||||
cd src/praisonai && python -m pytest tests/integration/ -v --tb=short --disable-warnings
|
||||
|
||||
- name: Debug Directory Structure
|
||||
run: |
|
||||
echo "🔍 Debugging directory structure for CrewAI tests..."
|
||||
cd src/praisonai
|
||||
echo "Current working directory: $(pwd)"
|
||||
echo "📁 Contents of current directory:"
|
||||
ls -la
|
||||
echo ""
|
||||
echo "📁 Contents of tests directory:"
|
||||
ls -la tests/ || echo "❌ tests/ directory not found"
|
||||
echo ""
|
||||
echo "📁 Contents of tests/integration:"
|
||||
ls -la tests/integration/ || echo "❌ tests/integration/ directory not found"
|
||||
echo ""
|
||||
echo "📁 Looking for crewai directory:"
|
||||
find . -name "crewai" -type d 2>/dev/null || echo "❌ No crewai directories found"
|
||||
echo ""
|
||||
echo "📁 Full directory tree of tests:"
|
||||
tree tests/ || find tests/ -type d 2>/dev/null || echo "❌ Cannot explore tests directory"
|
||||
|
||||
- name: Test AutoGen Framework
|
||||
run: |
|
||||
echo "🤖 Testing AutoGen Framework Integration..."
|
||||
cd src/praisonai && python tests/test_runner.py --pattern autogen --verbose
|
||||
|
||||
- name: Test CrewAI Framework
|
||||
run: |
|
||||
echo "⛵ Testing CrewAI Framework Integration..."
|
||||
cd src/praisonai
|
||||
echo "🔍 Trying test runner first..."
|
||||
python tests/test_runner.py --pattern crewai --verbose || {
|
||||
echo "❌ Test runner failed, trying direct pytest..."
|
||||
echo "📁 Current directory: $(pwd)"
|
||||
echo "📁 Looking for CrewAI tests..."
|
||||
find . -name "*crewai*" -type f 2>/dev/null
|
||||
echo "🧪 Trying direct pytest on integration/crewai..."
|
||||
python -m pytest tests/integration/crewai/ -v --tb=short --disable-warnings || {
|
||||
echo "❌ Direct path failed, trying relative path..."
|
||||
python -m pytest integration/crewai/ -v --tb=short --disable-warnings || {
|
||||
echo "❌ All CrewAI test attempts failed"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- name: Run Legacy Tests with API Key
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}
|
||||
OPENAI_API_BASE: ${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}
|
||||
OPENAI_MODEL_NAME: ${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}
|
||||
LOGLEVEL: DEBUG
|
||||
run: |
|
||||
echo "🧪 Running legacy tests with real API key..."
|
||||
echo "🔧 Final environment check before pytest:"
|
||||
echo " OPENAI_API_KEY set: $([ -n "$OPENAI_API_KEY" ] && echo 'YES' || echo 'NO')"
|
||||
echo " OPENAI_API_KEY length: ${#OPENAI_API_KEY}"
|
||||
echo " OPENAI_API_KEY starts with sk-: $(echo "$OPENAI_API_KEY" | grep -q '^sk-' && echo 'YES' || echo 'NO')"
|
||||
export OPENAI_API_KEY="$OPENAI_API_KEY"
|
||||
export OPENAI_API_BASE="$OPENAI_API_BASE"
|
||||
export OPENAI_MODEL_NAME="$OPENAI_MODEL_NAME"
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings -m "not real" --timeout=60
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.python-version == '3.11'
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: src/praisonai/coverage.xml
|
||||
flags: core-tests
|
||||
name: core-tests-coverage
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
|
||||
- name: Upload Coverage Reports (Artifacts)
|
||||
if: matrix.python-version == '3.11'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: |
|
||||
src/praisonai/.coverage
|
||||
src/praisonai/htmlcov/
|
||||
src/praisonai/coverage.xml
|
||||
retention-days: 7
|
||||
@@ -1,175 +0,0 @@
|
||||
name: Extended Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
# Run nightly at 2 AM UTC
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
|
||||
jobs:
|
||||
test-examples:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV
|
||||
echo "PRAISONAI_TELEMETRY_DISABLED=true" >> $GITHUB_ENV
|
||||
echo "PRAISONAI_DISABLE_TELEMETRY=true" >> $GITHUB_ENV
|
||||
echo "DO_NOT_TRACK=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Test Key Example Scripts
|
||||
run: |
|
||||
echo "🧪 Testing key example scripts from praisonai-agents..."
|
||||
|
||||
# Create a timeout function for consistent handling
|
||||
timeout_run() {
|
||||
timeout 30s "$@" || echo "⏱️ $1 test completed/timed out"
|
||||
}
|
||||
|
||||
# Test basic agent functionality
|
||||
timeout_run python ${{ github.workspace }}/src/praisonai-agents/basic-agents.py
|
||||
|
||||
# Test async functionality
|
||||
timeout_run python ${{ github.workspace }}/src/praisonai-agents/async_example.py
|
||||
|
||||
# Test knowledge/RAG functionality
|
||||
timeout_run python ${{ github.workspace }}/src/praisonai-agents/knowledge-agents.py
|
||||
|
||||
# Test MCP functionality
|
||||
timeout_run python ${{ github.workspace }}/src/praisonai-agents/mcp-basic.py
|
||||
|
||||
# Test UI functionality
|
||||
timeout_run python ${{ github.workspace }}/src/praisonai-agents/ui.py
|
||||
|
||||
echo "✅ Example script testing completed"
|
||||
continue-on-error: true
|
||||
|
||||
performance-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system pytest pytest-benchmark
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "PRAISONAI_TELEMETRY_DISABLED=true" >> $GITHUB_ENV
|
||||
echo "PRAISONAI_DISABLE_TELEMETRY=true" >> $GITHUB_ENV
|
||||
echo "DO_NOT_TRACK=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Run Performance Benchmarks
|
||||
run: |
|
||||
echo "🏃 Running performance benchmarks..."
|
||||
python -c "
|
||||
import time
|
||||
import sys
|
||||
import statistics
|
||||
sys.path.insert(0, 'src/praisonai')
|
||||
|
||||
print('🏃 Testing agent creation performance...')
|
||||
times = []
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
for i in range(5):
|
||||
start_time = time.time()
|
||||
agent = Agent(name=f'PerfAgent{i}')
|
||||
times.append(time.time() - start_time)
|
||||
|
||||
avg_time = statistics.mean(times)
|
||||
print(f'✅ Average agent creation time: {avg_time:.3f}s')
|
||||
print(f'📊 Min: {min(times):.3f}s, Max: {max(times):.3f}s')
|
||||
except Exception as e:
|
||||
print(f'❌ Agent creation benchmark failed: {e}')
|
||||
|
||||
print('🏃 Testing import performance...')
|
||||
start_time = time.time()
|
||||
try:
|
||||
import praisonaiagents
|
||||
import_time = time.time() - start_time
|
||||
print(f'✅ Import completed in {import_time:.3f}s')
|
||||
except Exception as e:
|
||||
print(f'❌ Import benchmark failed: {e}')
|
||||
|
||||
print('🏃 Testing memory usage...')
|
||||
try:
|
||||
import psutil
|
||||
import os
|
||||
process = psutil.Process(os.getpid())
|
||||
memory_mb = process.memory_info().rss / 1024 / 1024
|
||||
print(f'📊 Memory usage: {memory_mb:.1f} MB')
|
||||
except ImportError:
|
||||
print('⚠️ psutil not available for memory testing')
|
||||
except Exception as e:
|
||||
print(f'❌ Memory benchmark failed: {e}')
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Generate Performance Report
|
||||
run: |
|
||||
echo "## 📊 Performance Test Results" > performance_report.md
|
||||
echo "" >> performance_report.md
|
||||
echo "### Benchmarks Run:" >> performance_report.md
|
||||
echo "- ⚡ Agent creation speed" >> performance_report.md
|
||||
echo "- 📦 Import performance" >> performance_report.md
|
||||
echo "- 💾 Memory usage" >> performance_report.md
|
||||
echo "- 🧪 Example script execution" >> performance_report.md
|
||||
echo "" >> performance_report.md
|
||||
echo "_Performance results are logged in the CI output above._" >> performance_report.md
|
||||
|
||||
- name: Upload Performance Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: performance-report
|
||||
path: performance_report.md
|
||||
retention-days: 5
|
||||
@@ -1,154 +0,0 @@
|
||||
name: Framework Integration Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
inputs:
|
||||
framework:
|
||||
description: 'Framework to test'
|
||||
required: true
|
||||
default: 'all'
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- autogen
|
||||
- crewai
|
||||
schedule:
|
||||
# Run framework tests daily at 6 AM UTC
|
||||
- cron: '0 6 * * *'
|
||||
push:
|
||||
paths:
|
||||
- 'tests/integration/autogen/**'
|
||||
- 'tests/integration/crewai/**'
|
||||
- '.github/workflows/test-frameworks.yml'
|
||||
|
||||
jobs:
|
||||
framework-tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
framework: [autogen, crewai]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov pytest-timeout
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Test ${{ matrix.framework }} Framework
|
||||
run: |
|
||||
echo "🧪 Testing ${{ matrix.framework }} framework integration with Python ${{ matrix.python-version }}"
|
||||
cd src/praisonai && python tests/test_runner.py --pattern ${{ matrix.framework }} --verbose --coverage
|
||||
continue-on-error: false
|
||||
|
||||
- name: Generate Framework Test Report
|
||||
if: always()
|
||||
run: |
|
||||
echo "# 🤖 ${{ matrix.framework }} Framework Test Report" > ${{ matrix.framework }}_report.md
|
||||
echo "" >> ${{ matrix.framework }}_report.md
|
||||
echo "**Framework:** ${{ matrix.framework }}" >> ${{ matrix.framework }}_report.md
|
||||
echo "**Python Version:** ${{ matrix.python-version }}" >> ${{ matrix.framework }}_report.md
|
||||
echo "**Date:** $(date -u)" >> ${{ matrix.framework }}_report.md
|
||||
echo "**Trigger:** ${{ github.event_name }}" >> ${{ matrix.framework }}_report.md
|
||||
echo "" >> ${{ matrix.framework }}_report.md
|
||||
|
||||
if [ "${{ matrix.framework }}" == "autogen" ]; then
|
||||
echo "## AutoGen Integration Tests" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ AutoGen import verification" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Basic agent creation through PraisonAI" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Conversation flow testing" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Configuration validation" >> ${{ matrix.framework }}_report.md
|
||||
elif [ "${{ matrix.framework }}" == "crewai" ]; then
|
||||
echo "## CrewAI Integration Tests" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ CrewAI import verification" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Basic crew creation through PraisonAI" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Multi-agent workflow testing" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Agent collaboration verification" >> ${{ matrix.framework }}_report.md
|
||||
echo "- ✅ Configuration validation" >> ${{ matrix.framework }}_report.md
|
||||
fi
|
||||
|
||||
echo "" >> ${{ matrix.framework }}_report.md
|
||||
echo "## Test Commands" >> ${{ matrix.framework }}_report.md
|
||||
echo '```bash' >> ${{ matrix.framework }}_report.md
|
||||
echo "# Run ${{ matrix.framework }} tests locally:" >> ${{ matrix.framework }}_report.md
|
||||
echo "python tests/test_runner.py --pattern ${{ matrix.framework }} --verbose" >> ${{ matrix.framework }}_report.md
|
||||
echo '```' >> ${{ matrix.framework }}_report.md
|
||||
|
||||
- name: Upload Framework Test Results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: ${{ matrix.framework }}-test-results-python-${{ matrix.python-version }}
|
||||
path: |
|
||||
${{ matrix.framework }}_report.md
|
||||
htmlcov/
|
||||
coverage.xml
|
||||
.coverage
|
||||
retention-days: 14
|
||||
|
||||
framework-summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: framework-tests
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Generate Framework Summary
|
||||
run: |
|
||||
echo "# 🚀 Framework Integration Test Summary" > framework_summary.md
|
||||
echo "" >> framework_summary.md
|
||||
echo "## Test Results by Framework and Python Version:" >> framework_summary.md
|
||||
echo "" >> framework_summary.md
|
||||
echo "### AutoGen Framework:" >> framework_summary.md
|
||||
echo "- Python 3.11: ${{ needs.framework-tests.result }}" >> framework_summary.md
|
||||
echo "" >> framework_summary.md
|
||||
echo "### CrewAI Framework:" >> framework_summary.md
|
||||
echo "- Python 3.11: ${{ needs.framework-tests.result }}" >> framework_summary.md
|
||||
echo "" >> framework_summary.md
|
||||
echo "## Overall Status:" >> framework_summary.md
|
||||
if [ "${{ needs.framework-tests.result }}" == "success" ]; then
|
||||
echo "✅ **All framework integration tests passed!**" >> framework_summary.md
|
||||
else
|
||||
echo "❌ **Some framework tests failed. Check individual job logs for details.**" >> framework_summary.md
|
||||
fi
|
||||
|
||||
echo "" >> framework_summary.md
|
||||
echo "## Frameworks Tested:" >> framework_summary.md
|
||||
echo "- **AutoGen**: Microsoft's conversational AI framework" >> framework_summary.md
|
||||
echo "- **CrewAI**: Multi-agent collaboration framework" >> framework_summary.md
|
||||
echo "" >> framework_summary.md
|
||||
echo "## Test Coverage:" >> framework_summary.md
|
||||
echo "- Import verification" >> framework_summary.md
|
||||
echo "- Agent/crew creation" >> framework_summary.md
|
||||
echo "- Workflow execution" >> framework_summary.md
|
||||
echo "- Configuration validation" >> framework_summary.md
|
||||
echo "- Integration with PraisonAI" >> framework_summary.md
|
||||
|
||||
- name: Upload Framework Summary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: framework-test-summary
|
||||
path: framework_summary.md
|
||||
retention-days: 30
|
||||
@@ -1,530 +0,0 @@
|
||||
name: Optimized Test Suite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
schedule:
|
||||
# Nightly extended tests at 3 AM UTC
|
||||
- cron: '0 3 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tier:
|
||||
description: 'Test tier to run'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: choice
|
||||
options:
|
||||
- smoke
|
||||
- main
|
||||
- extended
|
||||
- nightly
|
||||
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}/src/praisonai-agents
|
||||
# Test gating environment variables
|
||||
PRAISONAI_ALLOW_NETWORK: '0'
|
||||
PRAISONAI_LIVE_TESTS: '0'
|
||||
|
||||
jobs:
|
||||
# ============================================
|
||||
# SMOKE TESTS - Fast unit tests, no network
|
||||
# Target: <= 2 minutes
|
||||
# ============================================
|
||||
smoke:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Cache pip packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-smoke-${{ hashFiles('**/pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-smoke-
|
||||
${{ runner.os }}-pip-
|
||||
|
||||
- name: Install minimal dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
# Install praisonai-agents with all deps (required for tests)
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e ".[chat,code]"
|
||||
pip install pytest pytest-asyncio pytest-timeout prompt_toolkit
|
||||
|
||||
- name: Run Smoke Tests
|
||||
env:
|
||||
PRAISONAI_TEST_TIER: smoke
|
||||
PRAISONAI_ALLOW_NETWORK: '0'
|
||||
run: |
|
||||
cd src/praisonai
|
||||
# Run only CLI tests for smoke - fast and critical path
|
||||
python -m pytest tests/unit/cli/ \
|
||||
-m "not slow and not network" \
|
||||
-q --tb=line \
|
||||
--timeout=30 \
|
||||
--ignore=tests/fixtures \
|
||||
--ignore=tests/unit/cli/test_message_queue.py \
|
||||
--maxfail=5 \
|
||||
|| echo "Some smoke tests failed"
|
||||
|
||||
- name: Smoke Test Summary
|
||||
if: always()
|
||||
run: echo "✅ Smoke tests completed"
|
||||
|
||||
# ============================================
|
||||
# MAIN TESTS - Unit + safe integration
|
||||
# Target: <= 5 minutes
|
||||
# ============================================
|
||||
main:
|
||||
needs: smoke
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.11']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Cache pip packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-main-${{ hashFiles('**/pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-main-
|
||||
${{ runner.os }}-pip-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
# Install praisonai-agents with all deps (required for tests)
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e ".[crewai,autogen]"
|
||||
pip install pytest pytest-asyncio pytest-timeout pytest-cov pytest-xdist
|
||||
|
||||
- name: Run Main Tests
|
||||
env:
|
||||
PRAISONAI_TEST_TIER: main
|
||||
PRAISONAI_ALLOW_NETWORK: '0'
|
||||
OPENAI_API_KEY: 'test-key'
|
||||
ANTHROPIC_API_KEY: 'test-key'
|
||||
GOOGLE_API_KEY: 'test-key'
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/unit/ tests/integration/ \
|
||||
-m "not provider_anthropic and not provider_google and not provider_ollama and not provider_grok_xai and not provider_groq and not provider_cohere" \
|
||||
-q --tb=line \
|
||||
--timeout=60 \
|
||||
--ignore=tests/fixtures \
|
||||
--ignore=tests/e2e \
|
||||
--ignore=tests/live \
|
||||
--ignore=tests/unit/jobs \
|
||||
--ignore=tests/unit/api \
|
||||
--cov=praisonai \
|
||||
--cov-report=xml \
|
||||
-n 4 \
|
||||
--maxfail=10 \
|
||||
|| echo "Some tests failed"
|
||||
|
||||
- name: Upload coverage
|
||||
if: matrix.python-version == '3.11'
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: src/praisonai/coverage.xml
|
||||
flags: main-tests
|
||||
fail_ci_if_error: false
|
||||
|
||||
# ============================================
|
||||
# OPENAI LIVE TESTS - Real OpenAI API calls
|
||||
# Only runs if OPENAI_API_KEY secret is set
|
||||
# Safe for forks (skips gracefully if no key)
|
||||
# ============================================
|
||||
openai-live:
|
||||
needs: smoke
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 8
|
||||
# This job always runs but steps are conditional on secret availability
|
||||
|
||||
steps:
|
||||
- name: Check if OpenAI key is available
|
||||
id: check-key
|
||||
run: |
|
||||
if [ -n "${{ secrets.OPENAI_API_KEY }}" ]; then
|
||||
echo "has_key=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_key=false" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ OPENAI_API_KEY not set - skipping live tests"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Cache pip packages
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-openai-${{ hashFiles('**/pyproject.toml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e ".[openai]"
|
||||
pip install pytest pytest-asyncio pytest-timeout
|
||||
|
||||
- name: Run OpenAI Live Tests
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
env:
|
||||
PRAISONAI_TEST_TIER: extended
|
||||
PRAISONAI_ALLOW_NETWORK: '1'
|
||||
PRAISONAI_LIVE_TESTS: '1'
|
||||
PRAISONAI_TEST_PROVIDERS: 'openai'
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_MODEL_NAME: ${{ secrets.OPENAI_MODEL_NAME || 'gpt-4o-mini' }}
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/ \
|
||||
-m "provider_openai or real" \
|
||||
-v --tb=short \
|
||||
--timeout=120 \
|
||||
--ignore=tests/fixtures \
|
||||
|| echo "Some OpenAI tests failed"
|
||||
|
||||
- name: Skip notice
|
||||
if: steps.check-key.outputs.has_key == 'false'
|
||||
run: echo "✅ OpenAI live tests skipped (no API key available - this is expected for forks)"
|
||||
|
||||
# ============================================
|
||||
# EXTENDED PROVIDER TESTS - Nightly only
|
||||
# Runs all provider-specific tests
|
||||
# ============================================
|
||||
extended-anthropic:
|
||||
if: github.event_name == 'schedule' || github.event.inputs.tier == 'extended' || github.event.inputs.tier == 'nightly'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Check if Anthropic key is available
|
||||
id: check-key
|
||||
run: |
|
||||
if [ -n "${{ secrets.ANTHROPIC_API_KEY }}" ]; then
|
||||
echo "has_key=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_key=false" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ ANTHROPIC_API_KEY not set - skipping tests"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e ".[anthropic]"
|
||||
pip install pytest pytest-asyncio pytest-timeout
|
||||
|
||||
- name: Run Anthropic Tests
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
env:
|
||||
PRAISONAI_ALLOW_NETWORK: '1'
|
||||
PRAISONAI_LIVE_TESTS: '1'
|
||||
PRAISONAI_TEST_PROVIDERS: 'anthropic'
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/ \
|
||||
-m "provider_anthropic" \
|
||||
-v --tb=short \
|
||||
--timeout=120 \
|
||||
--ignore=tests/fixtures \
|
||||
|| echo "Anthropic tests completed"
|
||||
|
||||
- name: Skip notice
|
||||
if: steps.check-key.outputs.has_key == 'false'
|
||||
run: echo "✅ Anthropic tests skipped (no API key available)"
|
||||
|
||||
extended-google:
|
||||
if: github.event_name == 'schedule' || github.event.inputs.tier == 'extended' || github.event.inputs.tier == 'nightly'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Check if Google key is available
|
||||
id: check-key
|
||||
run: |
|
||||
if [ -n "${{ secrets.GOOGLE_API_KEY }}" ]; then
|
||||
echo "has_key=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_key=false" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ GOOGLE_API_KEY not set - skipping tests"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e ".[google]"
|
||||
pip install pytest pytest-asyncio pytest-timeout
|
||||
|
||||
- name: Run Google/Gemini Tests
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
env:
|
||||
PRAISONAI_ALLOW_NETWORK: '1'
|
||||
PRAISONAI_LIVE_TESTS: '1'
|
||||
PRAISONAI_TEST_PROVIDERS: 'google'
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/ \
|
||||
-m "provider_google" \
|
||||
-v --tb=short \
|
||||
--timeout=120 \
|
||||
--ignore=tests/fixtures \
|
||||
|| echo "Google tests completed"
|
||||
|
||||
- name: Skip notice
|
||||
if: steps.check-key.outputs.has_key == 'false'
|
||||
run: echo "✅ Google tests skipped (no API key available)"
|
||||
|
||||
extended-groq:
|
||||
if: github.event_name == 'schedule' || github.event.inputs.tier == 'extended' || github.event.inputs.tier == 'nightly'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Check if Groq key is available
|
||||
id: check-key
|
||||
run: |
|
||||
if [ -n "${{ secrets.GROQ_API_KEY }}" ]; then
|
||||
echo "has_key=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_key=false" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ GROQ_API_KEY not set - skipping tests"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e .
|
||||
pip install pytest pytest-asyncio pytest-timeout
|
||||
|
||||
- name: Run Groq Tests
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
env:
|
||||
PRAISONAI_ALLOW_NETWORK: '1'
|
||||
PRAISONAI_LIVE_TESTS: '1'
|
||||
PRAISONAI_TEST_PROVIDERS: 'groq'
|
||||
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/ \
|
||||
-m "provider_groq" \
|
||||
-v --tb=short \
|
||||
--timeout=120 \
|
||||
--ignore=tests/fixtures \
|
||||
|| echo "Groq tests completed"
|
||||
|
||||
- name: Skip notice
|
||||
if: steps.check-key.outputs.has_key == 'false'
|
||||
run: echo "✅ Groq tests skipped (no API key available)"
|
||||
|
||||
extended-xai:
|
||||
if: github.event_name == 'schedule' || github.event.inputs.tier == 'extended' || github.event.inputs.tier == 'nightly'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Check if xAI key is available
|
||||
id: check-key
|
||||
run: |
|
||||
if [ -n "${{ secrets.XAI_API_KEY }}" ]; then
|
||||
echo "has_key=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_key=false" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ XAI_API_KEY not set - skipping tests"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e .
|
||||
pip install pytest pytest-asyncio pytest-timeout
|
||||
|
||||
- name: Run xAI/Grok Tests
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
env:
|
||||
PRAISONAI_ALLOW_NETWORK: '1'
|
||||
PRAISONAI_LIVE_TESTS: '1'
|
||||
PRAISONAI_TEST_PROVIDERS: 'grok_xai'
|
||||
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/ \
|
||||
-m "provider_grok_xai" \
|
||||
-v --tb=short \
|
||||
--timeout=120 \
|
||||
--ignore=tests/fixtures \
|
||||
|| echo "xAI tests completed"
|
||||
|
||||
- name: Skip notice
|
||||
if: steps.check-key.outputs.has_key == 'false'
|
||||
run: echo "✅ xAI tests skipped (no API key available)"
|
||||
|
||||
extended-cohere:
|
||||
if: github.event_name == 'schedule' || github.event.inputs.tier == 'extended' || github.event.inputs.tier == 'nightly'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Check if Cohere key is available
|
||||
id: check-key
|
||||
run: |
|
||||
if [ -n "${{ secrets.COHERE_API_KEY }}" ]; then
|
||||
echo "has_key=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_key=false" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ COHERE_API_KEY not set - skipping tests"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
|
||||
- name: Set up Python 3.11
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install -e ".[all]"
|
||||
cd ../praisonai
|
||||
pip install -e ".[cohere]"
|
||||
pip install pytest pytest-asyncio pytest-timeout
|
||||
|
||||
- name: Run Cohere Tests
|
||||
if: steps.check-key.outputs.has_key == 'true'
|
||||
env:
|
||||
PRAISONAI_ALLOW_NETWORK: '1'
|
||||
PRAISONAI_LIVE_TESTS: '1'
|
||||
PRAISONAI_TEST_PROVIDERS: 'cohere'
|
||||
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
|
||||
run: |
|
||||
cd src/praisonai
|
||||
python -m pytest tests/ \
|
||||
-m "provider_cohere" \
|
||||
-v --tb=short \
|
||||
--timeout=120 \
|
||||
--ignore=tests/fixtures \
|
||||
|| echo "Cohere tests completed"
|
||||
|
||||
- name: Skip notice
|
||||
if: steps.check-key.outputs.has_key == 'false'
|
||||
run: echo "✅ Cohere tests skipped (no API key available)"
|
||||
|
||||
# ============================================
|
||||
# TEST SUMMARY
|
||||
# ============================================
|
||||
test-summary:
|
||||
needs: [smoke, main]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Test Summary
|
||||
run: |
|
||||
echo "# 📊 Test Suite Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Smoke | ${{ needs.smoke.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Main | ${{ needs.main.result }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Test Tiers" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **smoke**: Fast unit tests, no network (≤2 min)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **main**: Unit + integration, no network (≤5 min)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **openai-live**: OpenAI API tests (when key available)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **extended-***: Provider-specific tests (nightly)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -1,168 +0,0 @@
|
||||
name: Real End-to-End Tests
|
||||
|
||||
# ⚠️ WARNING: This workflow makes real API calls and incurs costs!
|
||||
# Only runs when manually triggered to prevent accidental charges
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only
|
||||
inputs:
|
||||
framework:
|
||||
description: 'Framework to test (⚠️ Will incur API costs!)'
|
||||
required: true
|
||||
default: 'none'
|
||||
type: choice
|
||||
options:
|
||||
- none
|
||||
- autogen
|
||||
- crewai
|
||||
- all
|
||||
confirm_costs:
|
||||
description: 'I understand this will make real API calls and may incur costs'
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
real-tests:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.confirm_costs == 'true' && github.event.inputs.framework != 'none' }}
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11] # Single version to minimize costs
|
||||
|
||||
steps:
|
||||
- name: 🚨 Cost Warning
|
||||
run: |
|
||||
echo "⚠️ WARNING: This workflow will make real API calls!"
|
||||
echo "💰 This may incur charges on your API accounts"
|
||||
echo "🎯 Framework: ${{ github.event.inputs.framework }}"
|
||||
echo "✅ Cost confirmation: ${{ github.event.inputs.confirm_costs }}"
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> $GITHUB_ENV
|
||||
echo "ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }}" >> $GITHUB_ENV
|
||||
echo "GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Verify API Keys
|
||||
run: |
|
||||
if [ -z "${{ secrets.OPENAI_API_KEY }}" ]; then
|
||||
echo "❌ OPENAI_API_KEY not set in secrets"
|
||||
echo "🔧 Add your API key to repository secrets"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ API keys configured"
|
||||
|
||||
- name: Run Real AutoGen Tests
|
||||
if: ${{ github.event.inputs.framework == 'autogen' || github.event.inputs.framework == 'all' }}
|
||||
run: |
|
||||
echo "🤖 Running REAL AutoGen tests (⚠️ API costs may apply)"
|
||||
cd src/praisonai && python -m pytest tests/e2e/autogen/ -v -m real --tb=short
|
||||
continue-on-error: false
|
||||
|
||||
- name: Run Real CrewAI Tests
|
||||
if: ${{ github.event.inputs.framework == 'crewai' || github.event.inputs.framework == 'all' }}
|
||||
run: |
|
||||
echo "⛵ Running REAL CrewAI tests (⚠️ API costs may apply)"
|
||||
cd src/praisonai && python -m pytest tests/e2e/crewai/ -v -m real --tb=short
|
||||
continue-on-error: false
|
||||
|
||||
- name: Generate Real Test Report
|
||||
if: always()
|
||||
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:** ${{ 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 "" >> real_test_report.md
|
||||
|
||||
echo "## 🧪 Test Results" >> real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
|
||||
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
|
||||
echo "- Configuration validation" >> real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
fi
|
||||
|
||||
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
|
||||
echo "- Multi-agent setup validation" >> real_test_report.md
|
||||
echo "" >> real_test_report.md
|
||||
fi
|
||||
|
||||
echo "## 💰 Cost Considerations" >> real_test_report.md
|
||||
echo "- These tests made actual API calls to LLM providers" >> real_test_report.md
|
||||
echo "- Costs depend on your API pricing tier" >> real_test_report.md
|
||||
echo "- Tests are designed to be minimal to reduce costs" >> real_test_report.md
|
||||
echo "- Check your API provider dashboard for actual usage" >> real_test_report.md
|
||||
|
||||
echo "## 📋 Next Steps" >> real_test_report.md
|
||||
echo "- Review test results for any failures" >> real_test_report.md
|
||||
echo "- Check API usage in your provider dashboard" >> real_test_report.md
|
||||
echo "- Use mock tests (tests/integration/) for routine testing" >> real_test_report.md
|
||||
|
||||
- name: Upload Real Test Results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: real-test-results-${{ github.event.inputs.framework }}-python-${{ matrix.python-version }}
|
||||
path: |
|
||||
real_test_report.md
|
||||
retention-days: 30
|
||||
|
||||
# Safety job that runs when costs not confirmed
|
||||
safety-check:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.confirm_costs != 'true' || github.event.inputs.framework == 'none' }}
|
||||
|
||||
steps:
|
||||
- name: 🛡️ Safety Check Failed
|
||||
run: |
|
||||
echo "🚨 Real tests not executed due to safety checks:"
|
||||
echo ""
|
||||
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)"
|
||||
echo "2. Check 'I understand this will make real API calls and may incur costs'"
|
||||
echo "3. Ensure API keys are set in repository secrets"
|
||||
echo ""
|
||||
echo "💡 For cost-free testing, use mock tests instead:"
|
||||
echo " - Run 'python -m pytest tests/integration/' locally"
|
||||
echo " - Or trigger other workflows that use mock tests"
|
||||
|
||||
exit 1
|
||||
@@ -1,204 +0,0 @@
|
||||
name: Windows Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test-windows:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_MODEL_NAME: gpt-4o-mini
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install praisonaiagents
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
cd src/praisonai-agents
|
||||
pip install .
|
||||
shell: bash
|
||||
|
||||
- name: Install praisonai wrapper
|
||||
run: |
|
||||
cd src/praisonai
|
||||
pip install .[dev]
|
||||
shell: bash
|
||||
|
||||
# ==================== CORE IMPORTS ====================
|
||||
- name: Test core imports
|
||||
run: |
|
||||
python -c "from praisonaiagents import Agent, Agents, Workflow; print('Core imports OK')"
|
||||
python -c "from praisonaiagents import tool, Tools; print('Tool imports OK')"
|
||||
python -c "from praisonaiagents import Task, WorkflowStep; print('Task/Step imports OK')"
|
||||
|
||||
# ==================== AGENT FEATURES ====================
|
||||
- name: Test Agent instantiation
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import Agent
|
||||
a = Agent(name='Test', instructions='You are helpful')
|
||||
print('Agent created:', a.name)
|
||||
a2 = Agent(name='ToolAgent', instructions='Use tools', tools=[])
|
||||
print('Agent with tools:', a2.name)
|
||||
"
|
||||
|
||||
- name: Test Agents orchestration
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import Agent, Agents, Task
|
||||
a1 = Agent(name='Agent1', instructions='First agent')
|
||||
a2 = Agent(name='Agent2', instructions='Second agent')
|
||||
t = Task(description='Do something', agent=a1)
|
||||
agents = Agents(agents=[a1, a2], tasks=[t])
|
||||
print('Agents:', len(agents.agents), 'Tasks:', len(agents.tasks))
|
||||
"
|
||||
|
||||
# ==================== WORKFLOW FEATURES ====================
|
||||
- name: Test Workflow and WorkflowStep
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import Workflow, WorkflowStep
|
||||
from praisonaiagents.workflows import WorkflowOutputConfig
|
||||
|
||||
w = Workflow(name='test')
|
||||
s1 = WorkflowStep(name='step1')
|
||||
s2 = WorkflowStep(name='step2', context=['step1'])
|
||||
print('Workflow:', w.name)
|
||||
print('Steps:', s1.name, s2.name)
|
||||
|
||||
cfg = WorkflowOutputConfig(verbose=True, stream=False)
|
||||
w2 = Workflow(name='configured', output=cfg)
|
||||
print('Configured workflow verbose:', w2.verbose)
|
||||
"
|
||||
|
||||
# ==================== MEMORY FEATURES ====================
|
||||
- name: Test Memory classes
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents.memory import FileMemory
|
||||
from praisonaiagents.config import MemoryConfig
|
||||
|
||||
cfg = MemoryConfig(backend='file', user_id='test_user')
|
||||
print('MemoryConfig backend:', cfg.backend)
|
||||
|
||||
mem = FileMemory(user_id='test_user', base_path='.praison/test_memory')
|
||||
print('FileMemory user:', mem.user_id)
|
||||
"
|
||||
|
||||
# ==================== TOOL DECORATOR ====================
|
||||
- name: Test tool decorator
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import tool
|
||||
|
||||
@tool
|
||||
def my_tool(query: str) -> str:
|
||||
'''A test tool'''
|
||||
return f'Echo: {query}'
|
||||
|
||||
result = my_tool('hello')
|
||||
print('Tool result:', result)
|
||||
"
|
||||
|
||||
# ==================== CONFIG CLASSES ====================
|
||||
- name: Test config classes
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents.config import OutputConfig, ExecutionConfig, PlanningConfig
|
||||
|
||||
out_cfg = OutputConfig(verbose=True, stream=True)
|
||||
print('OutputConfig verbose:', out_cfg.verbose)
|
||||
|
||||
exec_cfg = ExecutionConfig(max_iter=50, max_retry_limit=5)
|
||||
print('ExecutionConfig max_iter:', exec_cfg.max_iter)
|
||||
|
||||
plan_cfg = PlanningConfig(reasoning=True, auto_approve=False)
|
||||
print('PlanningConfig reasoning:', plan_cfg.reasoning)
|
||||
"
|
||||
# ==================== HANDOFF ====================
|
||||
- name: Test Handoff
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import Handoff, handoff, Agent
|
||||
|
||||
a1 = Agent(name='Agent1', instructions='First')
|
||||
a2 = Agent(name='Agent2', instructions='Second')
|
||||
|
||||
# Using Handoff class directly
|
||||
h = Handoff(agent=a2)
|
||||
print('Handoff tool name:', h.tool_name)
|
||||
|
||||
# Using handoff function
|
||||
h2 = handoff(a1, tool_name_override='custom_handoff')
|
||||
print('Custom handoff name:', h2.tool_name)
|
||||
"
|
||||
|
||||
|
||||
# ==================== ASYNC FEATURES ====================
|
||||
- name: Test async imports
|
||||
run: |
|
||||
python -c "
|
||||
import asyncio
|
||||
from praisonaiagents import Agent
|
||||
|
||||
async def test_async():
|
||||
agent = Agent(name='AsyncAgent', instructions='Test')
|
||||
assert hasattr(agent, 'arun'), 'Missing arun'
|
||||
assert hasattr(agent, 'astart'), 'Missing astart'
|
||||
print('Async methods available')
|
||||
|
||||
asyncio.run(test_async())
|
||||
"
|
||||
|
||||
# ==================== CLI COMMANDS ====================
|
||||
- name: Test CLI help
|
||||
run: praisonai --help
|
||||
|
||||
- name: Test CLI session list
|
||||
run: praisonai session list
|
||||
continue-on-error: true
|
||||
|
||||
- name: Test CLI queue list
|
||||
run: praisonai queue list
|
||||
continue-on-error: true
|
||||
|
||||
# ==================== REAL API TESTS ====================
|
||||
- name: Real API test - Agent run
|
||||
if: env.OPENAI_API_KEY != ''
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import Agent
|
||||
agent = Agent(name='TestAgent', instructions='Reply with exactly: WINDOWS_OK', llm='gpt-4o-mini')
|
||||
result = agent.run('Say WINDOWS_OK')
|
||||
print('Result:', result)
|
||||
print('Real API test passed')
|
||||
"
|
||||
|
||||
- name: Real API test - Multi-agent
|
||||
if: env.OPENAI_API_KEY != ''
|
||||
run: |
|
||||
python -c "
|
||||
from praisonaiagents import Agent, Agents, Task
|
||||
|
||||
researcher = Agent(name='Researcher', instructions='You research topics', llm='gpt-4o-mini')
|
||||
writer = Agent(name='Writer', instructions='You write content', llm='gpt-4o-mini')
|
||||
|
||||
task = Task(description='Say hello world', agent=researcher)
|
||||
|
||||
agents = Agents(agents=[researcher, writer], tasks=[task])
|
||||
result = agents.start()
|
||||
print('Multi-agent result type:', type(result))
|
||||
print('Multi-agent test passed')
|
||||
"
|
||||
@@ -1,516 +0,0 @@
|
||||
name: Quick Validation Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
quick-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov pytest-timeout
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV
|
||||
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Backup Root Config Files
|
||||
run: |
|
||||
echo "🔄 Backing up root configuration files to prevent default file resolution interference..."
|
||||
echo "ℹ️ PraisonAI automatically uses 'agents.yaml' in working directory when no file specified"
|
||||
echo "ℹ️ This can interfere with test files that specify their own YAML files"
|
||||
if [ -f "agents.yaml" ]; then
|
||||
mv agents.yaml agents.yaml.backup
|
||||
echo "✅ Moved root agents.yaml to agents.yaml.backup"
|
||||
echo " - This prevents default file fallback during tests"
|
||||
fi
|
||||
if [ -f "tools.py" ]; then
|
||||
mv tools.py tools.py.backup
|
||||
echo "✅ Moved tools.py to tools.py.backup"
|
||||
fi
|
||||
|
||||
- name: Debug API Key Status
|
||||
run: |
|
||||
echo "🔍 Checking API key availability..."
|
||||
if [ -n "${{ secrets.OPENAI_API_KEY }}" ]; then
|
||||
echo "✅ GitHub secret OPENAI_API_KEY is available"
|
||||
echo "🔑 API key starts with: $(echo "$OPENAI_API_KEY" | cut -c1-7)..."
|
||||
else
|
||||
echo "⚠️ GitHub secret OPENAI_API_KEY is NOT set - using fallback"
|
||||
echo "🔑 Using fallback key: sk-test-key..."
|
||||
fi
|
||||
echo "🌐 API Base: $OPENAI_API_BASE"
|
||||
echo "🤖 Model: $OPENAI_MODEL_NAME"
|
||||
echo "🐛 Log Level: $LOGLEVEL"
|
||||
echo "📊 Environment Check:"
|
||||
echo " - OPENAI_API_KEY length: ${#OPENAI_API_KEY}"
|
||||
echo " - OPENAI_API_BASE: $OPENAI_API_BASE"
|
||||
echo " - OPENAI_MODEL_NAME: $OPENAI_MODEL_NAME"
|
||||
echo " - LOGLEVEL: $LOGLEVEL"
|
||||
|
||||
- name: Debug Python Environment Variables
|
||||
run: |
|
||||
python -c "
|
||||
import os
|
||||
print('🐍 Python Environment Variable Check:')
|
||||
api_key = os.environ.get('OPENAI_API_KEY', 'NOT_SET')
|
||||
if api_key != 'NOT_SET':
|
||||
print(f' ✅ OPENAI_API_KEY: {api_key[:7]}... (length: {len(api_key)})')
|
||||
else:
|
||||
print(' ❌ OPENAI_API_KEY: NOT_SET')
|
||||
print(f' 🌐 OPENAI_API_BASE: {os.environ.get(\"OPENAI_API_BASE\", \"NOT_SET\")}')
|
||||
print(f' 🤖 OPENAI_MODEL_NAME: {os.environ.get(\"OPENAI_MODEL_NAME\", \"NOT_SET\")}')
|
||||
print(f' 📋 All OPENAI env vars:')
|
||||
for key, value in os.environ.items():
|
||||
if key.startswith('OPENAI'):
|
||||
print(f' {key}: {value[:10] if len(value) > 10 else value}...')
|
||||
"
|
||||
|
||||
- name: Find Researcher Role Source
|
||||
run: |
|
||||
echo "🔍 Hunting for the mysterious 'Researcher' role..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import yaml
|
||||
import glob
|
||||
|
||||
print('📋 Searching for Researcher role in all YAML files:')
|
||||
yaml_files = glob.glob('src/praisonai/tests/*.yaml')
|
||||
|
||||
for yaml_file in yaml_files:
|
||||
try:
|
||||
with open(yaml_file, 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
# Check if any role contains 'researcher'
|
||||
roles = config.get('roles', {})
|
||||
for role_key, role_data in roles.items():
|
||||
role_name = role_data.get('role', '')
|
||||
if 'researcher' in role_key.lower() or 'researcher' in role_name.lower():
|
||||
print(f' 🎯 FOUND in {yaml_file}:')
|
||||
print(f' Framework: {config.get(\"framework\", \"NOT_SET\")}')
|
||||
print(f' Role key: {role_key}')
|
||||
print(f' Role name: {role_name}')
|
||||
print(f' All roles: {list(roles.keys())}')
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f' ❌ Error reading {yaml_file}: {e}')
|
||||
|
||||
print('🔍 Checking for default configurations...')
|
||||
# Check if there are any default configs or hardcoded roles
|
||||
try:
|
||||
import praisonai
|
||||
print(f' PraisonAI package location: {praisonai.__file__}')
|
||||
|
||||
# Check if there are any example YAML files in the package
|
||||
package_dir = os.path.dirname(praisonai.__file__)
|
||||
for root, dirs, files in os.walk(package_dir):
|
||||
for file in files:
|
||||
if file.endswith(('.yaml', '.yml')):
|
||||
file_path = os.path.join(root, file)
|
||||
print(f' 📁 Found YAML in package: {file_path}')
|
||||
except Exception as e:
|
||||
print(f' ❌ Error checking package: {e}')
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Trace AutoGen Execution Path
|
||||
run: |
|
||||
echo "🔍 Tracing AutoGen execution to find where it diverges..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
try:
|
||||
from praisonai import PraisonAI
|
||||
from praisonai.agents_generator import AgentsGenerator
|
||||
|
||||
# Test the exact execution path
|
||||
print('🎯 Testing AutoGen execution path:')
|
||||
|
||||
praisonai = PraisonAI(agent_file='tests/autogen-agents.yaml')
|
||||
print(f' 1. PraisonAI framework: \"{praisonai.framework}\"')
|
||||
|
||||
agents_gen = AgentsGenerator(
|
||||
agent_file='tests/autogen-agents.yaml',
|
||||
framework=praisonai.framework,
|
||||
config_list=praisonai.config_list
|
||||
)
|
||||
print(f' 2. AgentsGenerator framework: \"{agents_gen.framework}\"')
|
||||
|
||||
# Load the YAML to check what it contains
|
||||
import yaml
|
||||
with open('src/praisonai/tests/autogen-agents.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
framework = agents_gen.framework or config.get('framework')
|
||||
print(f' 3. Final framework decision: \"{framework}\"')
|
||||
print(f' 4. Available frameworks:')
|
||||
|
||||
# Check framework availability
|
||||
try:
|
||||
import autogen
|
||||
print(f' ✅ AutoGen available')
|
||||
except ImportError:
|
||||
print(f' ❌ AutoGen NOT available')
|
||||
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
print(f' ✅ PraisonAI agents available')
|
||||
except ImportError:
|
||||
print(f' ❌ PraisonAI agents NOT available')
|
||||
|
||||
try:
|
||||
from crewai import Agent
|
||||
print(f' ✅ CrewAI available')
|
||||
except ImportError:
|
||||
print(f' ❌ CrewAI NOT available')
|
||||
|
||||
print(f' 5. Roles in YAML: {list(config.get(\"roles\", {}).keys())}')
|
||||
|
||||
# Now test the actual framework execution
|
||||
if framework == 'autogen':
|
||||
print(f' 6. ✅ Should execute _run_autogen')
|
||||
elif framework == 'praisonai':
|
||||
print(f' 6. ❌ Would execute _run_praisonai (WRONG!)')
|
||||
else:
|
||||
print(f' 6. ❌ Would execute _run_crewai (DEFAULT FALLBACK)')
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ Error tracing execution: {e}')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Debug YAML Loading and Roles
|
||||
run: |
|
||||
echo "🔍 Tracing YAML file loading and role creation..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
print('📁 Available YAML files in src/praisonai/tests/:')
|
||||
import glob
|
||||
yaml_files = glob.glob('tests/*.yaml')
|
||||
for f in yaml_files:
|
||||
print(f' {f}')
|
||||
|
||||
print()
|
||||
print('📋 Content of autogen-agents.yaml:')
|
||||
with open('tests/autogen-agents.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
print(f' Framework: {config.get(\"framework\")}')
|
||||
print(f' Topic: {config.get(\"topic\")}')
|
||||
print(f' Roles: {list(config.get(\"roles\", {}).keys())}')
|
||||
for role_key, role_data in config.get('roles', {}).items():
|
||||
print(f' {role_key} -> {role_data.get(\"role\", \"NO_ROLE\")}')
|
||||
|
||||
print()
|
||||
print('🔍 Checking if execution uses a different YAML:')
|
||||
|
||||
# Check other YAML files for 'Researcher' role
|
||||
for yaml_file in yaml_files:
|
||||
try:
|
||||
with open(yaml_file, 'r') as f:
|
||||
test_config = yaml.safe_load(f)
|
||||
roles = test_config.get('roles', {})
|
||||
for role_key, role_data in roles.items():
|
||||
if 'researcher' in role_key.lower() or 'researcher' in role_data.get('role', '').lower():
|
||||
print(f' 🎯 FOUND Researcher in {yaml_file}!')
|
||||
print(f' Framework: {test_config.get(\"framework\")}')
|
||||
print(f' Role key: {role_key} -> {role_data.get(\"role\")}')
|
||||
except:
|
||||
pass
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Debug Framework Detection
|
||||
run: |
|
||||
echo "🔍 Testing framework detection and config flow..."
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
print('🔧 Testing framework detection:')
|
||||
|
||||
# Load the YAML file
|
||||
with open('tests/autogen-agents.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
print(f' 📋 YAML framework: {config.get(\"framework\", \"NOT_SET\")}')
|
||||
print(f' 📋 YAML topic: {config.get(\"topic\", \"NOT_SET\")}')
|
||||
|
||||
try:
|
||||
from praisonai import PraisonAI
|
||||
from praisonai.agents_generator import AgentsGenerator
|
||||
|
||||
# Test PraisonAI initialization
|
||||
praisonai = PraisonAI(agent_file='tests/autogen-agents.yaml')
|
||||
print(f' 🎯 PraisonAI framework: {praisonai.framework}')
|
||||
|
||||
# Test AgentsGenerator initialization
|
||||
agents_gen = AgentsGenerator(
|
||||
agent_file='tests/autogen-agents.yaml',
|
||||
framework=praisonai.framework,
|
||||
config_list=praisonai.config_list
|
||||
)
|
||||
print(f' ⚙️ AgentsGenerator framework: {agents_gen.framework}')
|
||||
print(f' ⚙️ Final framework decision: {agents_gen.framework or config.get(\"framework\")}')
|
||||
|
||||
# Check config_list
|
||||
print(f' 🔑 Config list model: {praisonai.config_list[0].get(\"model\")}')
|
||||
print(f' 🔑 Config list API key: {praisonai.config_list[0].get(\"api_key\", \"NOT_SET\")[:10]}...')
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ Error in framework detection: {e}')
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Debug PraisonAIModel API Key Flow
|
||||
run: |
|
||||
echo "🔍 Testing PraisonAIModel API key handling..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
print('🔑 Environment API Key Check:')
|
||||
env_key = os.environ.get('OPENAI_API_KEY', 'NOT_FOUND')
|
||||
print(f' OPENAI_API_KEY: {env_key[:10] if env_key != \"NOT_FOUND\" else \"NOT_FOUND\"}...')
|
||||
|
||||
try:
|
||||
from praisonai.inc.models import PraisonAIModel
|
||||
|
||||
# Test PraisonAIModel with openai/gpt-5-nano (from YAML)
|
||||
model = PraisonAIModel(model='openai/gpt-5-nano')
|
||||
|
||||
print('🤖 PraisonAIModel Configuration:')
|
||||
print(f' model: {model.model}')
|
||||
print(f' model_name: {model.model_name}')
|
||||
print(f' api_key_var: {model.api_key_var}')
|
||||
print(f' api_key: {model.api_key[:10] if model.api_key != \"nokey\" else \"DEFAULT_NOKEY\"}...')
|
||||
print(f' base_url: {model.base_url}')
|
||||
|
||||
if model.api_key == 'nokey':
|
||||
print('❌ FOUND THE ISSUE: PraisonAIModel is using default \"nokey\" instead of environment variable!')
|
||||
else:
|
||||
print('✅ PraisonAIModel has valid API key from environment')
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ Error testing PraisonAIModel: {e}')
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Validate API Key
|
||||
run: |
|
||||
echo "🔑 Testing API key validity with minimal OpenAI call..."
|
||||
python -c "
|
||||
import os
|
||||
try:
|
||||
from openai import OpenAI
|
||||
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))
|
||||
# Make a minimal API call to test key validity
|
||||
response = client.models.list()
|
||||
print('✅ API Key is VALID - OpenAI responded successfully')
|
||||
print(f'📊 Available models: {len(list(response.data))} models found')
|
||||
except Exception as e:
|
||||
print(f'❌ API Key is INVALID - Error: {e}')
|
||||
print('🔍 This explains why all API-dependent tests are failing')
|
||||
print('💡 The GitHub secret OPENAI_API_KEY needs to be updated with a valid key')
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Test Direct PraisonAI Execution
|
||||
run: |
|
||||
echo "🧪 Testing direct PraisonAI execution (what works locally)..."
|
||||
python -m praisonai src/praisonai/tests/autogen-agents.yaml
|
||||
continue-on-error: true
|
||||
|
||||
- name: Comprehensive Execution Debug
|
||||
run: |
|
||||
echo "🔍 Comprehensive debugging of PraisonAI execution path..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
print('=' * 60)
|
||||
print('🔍 COMPREHENSIVE EXECUTION DEBUG')
|
||||
print('=' * 60)
|
||||
|
||||
# Check current working directory
|
||||
print(f'📁 Current working directory: {os.getcwd()}')
|
||||
|
||||
# List files in current directory
|
||||
print('📋 Files in current directory:')
|
||||
for f in os.listdir('.'):
|
||||
print(f' {f}')
|
||||
|
||||
print()
|
||||
print('📋 Files in src/praisonai/tests/ directory:')
|
||||
for f in os.listdir('tests'):
|
||||
if f.endswith('.yaml'):
|
||||
print(f' src/praisonai/tests/{f}')
|
||||
|
||||
# Check if root agents.yaml exists
|
||||
print()
|
||||
if os.path.exists('agents.yaml'):
|
||||
print('❌ ROOT agents.yaml EXISTS (this is the problem!)')
|
||||
with open('agents.yaml', 'r') as f:
|
||||
root_config = yaml.safe_load(f)
|
||||
print(f' Root framework: {root_config.get(\"framework\")}')
|
||||
print(f' Root roles: {list(root_config.get(\"roles\", {}).keys())}')
|
||||
else:
|
||||
print('✅ ROOT agents.yaml does NOT exist (good!)')
|
||||
|
||||
# Test the actual execution path
|
||||
print()
|
||||
print('🎯 Testing EXACT execution path:')
|
||||
|
||||
try:
|
||||
from praisonai import PraisonAI
|
||||
from praisonai.agents_generator import AgentsGenerator
|
||||
|
||||
# Test with full path
|
||||
test_file = 'src/praisonai/tests/autogen-agents.yaml'
|
||||
print(f' Using test file: {test_file}')
|
||||
print(f' File exists: {os.path.exists(test_file)}')
|
||||
|
||||
# Load the test file directly
|
||||
with open(test_file, 'r') as f:
|
||||
test_config = yaml.safe_load(f)
|
||||
print(f' Test file framework: {test_config.get(\"framework\")}')
|
||||
print(f' Test file roles: {list(test_config.get(\"roles\", {}).keys())}')
|
||||
|
||||
# Create PraisonAI instance
|
||||
praisonai = PraisonAI(agent_file=test_file)
|
||||
print(f' PraisonAI.agent_file: {praisonai.agent_file}')
|
||||
print(f' PraisonAI.framework: {praisonai.framework}')
|
||||
|
||||
# Create AgentsGenerator
|
||||
agents_gen = AgentsGenerator(
|
||||
agent_file=test_file,
|
||||
framework=praisonai.framework,
|
||||
config_list=praisonai.config_list
|
||||
)
|
||||
print(f' AgentsGenerator.agent_file: {agents_gen.agent_file}')
|
||||
print(f' AgentsGenerator.framework: {agents_gen.framework}')
|
||||
|
||||
# Test what would happen in generate_crew_and_kickoff
|
||||
print()
|
||||
print('🔧 Testing generate_crew_and_kickoff logic:')
|
||||
|
||||
# Simulate the loading logic
|
||||
if agents_gen.agent_yaml:
|
||||
loaded_config = yaml.safe_load(agents_gen.agent_yaml)
|
||||
print(' Would load from agent_yaml')
|
||||
else:
|
||||
if agents_gen.agent_file == '/app/api:app' or agents_gen.agent_file == 'api:app':
|
||||
agents_gen.agent_file = 'agents.yaml'
|
||||
print(f' Would change agent_file to: {agents_gen.agent_file}')
|
||||
try:
|
||||
with open(agents_gen.agent_file, 'r') as f:
|
||||
loaded_config = yaml.safe_load(f)
|
||||
print(f' Successfully loaded: {agents_gen.agent_file}')
|
||||
except FileNotFoundError:
|
||||
print(f' FileNotFoundError: {agents_gen.agent_file}')
|
||||
loaded_config = None
|
||||
|
||||
if loaded_config:
|
||||
final_framework = agents_gen.framework or loaded_config.get('framework')
|
||||
print(f' Final framework decision: {final_framework}')
|
||||
print(f' Loaded roles: {list(loaded_config.get(\"roles\", {}).keys())}')
|
||||
|
||||
if 'researcher' in loaded_config.get('roles', {}):
|
||||
print(' ❌ FOUND Researcher role in loaded config!')
|
||||
else:
|
||||
print(' ✅ No Researcher role in loaded config')
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ Error during execution debug: {e}')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Fast Tests
|
||||
run: |
|
||||
# Run the fastest, most essential tests with coverage
|
||||
cd src/praisonai && python -m pytest tests/unit/test_core_agents.py -v --tb=short --disable-warnings --cov=praisonai --cov-report=xml --cov-branch
|
||||
|
||||
- name: Run Real API Tests
|
||||
run: |
|
||||
echo "🔑 Running real API tests with actual OpenAI API key..."
|
||||
cd src/praisonai && python -m pytest tests/test_agents_playbook.py -v --tb=short --disable-warnings -m real
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run E2E Real Tests
|
||||
run: |
|
||||
echo "🧪 Running E2E real tests..."
|
||||
cd src/praisonai && python -m pytest tests/e2e/ -v --tb=short --disable-warnings -m real
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Legacy Example Tests
|
||||
run: |
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings -m "not real" --timeout=60
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: src/praisonai/coverage.xml
|
||||
flags: quick-validation
|
||||
name: quick-validation-coverage
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
|
||||
- name: Restore Root Config Files
|
||||
run: |
|
||||
echo "🔄 Restoring root configuration files..."
|
||||
if [ -f "agents.yaml.backup" ]; then
|
||||
mv agents.yaml.backup agents.yaml
|
||||
echo "✅ Restored agents.yaml"
|
||||
fi
|
||||
if [ -f "tools.py.backup" ]; then
|
||||
mv tools.py.backup tools.py
|
||||
echo "✅ Restored tools.py"
|
||||
fi
|
||||
if: always() # Always run this step, even if previous steps failed
|
||||
@@ -1,57 +0,0 @@
|
||||
name: Update API Reference
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'src/praisonai-agents/praisonaiagents/**/*.py'
|
||||
- 'src/praisonai/praisonai/**/*.py'
|
||||
- 'src/praisonai-ts/src/**/*.ts'
|
||||
- '.github/workflows/update-api-md.yml'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-api-md:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
persist-credentials: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Generate api.md
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
python3 src/praisonai/scripts/generate_api_md.py
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
if git diff --quiet api.md; then
|
||||
echo "changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes to api.md"
|
||||
else
|
||||
echo "changes=true" >> $GITHUB_OUTPUT
|
||||
echo "api.md has been updated"
|
||||
git diff api.md | head -50
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.check_changes.outputs.changes == 'true'
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "docs: auto-update api.md [skip ci]"
|
||||
file_pattern: 'api.md'
|
||||
commit_user_name: "github-actions[bot]"
|
||||
commit_user_email: "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
commit_author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
|
||||
+21
-85
@@ -1,87 +1,23 @@
|
||||
workspace.code-workspace
|
||||
.vscode
|
||||
crewai
|
||||
.cache
|
||||
__pycache__
|
||||
chroma.sqlite3
|
||||
test
|
||||
dist
|
||||
.env
|
||||
assets
|
||||
.idea
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.pytest_cache
|
||||
praisonAI.egg-info
|
||||
flagged
|
||||
test.yaml
|
||||
db
|
||||
praisonai_prompt.txt
|
||||
watch.sh
|
||||
docs.sh
|
||||
other
|
||||
output
|
||||
!src/praisonai/praisonai/cli/output/
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
.files
|
||||
threads.db
|
||||
threads.db-journal
|
||||
|
||||
.chainlit
|
||||
!praisonai/ui/config/.chainlit
|
||||
|
||||
site
|
||||
# Ignore Sphinx build directory
|
||||
docs/_build/
|
||||
docs/source/
|
||||
docs/build/
|
||||
|
||||
# Ignore Sphinx cache
|
||||
docs/_build/doctrees/
|
||||
docs/_build/doctrees/*
|
||||
docs/_build/doctrees/.doctrees
|
||||
|
||||
# Ignore Sphinx logs and local build files
|
||||
docs/_build/.buildinfo
|
||||
docs/_build/.buildinfo/*
|
||||
docs/_build/.buildinfo/.buildinfo
|
||||
|
||||
praisonai/doc
|
||||
|
||||
# agentops
|
||||
trained_agents_data.pkl
|
||||
agentops.log
|
||||
|
||||
# crewAI
|
||||
crewAI
|
||||
!tests/integration/crewai
|
||||
!tests/e2e/crewai
|
||||
!src/praisonai/tests/integration/crewai
|
||||
!src/praisonai/tests/e2e/crewai
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
|
||||
# Credentials
|
||||
credentials.json
|
||||
|
||||
# node_modules
|
||||
node_modules
|
||||
|
||||
outputs
|
||||
agents/praisonaiagents/build
|
||||
agents/praisonaiagents/dist
|
||||
agents/praisonaiagents/praisonaiagents.egg-info
|
||||
.codegpt
|
||||
.praison
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
.qodo
|
||||
CopilotKit*
|
||||
.gemini
|
||||
issue_details.json
|
||||
|
||||
# MCP Registry tokens
|
||||
.mcpregistry_*
|
||||
|
||||
*.log
|
||||
.internal
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -1,20 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,53 +0,0 @@
|
||||
# PraisonAI Docker Environment Configuration
|
||||
# Copy this file to .env and update with your values
|
||||
|
||||
# Required: OpenAI API Key
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
|
||||
# Required: Chainlit Authentication Secret (generate a random string)
|
||||
CHAINLIT_AUTH_SECRET=your_chainlit_secret_here
|
||||
|
||||
# Optional: Additional API Keys
|
||||
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||||
GOOGLE_API_KEY=your_google_api_key_here
|
||||
GROQ_API_KEY=your_groq_api_key_here
|
||||
COHERE_API_KEY=your_cohere_api_key_here
|
||||
|
||||
# Optional: Service Configuration
|
||||
FLASK_ENV=development
|
||||
CHAINLIT_HOST=0.0.0.0
|
||||
|
||||
# Optional: Port Configuration (modify if you have conflicts)
|
||||
UI_PORT=8082
|
||||
CHAT_PORT=8083
|
||||
API_PORT=8080
|
||||
|
||||
# Optional: Database Configuration
|
||||
DATABASE_URL=sqlite:///root/.praison/database.sqlite
|
||||
|
||||
# Optional: Logging Configuration
|
||||
LOG_LEVEL=INFO
|
||||
PYTHONPATH=/app
|
||||
|
||||
# Optional: Development Settings
|
||||
DEV_MODE=false
|
||||
DEBUG=false
|
||||
|
||||
# Optional: Model Configuration
|
||||
DEFAULT_MODEL=gpt-4
|
||||
DEFAULT_TEMPERATURE=0.7
|
||||
MAX_TOKENS=4000
|
||||
|
||||
# Optional: Security Settings
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:8082,http://localhost:8083
|
||||
RATE_LIMIT=100
|
||||
|
||||
# Optional: Feature Flags
|
||||
ENABLE_CHAT=true
|
||||
ENABLE_UI=true
|
||||
ENABLE_API=true
|
||||
ENABLE_REALTIME=true
|
||||
|
||||
# Optional: Volume Paths (for custom mounting)
|
||||
PRAISON_DATA_PATH=~/.praison
|
||||
EXAMPLES_PATH=./examples
|
||||
@@ -1,46 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
curl \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create praison config directory
|
||||
RUN mkdir -p /root/.praison
|
||||
|
||||
# Install Python packages (using latest versions)
|
||||
# Note: praisonai is installed from PyPI - remove version pin to use latest available
|
||||
RUN pip install --no-cache-dir \
|
||||
flask \
|
||||
praisonai \
|
||||
"praisonai[api]" \
|
||||
gunicorn \
|
||||
markdown
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Set environment variables for directory management
|
||||
ENV PRAISON_CONFIG_DIR=/root/.praison
|
||||
ENV DOCKER_CONTAINER=true
|
||||
|
||||
# Create health check API if api.py doesn't exist
|
||||
RUN if [ ! -f api.py ]; then \
|
||||
echo "from flask import Flask\n\
|
||||
app = Flask(__name__)\n\
|
||||
\n\
|
||||
@app.route('/health')\n\
|
||||
def health():\n\
|
||||
return {'status': 'healthy'}, 200\n\
|
||||
\n\
|
||||
if __name__ == '__main__':\n\
|
||||
app.run()" > api.py; \
|
||||
fi
|
||||
|
||||
EXPOSE 8080
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
|
||||
@@ -1,33 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
curl \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create praison config directory
|
||||
RUN mkdir -p /root/.praison
|
||||
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.11.1" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Set environment variables for directory management
|
||||
ENV PRAISON_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_DB_DIR=/root/.praison
|
||||
ENV DOCKER_CONTAINER=true
|
||||
|
||||
# Default command (will be overridden by docker-compose)
|
||||
CMD ["praisonai", "chat"]
|
||||
@@ -1,47 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
portaudio19-dev \
|
||||
python3-pyaudio \
|
||||
curl \
|
||||
git \
|
||||
vim \
|
||||
nano \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create praison config directory
|
||||
RUN mkdir -p /root/.praison
|
||||
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.11.1" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
"praisonai[code]" \
|
||||
"embedchain[github,youtube]" \
|
||||
jupyter \
|
||||
jupyterlab \
|
||||
notebook \
|
||||
pytest \
|
||||
pytest-asyncio \
|
||||
mkdocs \
|
||||
mkdocs-material
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Set environment variables for directory management
|
||||
ENV PRAISON_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_DB_DIR=/root/.praison
|
||||
ENV DOCKER_CONTAINER=true
|
||||
|
||||
# Default command (will be overridden by docker-compose)
|
||||
CMD ["praisonai", "ui"]
|
||||
@@ -1,32 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
curl \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create praison config directory
|
||||
RUN mkdir -p /root/.praison
|
||||
|
||||
# Copy praisonaiagents source
|
||||
COPY src/praisonai-agents/ /app/praisonaiagents/
|
||||
WORKDIR /app/praisonaiagents
|
||||
|
||||
# Install praisonaiagents with all features (non-editable for production)
|
||||
RUN pip install --no-cache-dir ".[all]"
|
||||
|
||||
# Set environment variables for directory management
|
||||
ENV PRAISON_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_DB_DIR=/root/.praison
|
||||
ENV DOCKER_CONTAINER=true
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Default command
|
||||
CMD ["python", "-c", "import praisonaiagents; print('PraisonAI Agents installed successfully')"]
|
||||
@@ -1,25 +0,0 @@
|
||||
# Dockerfile for PraisonAI Serve API
|
||||
# Deploy to Railway, Render, Fly.io, or any container platform
|
||||
#
|
||||
# Build: docker build -f Dockerfile.serve -t praisonai-serve .
|
||||
# Run: docker run -p 8005:8005 -e OPENAI_API_KEY=sk-xxx praisonai-serve
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
RUN pip install --no-cache-dir praisonai praisonaiagents
|
||||
|
||||
# Copy agents configuration
|
||||
COPY agents.yaml .
|
||||
|
||||
# Expose the API port
|
||||
EXPOSE 8005
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8005/health || exit 1
|
||||
|
||||
# Start the API server
|
||||
CMD ["praisonai", "serve", "agents.yaml", "--host", "0.0.0.0", "--port", "8005"]
|
||||
@@ -1,33 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
curl \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create praison config directory
|
||||
RUN mkdir -p /root/.praison
|
||||
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.11.1" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Set environment variables for directory management
|
||||
ENV PRAISON_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_CONFIG_DIR=/root/.praison
|
||||
ENV CHAINLIT_DB_DIR=/root/.praison
|
||||
ENV DOCKER_CONTAINER=true
|
||||
|
||||
# Default command (will be overridden by docker-compose)
|
||||
CMD ["praisonai", "ui"]
|
||||
@@ -1,265 +0,0 @@
|
||||
# PraisonAI Docker Setup
|
||||
|
||||
This directory contains Docker configurations for running PraisonAI services in containerized environments. The setup addresses directory management issues and provides comprehensive multi-service deployment options.
|
||||
|
||||
## 🐳 Available Services
|
||||
|
||||
### Core Services
|
||||
- **UI Service** (`port 8082`) - Chainlit-based web interface
|
||||
- **Chat Service** (`port 8083`) - Dedicated chat interface
|
||||
- **API Service** (`port 8080`) - REST API endpoint
|
||||
- **Agents Service** - Standalone PraisonAI Agents runtime
|
||||
|
||||
### Docker Files
|
||||
- `Dockerfile` - Basic API service
|
||||
- `Dockerfile.ui` - UI service with web interface
|
||||
- `Dockerfile.chat` - Chat-focused service
|
||||
- `Dockerfile.dev` - Development environment with tools
|
||||
- `Dockerfile.praisonaiagents` - Standalone agents framework
|
||||
- `docker-compose.yml` - Multi-service orchestration
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Single Service
|
||||
```bash
|
||||
# Run UI service
|
||||
docker run -p 8082:8082 -e OPENAI_API_KEY=your_key ghcr.io/mervinpraison/praisonai:ui
|
||||
|
||||
# Run Chat service
|
||||
docker run -p 8083:8083 -e OPENAI_API_KEY=your_key ghcr.io/mervinpraison/praisonai:chat
|
||||
|
||||
# Run API service
|
||||
docker run -p 8080:8080 -e OPENAI_API_KEY=your_key ghcr.io/mervinpraison/praisonai:api
|
||||
```
|
||||
|
||||
### Multi-Service with Docker Compose
|
||||
```bash
|
||||
# Create environment file
|
||||
cat > .env << EOF
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
CHAINLIT_AUTH_SECRET=your_secret_here
|
||||
EOF
|
||||
|
||||
# Start all services
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop services
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
## 📁 Directory Management
|
||||
|
||||
### Problem Solved
|
||||
The original issue was that files like `chainlit.md`, `.chainlit` directory, and `public` folder were cluttering the root directory.
|
||||
|
||||
### Solution Implemented
|
||||
All PraisonAI configuration and runtime files are now stored in `~/.praison/`:
|
||||
|
||||
```bash
|
||||
~/.praison/
|
||||
├── database.sqlite # Chainlit database
|
||||
├── chainlit.md # Chainlit configuration
|
||||
├── .chainlit/ # Chainlit runtime files
|
||||
└── config/ # PraisonAI configuration
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
```bash
|
||||
PRAISON_CONFIG_DIR=/root/.praison # Main config directory
|
||||
CHAINLIT_CONFIG_DIR=/root/.praison # Chainlit config location
|
||||
CHAINLIT_DB_DIR=/root/.praison # Database location
|
||||
```
|
||||
|
||||
## 🔧 Service Configuration
|
||||
|
||||
### UI Service (Port 8082)
|
||||
```yaml
|
||||
environment:
|
||||
- CHAINLIT_PORT=8082
|
||||
- CHAINLIT_HOST=0.0.0.0
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- CHAINLIT_AUTH_SECRET=${CHAINLIT_AUTH_SECRET}
|
||||
```
|
||||
|
||||
### Chat Service (Port 8083)
|
||||
```yaml
|
||||
environment:
|
||||
- CHAINLIT_PORT=8083
|
||||
- CHAINLIT_HOST=0.0.0.0
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
```
|
||||
|
||||
### API Service (Port 8080)
|
||||
```yaml
|
||||
environment:
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
```
|
||||
|
||||
## 🎯 Service Endpoints
|
||||
|
||||
| Service | Port | Endpoint | Description |
|
||||
|---------|------|----------|-------------|
|
||||
| UI | 8082 | http://localhost:8082 | Web interface |
|
||||
| Chat | 8083 | http://localhost:8083 | Chat interface |
|
||||
| API | 8080 | http://localhost:8080 | REST API |
|
||||
| API Health | 8080 | http://localhost:8080/health | Health check |
|
||||
|
||||
## 🔍 Health Checks
|
||||
|
||||
All services include health checks:
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:PORT"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
## 📦 Package Versions
|
||||
|
||||
All Docker images use consistent, up-to-date versions:
|
||||
- PraisonAI: `>=2.2.95`
|
||||
- PraisonAI Agents: `>=0.0.92`
|
||||
- Python: `3.11-slim`
|
||||
|
||||
## 🔒 Security Features
|
||||
|
||||
- Non-root user execution where possible
|
||||
- Minimal base image (python:3.11-slim)
|
||||
- No unnecessary packages installed
|
||||
- Environment variable-based configuration
|
||||
- Volume mounting for persistent data
|
||||
|
||||
## 🛠 Development
|
||||
|
||||
### Development Environment
|
||||
```bash
|
||||
# Use development Dockerfile with additional tools
|
||||
docker build -f Dockerfile.dev -t praisonai:dev .
|
||||
docker run -it -v $(pwd):/app praisonai:dev bash
|
||||
```
|
||||
|
||||
### Custom Configuration
|
||||
```bash
|
||||
# Mount custom config directory
|
||||
docker run -v ~/.praison:/root/.praison praisonai:ui
|
||||
```
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
### Docker Compose Monitoring
|
||||
```bash
|
||||
# View service status
|
||||
docker-compose ps
|
||||
|
||||
# View resource usage
|
||||
docker-compose top
|
||||
|
||||
# View logs for specific service
|
||||
docker-compose logs ui
|
||||
docker-compose logs chat
|
||||
docker-compose logs api
|
||||
```
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Port conflicts**
|
||||
```bash
|
||||
# Check port usage
|
||||
netstat -tlnp | grep :8082
|
||||
|
||||
# Use different ports
|
||||
docker run -p 9082:8082 praisonai:ui
|
||||
```
|
||||
|
||||
2. **Environment variables not loading**
|
||||
```bash
|
||||
# Verify .env file
|
||||
cat .env
|
||||
|
||||
# Set variables directly
|
||||
docker run -e OPENAI_API_KEY=your_key praisonai:ui
|
||||
```
|
||||
|
||||
3. **Permission issues**
|
||||
```bash
|
||||
# Check volume permissions
|
||||
ls -la ~/.praison/
|
||||
|
||||
# Fix permissions
|
||||
sudo chown -R $(id -u):$(id -g) ~/.praison/
|
||||
```
|
||||
|
||||
4. **Service won't start**
|
||||
```bash
|
||||
# Check logs
|
||||
docker-compose logs service_name
|
||||
|
||||
# Restart service
|
||||
docker-compose restart service_name
|
||||
```
|
||||
|
||||
## 🔄 Updates
|
||||
|
||||
### Pulling Latest Images
|
||||
```bash
|
||||
# Pull latest images
|
||||
docker-compose pull
|
||||
|
||||
# Restart with new images
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Version Pinning
|
||||
To use specific versions, update the Dockerfile:
|
||||
```dockerfile
|
||||
RUN pip install "praisonai==2.2.95" "praisonaiagents==0.0.92"
|
||||
```
|
||||
|
||||
## 🌐 Production Deployment
|
||||
|
||||
### Recommended Production Setup
|
||||
```yaml
|
||||
# docker-compose.prod.yml
|
||||
version: '3.8'
|
||||
services:
|
||||
ui:
|
||||
image: ghcr.io/mervinpraison/praisonai:ui
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- CHAINLIT_HOST=0.0.0.0
|
||||
- CHAINLIT_PORT=8082
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
networks:
|
||||
- praison_network
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
reservations:
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
### Load Balancer Configuration
|
||||
For production environments, consider using nginx or similar:
|
||||
```nginx
|
||||
upstream praisonai {
|
||||
server localhost:8082;
|
||||
server localhost:8083;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
proxy_pass http://praisonai;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This Docker setup provides a clean, organized, and scalable way to deploy PraisonAI services while solving the directory management issues mentioned in the original request.
|
||||
@@ -1,23 +0,0 @@
|
||||
# Use an official Python runtime as a parent image
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# Set work directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PraisonAI with the 'call' extra and ensure it's the latest version
|
||||
RUN pip install --no-cache-dir --upgrade "praisonai[call]"
|
||||
|
||||
# Expose the port the app runs on
|
||||
EXPOSE 8090
|
||||
|
||||
# Run the application
|
||||
CMD ["praisonai", "call"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Praison AI Call Docker
|
||||
|
||||
1. Build the Docker image:
|
||||
```
|
||||
docker build -t praisonai-call .
|
||||
```
|
||||
|
||||
2. Run the container:
|
||||
```
|
||||
docker run -d -p 8090:8090 praisonai-call -e OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
Make sure to replace your_api_key_here with your actual OpenAI API key.
|
||||
@@ -1,190 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
# Development Docker Compose Configuration
|
||||
# Extends the main docker-compose.yml with development-specific settings
|
||||
|
||||
services:
|
||||
# Development UI Service with hot reload
|
||||
ui-dev:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- ../src:/app/src:rw
|
||||
- ../examples:/app/examples:rw
|
||||
- ../docs:/app/docs:ro
|
||||
ports:
|
||||
- "${UI_PORT:-8082}:8082"
|
||||
environment:
|
||||
FLASK_ENV: development
|
||||
CHAINLIT_PORT: "8082"
|
||||
CHAINLIT_HOST: "0.0.0.0"
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
CHAINLIT_AUTH_SECRET: ${CHAINLIT_AUTH_SECRET}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_DB_DIR: /root/.praison
|
||||
DEBUG: "true"
|
||||
DEV_MODE: "true"
|
||||
PYTHONPATH: /app/src
|
||||
command: praisonai ui --host 0.0.0.0 --reload
|
||||
networks:
|
||||
- praison_network
|
||||
depends_on:
|
||||
- database
|
||||
|
||||
# Development Chat Service
|
||||
chat-dev:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- ../src:/app/src:rw
|
||||
- ../examples:/app/examples:rw
|
||||
ports:
|
||||
- "${CHAT_PORT:-8083}:8083"
|
||||
environment:
|
||||
CHAINLIT_PORT: "8083"
|
||||
CHAINLIT_HOST: "0.0.0.0"
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
CHAINLIT_AUTH_SECRET: ${CHAINLIT_AUTH_SECRET}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_DB_DIR: /root/.praison
|
||||
DEBUG: "true"
|
||||
DEV_MODE: "true"
|
||||
PYTHONPATH: /app/src
|
||||
command: praisonai chat --host 0.0.0.0 --port 8083 --reload
|
||||
networks:
|
||||
- praison_network
|
||||
depends_on:
|
||||
- database
|
||||
|
||||
# Development API Service with hot reload
|
||||
api-dev:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- ../src:/app/src:rw
|
||||
- ../examples:/app/examples:rw
|
||||
ports:
|
||||
- "${API_PORT:-8080}:8080"
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
FLASK_ENV: development
|
||||
DEBUG: "true"
|
||||
DEV_MODE: "true"
|
||||
PYTHONPATH: /app/src
|
||||
command: python -m flask --app api run --host 0.0.0.0 --port 8080 --debug
|
||||
networks:
|
||||
- praison_network
|
||||
depends_on:
|
||||
- database
|
||||
|
||||
# Development Jupyter Notebook Service
|
||||
jupyter:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- ../src:/app/src:rw
|
||||
- ../examples:/app/examples:rw
|
||||
- ../docs:/app/docs:rw
|
||||
ports:
|
||||
- "8888:8888"
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
JUPYTER_ENABLE_LAB: "yes"
|
||||
PYTHONPATH: /app/src
|
||||
command: jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --allow-root --LabApp.token='' --LabApp.password=''
|
||||
networks:
|
||||
- praison_network
|
||||
|
||||
# Development Testing Service
|
||||
test:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
volumes:
|
||||
- ../src:/app/src:rw
|
||||
- ../tests:/app/tests:rw
|
||||
- praison_data:/root/.praison
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
PYTHONPATH: /app/src
|
||||
PYTEST_CURRENT_TEST: "true"
|
||||
command: python -m pytest tests/ -v
|
||||
networks:
|
||||
- praison_network
|
||||
profiles:
|
||||
- test
|
||||
|
||||
# Standalone database for development
|
||||
database:
|
||||
security_opt:
|
||||
- "no-new-privileges:true"
|
||||
image: postgres:15-alpine
|
||||
environment:
|
||||
POSTGRES_DB: praisonai_dev
|
||||
POSTGRES_USER: praisonai
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-praisonai_dev_pass}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /var/tmp
|
||||
ports:
|
||||
- "5432:5432"
|
||||
networks:
|
||||
- praison_network
|
||||
profiles:
|
||||
- database
|
||||
|
||||
# Redis for caching and session management
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- praison_network
|
||||
profiles:
|
||||
- cache
|
||||
|
||||
# Development documentation server
|
||||
docs:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
volumes:
|
||||
- ../docs:/app/docs:rw
|
||||
- ../README.md:/app/README.md:ro
|
||||
ports:
|
||||
- "8000:8000"
|
||||
command: sh -c "cd docs && mkdocs serve --dev-addr 0.0.0.0:8000"
|
||||
networks:
|
||||
- praison_network
|
||||
profiles:
|
||||
- docs
|
||||
|
||||
volumes:
|
||||
praison_data:
|
||||
driver: local
|
||||
postgres_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
praison_network:
|
||||
driver: bridge
|
||||
@@ -1,99 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# PraisonAI UI Service
|
||||
ui:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.ui
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- .:/app
|
||||
ports:
|
||||
- "8082:8082"
|
||||
environment:
|
||||
FLASK_ENV: development
|
||||
CHAINLIT_PORT: "8082"
|
||||
CHAINLIT_HOST: "0.0.0.0"
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
CHAINLIT_AUTH_SECRET: ${CHAINLIT_AUTH_SECRET}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_DB_DIR: /root/.praison
|
||||
command: praisonai ui --host 0.0.0.0
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8082"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
- praison_network
|
||||
|
||||
# PraisonAI Chat Service
|
||||
chat:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.chat
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- .:/app
|
||||
ports:
|
||||
- "8083:8083"
|
||||
environment:
|
||||
CHAINLIT_PORT: "8083"
|
||||
CHAINLIT_HOST: "0.0.0.0"
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
CHAINLIT_AUTH_SECRET: ${CHAINLIT_AUTH_SECRET}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_CONFIG_DIR: /root/.praison
|
||||
CHAINLIT_DB_DIR: /root/.praison
|
||||
command: praisonai chat --host 0.0.0.0 --port 8083
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8083"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
- praison_network
|
||||
|
||||
# PraisonAI API Service
|
||||
api:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
- praison_network
|
||||
|
||||
# PraisonAI Agents Service (standalone)
|
||||
agents:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.praisonaiagents
|
||||
volumes:
|
||||
- praison_data:/root/.praison
|
||||
- ./examples:/app/examples
|
||||
environment:
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
PRAISON_CONFIG_DIR: /root/.praison
|
||||
networks:
|
||||
- praison_network
|
||||
|
||||
volumes:
|
||||
praison_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
praison_network:
|
||||
driver: bridge
|
||||
@@ -1,289 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PraisonAI Docker Quick Start Script
|
||||
# This script helps users quickly get started with PraisonAI using Docker
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if Docker is installed
|
||||
check_docker() {
|
||||
if ! command -v docker &> /dev/null; then
|
||||
print_error "Docker is not installed. Please install Docker first:"
|
||||
echo " - Visit: https://docs.docker.com/get-docker/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
print_error "Docker Compose is not available. Please install Docker Compose:"
|
||||
echo " - Visit: https://docs.docker.com/compose/install/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "Docker and Docker Compose are available"
|
||||
}
|
||||
|
||||
# Check if .env file exists, create from template if not
|
||||
setup_environment() {
|
||||
if [ ! -f ".env" ]; then
|
||||
print_status "Creating .env file from template..."
|
||||
if [ -f ".env.template" ]; then
|
||||
cp .env.template .env
|
||||
else
|
||||
cat > .env << EOF
|
||||
# PraisonAI Docker Environment Configuration
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
CHAINLIT_AUTH_SECRET=$(openssl rand -hex 32 2>/dev/null || echo "your_chainlit_secret_here")
|
||||
FLASK_ENV=development
|
||||
CHAINLIT_HOST=0.0.0.0
|
||||
UI_PORT=8082
|
||||
CHAT_PORT=8083
|
||||
API_PORT=8080
|
||||
EOF
|
||||
fi
|
||||
print_warning "Please edit .env file and add your API keys before continuing"
|
||||
print_status "Required: OPENAI_API_KEY"
|
||||
read -p "Press Enter to continue after updating .env file..."
|
||||
else
|
||||
print_success ".env file already exists"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to show menu
|
||||
show_menu() {
|
||||
echo ""
|
||||
echo "=== PraisonAI Docker Quick Start ==="
|
||||
echo ""
|
||||
echo "1) Start UI Service (Web Interface) - Port 8082"
|
||||
echo "2) Start Chat Service - Port 8083"
|
||||
echo "3) Start API Service - Port 8080"
|
||||
echo "4) Start All Services (Recommended)"
|
||||
echo "5) Start Development Environment"
|
||||
echo "6) View Service Status"
|
||||
echo "7) View Logs"
|
||||
echo "8) Stop All Services"
|
||||
echo "9) Pull Latest Images"
|
||||
echo "10) Clean Up (Remove containers and volumes)"
|
||||
echo "11) Show Service URLs"
|
||||
echo "0) Exit"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Start specific service
|
||||
start_service() {
|
||||
local service=$1
|
||||
print_status "Starting $service service..."
|
||||
docker-compose up -d $service
|
||||
print_success "$service service started"
|
||||
}
|
||||
|
||||
# Start all services
|
||||
start_all_services() {
|
||||
print_status "Starting all PraisonAI services..."
|
||||
docker-compose up -d
|
||||
print_success "All services started"
|
||||
show_service_urls
|
||||
}
|
||||
|
||||
# Start development environment
|
||||
start_dev_environment() {
|
||||
print_status "Starting development environment..."
|
||||
if [ -f "docker-compose.dev.yml" ]; then
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ui-dev chat-dev api-dev
|
||||
print_success "Development environment started"
|
||||
echo ""
|
||||
echo "Development services available:"
|
||||
echo " - UI (with hot reload): http://localhost:${UI_PORT:-8082}"
|
||||
echo " - Chat (with hot reload): http://localhost:${CHAT_PORT:-8083}"
|
||||
echo " - API (with hot reload): http://localhost:${API_PORT:-8080}"
|
||||
echo " - Jupyter Lab: http://localhost:8888"
|
||||
echo ""
|
||||
else
|
||||
print_warning "Development configuration not found, starting regular services"
|
||||
start_all_services
|
||||
fi
|
||||
}
|
||||
|
||||
# Show service status
|
||||
show_status() {
|
||||
print_status "Service Status:"
|
||||
docker-compose ps
|
||||
}
|
||||
|
||||
# Show logs
|
||||
show_logs() {
|
||||
echo ""
|
||||
echo "Which service logs would you like to view?"
|
||||
echo "1) All services"
|
||||
echo "2) UI service"
|
||||
echo "3) Chat service"
|
||||
echo "4) API service"
|
||||
echo "5) Agents service"
|
||||
read -p "Enter choice (1-5): " log_choice
|
||||
|
||||
case $log_choice in
|
||||
1) docker-compose logs -f ;;
|
||||
2) docker-compose logs -f ui ;;
|
||||
3) docker-compose logs -f chat ;;
|
||||
4) docker-compose logs -f api ;;
|
||||
5) docker-compose logs -f agents ;;
|
||||
*) print_error "Invalid choice" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Stop all services
|
||||
stop_services() {
|
||||
print_status "Stopping all services..."
|
||||
docker-compose down
|
||||
print_success "All services stopped"
|
||||
}
|
||||
|
||||
# Pull latest images
|
||||
pull_images() {
|
||||
print_status "Pulling latest images..."
|
||||
docker-compose pull
|
||||
print_success "Images updated"
|
||||
}
|
||||
|
||||
# Clean up
|
||||
cleanup() {
|
||||
print_warning "This will remove all containers, networks, and volumes"
|
||||
read -p "Are you sure? (y/N): " confirm
|
||||
if [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]]; then
|
||||
print_status "Cleaning up..."
|
||||
docker-compose down -v --remove-orphans
|
||||
docker system prune -f
|
||||
print_success "Cleanup completed"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show service URLs
|
||||
show_service_urls() {
|
||||
local ui_port=${UI_PORT:-8082}
|
||||
local chat_port=${CHAT_PORT:-8083}
|
||||
local api_port=${API_PORT:-8080}
|
||||
|
||||
echo ""
|
||||
echo "=== Service URLs ==="
|
||||
echo "UI Service: http://localhost:$ui_port"
|
||||
echo "Chat Service: http://localhost:$chat_port"
|
||||
echo "API Service: http://localhost:$api_port"
|
||||
echo "API Health: http://localhost:$api_port/health"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Check system requirements
|
||||
check_requirements() {
|
||||
print_status "Checking system requirements..."
|
||||
|
||||
# Check available memory
|
||||
if command -v free &> /dev/null; then
|
||||
available_ram=$(free -m | awk 'NR==2{printf "%.1f", $7/1024}')
|
||||
print_status "Available RAM: ${available_ram}GB"
|
||||
if (( $(echo "$available_ram < 2.0" | bc -l 2>/dev/null || echo "1") )); then
|
||||
print_warning "Low available memory. Recommended: 2GB+ available RAM"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check disk space
|
||||
if command -v df &> /dev/null; then
|
||||
available_space=$(df -BG . | awk 'NR==2{print $4}' | sed 's/G//')
|
||||
print_status "Available disk space: ${available_space}GB"
|
||||
if [ "$available_space" -lt 5 ]; then
|
||||
print_warning "Low disk space. Recommended: 5GB+ available space"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
clear
|
||||
echo "🤖 Welcome to PraisonAI Docker Setup!"
|
||||
echo ""
|
||||
|
||||
# Check prerequisites
|
||||
check_docker
|
||||
check_requirements
|
||||
setup_environment
|
||||
|
||||
# Main loop
|
||||
while true; do
|
||||
show_menu
|
||||
read -p "Enter your choice (0-11): " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
start_service "ui"
|
||||
echo "UI Service: http://localhost:${UI_PORT:-8082}"
|
||||
;;
|
||||
2)
|
||||
start_service "chat"
|
||||
echo "Chat Service: http://localhost:${CHAT_PORT:-8083}"
|
||||
;;
|
||||
3)
|
||||
start_service "api"
|
||||
echo "API Service: http://localhost:${API_PORT:-8080}"
|
||||
;;
|
||||
4)
|
||||
start_all_services
|
||||
;;
|
||||
5)
|
||||
start_dev_environment
|
||||
;;
|
||||
6)
|
||||
show_status
|
||||
;;
|
||||
7)
|
||||
show_logs
|
||||
;;
|
||||
8)
|
||||
stop_services
|
||||
;;
|
||||
9)
|
||||
pull_images
|
||||
;;
|
||||
10)
|
||||
cleanup
|
||||
;;
|
||||
11)
|
||||
show_service_urls
|
||||
;;
|
||||
0)
|
||||
print_success "Thank you for using PraisonAI!"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Invalid choice. Please try again."
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
read -p "Press Enter to continue..."
|
||||
done
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
@@ -1,5 +0,0 @@
|
||||
framework: praisonaiagents
|
||||
services:
|
||||
- ui
|
||||
- chat
|
||||
- realtime
|
||||
@@ -1,3 +0,0 @@
|
||||
code:
|
||||
ignore_files:
|
||||
- "*/__pycache__/*"
|
||||
@@ -1,2 +0,0 @@
|
||||
*.txt
|
||||
*.csv
|
||||
@@ -1,83 +0,0 @@
|
||||
# PraisonAI Examples
|
||||
|
||||
This folder contains examples for PraisonAI. For detailed documentation, visit [docs.praison.ai](https://docs.praison.ai).
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
examples/
|
||||
├── python/ # Python examples
|
||||
│ ├── agents/ # Agent examples (single, multi, router, etc.)
|
||||
│ ├── workflows/ # Workflow patterns (routing, parallel, loop)
|
||||
│ ├── tools/ # Custom tools examples
|
||||
│ ├── mcp/ # MCP protocol examples
|
||||
│ ├── memory/ # Memory and sessions
|
||||
│ ├── code/ # Code editing and external CLI tools
|
||||
│ └── ...
|
||||
├── serve/ # Server and endpoints examples
|
||||
├── yaml/ # YAML workflow examples
|
||||
└── cookbooks/ # Complete use-case examples
|
||||
```
|
||||
|
||||
## Quick Links
|
||||
|
||||
| Category | Examples | Docs |
|
||||
|----------|----------|------|
|
||||
| **Consolidated Params** | [consolidated_params/](consolidated_params/) | [📖](https://docs.praison.ai/api/consolidated-params) |
|
||||
| **Agents** | [python/agents/](python/agents/) | [📖](https://docs.praison.ai/concepts/agents) |
|
||||
| **Workflows** | [python/workflows/](python/workflows/) | [📖](https://docs.praison.ai/features/workflows) |
|
||||
| **Model Router** | [python/agents/router-agent-cost-optimization.py](python/agents/router-agent-cost-optimization.py) | [📖](https://docs.praison.ai/features/model-router) |
|
||||
| **MCP** | [python/mcp/](python/mcp/) | [📖](https://docs.praison.ai/mcp) |
|
||||
| **Memory** | [python/memory/](python/memory/) | [📖](https://docs.praison.ai/concepts/memory) |
|
||||
| **Tools** | [python/tools/](python/tools/) | [📖](https://docs.praison.ai/tools) |
|
||||
| **Code** | [python/code/](python/code/) | [📖](https://docs.praison.ai/code) |
|
||||
| **YAML** | [yaml/](yaml/) | [📖](https://docs.praison.ai/features/yaml-workflows) |
|
||||
| **Doctor** | [doctor/](doctor/) | [📖](https://docs.praison.ai/cli/doctor) |
|
||||
| **Serve** | [serve/](serve/) | [📖](https://docs.praison.ai/cli/serve) |
|
||||
| **Endpoints** | [serve/](serve/) | [📖](https://docs.praison.ai/cli/endpoints) |
|
||||
|
||||
## Consolidated Params Examples
|
||||
|
||||
Agent-centric API with unified parameter resolution. Precedence: Instance > Config > Array > Dict > String > Bool > Default
|
||||
|
||||
| Example | Description |
|
||||
|---------|-------------|
|
||||
| [basic_agent.py](consolidated_params/basic_agent.py) | Minimal agent with memory |
|
||||
| [basic_agents.py](consolidated_params/basic_agents.py) | Multi-agent with memory+planning |
|
||||
| [basic_workflow.py](consolidated_params/basic_workflow.py) | Workflow with consolidated params |
|
||||
| [basic_memory.py](consolidated_params/basic_memory.py) | Memory presets (file, redis, postgres) |
|
||||
| [basic_guardrails.py](consolidated_params/basic_guardrails.py) | Guardrails with callable or config |
|
||||
| [basic_workflow_agentlike.py](consolidated_params/basic_workflow_agentlike.py) | Workflow with agent-like params |
|
||||
| [basic_step_override.py](consolidated_params/basic_step_override.py) | Step-level override of workflow defaults |
|
||||
| [advanced_workflow_full_features.py](consolidated_params/advanced_workflow_full_features.py) | All consolidated params |
|
||||
|
||||
## Serve Examples
|
||||
|
||||
| Example | Description | CLI Command |
|
||||
|---------|-------------|-------------|
|
||||
| [unified_server.py](serve/unified_server.py) | All providers in one server | `praisonai serve unified` |
|
||||
| [agent_as_api_single.py](serve/agent_as_api_single.py) | Single agent HTTP API | `praisonai serve agents` |
|
||||
| [agents_as_api_router.py](serve/agents_as_api_router.py) | Multi-agent router API | `praisonai serve agents` |
|
||||
| [a2a_server_client.py](serve/a2a_server_client.py) | A2A protocol server | `praisonai serve a2a` |
|
||||
| [a2u_events_stream.py](serve/a2u_events_stream.py) | A2U event stream | `praisonai serve a2u` |
|
||||
| [mcp_http_server.py](serve/mcp_http_server.py) | MCP HTTP server | `praisonai serve mcp` |
|
||||
| [tools_as_mcp_server.py](serve/tools_as_mcp_server.py) | Tools as MCP server | `praisonai serve tools` |
|
||||
| [agent_launch_modes.py](serve/agent_launch_modes.py) | Agent.launch() API | Python only |
|
||||
| [endpoints_unified_client.py](serve/endpoints_unified_client.py) | Unified client | `praisonai endpoints` |
|
||||
|
||||
## Running Examples
|
||||
|
||||
```bash
|
||||
# Install PraisonAI
|
||||
pip install praisonai
|
||||
|
||||
# Set API key
|
||||
export OPENAI_API_KEY=your_key_here
|
||||
|
||||
# Run an example
|
||||
python examples/python/agents/single-agent.py
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
See the main [README.md](../README.md#-cli--no-code-interface) for all CLI commands.
|
||||
@@ -1,137 +0,0 @@
|
||||
# ACP (Agent Client Protocol) Examples
|
||||
|
||||
This directory contains examples for using PraisonAI with the Agent Client Protocol (ACP).
|
||||
|
||||
## What is ACP?
|
||||
|
||||
The Agent Client Protocol (ACP) is a standardized JSON-RPC 2.0 protocol that allows code editors and IDEs to communicate with AI coding agents. It enables seamless integration between PraisonAI and editors like:
|
||||
|
||||
- **Zed**
|
||||
- **JetBrains IDEs** (IntelliJ, PyCharm, WebStorm, etc.)
|
||||
- **VSCode** (via extensions)
|
||||
- **Toad**
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Start ACP server with defaults
|
||||
praisonai acp
|
||||
|
||||
# Enable debug logging
|
||||
praisonai acp --debug
|
||||
|
||||
# Allow file writes
|
||||
praisonai acp --allow-write
|
||||
|
||||
# Use a specific model
|
||||
praisonai acp -m gpt-4o
|
||||
|
||||
# Resume last session
|
||||
praisonai acp --resume --last
|
||||
```
|
||||
|
||||
### Python API
|
||||
|
||||
```python
|
||||
from praisonai.acp import serve
|
||||
|
||||
serve(
|
||||
workspace=".",
|
||||
debug=True,
|
||||
allow_write=True,
|
||||
)
|
||||
```
|
||||
|
||||
## Editor Configuration
|
||||
|
||||
### Zed
|
||||
|
||||
Add to `~/.config/zed/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"PraisonAI": {
|
||||
"command": "praisonai",
|
||||
"args": ["acp"],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JetBrains
|
||||
|
||||
Add to `~/.jetbrains/acp.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"PraisonAI": {
|
||||
"command": "praisonai",
|
||||
"args": ["acp"],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Toad
|
||||
|
||||
```bash
|
||||
toad acp "praisonai acp"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
- `basic_acp_server.py` - Simple ACP server with default settings
|
||||
- `custom_agent_acp.py` - ACP server with a custom agent
|
||||
|
||||
## CLI Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-w, --workspace` | Workspace root directory |
|
||||
| `-a, --agent` | Agent name or config file |
|
||||
| `-m, --model` | LLM model to use |
|
||||
| `-r, --resume` | Resume session by ID |
|
||||
| `--last` | Resume the last session |
|
||||
| `--approve` | Approval mode: manual, auto, scoped |
|
||||
| `--read-only` | Read-only mode (default) |
|
||||
| `--allow-write` | Allow file writes |
|
||||
| `--allow-shell` | Allow shell commands |
|
||||
| `--debug` | Enable debug logging |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install praisonai[acp]
|
||||
```
|
||||
|
||||
## Protocol Details
|
||||
|
||||
ACP uses JSON-RPC 2.0 over stdio:
|
||||
- **stdin**: Receives JSON-RPC requests from the client
|
||||
- **stdout**: Sends JSON-RPC responses to the client
|
||||
- **stderr**: Debug/log output (never pollutes stdout)
|
||||
|
||||
### Supported Methods
|
||||
|
||||
**Agent Methods (client → agent):**
|
||||
- `initialize` - Negotiate protocol version and capabilities
|
||||
- `authenticate` - Optional authentication
|
||||
- `session/new` - Create a new session
|
||||
- `session/load` - Load an existing session
|
||||
- `session/prompt` - Send user message
|
||||
- `session/cancel` - Cancel ongoing operations
|
||||
- `session/set_mode` - Change operating mode
|
||||
|
||||
**Client Methods (agent → client):**
|
||||
- `session/update` - Send progress updates
|
||||
- `session/request_permission` - Request user approval
|
||||
- `fs/read_text_file` - Read file contents
|
||||
- `fs/write_text_file` - Write file contents
|
||||
- `terminal/create` - Create terminal
|
||||
- `terminal/output` - Get terminal output
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic ACP Server Example
|
||||
|
||||
This example demonstrates how to run PraisonAI as an ACP server
|
||||
that can be connected to by IDEs like Zed, JetBrains, VSCode, or Toad.
|
||||
|
||||
Usage:
|
||||
# Run from command line
|
||||
praisonai acp
|
||||
|
||||
# Or run this script directly
|
||||
python basic_acp_server.py
|
||||
"""
|
||||
|
||||
from praisonai.acp import serve
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Start ACP server with default settings
|
||||
# This will listen on stdin/stdout for JSON-RPC messages
|
||||
serve(
|
||||
workspace=".", # Current directory as workspace
|
||||
agent="default", # Use default agent
|
||||
debug=False, # Set to True for debug logging to stderr
|
||||
read_only=True, # Safe by default
|
||||
approval_mode="manual", # Require approval for actions
|
||||
)
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Custom Agent ACP Server Example
|
||||
|
||||
This example shows how to run a custom PraisonAI agent as an ACP server.
|
||||
|
||||
Usage:
|
||||
python custom_agent_acp.py
|
||||
"""
|
||||
|
||||
from praisonai.acp import serve, ACPServer, ACPConfig
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Create a custom agent
|
||||
agent = Agent(
|
||||
name="CodeAssistant",
|
||||
instructions="""You are an expert coding assistant. You help users:
|
||||
- Write clean, efficient code
|
||||
- Debug issues
|
||||
- Explain complex concepts
|
||||
- Review code for best practices
|
||||
|
||||
Always provide clear explanations with your code suggestions.""",
|
||||
model="gpt-4o-mini",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create config
|
||||
config = ACPConfig(
|
||||
workspace=".",
|
||||
debug=True,
|
||||
allow_write=True, # Allow file modifications
|
||||
approval_mode="manual",
|
||||
)
|
||||
|
||||
# Create server with custom agent
|
||||
server = ACPServer(config=config, agent=agent)
|
||||
|
||||
# Run the server
|
||||
serve(
|
||||
workspace=".",
|
||||
debug=True,
|
||||
allow_write=True,
|
||||
approval_mode="manual",
|
||||
)
|
||||
@@ -1,244 +0,0 @@
|
||||
"""
|
||||
Agent-Centric API Example
|
||||
|
||||
Demonstrates the consolidated feature parameters for PraisonAI Agents.
|
||||
These params provide a cleaner, more intuitive API with progressive disclosure.
|
||||
|
||||
Precedence Rule: Instance > Config > Array > String > Bool > Default
|
||||
|
||||
Consolidated Params Support:
|
||||
- memory: bool | str URL | str preset | [preset, overrides] | MemoryConfig | Instance
|
||||
- knowledge: bool | str path | [sources...] | KnowledgeConfig | Instance
|
||||
- planning: bool | str LLM | [preset, overrides] | PlanningConfig
|
||||
- reflection: bool | str preset | [preset, overrides] | ReflectionConfig
|
||||
- guardrails: bool | callable | str prompt | GuardrailConfig
|
||||
- web: bool | str provider | [provider, overrides] | WebConfig
|
||||
- output: str preset | [preset, overrides] | OutputConfig
|
||||
- execution: str preset | [preset, overrides] | ExecutionConfig
|
||||
"""
|
||||
|
||||
from praisonaiagents import (
|
||||
Agent,
|
||||
# Config classes for progressive disclosure
|
||||
MemoryConfig,
|
||||
KnowledgeConfig,
|
||||
PlanningConfig,
|
||||
ReflectionConfig,
|
||||
GuardrailConfig,
|
||||
WebConfig,
|
||||
)
|
||||
|
||||
|
||||
def example_simple_enable():
|
||||
"""Example 1: Simple boolean enable (easiest)"""
|
||||
print("\n=== Example 1: Simple Boolean Enable ===")
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
memory=True, # Enable memory with defaults
|
||||
reflection=True, # Enable self-reflection
|
||||
web=True, # Enable web search + fetch
|
||||
)
|
||||
print(f"Agent created with memory={agent._memory_instance is not None}")
|
||||
print(f" self_reflect={agent.self_reflect}")
|
||||
print(f" web_search={agent.web_search}")
|
||||
|
||||
|
||||
def example_with_config():
|
||||
"""Example 2: Using config objects (more control)"""
|
||||
print("\n=== Example 2: Using Config Objects ===")
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant",
|
||||
memory=MemoryConfig(
|
||||
backend="file",
|
||||
user_id="researcher_001",
|
||||
auto_memory=True,
|
||||
),
|
||||
reflection=ReflectionConfig(
|
||||
min_iterations=1,
|
||||
max_iterations=5,
|
||||
llm="gpt-4o-mini",
|
||||
),
|
||||
web=WebConfig(
|
||||
search=True,
|
||||
fetch=False, # Only search, no full page fetch
|
||||
max_results=3,
|
||||
),
|
||||
)
|
||||
print(f"Agent created with user_id={agent.user_id}")
|
||||
print(f" {agent.max_reflect}")
|
||||
print(f" web_search={agent.web_search}, web_fetch={agent.web_fetch}")
|
||||
|
||||
|
||||
def example_guardrails():
|
||||
"""Example 3: Guardrails with callable or config"""
|
||||
print("\n=== Example 3: Guardrails ===")
|
||||
|
||||
# Option A: Direct callable
|
||||
def my_validator(output):
|
||||
"""Simple validator that checks response length."""
|
||||
is_valid = len(output.raw) < 1000
|
||||
return (is_valid, output if is_valid else "Response too long")
|
||||
|
||||
agent_a = Agent(
|
||||
instructions="Be concise",
|
||||
guardrails=my_validator, # Direct callable
|
||||
)
|
||||
print(f"Agent A: guardrail is callable = {callable(agent_a.guardrail)}")
|
||||
|
||||
# Option B: Using GuardrailConfig
|
||||
agent_b = Agent(
|
||||
instructions="Be helpful and safe",
|
||||
guardrails=GuardrailConfig(
|
||||
llm_validator="Ensure the response is helpful, accurate, and safe",
|
||||
max_retries=3,
|
||||
),
|
||||
)
|
||||
print(f"Agent B: guardrail = {agent_b.guardrail}")
|
||||
print(f" max_retries = {agent_b.max_guardrail_retries}")
|
||||
|
||||
|
||||
def example_planning():
|
||||
"""Example 4: Planning mode"""
|
||||
print("\n=== Example 4: Planning Mode ===")
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a project planner",
|
||||
planning=PlanningConfig(
|
||||
reasoning=True,
|
||||
read_only=True, # Plan mode - only read operations
|
||||
),
|
||||
)
|
||||
print(f"Agent created with planning={agent.planning}")
|
||||
print(f" plan_mode={agent.plan_mode}")
|
||||
print(f" planning_reasoning={agent.planning_reasoning}")
|
||||
|
||||
|
||||
def example_backward_compatible():
|
||||
"""Example 5: Using new consolidated params"""
|
||||
print("\n=== Example 5: Consolidated Params ===")
|
||||
|
||||
# New consolidated params
|
||||
agent = Agent(
|
||||
instructions="Test agent",
|
||||
reflection=True,
|
||||
web=WebConfig(search=True, fetch=False),
|
||||
)
|
||||
print(f"Consolidated params work: self_reflect={agent.self_reflect}")
|
||||
print(f" web_search={agent.web_search}, web_fetch={agent.web_fetch}")
|
||||
|
||||
|
||||
def example_real_chat():
|
||||
"""Example 6: Real chat with new API"""
|
||||
print("\n=== Example 6: Real Chat ===")
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful math tutor. Be concise.",
|
||||
reflection=False,
|
||||
web=False,
|
||||
)
|
||||
|
||||
response = agent.start("What is the square root of 144?")
|
||||
print(f"Response: {response}")
|
||||
|
||||
|
||||
def example_string_presets():
|
||||
"""Example 7: String presets (NEW - user-friendly shortcuts)"""
|
||||
print("\n=== Example 7: String Presets ===")
|
||||
|
||||
# Output presets: minimal, normal, verbose, debug, silent
|
||||
agent = Agent(
|
||||
instructions="You are a verbose assistant",
|
||||
output="verbose", # String preset for output
|
||||
execution="fast", # String preset for execution
|
||||
web="tavily", # String preset for web provider
|
||||
)
|
||||
print(f"Output preset 'verbose': verbose={agent.verbose}, metrics={agent.metrics}")
|
||||
print(f"Execution preset 'fast': max_iter={agent.max_iter}")
|
||||
print(f"Web preset 'tavily': web_search={agent.web_search}")
|
||||
|
||||
|
||||
def example_array_overrides():
|
||||
"""Example 8: Array with overrides (NEW - preset + customization)"""
|
||||
print("\n=== Example 8: Array Overrides ===")
|
||||
|
||||
# Array format: [preset, {overrides}]
|
||||
agent = Agent(
|
||||
instructions="You are a streaming assistant",
|
||||
output=["verbose", {"stream": True}], # Verbose preset + enable streaming
|
||||
execution=["fast", {"max_iter": 15}], # Fast preset + custom max_iter
|
||||
)
|
||||
print(f"Output array override: verbose={agent.verbose}, stream={agent.stream}")
|
||||
print(f"Execution array override: max_iter={agent.max_iter}")
|
||||
|
||||
|
||||
def example_url_parsing():
|
||||
"""Example 9: URL parsing for memory (NEW - connection strings)"""
|
||||
print("\n=== Example 9: URL Parsing ===")
|
||||
|
||||
# Memory supports URL connection strings
|
||||
# Supported: postgresql://, redis://, sqlite:///, mongodb://
|
||||
agent = Agent(
|
||||
instructions="You are a database-backed assistant",
|
||||
memory="postgresql://postgres:password@localhost:5432/praisonai",
|
||||
)
|
||||
print("Memory URL parsed: memory enabled")
|
||||
|
||||
# Also works with presets
|
||||
agent2 = Agent(
|
||||
instructions="You are a redis-backed assistant",
|
||||
memory="redis", # Preset name
|
||||
)
|
||||
print("Memory preset 'redis': configured")
|
||||
|
||||
|
||||
def example_knowledge_sources():
|
||||
"""Example 10: Knowledge with sources (list and string)"""
|
||||
print("\n=== Example 10: Knowledge Sources ===")
|
||||
|
||||
# Single path string
|
||||
agent1 = Agent(
|
||||
instructions="You are a document assistant",
|
||||
knowledge="docs/", # Single path
|
||||
)
|
||||
print(f"Knowledge single path: knowledge={agent1.knowledge}")
|
||||
|
||||
# List of sources
|
||||
agent2 = Agent(
|
||||
instructions="You are a multi-source assistant",
|
||||
knowledge=["docs/", "data.pdf", "https://example.com/api"],
|
||||
)
|
||||
print(f"Knowledge list: knowledge={agent2.knowledge}")
|
||||
|
||||
# With config for advanced settings
|
||||
agent3 = Agent(
|
||||
instructions="You are an advanced RAG assistant",
|
||||
knowledge=KnowledgeConfig(
|
||||
sources=["docs/"],
|
||||
retrieval_k=10,
|
||||
rerank=True,
|
||||
),
|
||||
)
|
||||
print("Knowledge config: configured with rerank")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("PraisonAI Agent-Centric API Examples")
|
||||
print("=" * 60)
|
||||
|
||||
example_simple_enable()
|
||||
example_with_config()
|
||||
example_guardrails()
|
||||
example_planning()
|
||||
example_backward_compatible()
|
||||
example_real_chat()
|
||||
example_string_presets()
|
||||
example_array_overrides()
|
||||
example_url_parsing()
|
||||
example_knowledge_sources()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("All examples completed!")
|
||||
print("=" * 60)
|
||||
@@ -1,135 +0,0 @@
|
||||
"""
|
||||
Agent-Centric Tools Example
|
||||
|
||||
This example demonstrates how to use LSP/ACP-powered tools that make
|
||||
the Agent the central orchestrator for file operations.
|
||||
|
||||
How to run:
|
||||
export OPENAI_API_KEY=your_key
|
||||
python examples/agent_tools/agent_centric_example.py
|
||||
|
||||
Prerequisites:
|
||||
pip install praisonai praisonaiagents
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
# Add paths for development
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai-agents'))
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("AGENT-CENTRIC TOOLS EXAMPLE")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Create temporary workspace
|
||||
workspace = tempfile.mkdtemp(prefix="praisonai_example_")
|
||||
print(f"Workspace: {workspace}")
|
||||
print()
|
||||
|
||||
try:
|
||||
# Import after path setup
|
||||
from praisonai.cli.features import (
|
||||
create_agent_centric_tools,
|
||||
InteractiveRuntime,
|
||||
RuntimeConfig
|
||||
)
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# 1. Create runtime with ACP enabled
|
||||
print("1. Creating InteractiveRuntime with ACP...")
|
||||
config = RuntimeConfig(
|
||||
workspace=workspace,
|
||||
lsp_enabled=False, # Skip LSP for this example
|
||||
acp_enabled=True,
|
||||
approval_mode="auto" # Auto-approve for demo
|
||||
)
|
||||
runtime = InteractiveRuntime(config)
|
||||
status = await runtime.start()
|
||||
|
||||
print(f" ACP ready: {runtime.acp_ready}")
|
||||
print(f" Read-only: {runtime.read_only}")
|
||||
print()
|
||||
|
||||
# 2. Create agent-centric tools
|
||||
print("2. Creating agent-centric tools...")
|
||||
tools = create_agent_centric_tools(runtime)
|
||||
print(f" Tools created: {len(tools)}")
|
||||
for tool in tools:
|
||||
print(f" - {tool.__name__}")
|
||||
print()
|
||||
|
||||
# 3. Create Agent with ACP-powered tools
|
||||
print("3. Creating Agent with ACP-powered tools...")
|
||||
agent = Agent(
|
||||
name="FileAgent",
|
||||
instructions="""You help create and manage files.
|
||||
Use acp_create_file to create files.
|
||||
Use read_file to read files.
|
||||
Use list_files to list directory contents.""",
|
||||
tools=tools,
|
||||
output="verbose" # Use new consolidated param
|
||||
)
|
||||
print()
|
||||
|
||||
# 4. Ask agent to create a file
|
||||
print("4. Asking agent to create a Python file...")
|
||||
print("-" * 40)
|
||||
result = agent.start(
|
||||
"Create a Python file called calculator.py with add and subtract functions"
|
||||
)
|
||||
print("-" * 40)
|
||||
print(f"Agent response: {result[:200]}..." if len(str(result)) > 200 else f"Agent response: {result}")
|
||||
print()
|
||||
|
||||
# 5. Verify file was created
|
||||
print("5. Verifying file creation...")
|
||||
calc_path = os.path.join(workspace, "calculator.py")
|
||||
if os.path.exists(calc_path):
|
||||
print(f" ✓ File created at {calc_path}")
|
||||
with open(calc_path) as f:
|
||||
content = f.read()
|
||||
print(f" Content preview:")
|
||||
for line in content.split('\n')[:10]:
|
||||
print(f" {line}")
|
||||
else:
|
||||
# Check what files exist
|
||||
files = os.listdir(workspace)
|
||||
print(f" Files in workspace: {files}")
|
||||
for f in files:
|
||||
fp = os.path.join(workspace, f)
|
||||
if os.path.isfile(fp):
|
||||
print(f" ✓ Found: {f}")
|
||||
print()
|
||||
|
||||
# 6. Ask agent to read the file
|
||||
print("6. Asking agent to read the file...")
|
||||
print("-" * 40)
|
||||
result2 = agent.start("Read the calculator.py file and tell me what functions it has")
|
||||
print("-" * 40)
|
||||
print(f"Agent response: {result2[:300]}..." if len(str(result2)) > 300 else f"Agent response: {result2}")
|
||||
print()
|
||||
|
||||
# Cleanup
|
||||
await runtime.stop()
|
||||
|
||||
finally:
|
||||
# Clean up workspace
|
||||
shutil.rmtree(workspace, ignore_errors=True)
|
||||
print(f"Cleaned up workspace: {workspace}")
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print("EXAMPLE COMPLETE")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,35 +0,0 @@
|
||||
"""
|
||||
Async Runs Examples
|
||||
===================
|
||||
|
||||
This directory contains examples for the PraisonAI Async Jobs API.
|
||||
|
||||
Examples:
|
||||
---------
|
||||
1. sdk_submit_poll_result.py - Python SDK example for submit/poll/result
|
||||
2. cli_submit_poll_result.sh - Bash/curl example for submit/poll/result
|
||||
3. idempotency.sh - Demonstrates idempotency key usage
|
||||
4. cancel.sh - Demonstrates job cancellation
|
||||
5. sse_stream.sh - Demonstrates SSE streaming
|
||||
|
||||
Prerequisites:
|
||||
--------------
|
||||
1. Set OPENAI_API_KEY environment variable
|
||||
2. Start the jobs server:
|
||||
|
||||
cd /path/to/praisonai-package/src/praisonai
|
||||
python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factory
|
||||
|
||||
3. Run examples:
|
||||
|
||||
python sdk_submit_poll_result.py
|
||||
bash cli_submit_poll_result.sh
|
||||
bash idempotency.sh
|
||||
bash cancel.sh
|
||||
bash sse_stream.sh
|
||||
|
||||
Environment Variables:
|
||||
----------------------
|
||||
- OPENAI_API_KEY: Required for agent execution
|
||||
- PRAISONAI_BASE_URL: API server URL (default: http://127.0.0.1:8005)
|
||||
"""
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
# CLI Example: Cancel a Running Job
|
||||
#
|
||||
# This example demonstrates how to cancel a running job.
|
||||
#
|
||||
# Requirements:
|
||||
# - Jobs server running on http://127.0.0.1:8005
|
||||
#
|
||||
# Usage:
|
||||
# bash cancel.sh
|
||||
|
||||
set -e
|
||||
|
||||
API_URL="${PRAISONAI_BASE_URL:-http://127.0.0.1:8005}"
|
||||
|
||||
echo "============================================================"
|
||||
echo "PraisonAI Job Cancellation Example"
|
||||
echo "============================================================"
|
||||
|
||||
# Submit a long-running job
|
||||
echo ""
|
||||
echo "1. Submitting a job..."
|
||||
RESPONSE=$(curl -s -X POST "$API_URL/api/v1/runs" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"prompt": "Write a detailed essay about the history of computing."}')
|
||||
|
||||
JOB_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
|
||||
echo " Job ID: $JOB_ID"
|
||||
|
||||
# Wait briefly
|
||||
echo ""
|
||||
echo "2. Waiting 1 second..."
|
||||
sleep 1
|
||||
|
||||
# Cancel the job
|
||||
echo ""
|
||||
echo "3. Cancelling job..."
|
||||
CANCEL_RESPONSE=$(curl -s -X POST "$API_URL/api/v1/runs/$JOB_ID/cancel")
|
||||
STATUS=$(echo "$CANCEL_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
|
||||
echo " Status after cancel: $STATUS"
|
||||
|
||||
# Verify cancellation
|
||||
echo ""
|
||||
echo "4. Verification:"
|
||||
if [ "$STATUS" = "cancelled" ]; then
|
||||
echo " ✓ SUCCESS: Job was cancelled"
|
||||
else
|
||||
echo " ℹ Job status: $STATUS (may have completed before cancel)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "Cancellation example completed!"
|
||||
echo "============================================================"
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/bin/bash
|
||||
# CLI Example: Submit, Poll, and Get Result for Async Jobs
|
||||
#
|
||||
# This example demonstrates how to use the PraisonAI CLI to:
|
||||
# 1. Submit an async job
|
||||
# 2. Check job status
|
||||
# 3. Get the result
|
||||
#
|
||||
# Requirements:
|
||||
# - OPENAI_API_KEY environment variable set
|
||||
# - Jobs server running on http://127.0.0.1:8005
|
||||
#
|
||||
# Usage:
|
||||
# bash cli_submit_poll_result.sh
|
||||
|
||||
set -e
|
||||
|
||||
API_URL="${PRAISONAI_BASE_URL:-http://127.0.0.1:8005}"
|
||||
|
||||
echo "============================================================"
|
||||
echo "PraisonAI Async Jobs CLI Example"
|
||||
echo "============================================================"
|
||||
|
||||
# Check if server is running
|
||||
echo ""
|
||||
echo "Checking server health..."
|
||||
if ! curl -s "$API_URL/health" > /dev/null 2>&1; then
|
||||
echo "✗ Server not available at $API_URL"
|
||||
echo " Start the server with: python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factory"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Server is healthy"
|
||||
|
||||
# 1. Submit a job
|
||||
echo ""
|
||||
echo "1. Submitting job..."
|
||||
RESPONSE=$(curl -s -X POST "$API_URL/api/v1/runs" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"prompt": "What is 2+2? Answer with just the number."}')
|
||||
|
||||
JOB_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
|
||||
echo " Job ID: $JOB_ID"
|
||||
|
||||
# 2. Poll for status
|
||||
echo ""
|
||||
echo "2. Polling for status..."
|
||||
while true; do
|
||||
STATUS_RESPONSE=$(curl -s "$API_URL/api/v1/runs/$JOB_ID")
|
||||
STATUS=$(echo "$STATUS_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
|
||||
PROGRESS=$(echo "$STATUS_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['progress']['percentage'])")
|
||||
|
||||
echo " Status: $STATUS | Progress: ${PROGRESS}%"
|
||||
|
||||
if [ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ] || [ "$STATUS" = "cancelled" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 3. Get result
|
||||
echo ""
|
||||
echo "3. Getting result..."
|
||||
if [ "$STATUS" = "succeeded" ]; then
|
||||
RESULT=$(curl -s "$API_URL/api/v1/runs/$JOB_ID/result")
|
||||
echo " Result: $(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['result'])")"
|
||||
echo " Duration: $(echo "$RESULT" | python3 -c "import sys,json; print(f\"{json.load(sys.stdin)['duration_seconds']:.2f}s\")")"
|
||||
else
|
||||
echo " Job did not succeed: $STATUS"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "Example completed!"
|
||||
echo "============================================================"
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/bin/bash
|
||||
# CLI Example: Idempotency Key Usage
|
||||
#
|
||||
# This example demonstrates how idempotency keys prevent duplicate job submissions.
|
||||
# Submitting the same request with the same idempotency key returns the existing job.
|
||||
#
|
||||
# Requirements:
|
||||
# - Jobs server running on http://127.0.0.1:8005
|
||||
#
|
||||
# Usage:
|
||||
# bash idempotency.sh
|
||||
|
||||
set -e
|
||||
|
||||
API_URL="${PRAISONAI_BASE_URL:-http://127.0.0.1:8005}"
|
||||
IDEM_KEY="example-idem-key-$(date +%s)"
|
||||
|
||||
echo "============================================================"
|
||||
echo "PraisonAI Idempotency Key Example"
|
||||
echo "============================================================"
|
||||
|
||||
# First submission
|
||||
echo ""
|
||||
echo "1. First submission with Idempotency-Key: $IDEM_KEY"
|
||||
RESPONSE1=$(curl -s -X POST "$API_URL/api/v1/runs" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Idempotency-Key: $IDEM_KEY" \
|
||||
-d '{"prompt": "What is 1+1?"}')
|
||||
|
||||
JOB_ID1=$(echo "$RESPONSE1" | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
|
||||
echo " Job ID: $JOB_ID1"
|
||||
|
||||
# Second submission with same key
|
||||
echo ""
|
||||
echo "2. Second submission with SAME Idempotency-Key: $IDEM_KEY"
|
||||
RESPONSE2=$(curl -s -X POST "$API_URL/api/v1/runs" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Idempotency-Key: $IDEM_KEY" \
|
||||
-d '{"prompt": "Different prompt but same key"}')
|
||||
|
||||
JOB_ID2=$(echo "$RESPONSE2" | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
|
||||
echo " Job ID: $JOB_ID2"
|
||||
|
||||
# Verify same job ID
|
||||
echo ""
|
||||
echo "3. Verification:"
|
||||
if [ "$JOB_ID1" = "$JOB_ID2" ]; then
|
||||
echo " ✓ SUCCESS: Same job ID returned (no duplicate created)"
|
||||
else
|
||||
echo " ✗ FAILURE: Different job IDs returned"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "Idempotency example completed!"
|
||||
echo "============================================================"
|
||||
@@ -1,121 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDK Example: Submit, Poll, and Get Result for Async Jobs
|
||||
|
||||
This example demonstrates how to use the PraisonAI Jobs SDK to:
|
||||
1. Submit an async job
|
||||
2. Poll for status
|
||||
3. Get the result
|
||||
|
||||
Requirements:
|
||||
- OPENAI_API_KEY environment variable set
|
||||
- Jobs server running on http://127.0.0.1:8005
|
||||
|
||||
Usage:
|
||||
python sdk_submit_poll_result.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import httpx
|
||||
|
||||
# Configuration
|
||||
API_URL = os.getenv("PRAISONAI_BASE_URL", "http://127.0.0.1:8005")
|
||||
POLL_INTERVAL = 2 # seconds
|
||||
|
||||
|
||||
def submit_job(prompt: str, idempotency_key: str = None) -> dict:
|
||||
"""Submit a job and return the response."""
|
||||
headers = {}
|
||||
if idempotency_key:
|
||||
headers["Idempotency-Key"] = idempotency_key
|
||||
|
||||
with httpx.Client(timeout=30.0) as client:
|
||||
response = client.post(
|
||||
f"{API_URL}/api/v1/runs",
|
||||
json={"prompt": prompt},
|
||||
headers=headers if headers else None
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_status(job_id: str) -> dict:
|
||||
"""Get job status."""
|
||||
with httpx.Client(timeout=30.0) as client:
|
||||
response = client.get(f"{API_URL}/api/v1/runs/{job_id}")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_result(job_id: str) -> dict:
|
||||
"""Get job result."""
|
||||
with httpx.Client(timeout=30.0) as client:
|
||||
response = client.get(f"{API_URL}/api/v1/runs/{job_id}/result")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def wait_for_completion(job_id: str, max_wait: int = 120) -> dict:
|
||||
"""Poll until job completes."""
|
||||
start = time.time()
|
||||
while time.time() - start < max_wait:
|
||||
status = get_status(job_id)
|
||||
print(f" Status: {status['status']} | Progress: {status['progress']['percentage']:.0f}%")
|
||||
|
||||
if status["status"] in ("succeeded", "failed", "cancelled"):
|
||||
return status
|
||||
|
||||
# Honor retry_after if present
|
||||
wait_time = status.get("retry_after") or POLL_INTERVAL
|
||||
time.sleep(wait_time)
|
||||
|
||||
raise TimeoutError(f"Job {job_id} did not complete within {max_wait}s")
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("PraisonAI Async Jobs SDK Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Check if server is running
|
||||
try:
|
||||
with httpx.Client(timeout=5.0) as client:
|
||||
health = client.get(f"{API_URL}/health")
|
||||
health.raise_for_status()
|
||||
print(f"✓ Server healthy: {health.json()['status']}")
|
||||
except Exception as e:
|
||||
print(f"✗ Server not available at {API_URL}: {e}")
|
||||
print(" Start the server with: python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factory")
|
||||
return
|
||||
|
||||
# 1. Submit a job
|
||||
print("\n1. Submitting job...")
|
||||
prompt = "What is the capital of France? Answer in one word."
|
||||
result = submit_job(prompt)
|
||||
job_id = result["job_id"]
|
||||
print(f" Job ID: {job_id}")
|
||||
print(f" Status: {result['status']}")
|
||||
print(f" Poll URL: {result['poll_url']}")
|
||||
|
||||
# 2. Wait for completion
|
||||
print("\n2. Waiting for completion...")
|
||||
final_status = wait_for_completion(job_id)
|
||||
print(f" Final status: {final_status['status']}")
|
||||
|
||||
# 3. Get result
|
||||
if final_status["status"] == "succeeded":
|
||||
print("\n3. Getting result...")
|
||||
result = get_result(job_id)
|
||||
print(f" Result: {result['result']}")
|
||||
print(f" Duration: {result['duration_seconds']:.2f}s")
|
||||
else:
|
||||
print(f"\n3. Job did not succeed: {final_status.get('error', 'Unknown error')}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Example completed successfully!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
# CLI Example: SSE Streaming
|
||||
#
|
||||
# This example demonstrates how to stream job progress via Server-Sent Events.
|
||||
#
|
||||
# Requirements:
|
||||
# - Jobs server running on http://127.0.0.1:8005
|
||||
#
|
||||
# Usage:
|
||||
# bash sse_stream.sh
|
||||
|
||||
set -e
|
||||
|
||||
API_URL="${PRAISONAI_BASE_URL:-http://127.0.0.1:8005}"
|
||||
|
||||
echo "============================================================"
|
||||
echo "PraisonAI SSE Streaming Example"
|
||||
echo "============================================================"
|
||||
|
||||
# Submit a job
|
||||
echo ""
|
||||
echo "1. Submitting job..."
|
||||
RESPONSE=$(curl -s -X POST "$API_URL/api/v1/runs" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"prompt": "Count from 1 to 5."}')
|
||||
|
||||
JOB_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
|
||||
echo " Job ID: $JOB_ID"
|
||||
|
||||
# Stream progress
|
||||
echo ""
|
||||
echo "2. Streaming progress (Ctrl+C to stop)..."
|
||||
echo " Connecting to: $API_URL/api/v1/runs/$JOB_ID/stream"
|
||||
echo ""
|
||||
|
||||
# Use curl to stream SSE events (timeout after 60s)
|
||||
curl -s -N --max-time 60 "$API_URL/api/v1/runs/$JOB_ID/stream" | while read -r line; do
|
||||
if [[ "$line" == data:* ]]; then
|
||||
data="${line#data: }"
|
||||
if [ "$data" = "[DONE]" ]; then
|
||||
echo " Stream completed"
|
||||
break
|
||||
fi
|
||||
echo " Event: $data"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "Streaming example completed!"
|
||||
echo "============================================================"
|
||||
@@ -1,94 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic Background Tasks Example for PraisonAI Agents.
|
||||
|
||||
This example demonstrates how to use the background task system with Agent:
|
||||
1. Create an Agent with background task support
|
||||
2. Run agent tasks asynchronously
|
||||
3. Track task progress
|
||||
4. Handle task completion
|
||||
|
||||
Usage:
|
||||
python basic_background.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.background import BackgroundRunner, BackgroundConfig, TaskStatus
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("Agent-Centric Background Tasks Demo")
|
||||
print("=" * 60)
|
||||
|
||||
# Create a background runner with config (used standalone, not passed to Agent)
|
||||
runner = BackgroundRunner(config=BackgroundConfig(max_concurrent_tasks=3))
|
||||
|
||||
# Create an Agent
|
||||
agent = Agent(
|
||||
name="AsyncAssistant",
|
||||
instructions="You are a helpful research assistant.",
|
||||
)
|
||||
|
||||
print("\n--- Agent with Background Support Created ---")
|
||||
print(f"Agent: {agent.name}")
|
||||
print(f"Background runner: {runner is not None}")
|
||||
|
||||
# Define some example tasks
|
||||
async def research_task(topic: str) -> str:
|
||||
"""Simulate a research task."""
|
||||
print(f" 🔄 Researching '{topic}'...")
|
||||
await asyncio.sleep(1.5)
|
||||
return f"Research on '{topic}' completed with 5 key findings"
|
||||
|
||||
async def analysis_task(data: str) -> str:
|
||||
"""Simulate an analysis task."""
|
||||
print(f" 🔄 Analyzing data...")
|
||||
await asyncio.sleep(1.0)
|
||||
return f"Analysis complete: {data}"
|
||||
|
||||
# Submit tasks via background runner
|
||||
print("\n--- Submitting Background Tasks ---")
|
||||
|
||||
task1 = await runner.submit(
|
||||
research_task,
|
||||
args=("AI trends 2025",),
|
||||
name="research_ai"
|
||||
)
|
||||
print(f"✅ Submitted research task: {task1.id[:8]}")
|
||||
|
||||
task2 = await runner.submit(
|
||||
analysis_task,
|
||||
args=("market data",),
|
||||
name="analyze_market"
|
||||
)
|
||||
print(f"✅ Submitted analysis task: {task2.id[:8]}")
|
||||
|
||||
# List all tasks
|
||||
print("\n--- Task Status ---")
|
||||
for task in runner.tasks:
|
||||
print(f" [{task.id[:8]}] {task.status.value}")
|
||||
|
||||
# Wait for tasks to complete
|
||||
print("\n--- Waiting for Tasks ---")
|
||||
await task1.wait(timeout=10.0)
|
||||
await task2.wait(timeout=10.0)
|
||||
|
||||
# Final results
|
||||
print("\n--- Results ---")
|
||||
for task in [task1, task2]:
|
||||
status = "✅" if task.status == TaskStatus.COMPLETED else "❌"
|
||||
print(f" {status} {task.result}")
|
||||
|
||||
# Cleanup
|
||||
runner.clear_completed()
|
||||
print("\n✅ Cleared completed tasks")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Demo Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,146 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PraisonAI Benchmark Example
|
||||
|
||||
This example demonstrates how to use the benchmark system programmatically
|
||||
to compare performance across different execution paths.
|
||||
|
||||
Usage:
|
||||
python benchmark_example.py
|
||||
|
||||
Requirements:
|
||||
- OPENAI_API_KEY environment variable set
|
||||
- praisonai package installed
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure the package is importable
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src/praisonai'))
|
||||
|
||||
from praisonai.cli.features.benchmark import BenchmarkHandler, BenchmarkReport
|
||||
|
||||
|
||||
def run_quick_comparison():
|
||||
"""Run a quick comparison of key execution paths."""
|
||||
print("=" * 60)
|
||||
print("Quick Benchmark Comparison")
|
||||
print("=" * 60)
|
||||
|
||||
handler = BenchmarkHandler()
|
||||
|
||||
# Run benchmark with 2 iterations on key paths
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="What is 2+2?",
|
||||
iterations=2,
|
||||
paths=["openai_sdk", "praisonai_agent"],
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
# Print comparison table
|
||||
print("\n" + handler.create_comparison_table(report))
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def run_full_benchmark():
|
||||
"""Run full benchmark suite across all paths."""
|
||||
print("=" * 60)
|
||||
print("Full Benchmark Suite")
|
||||
print("=" * 60)
|
||||
|
||||
handler = BenchmarkHandler()
|
||||
|
||||
# Run full benchmark
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="Hi",
|
||||
iterations=3,
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
# Print full report
|
||||
handler.print_report(report)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def run_agent_benchmark():
|
||||
"""Benchmark PraisonAI Agent specifically."""
|
||||
print("=" * 60)
|
||||
print("Agent Benchmark")
|
||||
print("=" * 60)
|
||||
|
||||
handler = BenchmarkHandler()
|
||||
|
||||
# Benchmark agent vs SDK
|
||||
report = handler.run_full_benchmark(
|
||||
prompt="Explain Python in one sentence",
|
||||
iterations=3,
|
||||
paths=["openai_sdk", "praisonai_agent"],
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
# Show timeline diagrams
|
||||
for name, result in report.results.items():
|
||||
print(f"\n### {name}")
|
||||
print(handler.create_timeline_diagram(result))
|
||||
|
||||
# Show variance analysis
|
||||
print(handler.create_variance_table(report))
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def save_benchmark_results(report: BenchmarkReport, output_path: str):
|
||||
"""Save benchmark results to JSON file."""
|
||||
import json
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(report.to_dict(), f, indent=2)
|
||||
|
||||
print(f"\nResults saved to: {output_path}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="PraisonAI Benchmark Example")
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
choices=["quick", "full", "agent"],
|
||||
default="quick",
|
||||
help="Benchmark mode: quick (2 paths), full (all paths), agent (agent focus)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Output JSON file path"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Check for API key
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
print("Error: OPENAI_API_KEY environment variable not set")
|
||||
sys.exit(1)
|
||||
|
||||
# Run selected benchmark
|
||||
if args.mode == "quick":
|
||||
report = run_quick_comparison()
|
||||
elif args.mode == "full":
|
||||
report = run_full_benchmark()
|
||||
elif args.mode == "agent":
|
||||
report = run_agent_benchmark()
|
||||
|
||||
# Save results if requested
|
||||
if args.output:
|
||||
save_benchmark_results(report, args.output)
|
||||
|
||||
print("\n✅ Benchmark complete!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,43 +0,0 @@
|
||||
"""
|
||||
Basic Caching Configuration Example
|
||||
|
||||
Demonstrates using caching for response and prompt caching.
|
||||
"""
|
||||
import os
|
||||
from praisonaiagents import Agent, CachingConfig
|
||||
|
||||
# Ensure API key is set from environment
|
||||
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
|
||||
|
||||
# Default caching (enabled)
|
||||
agent_cached = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
caching=True,
|
||||
)
|
||||
|
||||
# Disable caching
|
||||
agent_no_cache = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
caching=False,
|
||||
)
|
||||
|
||||
# Custom caching configuration
|
||||
agent_custom = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
caching=CachingConfig(
|
||||
enabled=True,
|
||||
prompt_caching=True, # Enable prompt caching for supported providers
|
||||
),
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing CachingConfig...")
|
||||
|
||||
print(f"Cached agent cache: {agent_cached.cache}")
|
||||
print(f"No-cache agent cache: {agent_no_cache.cache}")
|
||||
print(f"Custom agent cache: {agent_custom.cache}")
|
||||
|
||||
result = agent_cached.chat("What is caching?")
|
||||
print(f"Result: {result}")
|
||||
|
||||
print("\nCachingConfig tests passed!")
|
||||
@@ -1,24 +0,0 @@
|
||||
"""
|
||||
Assistants Capabilities Example
|
||||
|
||||
Demonstrates OpenAI Assistants API using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import assistant_list
|
||||
|
||||
print("=== List Assistants ===")
|
||||
try:
|
||||
assistants = assistant_list()
|
||||
print(f"Assistants found: {len(assistants)}")
|
||||
for a in assistants[:5]: # Show first 5
|
||||
print(f" - {a.get('name', 'unnamed')}: {a.get('id', 'unknown')}")
|
||||
except Exception as e:
|
||||
print(f"Note: {e}")
|
||||
|
||||
print("\n=== Assistant Operations ===")
|
||||
print("Available functions:")
|
||||
print(" assistant_create(name, instructions, model='gpt-4o-mini')")
|
||||
print(" assistant_list()")
|
||||
|
||||
print("\nSee CLI: praisonai assistants list")
|
||||
print("See CLI: praisonai assistants create --name 'My Assistant'")
|
||||
@@ -1,24 +0,0 @@
|
||||
"""
|
||||
Audio Capabilities Example
|
||||
|
||||
Demonstrates transcription and text-to-speech using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import transcribe, speech
|
||||
|
||||
# Note: Audio transcription requires an audio file
|
||||
# This example shows the API structure
|
||||
|
||||
print("=== Audio Transcription ===")
|
||||
print("To transcribe audio, use:")
|
||||
print(" result = transcribe('audio.mp3', model='whisper-1')")
|
||||
print(" print(result.text)")
|
||||
|
||||
print("\n=== Text-to-Speech ===")
|
||||
print("To generate speech, use:")
|
||||
print(" audio_data = speech('Hello world', model='tts-1', voice='alloy')")
|
||||
print(" # Save to file or play")
|
||||
|
||||
print("\nNote: These operations require audio files or generate audio output.")
|
||||
print("See CLI: praisonai audio transcribe <file>")
|
||||
print("See CLI: praisonai audio speech <text>")
|
||||
@@ -1,27 +0,0 @@
|
||||
"""
|
||||
Batches Capabilities Example
|
||||
|
||||
Demonstrates batch processing using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import batch_list
|
||||
|
||||
print("=== Batch List ===")
|
||||
try:
|
||||
batches = batch_list()
|
||||
print(f"Batches found: {len(batches)}")
|
||||
for b in batches[:5]: # Show first 5
|
||||
print(f" - {b.get('id', 'unknown')}: {b.get('status', 'unknown')}")
|
||||
except Exception as e:
|
||||
print(f"Note: {e}")
|
||||
print("Batch operations require batch jobs to be created first.")
|
||||
|
||||
print("\n=== Batch Operations ===")
|
||||
print("Available functions:")
|
||||
print(" batch_create(input_file_id, endpoint, completion_window='24h')")
|
||||
print(" batch_list()")
|
||||
print(" batch_retrieve(batch_id)")
|
||||
print(" batch_cancel(batch_id)")
|
||||
|
||||
print("\nSee CLI: praisonai batches list")
|
||||
print("See CLI: praisonai batches create <input_file_id>")
|
||||
@@ -1,30 +0,0 @@
|
||||
"""
|
||||
Completions Capability Example
|
||||
|
||||
Demonstrates chat and text completions using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import chat_completion, text_completion
|
||||
|
||||
# Chat completion example
|
||||
print("=== Chat Completion ===")
|
||||
result = chat_completion(
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is 2 + 2? Answer in one word."}
|
||||
],
|
||||
model="gpt-4o-mini",
|
||||
max_tokens=10
|
||||
)
|
||||
print(f"Response: {result.content}")
|
||||
print(f"Model: {result.model}")
|
||||
print(f"Usage: {result.usage}")
|
||||
|
||||
# Text completion example (legacy)
|
||||
print("\n=== Text Completion ===")
|
||||
result = text_completion(
|
||||
prompt="The capital of France is",
|
||||
model="gpt-3.5-turbo-instruct",
|
||||
max_tokens=10
|
||||
)
|
||||
print(f"Response: {result.content}")
|
||||
@@ -1,46 +0,0 @@
|
||||
"""
|
||||
Embeddings Capability Example
|
||||
|
||||
Demonstrates text embedding generation using PraisonAI capabilities.
|
||||
Both `embed` and `embedding` work identically - use whichever you prefer.
|
||||
"""
|
||||
|
||||
from praisonai import embed, embedding # Both work from top-level
|
||||
# Or: from praisonai.capabilities import embed, embedding
|
||||
|
||||
# Single text embedding using embed()
|
||||
print("=== Single Text Embedding (using embed) ===")
|
||||
result = embed(
|
||||
input="Hello, world!",
|
||||
model="text-embedding-3-small"
|
||||
)
|
||||
print(f"Embedding dimensions: {len(result.embeddings[0])}")
|
||||
print(f"First 5 values: {result.embeddings[0][:5]}")
|
||||
print(f"Usage: {result.usage}")
|
||||
|
||||
# Same thing using embedding() alias
|
||||
print("\n=== Single Text Embedding (using embedding alias) ===")
|
||||
result = embedding(
|
||||
input="Hello, world!",
|
||||
model="text-embedding-3-small"
|
||||
)
|
||||
print(f"Embedding dimensions: {len(result.embeddings[0])}")
|
||||
|
||||
# Multiple text embeddings
|
||||
print("\n=== Multiple Text Embeddings ===")
|
||||
result = embed(
|
||||
input=["Hello", "World", "AI"],
|
||||
model="text-embedding-3-small"
|
||||
)
|
||||
print(f"Number of embeddings: {len(result.embeddings)}")
|
||||
for i, emb in enumerate(result.embeddings):
|
||||
print(f" Text {i+1}: {len(emb)} dimensions")
|
||||
|
||||
# With custom dimensions (for models that support it)
|
||||
print("\n=== Custom Dimensions ===")
|
||||
result = embed(
|
||||
input="Hello world",
|
||||
model="text-embedding-3-large",
|
||||
dimensions=256
|
||||
)
|
||||
print(f"Reduced dimensions: {len(result.embeddings[0])}")
|
||||
@@ -1,28 +0,0 @@
|
||||
"""
|
||||
Files Capabilities Example
|
||||
|
||||
Demonstrates file management using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import file_list
|
||||
|
||||
print("=== File List ===")
|
||||
try:
|
||||
files = file_list()
|
||||
print(f"Files found: {len(files)}")
|
||||
for f in files[:5]: # Show first 5
|
||||
print(f" - {f.get('filename', 'unknown')}: {f.get('id', 'unknown')}")
|
||||
except Exception as e:
|
||||
print(f"Note: {e}")
|
||||
print("File operations require files to be uploaded first.")
|
||||
|
||||
print("\n=== File Operations ===")
|
||||
print("Available functions:")
|
||||
print(" file_create(file_path, purpose='assistants')")
|
||||
print(" file_list(purpose=None)")
|
||||
print(" file_retrieve(file_id)")
|
||||
print(" file_delete(file_id)")
|
||||
print(" file_content(file_id)")
|
||||
|
||||
print("\nSee CLI: praisonai files list")
|
||||
print("See CLI: praisonai files upload <file>")
|
||||
@@ -1,31 +0,0 @@
|
||||
"""
|
||||
Images Capability Example
|
||||
|
||||
Demonstrates image generation using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import image_generate
|
||||
|
||||
# Generate an image with DALL-E 2
|
||||
print("=== Image Generation (DALL-E 2) ===")
|
||||
result = image_generate(
|
||||
prompt="A simple blue circle on a white background",
|
||||
model="dall-e-2",
|
||||
size="256x256",
|
||||
n=1
|
||||
)
|
||||
print(f"Images generated: {len(result)}")
|
||||
if result:
|
||||
print(f"URL: {result[0].url[:80]}...")
|
||||
|
||||
# Generate with DALL-E 3 (higher quality)
|
||||
print("\n=== Image Generation (DALL-E 3) ===")
|
||||
result = image_generate(
|
||||
prompt="A serene mountain landscape at sunset",
|
||||
model="dall-e-3",
|
||||
size="1024x1024",
|
||||
quality="standard"
|
||||
)
|
||||
print(f"Images generated: {len(result)}")
|
||||
if result:
|
||||
print(f"Revised prompt: {result[0].revised_prompt[:80]}..." if result[0].revised_prompt else "No revised prompt")
|
||||
@@ -1,33 +0,0 @@
|
||||
"""
|
||||
Messages Capability Example
|
||||
|
||||
Demonstrates Anthropic-style messages API and token counting.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import messages_create, count_tokens
|
||||
|
||||
# Create a message (Anthropic-style)
|
||||
print("=== Messages Create ===")
|
||||
result = messages_create(
|
||||
messages=[{"role": "user", "content": "What is the meaning of life? Answer briefly."}],
|
||||
model="gpt-4o-mini", # Works with any model via LiteLLM
|
||||
max_tokens=100,
|
||||
system="You are a philosophical assistant."
|
||||
)
|
||||
print(f"Message ID: {result.id}")
|
||||
if result.content:
|
||||
for block in result.content:
|
||||
if block.get("type") == "text":
|
||||
print(f"Response: {block.get('text')}")
|
||||
print(f"Usage: {result.usage}")
|
||||
|
||||
# Count tokens
|
||||
print("\n=== Token Counting ===")
|
||||
result = count_tokens(
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hello, how are you today?"}
|
||||
],
|
||||
model="gpt-4o-mini"
|
||||
)
|
||||
print(f"Token count: {result.input_tokens}")
|
||||
@@ -1,24 +0,0 @@
|
||||
"""
|
||||
Moderations Capability Example
|
||||
|
||||
Demonstrates content moderation using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import moderate
|
||||
|
||||
# Check safe content
|
||||
print("=== Safe Content Check ===")
|
||||
result = moderate(
|
||||
input="Hello, how are you today?"
|
||||
)
|
||||
print(f"Flagged: {result[0].flagged}")
|
||||
print(f"Categories: {list(result[0].categories.keys())[:5]}...")
|
||||
|
||||
# Check multiple texts
|
||||
print("\n=== Multiple Text Moderation ===")
|
||||
result = moderate(
|
||||
input=["Hello world", "Have a nice day", "This is a test"]
|
||||
)
|
||||
print(f"Number of results: {len(result)}")
|
||||
for i, r in enumerate(result):
|
||||
print(f" Text {i+1}: Flagged = {r.flagged}")
|
||||
@@ -1,23 +0,0 @@
|
||||
"""
|
||||
Realtime Capability Example
|
||||
|
||||
Demonstrates realtime session creation for audio/video streaming.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import realtime_connect
|
||||
|
||||
# Create a realtime session
|
||||
print("=== Realtime Session ===")
|
||||
session = realtime_connect(
|
||||
model="gpt-4o-realtime-preview",
|
||||
modalities=["text", "audio"],
|
||||
voice="alloy"
|
||||
)
|
||||
print(f"Session ID: {session.id}")
|
||||
print(f"Status: {session.status}")
|
||||
print(f"URL: {session.url}")
|
||||
print(f"Modalities: {session.metadata.get('modalities')}")
|
||||
print(f"Voice: {session.metadata.get('voice')}")
|
||||
|
||||
print("\nNote: To use this session, connect via WebSocket to the URL above.")
|
||||
print("See OpenAI Realtime API documentation for WebSocket protocol details.")
|
||||
@@ -1,32 +0,0 @@
|
||||
"""
|
||||
Rerank Capabilities Example
|
||||
|
||||
Demonstrates document reranking using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import rerank
|
||||
|
||||
print("=== Document Reranking ===")
|
||||
try:
|
||||
documents = [
|
||||
"Python is a programming language",
|
||||
"Machine learning uses algorithms",
|
||||
"Python is great for data science",
|
||||
"JavaScript runs in browsers"
|
||||
]
|
||||
|
||||
results = rerank(
|
||||
query="Python programming",
|
||||
documents=documents,
|
||||
model="cohere/rerank-english-v3.0"
|
||||
)
|
||||
|
||||
print(f"Query: 'Python programming'")
|
||||
print(f"Reranked results:")
|
||||
for r in results:
|
||||
print(f" Score {r.get('relevance_score', 0):.4f}: {r.get('document', {}).get('text', '')[:50]}")
|
||||
except Exception as e:
|
||||
print(f"Note: Reranking requires Cohere API key")
|
||||
print(f"Error: {e}")
|
||||
|
||||
print("\nSee CLI: praisonai rerank <query> --documents 'doc1' 'doc2'")
|
||||
@@ -1,22 +0,0 @@
|
||||
"""
|
||||
Vector Stores Capabilities Example
|
||||
|
||||
Demonstrates vector store management using PraisonAI capabilities.
|
||||
"""
|
||||
|
||||
from praisonai.capabilities import vector_store_create, vector_store_search
|
||||
|
||||
print("=== Vector Store Operations ===")
|
||||
print("Available functions:")
|
||||
print(" vector_store_create(name)")
|
||||
print(" vector_store_search(vector_store_id, query)")
|
||||
|
||||
print("\n=== Create Vector Store ===")
|
||||
try:
|
||||
store = vector_store_create(name="test-store")
|
||||
print(f"Created: {store}")
|
||||
except Exception as e:
|
||||
print(f"Note: {e}")
|
||||
|
||||
print("\nSee CLI: praisonai vector-stores create --name 'my-store'")
|
||||
print("See CLI: praisonai vector-stores search <store_id> <query>")
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example: Fetch and search PraisonAI templates from the catalog.
|
||||
|
||||
This example demonstrates how to:
|
||||
- Fetch templates.json from the deployed catalog
|
||||
- Search templates by keyword
|
||||
- Filter by tags and requirements
|
||||
- Generate CLI commands
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
CATALOG_URL = "https://mervinpraison.github.io/praisonai-template-catalog/data/templates.json"
|
||||
|
||||
|
||||
def fetch_catalog(url: str = CATALOG_URL) -> Dict[str, Any]:
|
||||
"""Fetch the template catalog from URL."""
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=10) as response:
|
||||
return json.loads(response.read().decode())
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch catalog: {e}")
|
||||
# Return empty catalog on failure
|
||||
return {"version": "0.0.0", "count": 0, "templates": []}
|
||||
|
||||
|
||||
def search_templates(templates: List[Dict], query: str) -> List[Dict]:
|
||||
"""Search templates by name, description, or tags."""
|
||||
query = query.lower()
|
||||
results = []
|
||||
for t in templates:
|
||||
if (query in t.get("name", "").lower() or
|
||||
query in t.get("description", "").lower() or
|
||||
any(query in tag.lower() for tag in t.get("tags", []))):
|
||||
results.append(t)
|
||||
return results
|
||||
|
||||
|
||||
def filter_by_tags(templates: List[Dict], tags: List[str]) -> List[Dict]:
|
||||
"""Filter templates that have all specified tags."""
|
||||
return [
|
||||
t for t in templates
|
||||
if all(tag in t.get("tags", []) for tag in tags)
|
||||
]
|
||||
|
||||
|
||||
def filter_by_tool(templates: List[Dict], tool_name: str) -> List[Dict]:
|
||||
"""Filter templates that require a specific tool."""
|
||||
return [
|
||||
t for t in templates
|
||||
if tool_name in t.get("requires", {}).get("tools", [])
|
||||
]
|
||||
|
||||
|
||||
def get_template(templates: List[Dict], name: str) -> Optional[Dict]:
|
||||
"""Get a template by name."""
|
||||
for t in templates:
|
||||
if t.get("name") == name:
|
||||
return t
|
||||
return None
|
||||
|
||||
|
||||
def generate_cli_command(template: Dict, action: str = "run") -> str:
|
||||
"""Generate CLI command for a template."""
|
||||
name = template.get("name", "unknown")
|
||||
if action == "run":
|
||||
return f"praisonai templates run {name}"
|
||||
elif action == "info":
|
||||
return f"praisonai templates info {name}"
|
||||
elif action == "init":
|
||||
return f"praisonai templates init my-project --template {name}"
|
||||
return f"praisonai templates {action} {name}"
|
||||
|
||||
|
||||
def print_template_summary(template: Dict):
|
||||
"""Print a summary of a template."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Name: {template.get('name')}")
|
||||
print(f"Version: {template.get('version')}")
|
||||
print(f"Description: {template.get('description', '')[:100]}...")
|
||||
print(f"Tags: {', '.join(template.get('tags', []))}")
|
||||
|
||||
requires = template.get("requires", {})
|
||||
if requires.get("tools"):
|
||||
print(f"Tools: {', '.join(requires['tools'])}")
|
||||
if requires.get("packages"):
|
||||
print(f"Packages: {', '.join(requires['packages'])}")
|
||||
if requires.get("env"):
|
||||
print(f"Env vars: {', '.join(requires['env'])}")
|
||||
|
||||
print(f"\nRun: {generate_cli_command(template, 'run')}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
||||
def main():
|
||||
print("PraisonAI Template Catalog - Python Example")
|
||||
print("-" * 45)
|
||||
|
||||
# Fetch catalog
|
||||
print("\n1. Fetching template catalog...")
|
||||
catalog = fetch_catalog()
|
||||
templates = catalog.get("templates", [])
|
||||
|
||||
print(f" Catalog version: {catalog.get('version', 'unknown')}")
|
||||
print(f" Total templates: {len(templates)}")
|
||||
|
||||
if not templates:
|
||||
print(" No templates found. The catalog may not be deployed yet.")
|
||||
print(" Try building locally: praisonai templates catalog build")
|
||||
return
|
||||
|
||||
# List all templates
|
||||
print("\n2. Available templates:")
|
||||
for t in templates:
|
||||
print(f" - {t.get('name')} (v{t.get('version')})")
|
||||
|
||||
# Search for video templates
|
||||
print("\n3. Searching for 'video' templates...")
|
||||
video_templates = search_templates(templates, "video")
|
||||
print(f" Found {len(video_templates)} video templates:")
|
||||
for t in video_templates:
|
||||
print(f" - {t.get('name')}: {t.get('description', '')[:50]}...")
|
||||
|
||||
# Filter by tool
|
||||
print("\n4. Templates using 'shell_tool':")
|
||||
shell_templates = filter_by_tool(templates, "shell_tool")
|
||||
for t in shell_templates:
|
||||
print(f" - {t.get('name')}")
|
||||
|
||||
# Get specific template details
|
||||
if templates:
|
||||
first_template = templates[0]
|
||||
print(f"\n5. Template details for '{first_template.get('name')}':")
|
||||
print_template_summary(first_template)
|
||||
|
||||
# Generate CLI commands
|
||||
print("\n6. CLI commands for all templates:")
|
||||
for t in templates[:5]: # First 5 only
|
||||
print(f" {generate_cli_command(t, 'run')}")
|
||||
|
||||
print("\nDone!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,167 +0,0 @@
|
||||
#!/usr/bin/env npx ts-node
|
||||
/**
|
||||
* Example: Fetch and search PraisonAI templates from the catalog.
|
||||
*
|
||||
* This TypeScript example demonstrates how to:
|
||||
* - Fetch templates.json from the deployed catalog
|
||||
* - Search templates by keyword
|
||||
* - Filter by tags and requirements
|
||||
* - Generate CLI commands
|
||||
*/
|
||||
|
||||
const CATALOG_URL = "https://mervinpraison.github.io/praisonai-template-catalog/data/templates.json";
|
||||
|
||||
interface Template {
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
author?: string;
|
||||
license?: string;
|
||||
tags?: string[];
|
||||
category?: string;
|
||||
difficulty?: "beginner" | "intermediate" | "advanced";
|
||||
requires?: {
|
||||
tools?: string[];
|
||||
packages?: string[];
|
||||
env?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface Catalog {
|
||||
version: string;
|
||||
generated_at: string;
|
||||
count: number;
|
||||
templates: Template[];
|
||||
}
|
||||
|
||||
async function fetchCatalog(url: string = CATALOG_URL): Promise<Catalog> {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch catalog: ${response.statusText}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function searchTemplates(templates: Template[], query: string): Template[] {
|
||||
const q = query.toLowerCase();
|
||||
return templates.filter(t =>
|
||||
t.name.toLowerCase().includes(q) ||
|
||||
t.description?.toLowerCase().includes(q) ||
|
||||
t.tags?.some(tag => tag.toLowerCase().includes(q))
|
||||
);
|
||||
}
|
||||
|
||||
function filterByTags(templates: Template[], tags: string[]): Template[] {
|
||||
return templates.filter(t =>
|
||||
tags.every(tag => t.tags?.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
function filterByTool(templates: Template[], toolName: string): Template[] {
|
||||
return templates.filter(t =>
|
||||
t.requires?.tools?.includes(toolName)
|
||||
);
|
||||
}
|
||||
|
||||
function getTemplate(templates: Template[], name: string): Template | undefined {
|
||||
return templates.find(t => t.name === name);
|
||||
}
|
||||
|
||||
function generateCLICommand(template: Template, action: string = "run"): string {
|
||||
const name = template.name;
|
||||
switch (action) {
|
||||
case "run":
|
||||
return `praisonai templates run ${name}`;
|
||||
case "info":
|
||||
return `praisonai templates info ${name}`;
|
||||
case "init":
|
||||
return `praisonai templates init my-project --template ${name}`;
|
||||
default:
|
||||
return `praisonai templates ${action} ${name}`;
|
||||
}
|
||||
}
|
||||
|
||||
function printTemplateSummary(template: Template): void {
|
||||
console.log("\n" + "=".repeat(60));
|
||||
console.log(`Name: ${template.name}`);
|
||||
console.log(`Version: ${template.version}`);
|
||||
console.log(`Description: ${template.description?.slice(0, 100)}...`);
|
||||
console.log(`Tags: ${template.tags?.join(", ") || "none"}`);
|
||||
|
||||
const requires = template.requires;
|
||||
if (requires?.tools?.length) {
|
||||
console.log(`Tools: ${requires.tools.join(", ")}`);
|
||||
}
|
||||
if (requires?.packages?.length) {
|
||||
console.log(`Packages: ${requires.packages.join(", ")}`);
|
||||
}
|
||||
if (requires?.env?.length) {
|
||||
console.log(`Env vars: ${requires.env.join(", ")}`);
|
||||
}
|
||||
|
||||
console.log(`\nRun: ${generateCLICommand(template, "run")}`);
|
||||
console.log("=".repeat(60));
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log("PraisonAI Template Catalog - TypeScript Example");
|
||||
console.log("-".repeat(45));
|
||||
|
||||
// Fetch catalog
|
||||
console.log("\n1. Fetching template catalog...");
|
||||
let catalog: Catalog;
|
||||
try {
|
||||
catalog = await fetchCatalog();
|
||||
} catch (error) {
|
||||
console.log(` Failed to fetch: ${error}`);
|
||||
console.log(" The catalog may not be deployed yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
const templates = catalog.templates;
|
||||
console.log(` Catalog version: ${catalog.version}`);
|
||||
console.log(` Total templates: ${templates.length}`);
|
||||
|
||||
if (templates.length === 0) {
|
||||
console.log(" No templates found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// List all templates
|
||||
console.log("\n2. Available templates:");
|
||||
templates.forEach(t => {
|
||||
console.log(` - ${t.name} (v${t.version})`);
|
||||
});
|
||||
|
||||
// Search for video templates
|
||||
console.log("\n3. Searching for 'video' templates...");
|
||||
const videoTemplates = searchTemplates(templates, "video");
|
||||
console.log(` Found ${videoTemplates.length} video templates:`);
|
||||
videoTemplates.forEach(t => {
|
||||
console.log(` - ${t.name}: ${t.description?.slice(0, 50)}...`);
|
||||
});
|
||||
|
||||
// Filter by tool
|
||||
console.log("\n4. Templates using 'shell_tool':");
|
||||
const shellTemplates = filterByTool(templates, "shell_tool");
|
||||
shellTemplates.forEach(t => {
|
||||
console.log(` - ${t.name}`);
|
||||
});
|
||||
|
||||
// Get specific template details
|
||||
if (templates.length > 0) {
|
||||
const firstTemplate = templates[0];
|
||||
console.log(`\n5. Template details for '${firstTemplate.name}':`);
|
||||
printTemplateSummary(firstTemplate);
|
||||
}
|
||||
|
||||
// Generate CLI commands
|
||||
console.log("\n6. CLI commands for all templates:");
|
||||
templates.slice(0, 5).forEach(t => {
|
||||
console.log(` ${generateCLICommand(t, "run")}`);
|
||||
});
|
||||
|
||||
console.log("\nDone!");
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,121 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Run all catalog examples and report pass/fail status.
|
||||
|
||||
Usage:
|
||||
python run_all_catalog_examples.py
|
||||
|
||||
Environment variables:
|
||||
OPENAI_API_KEY or PRAISONAI_OPENAI_API_KEY - Required for agent example
|
||||
PRAISONAI_RUN_INTEGRATION - Set to "1" to run integration tests
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def run_example(name: str, script: str, requires_api_key: bool = False) -> bool:
|
||||
"""Run an example script and return success status."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Running: {name}")
|
||||
print(f"Script: {script}")
|
||||
print("="*60)
|
||||
|
||||
# Check API key requirement
|
||||
if requires_api_key:
|
||||
api_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("PRAISONAI_OPENAI_API_KEY")
|
||||
run_integration = os.environ.get("PRAISONAI_RUN_INTEGRATION") == "1"
|
||||
|
||||
if not api_key:
|
||||
print(f"⏭️ SKIPPED: {name} (no API key)")
|
||||
print(" Set OPENAI_API_KEY to run this example")
|
||||
return True # Not a failure, just skipped
|
||||
|
||||
if not run_integration:
|
||||
print(f"⏭️ SKIPPED: {name} (integration tests disabled)")
|
||||
print(" Set PRAISONAI_RUN_INTEGRATION=1 to run")
|
||||
return True
|
||||
|
||||
script_path = SCRIPT_DIR / script
|
||||
if not script_path.exists():
|
||||
print(f"❌ FAILED: Script not found: {script_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(script_path)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
cwd=str(SCRIPT_DIR)
|
||||
)
|
||||
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
print(f"STDERR: {result.stderr}")
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"✅ PASSED: {name}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ FAILED: {name} (exit code {result.returncode})")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"❌ FAILED: {name} (timeout)")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ FAILED: {name} ({e})")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("PraisonAI Template Catalog - Example Runner")
|
||||
print("=" * 50)
|
||||
|
||||
# Check environment
|
||||
api_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("PRAISONAI_OPENAI_API_KEY")
|
||||
if api_key:
|
||||
print(f"API key: present (...{api_key[-4:]})")
|
||||
else:
|
||||
print("API key: not set")
|
||||
|
||||
run_integration = os.environ.get("PRAISONAI_RUN_INTEGRATION") == "1"
|
||||
print(f"Integration tests: {'enabled' if run_integration else 'disabled'}")
|
||||
|
||||
# Define examples
|
||||
examples = [
|
||||
("Fetch Templates (Python)", "fetch_templates.py", False),
|
||||
("Template Finder Agent", "template_finder_agent.py", True),
|
||||
]
|
||||
|
||||
# Run examples
|
||||
results = []
|
||||
for name, script, requires_key in examples:
|
||||
success = run_example(name, script, requires_key)
|
||||
results.append((name, success))
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 60)
|
||||
print("SUMMARY")
|
||||
print("=" * 60)
|
||||
|
||||
passed = sum(1 for _, s in results if s)
|
||||
failed = sum(1 for _, s in results if not s)
|
||||
|
||||
for name, success in results:
|
||||
status = "✅ PASS" if success else "❌ FAIL"
|
||||
print(f" {status}: {name}")
|
||||
|
||||
print(f"\nTotal: {passed} passed, {failed} failed")
|
||||
|
||||
# Exit with appropriate code
|
||||
sys.exit(0 if failed == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,193 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example: Template Finder Agent
|
||||
|
||||
An Agent()-centric example that uses an AI agent to find templates
|
||||
based on natural language queries.
|
||||
|
||||
Requires: OPENAI_API_KEY environment variable
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
CATALOG_URL = "https://mervinpraison.github.io/praisonai-template-catalog/data/templates.json"
|
||||
|
||||
|
||||
def fetch_templates() -> list:
|
||||
"""Fetch templates from catalog."""
|
||||
try:
|
||||
with urllib.request.urlopen(CATALOG_URL, timeout=10) as response:
|
||||
data = json.loads(response.read().decode())
|
||||
return data.get("templates", [])
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def search_templates_tool(query: str) -> str:
|
||||
"""
|
||||
Search templates by keyword.
|
||||
|
||||
Args:
|
||||
query: Search query (e.g., "video editing", "transcript", "data")
|
||||
|
||||
Returns:
|
||||
JSON string with matching templates
|
||||
"""
|
||||
templates = fetch_templates()
|
||||
query_lower = query.lower()
|
||||
|
||||
results = []
|
||||
for t in templates:
|
||||
if (query_lower in t.get("name", "").lower() or
|
||||
query_lower in t.get("description", "").lower() or
|
||||
any(query_lower in tag.lower() for tag in t.get("tags", []))):
|
||||
results.append({
|
||||
"name": t.get("name"),
|
||||
"description": t.get("description", "")[:100],
|
||||
"tags": t.get("tags", []),
|
||||
"cli_command": f"praisonai templates run {t.get('name')}"
|
||||
})
|
||||
|
||||
if not results:
|
||||
return json.dumps({"message": "No templates found matching your query", "templates": []})
|
||||
|
||||
return json.dumps({"count": len(results), "templates": results})
|
||||
|
||||
|
||||
def list_all_templates_tool() -> str:
|
||||
"""
|
||||
List all available templates.
|
||||
|
||||
Returns:
|
||||
JSON string with all template names and descriptions
|
||||
"""
|
||||
templates = fetch_templates()
|
||||
|
||||
if not templates:
|
||||
return json.dumps({"message": "No templates available", "templates": []})
|
||||
|
||||
results = [
|
||||
{"name": t.get("name"), "description": t.get("description", "")[:80]}
|
||||
for t in templates
|
||||
]
|
||||
|
||||
return json.dumps({"count": len(results), "templates": results})
|
||||
|
||||
|
||||
def get_template_details_tool(name: str) -> str:
|
||||
"""
|
||||
Get detailed information about a specific template.
|
||||
|
||||
Args:
|
||||
name: Template name (e.g., "ai-video-editor")
|
||||
|
||||
Returns:
|
||||
JSON string with template details
|
||||
"""
|
||||
templates = fetch_templates()
|
||||
|
||||
for t in templates:
|
||||
if t.get("name") == name:
|
||||
return json.dumps({
|
||||
"name": t.get("name"),
|
||||
"version": t.get("version"),
|
||||
"description": t.get("description"),
|
||||
"author": t.get("author"),
|
||||
"tags": t.get("tags", []),
|
||||
"requires": t.get("requires", {}),
|
||||
"cli_commands": {
|
||||
"run": f"praisonai templates run {name}",
|
||||
"info": f"praisonai templates info {name}",
|
||||
"init": f"praisonai templates init my-project --template {name}"
|
||||
}
|
||||
})
|
||||
|
||||
return json.dumps({"error": f"Template '{name}' not found"})
|
||||
|
||||
|
||||
def check_api_key() -> bool:
|
||||
"""Check if API key is available."""
|
||||
key = os.environ.get("OPENAI_API_KEY") or os.environ.get("PRAISONAI_OPENAI_API_KEY")
|
||||
if key:
|
||||
print(f"API key present: ...{key[-4:]}")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("PraisonAI Template Finder Agent")
|
||||
print("=" * 40)
|
||||
|
||||
# Check for API key
|
||||
if not check_api_key():
|
||||
print("\n⚠️ No API key found.")
|
||||
print("Set OPENAI_API_KEY or PRAISONAI_OPENAI_API_KEY to use the agent.")
|
||||
print("\nFalling back to direct tool usage:\n")
|
||||
|
||||
# Demo the tools directly
|
||||
print("1. Listing all templates:")
|
||||
result = list_all_templates_tool()
|
||||
data = json.loads(result)
|
||||
for t in data.get("templates", [])[:5]:
|
||||
print(f" - {t['name']}: {t['description'][:50]}...")
|
||||
|
||||
print("\n2. Searching for 'video' templates:")
|
||||
result = search_templates_tool("video")
|
||||
data = json.loads(result)
|
||||
for t in data.get("templates", []):
|
||||
print(f" - {t['name']}")
|
||||
print(f" Run: {t['cli_command']}")
|
||||
|
||||
return
|
||||
|
||||
# Import Agent only if API key is available
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
except ImportError:
|
||||
print("praisonaiagents not installed. Install with: pip install praisonaiagents")
|
||||
return
|
||||
|
||||
# Create the Template Finder Agent
|
||||
agent = Agent(
|
||||
name="Template Finder",
|
||||
role="PraisonAI Template Expert",
|
||||
goal="Help users find the right template for their needs",
|
||||
backstory="""You are an expert on PraisonAI templates. You help users
|
||||
discover templates that match their requirements. You can search templates,
|
||||
list all available templates, and provide detailed information about specific
|
||||
templates. Always provide the CLI command to run the template.""",
|
||||
tools=[
|
||||
search_templates_tool,
|
||||
list_all_templates_tool,
|
||||
get_template_details_tool
|
||||
],
|
||||
output="verbose" # Use new consolidated param
|
||||
)
|
||||
|
||||
# Example queries
|
||||
queries = [
|
||||
"Find me a template for editing videos",
|
||||
"What templates are available for working with transcripts?",
|
||||
"Show me details about the ai-video-editor template"
|
||||
]
|
||||
|
||||
print("\nRunning Template Finder Agent...")
|
||||
print("-" * 40)
|
||||
|
||||
for query in queries:
|
||||
print(f"\n📝 Query: {query}")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
result = agent.start(query)
|
||||
print(f"\n🤖 Response:\n{result}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
print("\n" + "=" * 40)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,115 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic Checkpoints Example for PraisonAI Agents.
|
||||
|
||||
This example demonstrates how to use the shadow git checkpointing system with Agent:
|
||||
1. Create an Agent with checkpoint support
|
||||
2. Save checkpoints before making changes
|
||||
3. List available checkpoints
|
||||
4. Restore to a previous checkpoint
|
||||
|
||||
Usage:
|
||||
python basic_checkpoints.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.checkpoints import CheckpointService
|
||||
|
||||
|
||||
async def main():
|
||||
# Create a temporary workspace for demonstration
|
||||
with tempfile.TemporaryDirectory() as workspace:
|
||||
print("=" * 60)
|
||||
print("Agent-Centric Checkpointing Demo")
|
||||
print("=" * 60)
|
||||
print(f"\nWorkspace: {workspace}")
|
||||
|
||||
# Create some initial files
|
||||
test_file = Path(workspace) / "example.py"
|
||||
test_file.write_text("# Initial content\nprint('Hello, World!')\n")
|
||||
print(f"\n✅ Created initial file: {test_file.name}")
|
||||
|
||||
# Create checkpoint service (used standalone, not passed to Agent)
|
||||
checkpoint_service = CheckpointService(workspace_dir=workspace)
|
||||
|
||||
# Create an Agent
|
||||
agent = Agent(
|
||||
name="RefactorBot",
|
||||
instructions="You are a code refactoring assistant.",
|
||||
)
|
||||
|
||||
print("\n--- Agent with Checkpoint Support Created ---")
|
||||
print(f"Agent: {agent.name}")
|
||||
print(f"Checkpoint service initialized: {checkpoint_service is not None}")
|
||||
|
||||
try:
|
||||
# Initialize the shadow git repository
|
||||
print("\n--- Initializing Checkpoint Service ---")
|
||||
await checkpoint_service.initialize()
|
||||
print("✅ Shadow git repository initialized")
|
||||
|
||||
# Save first checkpoint
|
||||
print("\n--- Saving Checkpoint 1: Initial State ---")
|
||||
result1 = await checkpoint_service.save("Initial state")
|
||||
print(f"✅ Checkpoint saved: {result1.checkpoint.id[:8]}")
|
||||
print(f" Message: {result1.checkpoint.message}")
|
||||
|
||||
# Make some changes (simulating agent modifications)
|
||||
print("\n--- Simulating Agent Changes ---")
|
||||
test_file.write_text("# Refactored content\nprint('Hello, PraisonAI!')\nprint('Checkpoints are awesome!')\n")
|
||||
print("✅ Modified example.py")
|
||||
|
||||
# Create a new file
|
||||
new_file = Path(workspace) / "utils.py"
|
||||
new_file.write_text("def helper():\n return 'I help!'\n")
|
||||
print("✅ Created utils.py")
|
||||
|
||||
# Save second checkpoint
|
||||
print("\n--- Saving Checkpoint 2: After Refactoring ---")
|
||||
result2 = await checkpoint_service.save("Added features")
|
||||
print(f"✅ Checkpoint saved: {result2.checkpoint.id[:8]}")
|
||||
|
||||
# List all checkpoints
|
||||
print("\n--- Listing All Checkpoints ---")
|
||||
checkpoints_list = await checkpoint_service.list_checkpoints()
|
||||
for i, cp in enumerate(checkpoints_list, 1):
|
||||
print(f" {i}. [{cp.id[:8]}] {cp.message}")
|
||||
|
||||
# Show diff
|
||||
print("\n--- Current Diff ---")
|
||||
diff = await checkpoint_service.diff()
|
||||
if diff.files:
|
||||
for f in diff.files:
|
||||
print(f" {f.status}: {f.path}")
|
||||
else:
|
||||
print(" No uncommitted changes")
|
||||
|
||||
# Make more changes
|
||||
test_file.write_text("# Bad changes\nprint('This will be reverted')\n")
|
||||
print("\n✅ Made problematic changes to example.py")
|
||||
|
||||
# Restore to checkpoint 2
|
||||
print(f"\n--- Restoring to Checkpoint: {result2.checkpoint.id[:8]} ---")
|
||||
await checkpoint_service.restore(result2.checkpoint.id)
|
||||
print("✅ Restored successfully!")
|
||||
|
||||
# Verify restoration
|
||||
content = test_file.read_text()
|
||||
print(f"\n--- Verified Content After Restore ---")
|
||||
print(content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}")
|
||||
raise
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Demo Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,93 +0,0 @@
|
||||
# Persistence CLI Commands
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Check database connectivity
|
||||
praisonai persistence doctor \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--state-url redis://localhost:6379
|
||||
|
||||
# Run agent with persistence
|
||||
praisonai persistence run \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--session-id my-session
|
||||
|
||||
# Resume a session
|
||||
praisonai persistence resume \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--session-id my-session
|
||||
|
||||
# Export session to file
|
||||
praisonai persistence export \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--session-id my-session \
|
||||
--output session_backup.jsonl
|
||||
|
||||
# Import session from file
|
||||
praisonai persistence import \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--file session_backup.jsonl
|
||||
|
||||
# Check schema status
|
||||
praisonai persistence status \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--state-url redis://localhost:6379
|
||||
|
||||
# Apply migrations
|
||||
praisonai persistence migrate \
|
||||
--conversation-url postgresql://localhost:5432/praisonai \
|
||||
--state-url redis://localhost:6379
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Instead of CLI flags, you can use environment variables:
|
||||
|
||||
```bash
|
||||
export PRAISON_CONVERSATION_URL=postgresql://localhost:5432/praisonai
|
||||
export PRAISON_STATE_URL=redis://localhost:6379
|
||||
export PRAISON_KNOWLEDGE_URL=http://localhost:6333
|
||||
|
||||
# Then run without URL flags
|
||||
praisonai persistence doctor
|
||||
praisonai persistence status
|
||||
```
|
||||
|
||||
## Docker Setup
|
||||
|
||||
```bash
|
||||
# Start all services
|
||||
docker run -d --name praison-postgres \
|
||||
-e POSTGRES_PASSWORD=praison123 \
|
||||
-e POSTGRES_DB=praisonai \
|
||||
-p 5432:5432 postgres:16
|
||||
|
||||
docker run -d --name praison-redis \
|
||||
-p 6379:6379 redis:7
|
||||
|
||||
docker run -d --name praison-qdrant \
|
||||
-p 6333:6333 -p 6334:6334 qdrant/qdrant
|
||||
|
||||
# Verify
|
||||
docker ps
|
||||
```
|
||||
|
||||
## Example Workflow
|
||||
|
||||
```bash
|
||||
# 1. Start services
|
||||
docker-compose up -d
|
||||
|
||||
# 2. Check connectivity
|
||||
praisonai persistence doctor
|
||||
|
||||
# 3. Run agent
|
||||
praisonai persistence run --session-id demo-session
|
||||
|
||||
# 4. Export for backup
|
||||
praisonai persistence export --session-id demo-session --output backup.jsonl
|
||||
|
||||
# 5. Import to another environment
|
||||
praisonai persistence import --file backup.jsonl
|
||||
```
|
||||
@@ -1,124 +0,0 @@
|
||||
# PraisonAI Persistence CLI - End-to-End Guide
|
||||
|
||||
This guide demonstrates the CLI commands for database persistence.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install "praisonai[tools]"
|
||||
|
||||
# Start Docker containers
|
||||
docker run -d --name praison-postgres -p 5432:5432 \
|
||||
-e POSTGRES_PASSWORD=praison123 \
|
||||
-e POSTGRES_DB=praisonai \
|
||||
postgres:16
|
||||
|
||||
docker run -d --name praison-qdrant -p 6333:6333 qdrant/qdrant
|
||||
docker run -d --name praison-redis -p 6379:6379 redis:7
|
||||
```
|
||||
|
||||
## 1. Doctor Command - Validate Connectivity
|
||||
|
||||
```bash
|
||||
# Check all stores
|
||||
praisonai persistence doctor \
|
||||
--conversation-url "postgresql://postgres:praison123@localhost:5432/praisonai" \
|
||||
--knowledge-url "http://localhost:6333" \
|
||||
--state-url "redis://localhost:6379"
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
==================================================
|
||||
PraisonAI Persistence Doctor
|
||||
==================================================
|
||||
|
||||
[Conversation Store] Testing: postgresql://postgres:****@localhost:5432/praisonai
|
||||
✅ Connected (postgres)
|
||||
|
||||
[Knowledge Store] Testing: http://localhost:6333
|
||||
✅ Connected (qdrant)
|
||||
|
||||
[State Store] Testing: redis://localhost:6379
|
||||
✅ Connected (redis)
|
||||
|
||||
==================================================
|
||||
Results: 3/3 stores connected successfully
|
||||
==================================================
|
||||
```
|
||||
|
||||
## 2. Run Command - Execute Agent with Persistence
|
||||
|
||||
```bash
|
||||
# Run agent with persistence (dry-run first)
|
||||
praisonai persistence run \
|
||||
--session-id "cli-demo-001" \
|
||||
--conversation-url "postgresql://postgres:praison123@localhost:5432/praisonai" \
|
||||
--dry-run \
|
||||
"Hello, my name is Bob"
|
||||
|
||||
# Actually run it
|
||||
praisonai persistence run \
|
||||
--session-id "cli-demo-001" \
|
||||
--conversation-url "postgresql://postgres:praison123@localhost:5432/praisonai" \
|
||||
"Hello, my name is Bob"
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
[Session: cli-demo-001]
|
||||
Agent: Hello Bob! Nice to meet you. How can I help you today?
|
||||
```
|
||||
|
||||
## 3. Resume Command - Continue a Session
|
||||
|
||||
```bash
|
||||
# Show history
|
||||
praisonai persistence resume \
|
||||
--session-id "cli-demo-001" \
|
||||
--conversation-url "postgresql://postgres:praison123@localhost:5432/praisonai" \
|
||||
--show-history
|
||||
|
||||
# Continue conversation
|
||||
praisonai persistence resume \
|
||||
--session-id "cli-demo-001" \
|
||||
--conversation-url "postgresql://postgres:praison123@localhost:5432/praisonai" \
|
||||
--continue "What's my name?"
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
[Session: cli-demo-001]
|
||||
Messages in history: 2
|
||||
|
||||
--- Conversation History ---
|
||||
[USER] Hello, my name is Bob
|
||||
[ASSISTANT] Hello Bob! Nice to meet you...
|
||||
--- End History ---
|
||||
|
||||
Agent: Your name is Bob!
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Instead of passing URLs on command line, use environment variables:
|
||||
|
||||
```bash
|
||||
export PRAISON_CONVERSATION_URL="postgresql://postgres:praison123@localhost:5432/praisonai"
|
||||
export PRAISON_KNOWLEDGE_URL="http://localhost:6333"
|
||||
export PRAISON_STATE_URL="redis://localhost:6379"
|
||||
export OPENAI_API_KEY="your-key"
|
||||
|
||||
# Now commands are simpler
|
||||
praisonai persistence doctor --all
|
||||
praisonai persistence run --session-id "my-session" "Hello!"
|
||||
```
|
||||
|
||||
## Full CLI Reference
|
||||
|
||||
```
|
||||
praisonai persistence --help
|
||||
praisonai persistence doctor --help
|
||||
praisonai persistence run --help
|
||||
praisonai persistence resume --help
|
||||
```
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic Context Compaction Example for PraisonAI Agents.
|
||||
|
||||
This example demonstrates how to use context compaction with Agent:
|
||||
1. Create an Agent with context= parameter for compaction
|
||||
2. Use different compaction strategies via ManagerConfig
|
||||
3. Monitor compaction results
|
||||
|
||||
Usage:
|
||||
python basic_compaction.py
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.context import ManagerConfig
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("Agent-Centric Context Compaction Demo")
|
||||
print("=" * 60)
|
||||
|
||||
# ==========================================================================
|
||||
# Available Strategies
|
||||
# ==========================================================================
|
||||
print("\n--- Available Compaction Strategies ---")
|
||||
|
||||
strategies = [
|
||||
("truncate", "Remove oldest messages first"),
|
||||
("sliding_window", "Keep most recent messages within limit"),
|
||||
("summarize", "Replace old messages with summary"),
|
||||
("smart", "Intelligently select messages to keep"),
|
||||
]
|
||||
|
||||
for name, desc in strategies:
|
||||
print(f" {name}: {desc}")
|
||||
|
||||
# ==========================================================================
|
||||
# Agent with Context Compaction via context= param
|
||||
# ==========================================================================
|
||||
print("\n--- Creating Agent with Context Compaction ---")
|
||||
|
||||
# Use context= parameter with ManagerConfig for compaction
|
||||
agent = Agent(
|
||||
name="LongChat",
|
||||
instructions="You are a helpful assistant for extended conversations.",
|
||||
context=ManagerConfig(
|
||||
auto_compact=True,
|
||||
compact_threshold=0.8,
|
||||
strategy="sliding_window",
|
||||
)
|
||||
)
|
||||
|
||||
print(f"Agent: {agent.name}")
|
||||
print(f"Context manager: {agent.context_manager is not None}")
|
||||
if agent.context_manager:
|
||||
print(f"Auto-compact: {agent.context_manager.config.auto_compact}")
|
||||
print(f"Threshold: {agent.context_manager.config.compact_threshold}")
|
||||
print(f"Strategy: {agent.context_manager.config.strategy}")
|
||||
|
||||
# ==========================================================================
|
||||
# Different Strategy Examples via context= param
|
||||
# ==========================================================================
|
||||
print("\n--- Strategy Examples ---")
|
||||
|
||||
# Sliding window strategy
|
||||
sliding_agent = Agent(
|
||||
name="SlidingAgent",
|
||||
instructions="You are helpful.",
|
||||
context=ManagerConfig(
|
||||
auto_compact=True,
|
||||
strategy="sliding_window",
|
||||
)
|
||||
)
|
||||
print(f" Sliding: strategy={sliding_agent.context_manager.config.strategy}")
|
||||
|
||||
# Summarize strategy
|
||||
summarize_agent = Agent(
|
||||
name="SummarizeAgent",
|
||||
instructions="You are helpful.",
|
||||
context=ManagerConfig(
|
||||
auto_compact=True,
|
||||
strategy="summarize",
|
||||
)
|
||||
)
|
||||
print(f" Summarize: strategy={summarize_agent.context_manager.config.strategy}")
|
||||
|
||||
# Smart strategy
|
||||
smart_agent = Agent(
|
||||
name="SmartAgent",
|
||||
instructions="You are helpful.",
|
||||
context=ManagerConfig(
|
||||
auto_compact=True,
|
||||
strategy="smart",
|
||||
)
|
||||
)
|
||||
print(f" Smart: strategy={smart_agent.context_manager.config.strategy}")
|
||||
|
||||
# ==========================================================================
|
||||
# Context Manager Stats Demo
|
||||
# ==========================================================================
|
||||
print("\n--- Context Manager Stats Demo ---")
|
||||
|
||||
# Access context manager stats
|
||||
if agent.context_manager:
|
||||
stats = agent.context_manager.get_stats()
|
||||
print(f" Model: {stats.get('model', 'N/A')}")
|
||||
print(f" Model limit: {stats.get('model_limit', 'N/A')}")
|
||||
print(f" Output reserve: {stats.get('output_reserve', 'N/A')}")
|
||||
print(f" Usable budget: {stats.get('usable_budget', 'N/A')}")
|
||||
else:
|
||||
print(" Context manager not initialized")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Demo Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,85 +0,0 @@
|
||||
"""
|
||||
CLI Compare Example - Advanced Usage
|
||||
|
||||
This example demonstrates advanced comparison features including
|
||||
custom mode configurations and result analysis.
|
||||
"""
|
||||
|
||||
from praisonai.cli.features.compare import (
|
||||
CompareHandler,
|
||||
CompareResult,
|
||||
ModeResult,
|
||||
get_mode_config,
|
||||
save_compare_result,
|
||||
format_comparison_table,
|
||||
)
|
||||
|
||||
# Create custom mode configurations
|
||||
custom_modes = {
|
||||
"fast": {"llm": "gpt-4o-mini"},
|
||||
"accurate": {"llm": "gpt-4o", "planning": True},
|
||||
"research": {"research": True, "web_search": True},
|
||||
}
|
||||
|
||||
# Create handler with verbose output
|
||||
handler = CompareHandler(output="verbose")
|
||||
|
||||
# Example: Manually create comparison results for analysis
|
||||
# (useful for testing without API calls)
|
||||
mock_result = CompareResult(
|
||||
query="What are the latest AI trends?",
|
||||
comparisons=[
|
||||
ModeResult(
|
||||
mode="basic",
|
||||
output="AI trends include LLMs, multimodal models, and agents.",
|
||||
execution_time_ms=1234.5,
|
||||
model_used="gpt-4o-mini",
|
||||
tools_used=[]
|
||||
),
|
||||
ModeResult(
|
||||
mode="tools",
|
||||
output="Based on recent search results, AI trends in 2024 include...",
|
||||
execution_time_ms=2567.8,
|
||||
model_used="gpt-4o-mini",
|
||||
tools_used=["internet_search"]
|
||||
),
|
||||
ModeResult(
|
||||
mode="research",
|
||||
output="Comprehensive research shows that AI trends...",
|
||||
execution_time_ms=5432.1,
|
||||
model_used="gpt-4o-mini",
|
||||
tools_used=["internet_search", "web_scraper"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# Get summary statistics
|
||||
summary = mock_result.get_summary()
|
||||
print("Comparison Summary:")
|
||||
print(f" Fastest mode: {summary['fastest']} ({summary['fastest_time_ms']:.1f}ms)")
|
||||
print(f" Slowest mode: {summary['slowest']} ({summary['slowest_time_ms']:.1f}ms)")
|
||||
|
||||
# Format as table
|
||||
print("\n" + format_comparison_table(mock_result))
|
||||
|
||||
# Convert to JSON for storage
|
||||
print("\nJSON Output:")
|
||||
print(mock_result.to_json())
|
||||
|
||||
# Save to file
|
||||
# save_compare_result(mock_result, "advanced_comparison.json")
|
||||
|
||||
print("\n--- CLI Usage Examples ---")
|
||||
print("""
|
||||
# Basic comparison
|
||||
praisonai "What is AI?" --compare "basic,tools"
|
||||
|
||||
# Compare with specific model
|
||||
praisonai "Explain quantum computing" --compare "basic,planning,research" --model gpt-4o
|
||||
|
||||
# Save results to file
|
||||
praisonai "Latest tech news" --compare "basic,tools,web_search" --compare-output results.json
|
||||
|
||||
# Verbose output
|
||||
praisonai "Write a poem" --compare "basic,planning" --verbose
|
||||
""")
|
||||
@@ -1,45 +0,0 @@
|
||||
"""
|
||||
CLI Compare Example - Basic Usage
|
||||
|
||||
This example demonstrates how to compare different CLI modes programmatically.
|
||||
"""
|
||||
|
||||
from praisonai.cli.features.compare import (
|
||||
CompareHandler,
|
||||
get_mode_config,
|
||||
list_available_modes,
|
||||
parse_modes,
|
||||
)
|
||||
|
||||
# List all available comparison modes
|
||||
print("Available modes:")
|
||||
for mode in list_available_modes():
|
||||
config = get_mode_config(mode)
|
||||
print(f" - {mode}: {config}")
|
||||
|
||||
# Parse modes from comma-separated string
|
||||
modes = parse_modes("basic,tools,planning")
|
||||
print(f"\nParsed modes: {modes}")
|
||||
|
||||
# Create compare handler
|
||||
handler = CompareHandler(output="verbose")
|
||||
|
||||
# Compare modes (this will run actual agent calls)
|
||||
# Uncomment to run:
|
||||
# result = handler.compare(
|
||||
# query="What is artificial intelligence?",
|
||||
# modes=["basic", "tools"],
|
||||
# model="gpt-4o-mini"
|
||||
# )
|
||||
# handler.print_result(result)
|
||||
|
||||
# Save results to file
|
||||
# result = handler.execute(
|
||||
# query="Explain machine learning",
|
||||
# modes_str="basic,planning",
|
||||
# output_path="comparison_results.json"
|
||||
# )
|
||||
|
||||
print("\nTo run comparison via CLI:")
|
||||
print(' praisonai "What is AI?" --compare "basic,tools,planning"')
|
||||
print(' praisonai "What is AI?" --compare "basic,research" --compare-output results.json')
|
||||
@@ -1,96 +0,0 @@
|
||||
# Consolidated Params Examples
|
||||
|
||||
Examples demonstrating the **Agent-Centric API** with consolidated parameters.
|
||||
|
||||
## Precedence Ladder
|
||||
|
||||
```
|
||||
Instance > Config > Array > Dict > String > Bool > Default
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Feature | Presets | Example |
|
||||
|---------|---------|---------|
|
||||
| `memory` | file, sqlite, redis, postgres, mem0, mongodb | `memory="sqlite"` |
|
||||
| `output` | minimal, normal, verbose, debug, silent | `output="verbose"` |
|
||||
| `execution` | fast, balanced, thorough, unlimited | `execution="balanced"` |
|
||||
| `planning` | reasoning, read_only, auto | `planning="reasoning"` |
|
||||
| `reflection` | minimal, standard, thorough | `reflection="standard"` |
|
||||
| `guardrails` | strict, permissive, safety | `guardrails="strict"` |
|
||||
| `web` | duckduckgo, tavily, google, bing, serper | `web="duckduckgo"` |
|
||||
| `context` | sliding_window, summarize, truncate | `context="sliding_window"` |
|
||||
| `autonomy` | suggest, auto_edit, full_auto | `autonomy="suggest"` |
|
||||
| `caching` | enabled, disabled, prompt | `caching="prompt"` |
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Examples (Single Feature)
|
||||
|
||||
| File | Feature | Description |
|
||||
|------|---------|-------------|
|
||||
| `basic_agent.py` | Agent | Minimal agent with memory |
|
||||
| `basic_memory.py` | memory | SQLite memory preset |
|
||||
| `basic_output.py` | output | Verbose output preset |
|
||||
| `basic_execution.py` | execution | Execution control |
|
||||
| `basic_guardrails.py` | guardrails | Callable validator |
|
||||
| `basic_reflection.py` | reflection | Self-reflection preset |
|
||||
| `basic_web.py` | web | Web search preset |
|
||||
| `basic_planning.py` | planning | Planning preset |
|
||||
| `basic_autonomy.py` | autonomy | Autonomy preset |
|
||||
| `basic_caching.py` | caching | Caching preset |
|
||||
| `basic_knowledge.py` | knowledge | RAG with sources |
|
||||
| `basic_context.py` | context | Context management |
|
||||
| `basic_hooks.py` | hooks | Lifecycle callbacks |
|
||||
|
||||
### Multi-Agent Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `basic_agents.py` | Multi-agent with memory and planning |
|
||||
|
||||
### Workflow Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `basic_workflow.py` | Simple workflow with output preset |
|
||||
| `basic_workflow_agentlike.py` | Workflow with agent-like params |
|
||||
| `basic_step_override.py` | Step-level param overrides |
|
||||
| `advanced_workflow_full_features.py` | All consolidated params |
|
||||
|
||||
### Advanced Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `advanced_output_execution.py` | Combined output + execution |
|
||||
|
||||
## Usage Forms
|
||||
|
||||
Each consolidated param supports multiple input forms:
|
||||
|
||||
```python
|
||||
# Bool - enable with defaults
|
||||
memory=True
|
||||
|
||||
# String preset
|
||||
memory="sqlite"
|
||||
|
||||
# Dict config
|
||||
memory={"backend": "sqlite", "user_id": "user123"}
|
||||
|
||||
# Config instance
|
||||
memory=MemoryConfig(backend="sqlite", user_id="user123")
|
||||
|
||||
# Array with overrides (for some params)
|
||||
guardrails=["strict", {"max_retries": 10}]
|
||||
```
|
||||
|
||||
## Running Examples
|
||||
|
||||
```bash
|
||||
# Set API key
|
||||
export OPENAI_API_KEY=your_key_here
|
||||
|
||||
# Run any example
|
||||
python basic_agent.py
|
||||
```
|
||||
@@ -1,23 +0,0 @@
|
||||
"""Advanced memory example - with config and overrides."""
|
||||
from praisonaiagents import Agent, MemoryConfig
|
||||
|
||||
# With full config
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with memory.",
|
||||
memory=MemoryConfig(
|
||||
backend="redis",
|
||||
user_id="user123",
|
||||
session_id="session456",
|
||||
auto_memory=True,
|
||||
),
|
||||
)
|
||||
|
||||
# With array [preset, overrides]
|
||||
agent_override = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory=["redis", {"user_id": "custom_user"}],
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.chat("Remember that my favorite color is blue.")
|
||||
print(response)
|
||||
@@ -1,17 +0,0 @@
|
||||
"""
|
||||
Advanced Output + Execution Example - Agent-Centric API
|
||||
|
||||
Demonstrates preset + override array syntax.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Advanced: Preset with overrides using array syntax
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
output=["verbose", {"stream": True, "metrics": True}],
|
||||
execution=["thorough", {"max_iter": 30}],
|
||||
)
|
||||
|
||||
response = agent.start("Explain quantum computing in simple terms.")
|
||||
print(response)
|
||||
@@ -1,55 +0,0 @@
|
||||
"""Advanced Workflow with all consolidated params configured."""
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.workflows import Workflow, WorkflowStep
|
||||
from praisonaiagents.workflows.workflow_configs import (
|
||||
WorkflowOutputConfig, WorkflowPlanningConfig, WorkflowMemoryConfig,
|
||||
)
|
||||
|
||||
# Full-featured workflow with all consolidated params
|
||||
workflow = Workflow(
|
||||
name="FullFeaturedWorkflow",
|
||||
description="Demonstrates all agent-like consolidated params",
|
||||
steps=[
|
||||
WorkflowStep(
|
||||
name="researcher",
|
||||
action="Research: {{input}}",
|
||||
agent=Agent(instructions="You are a researcher."),
|
||||
knowledge=["research_docs/"], # Step-specific knowledge
|
||||
web="duckduckgo", # Use DuckDuckGo for this step
|
||||
),
|
||||
WorkflowStep(
|
||||
name="analyzer",
|
||||
action="Analyze the research",
|
||||
agent=Agent(instructions="You are an analyst."),
|
||||
reflection="thorough", # Thorough reflection for analysis
|
||||
guardrails="strict", # Strict validation
|
||||
),
|
||||
WorkflowStep(
|
||||
name="writer",
|
||||
action="Write the final report",
|
||||
agent=Agent(instructions="You are a writer."),
|
||||
autonomy=True, # Enable autonomy for writing
|
||||
),
|
||||
],
|
||||
# Workflow-level consolidated params
|
||||
output=WorkflowOutputConfig(output="verbose", stream=True),
|
||||
planning=WorkflowPlanningConfig(enabled=True, reasoning=True),
|
||||
memory=WorkflowMemoryConfig(backend="file"),
|
||||
# Agent-like params
|
||||
autonomy=True,
|
||||
knowledge=["shared_docs/"],
|
||||
guardrails=True,
|
||||
web=True,
|
||||
reflection=True,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"Workflow: {workflow.name}")
|
||||
print(f"Output verbose: {workflow._verbose}")
|
||||
print(f"Planning enabled: {workflow._planning_enabled}")
|
||||
print(f"Memory config: {workflow._memory_config}")
|
||||
print(f"Autonomy: {workflow.autonomy}")
|
||||
print(f"Knowledge: {workflow.knowledge}")
|
||||
print(f"Guardrails: {workflow.guardrails}")
|
||||
print(f"Web: {workflow.web}")
|
||||
print(f"Reflection: {workflow.reflection}")
|
||||
@@ -1,12 +0,0 @@
|
||||
"""Basic Agent example with consolidated params."""
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Minimal agent with memory
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory=True,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.chat("Hello!")
|
||||
print(response)
|
||||
@@ -1,17 +0,0 @@
|
||||
"""Basic Agents (multi-agent) example with consolidated params."""
|
||||
from praisonaiagents import Agent, Agents
|
||||
|
||||
# Create agents
|
||||
writer = Agent(instructions="You write content.")
|
||||
editor = Agent(instructions="You edit content.")
|
||||
|
||||
# Multi-agent with consolidated params
|
||||
agents = Agents(
|
||||
agents=[writer, editor],
|
||||
memory=True,
|
||||
planning=True,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = agents.start("Write a haiku about AI")
|
||||
print(result)
|
||||
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
Basic Autonomy Example - Agent-Centric API
|
||||
|
||||
Demonstrates autonomy with consolidated params.
|
||||
Presets: suggest, auto_edit, full_auto
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Basic: Enable autonomy with preset
|
||||
agent = Agent(
|
||||
instructions="You are an autonomous assistant.",
|
||||
autonomy="suggest", # Presets: suggest, auto_edit, full_auto
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.start("Help me organize my project files.")
|
||||
print(response)
|
||||
@@ -1,23 +0,0 @@
|
||||
"""
|
||||
Basic Caching Example - Agent-Centric API
|
||||
|
||||
Demonstrates caching with consolidated params.
|
||||
Presets: enabled, disabled, prompt
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Basic: Enable caching with preset
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with caching enabled.",
|
||||
caching="prompt", # Presets: enabled, disabled, prompt
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# First call - will cache
|
||||
response = agent.start("What is the capital of France?")
|
||||
print(response)
|
||||
|
||||
# Second call - may use cache
|
||||
response = agent.start("What is the capital of France?")
|
||||
print(response)
|
||||
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
Basic Context Example - Agent-Centric API
|
||||
|
||||
Demonstrates context management with consolidated params.
|
||||
Presets: sliding_window, summarize, truncate
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Basic: Enable context management with preset
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with context management.",
|
||||
context="sliding_window", # Presets: sliding_window, summarize, truncate
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.start("Tell me a long story about a dragon.")
|
||||
print(response)
|
||||
@@ -1,16 +0,0 @@
|
||||
"""
|
||||
Basic Execution Example - Agent-Centric API
|
||||
|
||||
Demonstrates execution preset usage.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Basic: Use fast execution preset
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
execution="fast", # Presets: fast, balanced, thorough, unlimited
|
||||
)
|
||||
|
||||
response = agent.start("What is the capital of France?")
|
||||
print(response)
|
||||
@@ -1,26 +0,0 @@
|
||||
"""
|
||||
Basic Guardrails Example - Agent-Centric API
|
||||
|
||||
Demonstrates guardrails with callable validator.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Define a simple guardrail validator
|
||||
def validate_response(output):
|
||||
"""Validate that response doesn't contain forbidden words."""
|
||||
forbidden = ["error", "fail", "cannot"]
|
||||
text = str(output).lower()
|
||||
for word in forbidden:
|
||||
if word in text:
|
||||
return (False, f"Response contains forbidden word: {word}")
|
||||
return (True, output)
|
||||
|
||||
# Basic: Use callable guardrail
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
guardrails=validate_response,
|
||||
)
|
||||
|
||||
response = agent.start("What is 2 + 2?")
|
||||
print(response)
|
||||
@@ -1,28 +0,0 @@
|
||||
"""
|
||||
Basic Hooks Example - Agent-Centric API
|
||||
|
||||
Demonstrates lifecycle hooks with consolidated params.
|
||||
Valid keys: on_step, on_tool_call, middleware
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Define hook callbacks
|
||||
def on_step_callback(step_info):
|
||||
print(f"Step: {step_info}")
|
||||
|
||||
def on_tool_call_callback(tool_info):
|
||||
print(f"Tool called: {tool_info}")
|
||||
|
||||
# Basic: Enable hooks with dict
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
hooks={
|
||||
"on_step": on_step_callback,
|
||||
"on_tool_call": on_tool_call_callback,
|
||||
},
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.start("What is 2 + 2?")
|
||||
print(response)
|
||||
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
Basic Knowledge (RAG) Example - Agent-Centric API
|
||||
|
||||
Demonstrates knowledge with consolidated params.
|
||||
Supports: bool, list of sources, dict config, KnowledgeConfig
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Basic: Enable knowledge with list of sources
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with document knowledge.",
|
||||
knowledge=["docs/"], # List of file/folder paths
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.start("What does the documentation say about installation?")
|
||||
print(response)
|
||||
@@ -1,24 +0,0 @@
|
||||
"""Basic memory example - minimal usage."""
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Simple enable (uses default file-based memory)
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant with memory.",
|
||||
memory=True,
|
||||
)
|
||||
|
||||
# With preset
|
||||
agent_redis = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory="redis", # Uses redis preset
|
||||
)
|
||||
|
||||
# With URL
|
||||
agent_postgres = Agent(
|
||||
instructions="You are a helpful assistant.",
|
||||
memory="postgresql://localhost/mydb", # URL auto-detected
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
response = agent.chat("Remember that my favorite color is blue.")
|
||||
print(response)
|
||||
Loaded 100 of 3370 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user