mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
312d1a8734 | ||
|
|
311034e78b | ||
|
|
509d8492ec | ||
|
|
cb4ee28e7e | ||
|
|
7f23daf670 | ||
|
|
223650dad5 | ||
|
|
1e23d76742 | ||
|
|
83adac3709 | ||
|
|
e3a5f87a1f | ||
|
|
63f94c6a4d | ||
|
|
b95b88d0cb | ||
|
|
9b155e54e2 | ||
|
|
a2d7629492 | ||
|
|
a904673325 | ||
|
|
a6d1ef93e0 | ||
|
|
d1989159bc | ||
|
|
b2a0f5b341 | ||
|
|
caabe553ce | ||
|
|
f62601ed47 | ||
|
|
402796020c | ||
|
|
3a30c3813d | ||
|
|
8e16621f46 | ||
|
|
f86e9ef470 | ||
|
|
c4058143aa | ||
|
|
6b4ed568a8 | ||
|
|
1ba540f0ab | ||
|
|
ce4103d311 | ||
|
|
a21c9ea15a | ||
|
|
422d638cd4 | ||
|
|
baa5caf3c3 | ||
|
|
4a51cae4f8 | ||
|
|
caa4473871 | ||
|
|
acdfc54302 | ||
|
|
3856be09df | ||
|
|
81f0f49d15 | ||
|
|
13e425e0ac | ||
|
|
9c9a8b249e | ||
|
|
ff4ab536af | ||
|
|
7a7b9bd0d4 | ||
|
|
8900fdcc95 | ||
|
|
e7f7b1239c | ||
|
|
9596b49f3c | ||
|
|
6fa66157ae | ||
|
|
c50e3d94b5 | ||
|
|
0ef82bdf7e | ||
|
|
a7ce92f9ce | ||
|
|
f36415d4ea | ||
|
|
13c3e31458 | ||
|
|
41d550c4a5 | ||
|
|
ac570e5b93 | ||
|
|
65e389af03 | ||
|
|
a5465882ee | ||
|
|
f86b03e5d8 | ||
|
|
397a466f55 | ||
|
|
36c3066ba4 | ||
|
|
df06e794b3 | ||
|
|
71fe678d66 | ||
|
|
c8875eec81 | ||
|
|
f88204d2e8 | ||
|
|
fc01a80956 | ||
|
|
cd91565b12 | ||
|
|
d233d4597c | ||
|
|
a9fabce0ea | ||
|
|
6bcd3ba9bd | ||
|
|
37028b0dd2 | ||
|
|
6807d0e017 | ||
|
|
bca845d771 | ||
|
|
0596d2da2e | ||
|
|
368498452c | ||
|
|
47e5ad2a65 | ||
|
|
eb346be5f8 | ||
|
|
62ab76f3e9 | ||
|
|
ac26c5fbe8 | ||
|
|
22c10ff6ac | ||
|
|
494f835e73 | ||
|
|
3686eb2eaa | ||
|
|
58c63f0dd4 | ||
|
|
7bf5c0a4d8 | ||
|
|
082d802fd9 | ||
|
|
8cd926a244 | ||
|
|
987f63f032 | ||
|
|
bec938883d | ||
|
|
76079a92fe | ||
|
|
c14fb2ee5d | ||
|
|
31c9a6a994 | ||
|
|
b5b4370cc8 | ||
|
|
745f82b9c4 | ||
|
|
6845c7b0f6 | ||
|
|
e7be52722e | ||
|
|
e673189454 | ||
|
|
ab9192c57e | ||
|
|
819874275f | ||
|
|
0a36fdb966 | ||
|
|
768549bb52 | ||
|
|
5d1bc19e15 | ||
|
|
05a114f939 | ||
|
|
4b9d291d16 | ||
|
|
5eb8862a7e | ||
|
|
0bacff3997 | ||
|
|
ac8e6daadf | ||
|
|
f4cf97abdb | ||
|
|
ff309010b9 |
No files matched your search
@@ -1,8 +0,0 @@
|
||||
1. Make minimal code changes as possible
|
||||
2. if you are creating function, make sure to name it in more easy, user friendly , optimised manner.
|
||||
3. Never copy the exact code if presented with a code from other location or file or codebase, just make it optimised
|
||||
4. Always Follow the principle LESS IS MORE
|
||||
5. Make it minimal change as possible
|
||||
6. Firstly try not to make any modification to the existing code as possible
|
||||
7. Only modify the existing code if its highly required, without that if it cant be done, then add new code section.
|
||||
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:mdx/recommended"],
|
||||
// optional, if you want to lint code blocks at the same time
|
||||
"settings": {
|
||||
"mdx/code-blocks": true,
|
||||
// optional, if you want to disable language mapper, set it to `false`
|
||||
// if you want to override the default language mapper inside, you can provide your own
|
||||
"mdx/language-mapper": {}
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,48 @@ jobs:
|
||||
python-version: ["3.10", "3.11"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v3
|
||||
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-4o-mini' }}" >> $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
|
||||
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: |
|
||||
# pytest
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
cd src/praisonai && python -m pytest
|
||||
@@ -21,9 +21,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
@@ -31,9 +31,10 @@ jobs:
|
||||
python -m pip install --upgrade pip
|
||||
pip install build
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
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/
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
git reset --hard ${{ steps.get_commit.outputs.commit_hash }}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
@@ -422,7 +422,7 @@ jobs:
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: praisonai-output-${{ matrix.os }}-${{ matrix.framework }}
|
||||
path: test_output/
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
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
|
||||
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
|
||||
|
||||
- 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-4o-mini' }}" >> $GITHUB_ENV
|
||||
|
||||
- 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 tests/test_runner.py --pattern unit
|
||||
;;
|
||||
"integration")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern integration
|
||||
;;
|
||||
"fast")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern fast
|
||||
;;
|
||||
"performance")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern "performance"
|
||||
;;
|
||||
"frameworks")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern frameworks
|
||||
;;
|
||||
"autogen")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern autogen
|
||||
;;
|
||||
"crewai")
|
||||
cd src/praisonai && python tests/test_runner.py --pattern crewai
|
||||
;;
|
||||
"all"|*)
|
||||
cd src/praisonai && python tests/test_runner.py --pattern all
|
||||
;;
|
||||
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 Comprehensive Test Results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: comprehensive-test-results-python-${{ matrix.python-version }}
|
||||
path: |
|
||||
comprehensive_report.md
|
||||
htmlcov/
|
||||
coverage.xml
|
||||
.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
|
||||
@@ -0,0 +1,247 @@
|
||||
name: Core Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
test-core:
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
|
||||
- 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-4o-mini' }}" >> $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-4o-mini' }}"
|
||||
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-4o-mini' }}
|
||||
LOGLEVEL: DEBUG
|
||||
run: |
|
||||
echo "🔍 Testing PraisonAI API key usage directly..."
|
||||
cd src/praisonai
|
||||
python -c "
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
print('🔧 Direct PraisonAI API Key Check:')
|
||||
print(f'Environment OPENAI_API_KEY: {os.environ.get(\"OPENAI_API_KEY\", \"NOT_SET\")[:10]}...')
|
||||
|
||||
# Test PraisonAI initialization
|
||||
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: {api_key_from_config[:10] if api_key_from_config != \"NOT_SET\" else \"NOT_SET\"}...')
|
||||
|
||||
# Test PraisonAIModel with explicit API key (the way CrewAI will use it)
|
||||
from praisonai.inc.models import PraisonAIModel
|
||||
|
||||
print('\\n🧪 Testing PraisonAIModel with explicit API key (CrewAI method):')
|
||||
model_with_explicit_key = PraisonAIModel(
|
||||
model='openai/gpt-4o-mini',
|
||||
base_url=praisonai.config_list[0].get('base_url'),
|
||||
api_key=praisonai.config_list[0].get('api_key')
|
||||
)
|
||||
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}')
|
||||
print(f' API key (explicit): {model_with_explicit_key.api_key[:10] if model_with_explicit_key.api_key != \"nokey\" else \"NOT_SET\"}...')
|
||||
print(f' Base URL: {model_with_explicit_key.base_url}')
|
||||
|
||||
# Test if the model can be created without errors
|
||||
try:
|
||||
llm_instance = model_with_explicit_key.get_model()
|
||||
print(f' ✅ LLM instance created successfully: {type(llm_instance).__name__}')
|
||||
print(f' LLM instance API key: {getattr(llm_instance, \"openai_api_key\", \"NOT_FOUND\")[:10] if hasattr(llm_instance, \"openai_api_key\") else \"NO_API_KEY_ATTR\"}...')
|
||||
except Exception as e:
|
||||
print(f' ❌ Failed to create LLM instance: {e}')
|
||||
"
|
||||
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
|
||||
|
||||
- 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-4o-mini' }}
|
||||
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')"
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings
|
||||
|
||||
- name: Upload Coverage Reports
|
||||
if: matrix.python-version == '3.11'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: |
|
||||
.coverage
|
||||
htmlcov/
|
||||
retention-days: 7
|
||||
@@ -0,0 +1,160 @@
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
- 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-4o-mini' }}" >> $GITHUB_ENV
|
||||
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $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
|
||||
|
||||
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
|
||||
|
||||
- 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: 30
|
||||
@@ -0,0 +1,153 @@
|
||||
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
|
||||
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
|
||||
|
||||
- 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-4o-mini' }}" >> $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
|
||||
@@ -0,0 +1,165 @@
|
||||
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' }}
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
+480
-12
@@ -1,35 +1,503 @@
|
||||
name: Run specific unittest
|
||||
name: Quick Validation Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
quick-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install UV
|
||||
run: |
|
||||
pip install uv
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install duckduckgo_search
|
||||
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
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME }}" >> $GITHUB_ENV
|
||||
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-4o-mini' }}" >> $GITHUB_ENV
|
||||
echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV
|
||||
echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Run specific unittest
|
||||
- name: Backup Root Config Files
|
||||
run: |
|
||||
uv run python -m unittest tests.test.TestExamples
|
||||
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-4o-mini (from YAML)
|
||||
model = PraisonAIModel(model='openai/gpt-4o-mini')
|
||||
|
||||
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
|
||||
cd src/praisonai && python tests/test_runner.py --pattern fast
|
||||
|
||||
- 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
|
||||
continue-on-error: 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
|
||||
@@ -52,6 +52,10 @@ agentops.log
|
||||
|
||||
# crewAI
|
||||
crewAI
|
||||
!tests/integration/crewai
|
||||
!tests/e2e/crewai
|
||||
!src/praisonai/tests/integration/crewai
|
||||
!src/praisonai/tests/e2e/crewai
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
@@ -70,3 +74,4 @@ agents/praisonaiagents/praisonaiagents.egg-info
|
||||
.praison
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
.qodo
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN pip install flask praisonai==2.2.2 gunicorn markdown
|
||||
RUN pip install flask praisonai==2.2.17 gunicorn markdown
|
||||
EXPOSE 8080
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonaiagents>=0.0.4 \
|
||||
praisonai_tools \
|
||||
"praisonai==2.2.2" \
|
||||
"praisonai==2.2.17" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonaiagents>=0.0.4 \
|
||||
praisonai_tools \
|
||||
"praisonai==2.2.2" \
|
||||
"praisonai==2.2.17" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonaiagents>=0.0.4 \
|
||||
praisonai_tools \
|
||||
"praisonai==2.2.2" \
|
||||
"praisonai==2.2.17" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
File renamed without changes.
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.2 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.17 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.2.2 watchdog
|
||||
RUN pip install flask praisonai==2.2.17 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@
|
||||
"models/google",
|
||||
"models/openrouter",
|
||||
"models/anthropic",
|
||||
"models/aws",
|
||||
"models/cohere",
|
||||
"models/mistral",
|
||||
"models/deepseek",
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,173 @@
|
||||
---
|
||||
title: "AWS Bedrock"
|
||||
description: "Instructions for integrating AWS Bedrock models with PraisonAI, including API setup and agent configuration"
|
||||
icon: "aws"
|
||||
---
|
||||
|
||||
# Add AWS Bedrock to PraisonAI
|
||||
|
||||
AWS Bedrock provides access to high-performing foundation models from leading AI companies like Anthropic, Cohere, Meta, Stability AI, and Amazon through a single API.
|
||||
|
||||
## Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Make sure you have AWS credentials configured:
|
||||
|
||||
```bash
|
||||
pip install boto3
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Set up your AWS credentials:
|
||||
|
||||
```bash
|
||||
export AWS_ACCESS_KEY_ID=your_access_key_id
|
||||
export AWS_SECRET_ACCESS_KEY=your_secret_access_key
|
||||
export AWS_REGION=us-east-1
|
||||
```
|
||||
|
||||
## Using AWS Bedrock Models
|
||||
|
||||
### Available Models
|
||||
|
||||
AWS Bedrock supports various model providers:
|
||||
|
||||
- **Anthropic Claude**: `bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0`
|
||||
- **Anthropic Claude Instant**: `bedrock/anthropic.claude-instant-v1`
|
||||
- **Amazon Titan**: `bedrock/amazon.titan-text-express-v1`
|
||||
- **Cohere Command**: `bedrock/cohere.command-text-v14`
|
||||
- **Meta Llama**: `bedrock/meta.llama2-70b-chat-v1`
|
||||
|
||||
### agents.yaml Configuration
|
||||
|
||||
```yaml
|
||||
framework: crewai
|
||||
topic: create movie script about cat in mars
|
||||
roles:
|
||||
researcher:
|
||||
backstory: Skilled in finding and organizing information, with a focus on research efficiency.
|
||||
goal: Gather information about Mars and cats
|
||||
role: Researcher
|
||||
llm:
|
||||
model: "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
temperature: 0.7
|
||||
tasks:
|
||||
gather_research:
|
||||
description: Research and gather information about Mars, its environment, and cats, including their behavior and characteristics.
|
||||
expected_output: Document with research findings, including interesting facts and information.
|
||||
tools:
|
||||
- ''
|
||||
```
|
||||
|
||||
### Python Code Example
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Using Anthropic Claude via Bedrock
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm={
|
||||
"model": "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"temperature": 0.7
|
||||
}
|
||||
)
|
||||
|
||||
# Using Amazon Titan via Bedrock
|
||||
titan_agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm={
|
||||
"model": "bedrock/amazon.titan-text-express-v1",
|
||||
"temperature": 0.7
|
||||
}
|
||||
)
|
||||
|
||||
response = agent.ask("What is artificial intelligence?")
|
||||
print(response)
|
||||
```
|
||||
|
||||
## IAM Permissions
|
||||
|
||||
Ensure your AWS IAM user/role has the necessary permissions to access Bedrock:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"bedrock:InvokeModel",
|
||||
"bedrock:InvokeModelWithResponseStream"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Regional Availability
|
||||
|
||||
AWS Bedrock is available in the following regions:
|
||||
- `us-east-1` (N. Virginia)
|
||||
- `us-west-2` (Oregon)
|
||||
- `ap-southeast-1` (Singapore)
|
||||
- `ap-northeast-1` (Tokyo)
|
||||
- `eu-central-1` (Frankfurt)
|
||||
- `eu-west-3` (Paris)
|
||||
|
||||
Make sure to set your `AWS_REGION` environment variable to a supported region.
|
||||
|
||||
## Cost Optimization
|
||||
|
||||
AWS Bedrock charges are based on:
|
||||
- **Input tokens**: Text sent to the model
|
||||
- **Output tokens**: Text generated by the model
|
||||
|
||||
Consider using smaller models for development and testing to optimize costs.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Common errors and solutions:
|
||||
|
||||
- **AccessDeniedException**: Check your IAM permissions
|
||||
- **ResourceNotFoundException**: Verify the model ID is correct and available in your region
|
||||
- **ThrottlingException**: Implement retry logic with exponential backoff
|
||||
- **ValidationException**: Check your input parameters and format
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Endpoint
|
||||
|
||||
For specific regions or custom endpoints:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm={
|
||||
"model": "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"aws_region": "us-west-2",
|
||||
"temperature": 0.7
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Streaming Responses
|
||||
|
||||
For real-time responses:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm={
|
||||
"model": "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"stream": True
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
| PraisonAI Chat | PraisonAI Code | PraisonAI (Multi-Agents) |
|
||||
| --- | --- | --- |
|
||||
| [LiteLLM](https://litellm.vercel.app/docs/providers) | [LiteLLM](https://litellm.vercel.app/docs/providers) | [Models](../models.md) |
|
||||
@@ -10,8 +10,41 @@ icon: "terminal"
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Ollama Integration" defaultOpen>
|
||||
**Standard OpenAI SDK Environment Variables (Recommended):**
|
||||
```bash
|
||||
export OPENAI_BASE_URL=http://localhost:11434/v1
|
||||
export MODEL_NAME=deepseek-r1:14b
|
||||
export OPENAI_API_KEY=NA
|
||||
```
|
||||
|
||||
**Alternative Environment Variable Patterns:**
|
||||
```bash
|
||||
# Community recommended pattern
|
||||
export OLLAMA_API_BASE=http://localhost:11434
|
||||
export MODEL_NAME=gemma3:4b
|
||||
export OPENAI_API_KEY=fake-key
|
||||
|
||||
# Legacy pattern (still supported)
|
||||
export OPENAI_API_BASE=http://localhost:11434/v1
|
||||
export OPENAI_MODEL_NAME=llama2
|
||||
export OPENAI_API_KEY=not-needed
|
||||
```
|
||||
|
||||
**Usage with praisonai --init:**
|
||||
```bash
|
||||
# Make sure Ollama is running
|
||||
ollama serve
|
||||
|
||||
# Pull your desired model
|
||||
ollama pull deepseek-r1:14b
|
||||
|
||||
# Set environment variables
|
||||
export OPENAI_BASE_URL=http://localhost:11434/v1
|
||||
export MODEL_NAME=deepseek-r1:14b
|
||||
export OPENAI_API_KEY=NA
|
||||
|
||||
# Initialize agents
|
||||
praisonai --init "Create a story about AI"
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.2.2 watchdog
|
||||
RUN pip install flask praisonai==2.2.17 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.2.2 watchdog
|
||||
RUN pip install flask praisonai==2.2.17 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import globals from "globals";
|
||||
import * as mdx from 'eslint-plugin-mdx';
|
||||
|
||||
export default [
|
||||
{
|
||||
files: ["**/*.{md,mdx}"],
|
||||
processor: mdx.createRemarkProcessor({
|
||||
lintCodeBlocks: true,
|
||||
}),
|
||||
languageOptions: {
|
||||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
'no-var': 'error',
|
||||
'prefer-const': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,98 @@
|
||||
framework: crewai
|
||||
topic: "Technology Research and Content Creation using AWS Bedrock"
|
||||
|
||||
roles:
|
||||
research_specialist:
|
||||
role: "Research Specialist"
|
||||
backstory: |
|
||||
You are an experienced technology researcher with deep expertise in analyzing
|
||||
emerging technologies and market trends. You excel at gathering comprehensive
|
||||
information from various sources and synthesizing it into actionable insights.
|
||||
goal: |
|
||||
Research and analyze the latest developments in artificial intelligence and
|
||||
cloud computing technologies, focusing on practical applications and market impact.
|
||||
verbose: true
|
||||
allow_delegation: false
|
||||
llm:
|
||||
model: "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
temperature: 0.3
|
||||
tools:
|
||||
- "InternetSearchTool"
|
||||
tasks:
|
||||
technology_research:
|
||||
description: |
|
||||
Research the latest developments in AI and cloud computing. Focus on:
|
||||
1. Emerging AI model architectures and capabilities
|
||||
2. Cloud service innovations and their business impact
|
||||
3. Integration patterns between AI and cloud platforms
|
||||
4. Market adoption trends and future predictions
|
||||
expected_output: |
|
||||
A comprehensive research report with:
|
||||
- Executive summary of key findings
|
||||
- Detailed analysis of 3-5 major technology trends
|
||||
- Market impact assessment
|
||||
- Future outlook and recommendations
|
||||
|
||||
content_creator:
|
||||
role: "Technical Content Creator"
|
||||
backstory: |
|
||||
You are a skilled technical writer and content creator who specializes in
|
||||
translating complex technology concepts into engaging, accessible content
|
||||
for various audiences including developers, business leaders, and end users.
|
||||
goal: |
|
||||
Create compelling and informative content that explains complex technologies
|
||||
in clear, engaging ways while maintaining technical accuracy and relevance.
|
||||
verbose: true
|
||||
allow_delegation: false
|
||||
llm:
|
||||
model: "bedrock/amazon.titan-text-express-v1"
|
||||
temperature: 0.7
|
||||
tasks:
|
||||
content_creation:
|
||||
description: |
|
||||
Based on the research findings, create engaging technical content including:
|
||||
1. A blog post explaining the technology trends for a business audience
|
||||
2. Technical documentation for developers
|
||||
3. Executive summary for decision makers
|
||||
4. Social media content highlighting key insights
|
||||
expected_output: |
|
||||
A content package including:
|
||||
- 800-word blog post with clear structure and examples
|
||||
- Technical implementation guide (500 words)
|
||||
- Executive summary (200 words)
|
||||
- 3-5 social media posts with key takeaways
|
||||
|
||||
quality_reviewer:
|
||||
role: "Content Quality Reviewer"
|
||||
backstory: |
|
||||
You are a meticulous editor and quality assurance specialist with extensive
|
||||
experience in technical content review. You ensure accuracy, clarity, and
|
||||
consistency while maintaining the intended tone and message.
|
||||
goal: |
|
||||
Review and refine all content to ensure it meets high standards for accuracy,
|
||||
clarity, engagement, and professional presentation.
|
||||
verbose: true
|
||||
allow_delegation: false
|
||||
llm:
|
||||
model: "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0"
|
||||
temperature: 0.2
|
||||
tasks:
|
||||
content_review:
|
||||
description: |
|
||||
Review all created content for:
|
||||
1. Technical accuracy and factual correctness
|
||||
2. Clarity and readability for target audiences
|
||||
3. Consistency in tone and messaging
|
||||
4. Grammar, style, and formatting
|
||||
5. Overall engagement and impact
|
||||
expected_output: |
|
||||
A comprehensive review report with:
|
||||
- Content quality assessment
|
||||
- Specific recommendations for improvements
|
||||
- Final polished versions of all content
|
||||
- Quality score and approval status
|
||||
|
||||
dependencies:
|
||||
- research_specialist
|
||||
- content_creator
|
||||
- quality_reviewer
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 539 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user