mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-10 03:31:28 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94d991f4ec | ||
|
|
632cf68423 | ||
|
|
d956c605f7 | ||
|
|
7e3de10f32 | ||
|
|
35199979f0 |
No files matched your search
@@ -0,0 +1,291 @@
|
||||
# Claude Code Integration with PraisonAI Agents
|
||||
|
||||
This document describes the integration of Claude Code with PraisonAI Agents, allowing AI agents to intelligently decide when to use Claude Code for file modifications and coding tasks.
|
||||
|
||||
## Overview
|
||||
|
||||
The refactored implementation replaces direct `litellm` usage with `praisonaiagents`, making Claude Code a custom tool that the AI agent can choose to use based on the user's request. This provides better intelligence and flexibility while maintaining backward compatibility.
|
||||
|
||||
## Key Features
|
||||
|
||||
### 🤖 Agent-Driven Decision Making
|
||||
- AI agent intelligently decides when to use Claude Code vs. regular responses
|
||||
- No manual detection logic - the agent understands context
|
||||
- Automatic tool selection based on request type
|
||||
|
||||
### 🔧 Claude Code as a Custom Tool
|
||||
- Claude Code is implemented as a tool function for PraisonAI Agents
|
||||
- Executes `claude --dangerously-skip-permissions -p "query"`
|
||||
- Supports `--continue` flag for conversation continuity
|
||||
- Git operations with automatic branch creation and PR URLs
|
||||
|
||||
### ⚙️ Flexible Configuration
|
||||
- CLI flag: `praisonai code --claudecode`
|
||||
- UI toggle: "Enable Claude Code (file modifications & coding)"
|
||||
- Environment variable: `PRAISONAI_CLAUDECODE_ENABLED=true`
|
||||
|
||||
### 🔄 Backward Compatibility
|
||||
- Falls back to `litellm` if `praisonaiagents` is not available
|
||||
- Maintains all existing functionality
|
||||
- No breaking changes to existing workflows
|
||||
|
||||
## Architecture
|
||||
|
||||
### Agent Instructions
|
||||
|
||||
The agent receives these instructions to guide tool usage:
|
||||
|
||||
```
|
||||
You are a helpful AI assistant. Use the available tools when needed to provide comprehensive responses.
|
||||
|
||||
If Claude Code tool is available and the user's request involves:
|
||||
- File modifications, code changes, or implementation tasks
|
||||
- Creating, editing, or debugging code
|
||||
- Project setup or development tasks
|
||||
- Git operations or version control
|
||||
|
||||
Then use the Claude Code tool to handle those requests.
|
||||
|
||||
For informational questions, explanations, or general conversations, respond normally without using Claude Code.
|
||||
```
|
||||
|
||||
### Tool Function: `claude_code_tool`
|
||||
|
||||
```python
|
||||
async def claude_code_tool(query: str) -> str:
|
||||
"""
|
||||
Execute Claude Code CLI commands for file modifications and coding tasks.
|
||||
|
||||
Args:
|
||||
query: The user's request that requires file modifications or coding assistance
|
||||
|
||||
Returns:
|
||||
The output from Claude Code execution
|
||||
"""
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Executes Claude Code with `--dangerously-skip-permissions`
|
||||
- Supports conversation continuity with `--continue` flag
|
||||
- Automatic git branch creation and management
|
||||
- PR URL generation for GitHub repositories
|
||||
- 5-minute timeout for long operations
|
||||
- Comprehensive error handling
|
||||
|
||||
### Available Tools
|
||||
|
||||
When Claude Code is enabled, the agent has access to:
|
||||
|
||||
1. **Claude Code Tool** - For file modifications and coding tasks
|
||||
2. **Tavily Web Search** - For internet research (if API key configured)
|
||||
|
||||
The agent decides which tool(s) to use based on the user's request.
|
||||
|
||||
## Usage
|
||||
|
||||
### Command Line Interface
|
||||
|
||||
```bash
|
||||
# Enable Claude Code integration
|
||||
praisonai code --claudecode
|
||||
|
||||
# Use regular mode (no Claude Code)
|
||||
praisonai code
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Force enable Claude Code (overrides UI setting)
|
||||
export PRAISONAI_CLAUDECODE_ENABLED=true
|
||||
|
||||
# Set repository path (defaults to current directory)
|
||||
export PRAISONAI_CODE_REPO_PATH=/path/to/your/repo
|
||||
```
|
||||
|
||||
### UI Settings
|
||||
|
||||
In the PraisonAI Code UI, you can:
|
||||
- Toggle "Enable Claude Code (file modifications & coding)" switch
|
||||
- Setting is saved to database for persistence
|
||||
- CLI flag takes precedence over UI setting
|
||||
|
||||
## Examples
|
||||
|
||||
### File Modification Request
|
||||
**User:** "Create a new Python function to calculate fibonacci numbers"
|
||||
|
||||
**Agent Decision:** Uses Claude Code tool → Creates/modifies files → Returns implementation details
|
||||
|
||||
### Informational Request
|
||||
**User:** "How does fibonacci algorithm work?"
|
||||
|
||||
**Agent Decision:** Responds normally → Provides explanation without file modifications
|
||||
|
||||
### Mixed Request
|
||||
**User:** "Explain how sorting works and implement quicksort in Python"
|
||||
|
||||
**Agent Decision:** May use Claude Code tool for implementation → Provides both explanation and code files
|
||||
|
||||
## Git Integration
|
||||
|
||||
When Claude Code makes changes and git is available:
|
||||
|
||||
1. **Automatic Branch Creation:** Creates `claude-code-YYYYMMDD_HHMMSS` branch
|
||||
2. **Commit Changes:** Commits all modifications with descriptive message
|
||||
3. **Remote Push:** Pushes branch to remote repository (if configured)
|
||||
4. **PR URL Generation:** Generates GitHub PR creation URL
|
||||
|
||||
Example output:
|
||||
```
|
||||
📋 **Pull Request Created:**
|
||||
https://github.com/user/repo/compare/main...claude-code-20241210_143022?quick_pull=1
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Graceful Degradation
|
||||
- If `praisonaiagents` unavailable → Falls back to `litellm`
|
||||
- If Claude Code CLI unavailable → Agent works without the tool
|
||||
- If git unavailable → Code changes without git operations
|
||||
- Network issues → Continues with local operations
|
||||
|
||||
### Error Messages
|
||||
- Clear error reporting in streaming responses
|
||||
- Timeout handling for long operations
|
||||
- Detailed logging for debugging
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### File Structure
|
||||
```
|
||||
src/praisonai/praisonai/
|
||||
├── cli.py # Added --claudecode flag
|
||||
└── ui/
|
||||
└── code.py # Refactored to use praisonaiagents
|
||||
```
|
||||
|
||||
### Key Functions
|
||||
|
||||
1. **`handle_with_praisonai_agents()`** - Main agent execution handler
|
||||
2. **`handle_with_litellm()`** - Fallback for backward compatibility
|
||||
3. **`claude_code_tool()`** - Claude Code tool implementation
|
||||
4. **Settings management** - UI toggles and persistence
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Required:**
|
||||
- `praisonaiagents` - Core agent framework
|
||||
- `subprocess` - For Claude Code CLI execution
|
||||
- `chainlit` - UI framework
|
||||
|
||||
**Optional:**
|
||||
- `tavily` - Web search capability
|
||||
- `git` - Version control operations
|
||||
- `claude` CLI - File modification capabilities
|
||||
|
||||
## Testing
|
||||
|
||||
Use the included test script to verify the integration:
|
||||
|
||||
```bash
|
||||
python test_claude_code_integration.py
|
||||
```
|
||||
|
||||
Tests verify:
|
||||
- Import functionality
|
||||
- PraisonAI Agents availability
|
||||
- Claude Code CLI availability
|
||||
- Environment variable configuration
|
||||
- Basic tool execution
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From Previous Implementation
|
||||
|
||||
The previous implementation used direct Claude Code detection logic. The new implementation:
|
||||
|
||||
1. **Replaces detection logic** with agent intelligence
|
||||
2. **Maintains same UI/CLI interfaces** for easy migration
|
||||
3. **Preserves all existing functionality** while adding flexibility
|
||||
4. **Requires no user workflow changes**
|
||||
|
||||
### Upgrading
|
||||
|
||||
1. Ensure `praisonaiagents` is installed
|
||||
2. Update PraisonAI to include the refactored code
|
||||
3. Use `--claudecode` flag or UI toggle as before
|
||||
4. Existing settings and workflows continue working
|
||||
|
||||
## Best Practices
|
||||
|
||||
### When to Enable Claude Code
|
||||
- File modification projects
|
||||
- Code development workflows
|
||||
- Repository maintenance tasks
|
||||
- Implementation and debugging sessions
|
||||
|
||||
### When to Disable Claude Code
|
||||
- Read-only analysis
|
||||
- Documentation-only sessions
|
||||
- Learning and educational content
|
||||
- General Q&A conversations
|
||||
|
||||
### Configuration Recommendations
|
||||
- Use CLI flag for development sessions
|
||||
- Use UI toggle for mixed workflows
|
||||
- Set environment variables for CI/CD integration
|
||||
- Configure repository path for multi-project setups
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Agent doesn't use Claude Code tool:**
|
||||
- Verify Claude Code is enabled (`--claudecode` flag or UI toggle)
|
||||
- Check that request involves file modifications
|
||||
- Ensure `praisonaiagents` is installed
|
||||
|
||||
**Claude Code tool fails:**
|
||||
- Verify `claude` CLI is installed and in PATH
|
||||
- Check repository permissions
|
||||
- Confirm git repository status (if git operations needed)
|
||||
|
||||
**Fallback to litellm:**
|
||||
- Install `praisonaiagents`: `pip install praisonaiagents`
|
||||
- Check import errors in logs
|
||||
- Verify Python environment compatibility
|
||||
|
||||
### Debug Information
|
||||
|
||||
Enable verbose logging:
|
||||
```bash
|
||||
export LOGLEVEL=DEBUG
|
||||
praisonai code --claudecode
|
||||
```
|
||||
|
||||
Check environment:
|
||||
```python
|
||||
import os
|
||||
print("Claude Code enabled:", os.getenv("PRAISONAI_CLAUDECODE_ENABLED"))
|
||||
print("Repo path:", os.getenv("PRAISONAI_CODE_REPO_PATH"))
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
- Real-time streaming from PraisonAI Agents
|
||||
- Enhanced git workflow integration
|
||||
- Multi-repository support
|
||||
- Advanced tool coordination
|
||||
- Performance optimizations
|
||||
|
||||
### Extension Points
|
||||
- Additional coding tools integration
|
||||
- Custom agent instructions
|
||||
- Workflow templates
|
||||
- Integration with other development tools
|
||||
|
||||
---
|
||||
|
||||
For technical support or questions, please refer to the main PraisonAI documentation or submit issues to the repository.
|
||||
+1
-1
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
flask \
|
||||
"praisonai>=2.2.32" \
|
||||
"praisonai>=2.2.33" \
|
||||
"praisonai[api]" \
|
||||
gunicorn \
|
||||
markdown
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.32" \
|
||||
"praisonai>=2.2.33" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.32" \
|
||||
"praisonai>=2.2.33" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.32" \
|
||||
"praisonai>=2.2.33" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
+2
-2
@@ -121,7 +121,7 @@ healthcheck:
|
||||
## 📦 Package Versions
|
||||
|
||||
All Docker images use consistent, up-to-date versions:
|
||||
- PraisonAI: `>=2.2.32`
|
||||
- PraisonAI: `>=2.2.33`
|
||||
- PraisonAI Agents: `>=0.0.92`
|
||||
- Python: `3.11-slim`
|
||||
|
||||
@@ -218,7 +218,7 @@ docker-compose up -d
|
||||
### Version Pinning
|
||||
To use specific versions, update the Dockerfile:
|
||||
```dockerfile
|
||||
RUN pip install "praisonai==2.2.32" "praisonaiagents==0.0.92"
|
||||
RUN pip install "praisonai==2.2.33" "praisonaiagents==0.0.92"
|
||||
```
|
||||
|
||||
## 🌐 Production Deployment
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.105"
|
||||
version = "0.0.106"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
|
||||
Generated
+1
-1
@@ -2374,7 +2374,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.105"
|
||||
version = "0.0.106"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.32.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.32.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.33.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.33.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -529,6 +529,7 @@ class PraisonAI:
|
||||
parser.add_argument("--call", action="store_true", help="Start the PraisonAI Call server")
|
||||
parser.add_argument("--public", action="store_true", help="Use ngrok to expose the server publicly (only with --call)")
|
||||
parser.add_argument("--merge", action="store_true", help="Merge existing agents.yaml with auto-generated agents instead of overwriting")
|
||||
parser.add_argument("--claudecode", action="store_true", help="Enable Claude Code integration for file modifications and coding tasks")
|
||||
|
||||
# If we're in a test environment, parse with empty args to avoid pytest interference
|
||||
if in_test_env:
|
||||
@@ -549,6 +550,10 @@ class PraisonAI:
|
||||
if args.command == 'code':
|
||||
args.ui = 'chainlit'
|
||||
args.code = True
|
||||
|
||||
# Handle --claudecode flag for code command
|
||||
if getattr(args, 'claudecode', False):
|
||||
os.environ["PRAISONAI_CLAUDECODE_ENABLED"] = "true"
|
||||
if args.command == 'realtime':
|
||||
args.realtime = True
|
||||
if args.command == 'call':
|
||||
|
||||
@@ -56,7 +56,7 @@ class CloudDeployer:
|
||||
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.32 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.33 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -13,16 +13,25 @@ from PIL import Image
|
||||
from context import ContextGatherer
|
||||
from tavily import TavilyClient
|
||||
from crawl4ai import AsyncWebCrawler
|
||||
import subprocess
|
||||
|
||||
# Local application/library imports
|
||||
import chainlit as cl
|
||||
from chainlit.input_widget import TextInput
|
||||
from chainlit.input_widget import TextInput, Switch
|
||||
from chainlit.types import ThreadDict
|
||||
import chainlit.data as cl_data
|
||||
from litellm import acompletion
|
||||
import litellm
|
||||
from db import DatabaseManager
|
||||
|
||||
# PraisonAI Agents imports
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
PRAISONAI_AGENTS_AVAILABLE = True
|
||||
except ImportError:
|
||||
PRAISONAI_AGENTS_AVAILABLE = False
|
||||
# Fallback to litellm for backward compatibility
|
||||
from litellm import acompletion
|
||||
import litellm
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
@@ -41,14 +50,131 @@ logger.addHandler(console_handler)
|
||||
# Set the logging level for the logger
|
||||
logger.setLevel(log_level)
|
||||
|
||||
# Configure litellm same as in llm.py
|
||||
litellm.set_verbose = False
|
||||
litellm.success_callback = []
|
||||
litellm._async_success_callback = []
|
||||
litellm.callbacks = []
|
||||
litellm.drop_params = True
|
||||
litellm.modify_params = True
|
||||
litellm.suppress_debug_messages = True
|
||||
# Configure litellm for backward compatibility (only if praisonaiagents not available)
|
||||
if not PRAISONAI_AGENTS_AVAILABLE:
|
||||
import litellm
|
||||
litellm.set_verbose = False
|
||||
litellm.success_callback = []
|
||||
litellm._async_success_callback = []
|
||||
litellm.callbacks = []
|
||||
litellm.drop_params = True
|
||||
litellm.modify_params = True
|
||||
litellm.suppress_debug_messages = True
|
||||
|
||||
# Claude Code Tool Function
|
||||
async def claude_code_tool(query: str) -> str:
|
||||
"""
|
||||
Execute Claude Code CLI commands for file modifications and coding tasks.
|
||||
|
||||
Args:
|
||||
query: The user's request that requires file modifications or coding assistance
|
||||
|
||||
Returns:
|
||||
The output from Claude Code execution
|
||||
"""
|
||||
try:
|
||||
# Check if the current working directory is a git repository
|
||||
repo_path = os.environ.get("PRAISONAI_CODE_REPO_PATH", ".")
|
||||
|
||||
# Try to detect if git is available and if we're in a git repo
|
||||
git_available = False
|
||||
try:
|
||||
subprocess.run(["git", "status"], cwd=repo_path, capture_output=True, check=True)
|
||||
git_available = True
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
git_available = False
|
||||
|
||||
# Build Claude Code command
|
||||
claude_cmd = ["claude", "--dangerously-skip-permissions", "-p", query]
|
||||
|
||||
# Check if it's a continuation (simple heuristic)
|
||||
user_session_context = cl.user_session.get("claude_code_context", False)
|
||||
if user_session_context:
|
||||
claude_cmd.insert(1, "--continue")
|
||||
|
||||
# Execute Claude Code command
|
||||
result = subprocess.run(
|
||||
claude_cmd,
|
||||
cwd=repo_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300 # 5 minutes timeout
|
||||
)
|
||||
|
||||
# Set context for future requests
|
||||
cl.user_session.set("claude_code_context", True)
|
||||
|
||||
output = result.stdout
|
||||
if result.stderr:
|
||||
output += f"\n\nErrors:\n{result.stderr}"
|
||||
|
||||
# If git is available and changes were made, try to create a branch and PR
|
||||
if git_available and result.returncode == 0:
|
||||
try:
|
||||
# Check for changes
|
||||
git_status = subprocess.run(
|
||||
["git", "status", "--porcelain"],
|
||||
cwd=repo_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
|
||||
if git_status.stdout.strip():
|
||||
# Create a branch for the changes
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
branch_name = f"claude-code-{timestamp}"
|
||||
|
||||
# Create and switch to new branch
|
||||
subprocess.run(["git", "checkout", "-b", branch_name], cwd=repo_path, check=True)
|
||||
|
||||
# Add and commit changes
|
||||
subprocess.run(["git", "add", "."], cwd=repo_path, check=True)
|
||||
commit_message = f"Claude Code changes: {query[:50]}..."
|
||||
subprocess.run(
|
||||
["git", "commit", "-m", commit_message],
|
||||
cwd=repo_path,
|
||||
check=True
|
||||
)
|
||||
|
||||
# Push to remote (if configured)
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "push", "-u", "origin", branch_name],
|
||||
cwd=repo_path,
|
||||
check=True
|
||||
)
|
||||
|
||||
# Generate PR URL (assuming GitHub)
|
||||
remote_url = subprocess.run(
|
||||
["git", "config", "--get", "remote.origin.url"],
|
||||
cwd=repo_path,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if remote_url.returncode == 0:
|
||||
repo_url = remote_url.stdout.strip()
|
||||
if repo_url.endswith(".git"):
|
||||
repo_url = repo_url[:-4]
|
||||
if "github.com" in repo_url:
|
||||
pr_url = f"{repo_url}/compare/main...{branch_name}?quick_pull=1"
|
||||
output += f"\n\n📋 **Pull Request Created:**\n{pr_url}"
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
output += f"\n\n🌲 **Branch created:** {branch_name} (push manually if needed)"
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
output += f"\n\nGit operations failed: {e}"
|
||||
|
||||
return output
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return "Claude Code execution timed out after 5 minutes."
|
||||
except subprocess.CalledProcessError as e:
|
||||
return f"Claude Code execution failed: {e}\nStdout: {e.stdout}\nStderr: {e.stderr}"
|
||||
except Exception as e:
|
||||
return f"Error executing Claude Code: {str(e)}"
|
||||
|
||||
CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
|
||||
|
||||
@@ -109,6 +235,12 @@ async def start():
|
||||
model_name = os.getenv("MODEL_NAME", "gpt-4o-mini")
|
||||
cl.user_session.set("model_name", model_name)
|
||||
logger.debug(f"Model name: {model_name}")
|
||||
|
||||
# Load Claude Code setting (check CLI flag first, then database setting)
|
||||
claude_code_enabled = os.getenv("PRAISONAI_CLAUDECODE_ENABLED", "false").lower() == "true"
|
||||
if not claude_code_enabled:
|
||||
claude_code_enabled = (load_setting("claude_code_enabled") or "false").lower() == "true"
|
||||
|
||||
settings = cl.ChatSettings(
|
||||
[
|
||||
TextInput(
|
||||
@@ -116,6 +248,11 @@ async def start():
|
||||
label="Enter the Model Name",
|
||||
placeholder="e.g., gpt-4o-mini",
|
||||
initial=model_name
|
||||
),
|
||||
Switch(
|
||||
id="claude_code_enabled",
|
||||
label="Enable Claude Code (file modifications & coding)",
|
||||
initial=claude_code_enabled
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -134,10 +271,13 @@ async def setup_agent(settings):
|
||||
logger.debug(settings)
|
||||
cl.user_session.set("settings", settings)
|
||||
model_name = settings["model_name"]
|
||||
claude_code_enabled = settings.get("claude_code_enabled", False)
|
||||
cl.user_session.set("model_name", model_name)
|
||||
cl.user_session.set("claude_code_enabled", claude_code_enabled)
|
||||
|
||||
# Save in settings table
|
||||
save_setting("model_name", model_name)
|
||||
save_setting("claude_code_enabled", str(claude_code_enabled).lower())
|
||||
|
||||
# Save in thread metadata
|
||||
thread_id = cl.user_session.get("thread_id")
|
||||
@@ -152,6 +292,7 @@ async def setup_agent(settings):
|
||||
metadata = {}
|
||||
|
||||
metadata["model_name"] = model_name
|
||||
metadata["claude_code_enabled"] = claude_code_enabled
|
||||
|
||||
# Always store metadata as a dictionary
|
||||
await cl_data._data_layer.update_thread(thread_id, metadata=metadata)
|
||||
@@ -221,6 +362,7 @@ tools = [{
|
||||
@cl.on_message
|
||||
async def main(message: cl.Message):
|
||||
model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
|
||||
claude_code_enabled = cl.user_session.get("claude_code_enabled", False)
|
||||
message_history = cl.user_session.get("message_history", [])
|
||||
repo_path_to_use = os.environ.get("PRAISONAI_CODE_REPO_PATH", ".")
|
||||
gatherer = ContextGatherer(directory=repo_path_to_use)
|
||||
@@ -258,6 +400,87 @@ Context:
|
||||
msg = cl.Message(content="")
|
||||
await msg.send()
|
||||
|
||||
# Use PraisonAI Agents if available, otherwise fallback to litellm
|
||||
if PRAISONAI_AGENTS_AVAILABLE:
|
||||
await handle_with_praisonai_agents(message, user_message, model_name, claude_code_enabled, msg, image)
|
||||
else:
|
||||
await handle_with_litellm(user_message, model_name, message_history, msg, image)
|
||||
|
||||
async def handle_with_praisonai_agents(message, user_message, model_name, claude_code_enabled, msg, image):
|
||||
"""Handle message using PraisonAI Agents framework with optional Claude Code tool"""
|
||||
try:
|
||||
# Prepare tools list
|
||||
available_tools = []
|
||||
|
||||
# Add Tavily search tool if API key available
|
||||
if tavily_api_key:
|
||||
available_tools.append(tavily_web_search)
|
||||
|
||||
# Add Claude Code tool if enabled
|
||||
if claude_code_enabled:
|
||||
available_tools.append(claude_code_tool)
|
||||
|
||||
# Create agent instructions
|
||||
instructions = """You are a helpful AI assistant. Use the available tools when needed to provide comprehensive responses.
|
||||
|
||||
If Claude Code tool is available and the user's request involves:
|
||||
- File modifications, code changes, or implementation tasks
|
||||
- Creating, editing, or debugging code
|
||||
- Project setup or development tasks
|
||||
- Git operations or version control
|
||||
|
||||
Then use the Claude Code tool to handle those requests.
|
||||
|
||||
For informational questions, explanations, or general conversations, respond normally without using Claude Code."""
|
||||
|
||||
# Create agent
|
||||
agent = Agent(
|
||||
name="PraisonAI Assistant",
|
||||
instructions=instructions,
|
||||
llm=model_name,
|
||||
tools=available_tools if available_tools else None
|
||||
)
|
||||
|
||||
# Execute agent with streaming
|
||||
full_response = ""
|
||||
|
||||
# Use agent's streaming capabilities if available
|
||||
try:
|
||||
# For now, use synchronous execution and stream the result
|
||||
# TODO: Implement proper streaming when PraisonAI agents support it
|
||||
result = agent.start(user_message)
|
||||
|
||||
# Stream the response character by character for better UX
|
||||
if hasattr(result, 'raw'):
|
||||
response_text = result.raw
|
||||
else:
|
||||
response_text = str(result)
|
||||
|
||||
for char in response_text:
|
||||
await msg.stream_token(char)
|
||||
full_response += char
|
||||
# Small delay to make streaming visible
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
except Exception as e:
|
||||
error_response = f"Error executing agent: {str(e)}"
|
||||
for char in error_response:
|
||||
await msg.stream_token(char)
|
||||
full_response += char
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
msg.content = full_response
|
||||
await msg.update()
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Failed to use PraisonAI Agents: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
await msg.stream_token(error_msg)
|
||||
msg.content = error_msg
|
||||
await msg.update()
|
||||
|
||||
async def handle_with_litellm(user_message, model_name, message_history, msg, image):
|
||||
"""Fallback handler using litellm for backward compatibility"""
|
||||
# Prepare the completion parameters using the helper function
|
||||
completion_params = _build_completion_params(
|
||||
model_name,
|
||||
@@ -279,7 +502,7 @@ Context:
|
||||
]
|
||||
}
|
||||
# Use a vision-capable model when an image is present
|
||||
completion_params["model"] = "gpt-4-vision-preview" # Adjust this to your actual vision-capable model
|
||||
completion_params["model"] = "gpt-4-vision-preview"
|
||||
|
||||
# Only add tools and tool_choice if Tavily API key is available and no image is uploaded
|
||||
if tavily_api_key:
|
||||
@@ -412,6 +635,10 @@ async def send_count():
|
||||
async def on_chat_resume(thread: ThreadDict):
|
||||
logger.info(f"Resuming chat: {thread['id']}")
|
||||
model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
|
||||
# Load Claude Code setting (check CLI flag first, then database setting)
|
||||
claude_code_enabled = os.getenv("PRAISONAI_CLAUDECODE_ENABLED", "false").lower() == "true"
|
||||
if not claude_code_enabled:
|
||||
claude_code_enabled = (load_setting("claude_code_enabled") or "false").lower() == "true"
|
||||
logger.debug(f"Model name: {model_name}")
|
||||
settings = cl.ChatSettings(
|
||||
[
|
||||
@@ -420,6 +647,11 @@ async def on_chat_resume(thread: ThreadDict):
|
||||
label="Enter the Model Name",
|
||||
placeholder="e.g., gpt-4o-mini",
|
||||
initial=model_name
|
||||
),
|
||||
Switch(
|
||||
id="claude_code_enabled",
|
||||
label="Enable Claude Code (file modifications & coding)",
|
||||
initial=claude_code_enabled
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.32"
|
||||
version = "2.2.33"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
readme = "README.md"
|
||||
license = ""
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.105",
|
||||
"praisonaiagents>=0.0.106",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.32"
|
||||
version = "2.2.33"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
authors = ["Mervin Praison"]
|
||||
license = ""
|
||||
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.105"
|
||||
praisonaiagents = ">=0.0.106"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for Claude Code integration with PraisonAI Agents
|
||||
|
||||
This script tests the basic functionality of the refactored Claude Code integration
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
import unittest
|
||||
|
||||
# Add the src directory to the path for imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai'))
|
||||
|
||||
def test_claude_code_tool_import():
|
||||
"""Test that we can import the Claude Code tool function"""
|
||||
try:
|
||||
from praisonai.ui.code import claude_code_tool
|
||||
print("✅ Successfully imported claude_code_tool")
|
||||
return True
|
||||
except ImportError as e:
|
||||
print(f"❌ Failed to import claude_code_tool: {e}")
|
||||
return False
|
||||
|
||||
def test_praisonai_agents_import():
|
||||
"""Test praisonaiagents import availability"""
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
print("✅ PraisonAI Agents is available")
|
||||
return True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ PraisonAI Agents not available (fallback to litellm): {e}")
|
||||
return False
|
||||
|
||||
def test_claude_code_availability():
|
||||
"""Test if Claude Code CLI is available"""
|
||||
import subprocess
|
||||
try:
|
||||
result = subprocess.run(['claude', '--version'], capture_output=True, text=True, timeout=10)
|
||||
if result.returncode == 0:
|
||||
print(f"✅ Claude Code CLI is available: {result.stdout.strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Claude Code CLI returned error: {result.stderr}")
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
print("⚠️ Claude Code CLI not found in PATH")
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
print("⚠️ Claude Code CLI check timed out")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error checking Claude Code CLI: {e}")
|
||||
return False
|
||||
|
||||
async def test_claude_code_tool_execution():
|
||||
"""Test basic Claude Code tool execution (simple query)"""
|
||||
try:
|
||||
from praisonai.ui.code import claude_code_tool
|
||||
|
||||
# Test with a simple query that shouldn't modify files
|
||||
test_query = "What is the current directory?"
|
||||
result = await claude_code_tool(test_query)
|
||||
|
||||
print(f"✅ Claude Code tool executed successfully")
|
||||
print(f"Query: {test_query}")
|
||||
print(f"Result (first 100 chars): {str(result)[:100]}...")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Claude Code tool execution failed: {e}")
|
||||
return False
|
||||
|
||||
def test_environment_variables():
|
||||
"""Test that environment variables are properly set"""
|
||||
test_vars = [
|
||||
"PRAISONAI_CLAUDECODE_ENABLED",
|
||||
"PRAISONAI_CODE_REPO_PATH"
|
||||
]
|
||||
|
||||
print("Environment Variables:")
|
||||
for var in test_vars:
|
||||
value = os.getenv(var, "NOT_SET")
|
||||
print(f" {var}: {value}")
|
||||
|
||||
return True
|
||||
|
||||
async def run_tests():
|
||||
"""Run all tests"""
|
||||
print("🧪 Testing Claude Code Integration with PraisonAI Agents")
|
||||
print("=" * 60)
|
||||
|
||||
tests = [
|
||||
("Import Claude Code Tool", test_claude_code_tool_import),
|
||||
("Import PraisonAI Agents", test_praisonai_agents_import),
|
||||
("Environment Variables", test_environment_variables),
|
||||
("Claude Code CLI Availability", test_claude_code_availability),
|
||||
]
|
||||
|
||||
results = {}
|
||||
for test_name, test_func in tests:
|
||||
print(f"\n🔍 {test_name}:")
|
||||
try:
|
||||
results[test_name] = test_func()
|
||||
except Exception as e:
|
||||
print(f"❌ Test failed with exception: {e}")
|
||||
results[test_name] = False
|
||||
|
||||
# Only test Claude Code tool execution if CLI is available
|
||||
if results.get("Claude Code CLI Availability", False):
|
||||
print(f"\n🔍 Claude Code Tool Execution:")
|
||||
try:
|
||||
results["Claude Code Tool Execution"] = await test_claude_code_tool_execution()
|
||||
except Exception as e:
|
||||
print(f"❌ Test failed with exception: {e}")
|
||||
results["Claude Code Tool Execution"] = False
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 Test Summary:")
|
||||
passed = sum(1 for result in results.values() if result)
|
||||
total = len(results)
|
||||
|
||||
for test_name, result in results.items():
|
||||
status = "✅ PASS" if result else "❌ FAIL"
|
||||
print(f" {status} {test_name}")
|
||||
|
||||
print(f"\nResults: {passed}/{total} tests passed")
|
||||
|
||||
if passed == total:
|
||||
print("🎉 All tests passed! Integration is working correctly.")
|
||||
else:
|
||||
print("⚠️ Some tests failed. Please check the implementation.")
|
||||
|
||||
return passed == total
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_tests())
|
||||
Generated
+5
-5
@@ -3931,7 +3931,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonai"
|
||||
version = "2.2.32"
|
||||
version = "2.2.33"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "instructor" },
|
||||
@@ -4073,7 +4073,7 @@ requires-dist = [
|
||||
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.105" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.106" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4130,7 +4130,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.105"
|
||||
version = "0.0.106"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
@@ -4139,9 +4139,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/9b/a95d221c85d222bc265c5817335a8c57dc345e6f7808b6d7dac0b50bfe42/praisonaiagents-0.0.105.tar.gz", hash = "sha256:347ac596f9baea7daeac718ef5c9992c5f3a487b22d53623fc53f6b32dbf4808", size = 154675 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/9a/7b488726d34ebf7f98c27dd3e730ed0ca14dec7e63f9cb8fe4129aaf0e24/praisonaiagents-0.0.106.tar.gz", hash = "sha256:0fecb0df6c1ace618374a3d78eb6f03a058e70c606449069363cda0505a59487", size = 154663 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/93/3797957c4a72e756d0a2d49b25cf300e3c0781b86b0308ae2871a52761e4/praisonaiagents-0.0.105-py3-none-any.whl", hash = "sha256:ad52505ba715970d812a72ae18f00bde6c92ed6838fee0375324b15cd3e32324", size = 175599 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/75/6d56dbb2a03a4d0160ba6f3ebe60b468e3ee859f31c2833ac9d03130bdaf/praisonaiagents-0.0.106-py3-none-any.whl", hash = "sha256:504c4e11558bc21b8757efba8211b231c7ee235c46cbaafe02038fa493124f83", size = 175597 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user