mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-09 03:39:37 -10:00
Compare commits
No files matched your search
@@ -11,6 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
@@ -10,6 +10,7 @@ jobs:
|
||||
test:
|
||||
name: Run tests and collect coverage
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -26,6 +26,7 @@ on:
|
||||
jobs:
|
||||
comprehensive-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.11"]
|
||||
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
jobs:
|
||||
test-core:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
jobs:
|
||||
test-examples:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -43,6 +44,9 @@ jobs:
|
||||
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
|
||||
echo "PRAISONAI_TELEMETRY_DISABLED=true" >> $GITHUB_ENV
|
||||
echo "PRAISONAI_DISABLE_TELEMETRY=true" >> $GITHUB_ENV
|
||||
echo "DO_NOT_TRACK=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Test Key Example Scripts
|
||||
run: |
|
||||
@@ -73,6 +77,7 @@ jobs:
|
||||
|
||||
performance-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -97,6 +102,12 @@ jobs:
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "PRAISONAI_TELEMETRY_DISABLED=true" >> $GITHUB_ENV
|
||||
echo "PRAISONAI_DISABLE_TELEMETRY=true" >> $GITHUB_ENV
|
||||
echo "DO_NOT_TRACK=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Run Performance Benchmarks
|
||||
run: |
|
||||
echo "🏃 Running performance benchmarks..."
|
||||
@@ -161,4 +172,4 @@ jobs:
|
||||
with:
|
||||
name: performance-report
|
||||
path: performance_report.md
|
||||
retention-days: 30
|
||||
retention-days: 5
|
||||
@@ -24,6 +24,7 @@ on:
|
||||
jobs:
|
||||
framework-tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
real-tests:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.confirm_costs == 'true' && github.event.inputs.framework != 'none' }}
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -5,6 +5,7 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
quick-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
+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.70" \
|
||||
"praisonai>=2.2.71" \
|
||||
"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.70" \
|
||||
"praisonai>=2.2.71" \
|
||||
"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.70" \
|
||||
"praisonai>=2.2.71" \
|
||||
"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.70" \
|
||||
"praisonai>=2.2.71" \
|
||||
"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.70`
|
||||
- PraisonAI: `>=2.2.71`
|
||||
- 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.70" "praisonaiagents==0.0.92"
|
||||
RUN pip install "praisonai==2.2.71" "praisonaiagents==0.0.92"
|
||||
```
|
||||
|
||||
## 🌐 Production Deployment
|
||||
|
||||
@@ -130,9 +130,11 @@ def example_mcp_tracking():
|
||||
)
|
||||
plan = agent.chat("Plan: Search for Python documentation")
|
||||
|
||||
# Track tool usage
|
||||
# Track tool usage (this will now have actual timing)
|
||||
with tracker.track("tool_usage", request_id):
|
||||
# Simulate tool execution
|
||||
# Simulate tool execution with realistic timing
|
||||
import time
|
||||
time.sleep(0.1) # Simulate some work
|
||||
tool_result = "Found 5 relevant documentation pages"
|
||||
|
||||
# Track LLM generation
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a creative designer AI agent. "
|
||||
"Help users with graphic design concepts, branding strategies, "
|
||||
"visual communication, and creative problem-solving. Provide guidance "
|
||||
"on design principles, color theory, typography, and user experience.",
|
||||
llm="anthropic/claude-3-5-sonnet-20241022"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your creative designer assistant. "
|
||||
"How can I help you with your design projects today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are an educational tutor AI agent. "
|
||||
"Help students learn various subjects, explain complex concepts, "
|
||||
"provide step-by-step solutions, and create personalized learning "
|
||||
"materials for different educational levels.",
|
||||
llm="anthropic/claude-3-5-sonnet-20241022"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your educational tutor assistant. "
|
||||
"How can I help you learn today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a financial advisor AI agent. "
|
||||
"Help users understand financial concepts, analyze investment opportunities, "
|
||||
"and provide guidance on personal finance, budgeting, and financial planning. "
|
||||
"Note: This is for educational purposes only, not financial advice.",
|
||||
llm="anthropic/claude-3-5-sonnet-20241022"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your financial advisor assistant. "
|
||||
"How can I help you understand financial concepts today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a legal advisor AI agent. "
|
||||
"Help users understand legal concepts, review contracts, "
|
||||
"analyze legal documents, and provide guidance on compliance "
|
||||
"and regulatory requirements. Note: This is for educational purposes only.",
|
||||
llm="anthropic/claude-3-5-sonnet-20241022"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your legal advisor assistant. "
|
||||
"How can I help you understand legal concepts today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a medical research AI agent. "
|
||||
"Help users understand medical concepts, analyze research papers, "
|
||||
"and provide insights on healthcare trends and developments. "
|
||||
"Note: This is for educational purposes only, not medical advice.",
|
||||
llm="anthropic/claude-3-5-sonnet-20241022"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your medical research assistant. "
|
||||
"How can I help you understand medical concepts today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a cybersecurity AI agent. "
|
||||
"Help users understand security concepts, analyze security threats, "
|
||||
"and provide guidance on security best practices, risk assessment, "
|
||||
"and security architecture design.",
|
||||
llm="openai/gpt-4o"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your cybersecurity assistant. "
|
||||
"How can I help you with security concepts today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a data engineer AI agent. "
|
||||
"Help users design data pipelines, optimize database performance, "
|
||||
"create ETL processes, and provide guidance on data architecture, "
|
||||
"data modeling, and big data technologies.",
|
||||
llm="openai/gpt-4o"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your data engineer assistant. "
|
||||
"How can I help you with your data engineering projects today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a marketing strategist AI agent. "
|
||||
"Help users develop marketing strategies, analyze market trends, "
|
||||
"create campaign plans, and provide guidance on digital marketing, "
|
||||
"brand positioning, and customer acquisition strategies.",
|
||||
llm="openai/gpt-4o"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your marketing strategist assistant. "
|
||||
"How can I help you with your marketing strategy today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a product manager AI agent. "
|
||||
"Help users with product strategy, feature prioritization, "
|
||||
"user research analysis, and product roadmap planning. Provide "
|
||||
"guidance on market analysis, competitive positioning, and user experience.",
|
||||
llm="openai/gpt-4o"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your product manager assistant. "
|
||||
"How can I help you with your product strategy today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a software architect AI agent. "
|
||||
"Help users design software systems, create architecture diagrams, "
|
||||
"plan system integrations, and provide guidance on scalability, "
|
||||
"security, and best practices for software development.",
|
||||
llm="openai/gpt-4o"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your software architect assistant. "
|
||||
"How can I help you design your software system today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a business analyst AI agent. "
|
||||
"Help users analyze business performance, market trends, "
|
||||
"competitive landscape, and strategic opportunities. Provide insights "
|
||||
"on business metrics, financial analysis, and growth strategies.",
|
||||
llm="meta-llama/Llama-3.1-8B-Instruct"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your business analyst assistant. "
|
||||
"How can I help you analyze your business data and strategies today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a code review AI agent. "
|
||||
"Help developers review code for best practices, security vulnerabilities, "
|
||||
"performance issues, and maintainability. Provide constructive feedback "
|
||||
"and suggest improvements for code quality and efficiency.",
|
||||
llm="meta-llama/Llama-3.1-8B-Instruct"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your code review assistant. "
|
||||
"How can I help you improve your code quality today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a content writing AI agent. "
|
||||
"Help users create engaging blog posts, articles, marketing copy, "
|
||||
"and social media content. Provide guidance on SEO optimization, "
|
||||
"content structure, tone adjustment, and audience targeting.",
|
||||
llm="meta-llama/Llama-3.1-8B-Instruct"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your content writing assistant. "
|
||||
"How can I help you create compelling content today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a data science AI agent. "
|
||||
"Help users with data analysis, machine learning model development, "
|
||||
"statistical analysis, and data visualization. Provide guidance on "
|
||||
"data preprocessing, feature engineering, model selection, and evaluation.",
|
||||
llm="meta-llama/Llama-3.1-8B-Instruct"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your data science assistant. "
|
||||
"How can I help you with your data analysis and machine learning projects today?")
|
||||
@@ -0,0 +1,12 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant AI agent. "
|
||||
"Help users conduct comprehensive research on various topics, "
|
||||
"analyze academic papers, synthesize information, and provide "
|
||||
"evidence-based insights and recommendations.",
|
||||
llm="meta-llama/Llama-3.1-8B-Instruct"
|
||||
)
|
||||
|
||||
response = agent.start("Hello! I'm your research assistant. "
|
||||
"How can I help you with your research project today?")
|
||||
@@ -0,0 +1,315 @@
|
||||
# MongoDB Integration Examples for PraisonAI Agents
|
||||
|
||||
This directory contains comprehensive examples demonstrating how to integrate MongoDB with PraisonAI agents for memory, knowledge, and data operations.
|
||||
|
||||
## Features
|
||||
|
||||
MongoDB integration with PraisonAI provides:
|
||||
|
||||
- **Memory Provider**: Use MongoDB as a persistent memory store for agents
|
||||
- **Knowledge Store**: Store and retrieve documents with vector search capabilities
|
||||
- **Tools Integration**: Perform MongoDB operations directly from agents
|
||||
- **Vector Search**: Leverage MongoDB Atlas Vector Search for semantic similarity
|
||||
- **Scalability**: Handle large datasets with MongoDB's scalable architecture
|
||||
- **Flexibility**: Use MongoDB as both key-value store and vector database
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Install PraisonAI with MongoDB support
|
||||
pip install 'praisonaiagents[mongodb]'
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **MongoDB**: Local MongoDB instance or MongoDB Atlas
|
||||
- **OpenAI API Key**: For embeddings and LLM operations
|
||||
- **Python 3.10+**: Required for PraisonAI
|
||||
|
||||
### Environment Setup
|
||||
|
||||
```bash
|
||||
# Set your OpenAI API key
|
||||
export OPENAI_API_KEY="your-openai-api-key"
|
||||
|
||||
# Optional: Set MongoDB connection string
|
||||
export MONGODB_CONNECTION_STRING="mongodb://localhost:27017/"
|
||||
```
|
||||
|
||||
## Examples Overview
|
||||
|
||||
### 1. [mongodb_memory_example.py](mongodb_memory_example.py)
|
||||
|
||||
Demonstrates using MongoDB as a memory provider for PraisonAI agents.
|
||||
|
||||
**Features:**
|
||||
- MongoDB as persistent memory storage
|
||||
- Quality scoring and filtering
|
||||
- Vector search for memory retrieval
|
||||
- Multi-session memory persistence
|
||||
- Memory search and context building
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python mongodb_memory_example.py
|
||||
```
|
||||
|
||||
### 2. [mongodb_knowledge_example.py](mongodb_knowledge_example.py)
|
||||
|
||||
Shows how to use MongoDB as a knowledge store with vector search capabilities.
|
||||
|
||||
**Features:**
|
||||
- MongoDB as knowledge vector store
|
||||
- Document processing and storage
|
||||
- Vector search for knowledge retrieval
|
||||
- Knowledge-based agent interactions
|
||||
- File processing with MongoDB storage
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python mongodb_knowledge_example.py
|
||||
```
|
||||
|
||||
### 3. [mongodb_tools_example.py](mongodb_tools_example.py)
|
||||
|
||||
Demonstrates MongoDB tools integration for database operations.
|
||||
|
||||
**Features:**
|
||||
- MongoDB CRUD operations
|
||||
- Vector search with embeddings
|
||||
- Data analysis and aggregation
|
||||
- Collection management
|
||||
- Index creation and optimization
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python mongodb_tools_example.py
|
||||
```
|
||||
|
||||
### 4. [mongodb_comprehensive_example.py](mongodb_comprehensive_example.py)
|
||||
|
||||
Complete business scenario simulation using all MongoDB features.
|
||||
|
||||
**Features:**
|
||||
- Full MongoDB integration (memory + knowledge + tools)
|
||||
- Multi-agent business workflow
|
||||
- Real-world e-commerce simulation
|
||||
- Business intelligence analytics
|
||||
- Customer service automation
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python mongodb_comprehensive_example.py
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### MongoDB Memory Configuration
|
||||
|
||||
```python
|
||||
mongodb_memory_config = {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": "mongodb://localhost:27017/",
|
||||
"database": "praisonai_memory",
|
||||
"use_vector_search": True, # Enable Atlas Vector Search
|
||||
"max_pool_size": 50,
|
||||
"min_pool_size": 10,
|
||||
"server_selection_timeout": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MongoDB Knowledge Configuration
|
||||
|
||||
```python
|
||||
mongodb_knowledge_config = {
|
||||
"vector_store": {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": "mongodb://localhost:27017/",
|
||||
"database": "praisonai_knowledge",
|
||||
"collection": "knowledge_base",
|
||||
"use_vector_search": True
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MongoDB Atlas Vector Search
|
||||
|
||||
For production use with advanced vector search capabilities:
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Create MongoDB Atlas Cluster**
|
||||
- Sign up for MongoDB Atlas
|
||||
- Create a new cluster
|
||||
- Get your connection string
|
||||
|
||||
2. **Enable Vector Search**
|
||||
- Create vector search indexes
|
||||
- Configure embedding dimensions (1536 for OpenAI)
|
||||
- Set similarity metric (cosine, euclidean, dotProduct)
|
||||
|
||||
3. **Update Connection String**
|
||||
```python
|
||||
"connection_string": "mongodb+srv://username:[email protected]/"
|
||||
```
|
||||
|
||||
### Vector Search Index Creation
|
||||
|
||||
```python
|
||||
# Vector search index definition
|
||||
{
|
||||
"mappings": {
|
||||
"dynamic": True,
|
||||
"fields": {
|
||||
"embedding": {
|
||||
"type": "knnVector",
|
||||
"dimensions": 1536,
|
||||
"similarity": "cosine"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Connection Management
|
||||
|
||||
```python
|
||||
# Use connection pooling
|
||||
mongodb_config = {
|
||||
"connection_string": "mongodb://localhost:27017/",
|
||||
"max_pool_size": 50,
|
||||
"min_pool_size": 10,
|
||||
"maxIdleTimeMS": 30000,
|
||||
"serverSelectionTimeoutMS": 5000
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Indexing Strategy
|
||||
|
||||
```python
|
||||
# Create appropriate indexes
|
||||
collection.create_index([("content", "text")]) # Text search
|
||||
collection.create_index([("created_at", -1)]) # Time-based queries
|
||||
collection.create_index([("metadata.category", 1)]) # Category filtering
|
||||
```
|
||||
|
||||
### 3. Error Handling
|
||||
|
||||
```python
|
||||
try:
|
||||
# MongoDB operations
|
||||
result = collection.insert_one(document)
|
||||
except PyMongoError as e:
|
||||
logger.error(f"MongoDB error: {e}")
|
||||
# Implement fallback strategy
|
||||
```
|
||||
|
||||
### 4. Data Validation
|
||||
|
||||
```python
|
||||
# Validate data before storage
|
||||
def validate_document(doc):
|
||||
required_fields = ["content", "metadata", "created_at"]
|
||||
return all(field in doc for field in required_fields)
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### 1. Indexing
|
||||
|
||||
- Create indexes on frequently queried fields
|
||||
- Use compound indexes for complex queries
|
||||
- Monitor index usage with MongoDB profiler
|
||||
|
||||
### 2. Vector Search Optimization
|
||||
|
||||
- Use appropriate vector dimensions
|
||||
- Optimize numCandidates parameter
|
||||
- Consider filtering to reduce search space
|
||||
|
||||
### 3. Connection Pooling
|
||||
|
||||
- Configure appropriate pool sizes
|
||||
- Use connection pooling for high-concurrency scenarios
|
||||
- Monitor connection metrics
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection Errors**
|
||||
- Check MongoDB server is running
|
||||
- Verify connection string format
|
||||
- Ensure network connectivity
|
||||
|
||||
2. **Vector Search Issues**
|
||||
- Verify Atlas Vector Search indexes exist
|
||||
- Check embedding dimensions match
|
||||
- Ensure proper index configuration
|
||||
|
||||
3. **Memory Issues**
|
||||
- Monitor memory usage with large datasets
|
||||
- Use appropriate batch sizes
|
||||
- Consider data pagination
|
||||
|
||||
### Debugging
|
||||
|
||||
```python
|
||||
# Enable detailed logging
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
# Monitor MongoDB operations
|
||||
from pymongo import monitoring
|
||||
monitoring.register(CommandLogger())
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### 1. Security
|
||||
|
||||
```python
|
||||
# Use authentication
|
||||
connection_string = "mongodb://username:password@host:port/database"
|
||||
|
||||
# Enable SSL/TLS
|
||||
client = MongoClient(connection_string, tls=True)
|
||||
```
|
||||
|
||||
### 2. Monitoring
|
||||
|
||||
- Use MongoDB Atlas monitoring
|
||||
- Implement application-level metrics
|
||||
- Set up alerting for critical issues
|
||||
|
||||
### 3. Backup and Recovery
|
||||
|
||||
- Configure automated backups
|
||||
- Test restore procedures
|
||||
- Implement disaster recovery plans
|
||||
|
||||
## Support
|
||||
|
||||
For additional support:
|
||||
|
||||
- [PraisonAI Documentation](https://docs.praisonai.com)
|
||||
- [MongoDB Documentation](https://docs.mongodb.com)
|
||||
- [MongoDB Atlas Vector Search](https://docs.atlas.mongodb.com/atlas-vector-search/)
|
||||
|
||||
## License
|
||||
|
||||
These examples are provided under the same license as PraisonAI.
|
||||
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive MongoDB Integration Example for PraisonAI Agents
|
||||
|
||||
This example demonstrates a complete MongoDB integration with PraisonAI agents,
|
||||
combining memory, knowledge, and tools for a comprehensive data management system.
|
||||
|
||||
Prerequisites:
|
||||
- Install MongoDB dependencies: pip install 'praisonaiagents[mongodb]'
|
||||
- MongoDB server running (local or Atlas)
|
||||
- OpenAI API key for embeddings
|
||||
|
||||
Features demonstrated:
|
||||
- MongoDB as memory provider
|
||||
- MongoDB as knowledge store
|
||||
- MongoDB tools for data operations
|
||||
- Vector search capabilities
|
||||
- Multi-agent coordination with MongoDB
|
||||
- Real-world business scenario simulation
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import mongodb_tools
|
||||
|
||||
# Ensure OpenAI API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
raise ValueError("Please set the OPENAI_API_KEY environment variable")
|
||||
|
||||
def main():
|
||||
# MongoDB configuration for different components
|
||||
mongodb_memory_config = {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": "mongodb://localhost:27017/", # Replace with your connection string
|
||||
"database": "praisonai_comprehensive",
|
||||
"use_vector_search": True,
|
||||
"max_pool_size": 50
|
||||
}
|
||||
}
|
||||
|
||||
mongodb_knowledge_config = {
|
||||
"vector_store": {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": "mongodb://localhost:27017/",
|
||||
"database": "praisonai_comprehensive",
|
||||
"collection": "knowledge_base",
|
||||
"use_vector_search": True
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"api_key": os.getenv("OPENAI_API_KEY")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create specialized agents for different business functions
|
||||
|
||||
# 1. Data Manager Agent - handles data operations
|
||||
data_manager = Agent(
|
||||
name="Data Manager",
|
||||
role="Senior Data Operations Manager",
|
||||
goal="Manage and organize all data operations using MongoDB",
|
||||
backstory="""You are an expert data operations manager who specializes in
|
||||
MongoDB database management. You handle data ingestion, organization,
|
||||
indexing, and ensure data quality and accessibility across the organization.""",
|
||||
tools=[mongodb_tools],
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# 2. Knowledge Curator Agent - manages knowledge base
|
||||
knowledge_curator = Agent(
|
||||
name="Knowledge Curator",
|
||||
role="Knowledge Management Specialist",
|
||||
goal="Curate and maintain organizational knowledge using MongoDB",
|
||||
backstory="""You are a knowledge management specialist who excels at
|
||||
organizing, categorizing, and retrieving information from the knowledge base.
|
||||
You ensure that knowledge is properly stored, indexed, and accessible.""",
|
||||
knowledge=True,
|
||||
knowledge_config=mongodb_knowledge_config,
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# 3. Business Analyst Agent - analyzes data and provides insights
|
||||
business_analyst = Agent(
|
||||
name="Business Analyst",
|
||||
role="Senior Business Intelligence Analyst",
|
||||
goal="Analyze business data and provide actionable insights",
|
||||
backstory="""You are a senior business analyst who specializes in extracting
|
||||
insights from data stored in MongoDB. You create reports, identify trends,
|
||||
and provide strategic recommendations based on data analysis.""",
|
||||
tools=[mongodb_tools],
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# 4. Customer Service Agent - handles customer interactions
|
||||
customer_service = Agent(
|
||||
name="Customer Service Agent",
|
||||
role="Customer Service Representative",
|
||||
goal="Provide excellent customer service using stored knowledge and data",
|
||||
backstory="""You are a customer service representative who uses the
|
||||
knowledge base and customer data to provide excellent support. You can
|
||||
access customer history, product information, and troubleshooting guides.""",
|
||||
knowledge=True,
|
||||
knowledge_config=mongodb_knowledge_config,
|
||||
tools=[mongodb_tools],
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create comprehensive business tasks
|
||||
business_tasks = [
|
||||
Task(
|
||||
description="""Set up comprehensive business data infrastructure:
|
||||
1. Create collections for customers, products, orders, and support tickets
|
||||
2. Insert sample business data for an e-commerce platform
|
||||
3. Set up appropriate indexes for performance optimization
|
||||
4. Create vector indexes for product descriptions and customer support
|
||||
5. Implement data validation and business rules
|
||||
|
||||
Focus on creating a realistic business data structure.
|
||||
""",
|
||||
expected_output="Complete business data infrastructure with sample data",
|
||||
agent=data_manager
|
||||
),
|
||||
Task(
|
||||
description="""Build organizational knowledge base:
|
||||
1. Store product documentation and specifications
|
||||
2. Create customer service guidelines and FAQs
|
||||
3. Add troubleshooting guides and best practices
|
||||
4. Include company policies and procedures
|
||||
5. Organize knowledge by categories and tags
|
||||
|
||||
Ensure knowledge is searchable and well-organized.
|
||||
""",
|
||||
expected_output="Comprehensive knowledge base with categorized information",
|
||||
agent=knowledge_curator
|
||||
),
|
||||
Task(
|
||||
description="""Perform business intelligence analysis:
|
||||
1. Analyze customer purchase patterns and behavior
|
||||
2. Calculate key business metrics (revenue, conversion rates, etc.)
|
||||
3. Identify top-selling products and categories
|
||||
4. Analyze customer support ticket trends
|
||||
5. Generate insights for business optimization
|
||||
|
||||
Use MongoDB aggregation and analytics capabilities.
|
||||
""",
|
||||
expected_output="Business intelligence report with actionable insights",
|
||||
agent=business_analyst
|
||||
),
|
||||
Task(
|
||||
description="""Handle customer service scenarios:
|
||||
1. Process customer inquiries using knowledge base
|
||||
2. Look up customer order history and preferences
|
||||
3. Provide product recommendations based on customer data
|
||||
4. Access troubleshooting guides for technical issues
|
||||
5. Update customer records and interaction history
|
||||
|
||||
Demonstrate excellent customer service with data-driven support.
|
||||
""",
|
||||
expected_output="Customer service interactions with knowledge-based responses",
|
||||
agent=customer_service
|
||||
),
|
||||
Task(
|
||||
description="""Generate comprehensive business report:
|
||||
1. Combine insights from all data sources
|
||||
2. Create executive summary with key findings
|
||||
3. Provide recommendations for business improvement
|
||||
4. Include data visualizations and metrics
|
||||
5. Store report in knowledge base for future reference
|
||||
|
||||
Create a comprehensive business intelligence report.
|
||||
""",
|
||||
expected_output="Executive business report with comprehensive insights",
|
||||
agent=business_analyst
|
||||
)
|
||||
]
|
||||
|
||||
# Initialize the comprehensive business system
|
||||
print("🚀 Starting Comprehensive MongoDB Business System...")
|
||||
print("=" * 60)
|
||||
|
||||
business_system = PraisonAIAgents(
|
||||
agents=[data_manager, knowledge_curator, business_analyst, customer_service],
|
||||
tasks=business_tasks,
|
||||
memory=True,
|
||||
memory_config=mongodb_memory_config,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Execute the comprehensive business pipeline
|
||||
try:
|
||||
results = business_system.start()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 Comprehensive Business System Results:")
|
||||
print("=" * 60)
|
||||
|
||||
for i, result in enumerate(results, 1):
|
||||
print(f"\n{i}. Task Result:")
|
||||
print(f" Output: {result.raw[:200]}...")
|
||||
print(f" Agent: {result.agent}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("💾 Comprehensive MongoDB Integration Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
# Demonstrate system integration
|
||||
print("\n🔍 Demonstrating System Integration:")
|
||||
print("-" * 40)
|
||||
|
||||
# Access different components
|
||||
memory_system = business_system.memory
|
||||
|
||||
# Search business memories
|
||||
print("📋 Searching business memories...")
|
||||
business_memories = memory_system.search_long_term("business analysis", limit=3)
|
||||
print(f"Found {len(business_memories)} business-related memories")
|
||||
|
||||
# Access knowledge system
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
knowledge_system = Knowledge(config=mongodb_knowledge_config)
|
||||
|
||||
# Search knowledge base
|
||||
print("\n📚 Searching knowledge base...")
|
||||
knowledge_results = knowledge_system.search("customer service", limit=3)
|
||||
print(f"Found {len(knowledge_results)} knowledge entries")
|
||||
|
||||
# Direct MongoDB operations
|
||||
print("\n🔧 Direct MongoDB operations...")
|
||||
mongo_client = mongodb_tools.connect_mongodb(
|
||||
"mongodb://localhost:27017/",
|
||||
"praisonai_comprehensive"
|
||||
)
|
||||
|
||||
# Get database statistics
|
||||
stats = mongo_client.get_stats("customers")
|
||||
print(f"Customer collection stats: {stats}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 Comprehensive MongoDB Example Completed Successfully!")
|
||||
print("=" * 60)
|
||||
|
||||
print("\n📈 System Overview:")
|
||||
print("- Memory System: MongoDB-based persistent memory")
|
||||
print("- Knowledge Base: MongoDB vector search enabled")
|
||||
print("- Tools Integration: Full MongoDB CRUD operations")
|
||||
print("- Multi-Agent Coordination: Business workflow simulation")
|
||||
print("- Vector Search: Semantic search capabilities")
|
||||
print("- Data Analytics: Business intelligence insights")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error during execution: {e}")
|
||||
print("Please check your MongoDB connection and API keys.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Comprehensive MongoDB Integration Example")
|
||||
print("=" * 50)
|
||||
print("This example demonstrates complete MongoDB integration with PraisonAI")
|
||||
print("Features:")
|
||||
print("- MongoDB Memory Provider")
|
||||
print("- MongoDB Knowledge Store")
|
||||
print("- MongoDB Tools Integration")
|
||||
print("- Vector Search Capabilities")
|
||||
print("- Multi-Agent Business Simulation")
|
||||
print("=" * 50)
|
||||
print("Make sure you have:")
|
||||
print("1. MongoDB server running (local or Atlas)")
|
||||
print("2. OpenAI API key set in environment variables")
|
||||
print("3. MongoDB dependencies installed: pip install 'praisonaiagents[mongodb]'")
|
||||
print("=" * 50)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MongoDB Knowledge Example for PraisonAI Agents
|
||||
|
||||
This example demonstrates how to use MongoDB as a knowledge store for PraisonAI agents.
|
||||
MongoDB can store and retrieve knowledge documents with vector search capabilities.
|
||||
|
||||
Prerequisites:
|
||||
- Install MongoDB dependencies: pip install 'praisonaiagents[mongodb]'
|
||||
- MongoDB server running (local or Atlas)
|
||||
- OpenAI API key for embeddings
|
||||
|
||||
Features demonstrated:
|
||||
- MongoDB as knowledge vector store
|
||||
- Document processing and storage
|
||||
- Vector search for knowledge retrieval
|
||||
- Knowledge-based agent interactions
|
||||
- File processing with MongoDB storage
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Ensure OpenAI API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
raise ValueError("Please set the OPENAI_API_KEY environment variable")
|
||||
|
||||
def main():
|
||||
# MongoDB knowledge configuration
|
||||
mongodb_knowledge_config = {
|
||||
"vector_store": {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": "mongodb://localhost:27017/", # Replace with your MongoDB connection string
|
||||
"database": "praisonai_knowledge",
|
||||
"collection": "knowledge_base",
|
||||
"use_vector_search": True # Enable Atlas Vector Search
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"api_key": os.getenv("OPENAI_API_KEY")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create a knowledge agent with MongoDB knowledge store
|
||||
knowledge_agent = Agent(
|
||||
name="MongoDB Knowledge Agent",
|
||||
role="Knowledge Specialist",
|
||||
goal="Provide accurate information from MongoDB knowledge base",
|
||||
backstory="""You are an expert knowledge specialist who can access and
|
||||
retrieve information from a comprehensive MongoDB knowledge base. You excel
|
||||
at finding relevant information, synthesizing knowledge from multiple sources,
|
||||
and providing accurate, context-aware responses.""",
|
||||
knowledge=True,
|
||||
knowledge_config=mongodb_knowledge_config,
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create a research assistant agent
|
||||
research_agent = Agent(
|
||||
name="Research Assistant",
|
||||
role="Research Assistant",
|
||||
goal="Gather information and store it in the knowledge base",
|
||||
backstory="""You are a research assistant who specializes in gathering
|
||||
information from various sources and organizing it for storage in the
|
||||
knowledge base. You ensure information is accurate, well-structured,
|
||||
and properly categorized.""",
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create tasks for knowledge management
|
||||
knowledge_tasks = [
|
||||
Task(
|
||||
description="""Research and store information about MongoDB Atlas Vector Search:
|
||||
1. Gather comprehensive information about MongoDB Atlas Vector Search
|
||||
2. Include technical specifications, use cases, and best practices
|
||||
3. Store the information in the MongoDB knowledge base
|
||||
4. Organize information by categories (features, performance, integration)
|
||||
""",
|
||||
expected_output="MongoDB Atlas Vector Search information stored in knowledge base",
|
||||
agent=research_agent
|
||||
),
|
||||
Task(
|
||||
description="""Research and store information about AI agent frameworks:
|
||||
1. Research popular AI agent frameworks (LangChain, AutoGen, etc.)
|
||||
2. Compare their features, capabilities, and use cases
|
||||
3. Store comparative analysis in the knowledge base
|
||||
4. Include code examples and best practices
|
||||
""",
|
||||
expected_output="AI agent framework comparison stored in knowledge base",
|
||||
agent=research_agent
|
||||
),
|
||||
Task(
|
||||
description="""Query the knowledge base for MongoDB information:
|
||||
1. Search for information about MongoDB Atlas Vector Search
|
||||
2. Extract key features and capabilities
|
||||
3. Provide a comprehensive summary
|
||||
4. Include technical recommendations
|
||||
""",
|
||||
expected_output="Comprehensive MongoDB Atlas Vector Search summary from knowledge base",
|
||||
agent=knowledge_agent
|
||||
),
|
||||
Task(
|
||||
description="""Query the knowledge base for AI agent framework information:
|
||||
1. Search for information about AI agent frameworks
|
||||
2. Compare different frameworks based on stored knowledge
|
||||
3. Provide recommendations for different use cases
|
||||
4. Include best practices and examples
|
||||
""",
|
||||
expected_output="AI agent framework comparison and recommendations from knowledge base",
|
||||
agent=knowledge_agent
|
||||
)
|
||||
]
|
||||
|
||||
# Initialize the multi-agent system with MongoDB knowledge
|
||||
print("🚀 Starting MongoDB Knowledge Management System...")
|
||||
print("=" * 60)
|
||||
|
||||
knowledge_system = PraisonAIAgents(
|
||||
agents=[research_agent, knowledge_agent],
|
||||
tasks=knowledge_tasks,
|
||||
memory=True,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Execute the knowledge management pipeline
|
||||
try:
|
||||
results = knowledge_system.start()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 Knowledge Management System Results:")
|
||||
print("=" * 60)
|
||||
|
||||
for i, result in enumerate(results, 1):
|
||||
print(f"\n{i}. Task Result:")
|
||||
print(f" Output: {result.raw[:200]}...")
|
||||
print(f" Agent: {result.agent}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("💾 MongoDB Knowledge Integration Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
# Demonstrate direct knowledge access
|
||||
print("\n🔍 Demonstrating Direct Knowledge Access:")
|
||||
print("-" * 40)
|
||||
|
||||
# Access knowledge system directly
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
|
||||
knowledge_system = Knowledge(config=mongodb_knowledge_config)
|
||||
|
||||
# Store a sample document
|
||||
sample_doc = """
|
||||
MongoDB is a document-oriented database that provides high performance,
|
||||
high availability, and easy scalability. It works on the concept of
|
||||
collections and documents, using a flexible, JSON-like document model.
|
||||
"""
|
||||
|
||||
print("📄 Storing sample document...")
|
||||
knowledge_system.store(sample_doc, metadata={"category": "database", "type": "overview"})
|
||||
|
||||
# Search for stored knowledge
|
||||
print("\n🔍 Searching knowledge base...")
|
||||
results = knowledge_system.search("MongoDB document database", limit=3)
|
||||
|
||||
print(f"Found {len(results)} knowledge entries:")
|
||||
for result in results:
|
||||
print(f" - {result.get('memory', '')[:100]}...")
|
||||
print(f" Score: {result.get('score', 'N/A')}")
|
||||
|
||||
print("\n✅ MongoDB Knowledge Example Completed Successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error during execution: {e}")
|
||||
print("Please check your MongoDB connection and API keys.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("MongoDB Knowledge Example for PraisonAI Agents")
|
||||
print("=" * 50)
|
||||
print("This example demonstrates MongoDB integration for knowledge management")
|
||||
print("Make sure you have:")
|
||||
print("1. MongoDB server running (local or Atlas)")
|
||||
print("2. OpenAI API key set in environment variables")
|
||||
print("3. MongoDB dependencies installed: pip install 'praisonaiagents[mongodb]'")
|
||||
print("=" * 50)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MongoDB Memory Example for PraisonAI Agents
|
||||
|
||||
This example demonstrates how to use MongoDB as a memory provider for PraisonAI agents.
|
||||
MongoDB can be used as both a key-value store and a vector database for memory operations.
|
||||
|
||||
Prerequisites:
|
||||
- Install MongoDB dependencies: pip install 'praisonaiagents[mongodb]'
|
||||
- MongoDB server running (local or Atlas)
|
||||
- OpenAI API key for embeddings
|
||||
|
||||
Features demonstrated:
|
||||
- MongoDB as memory provider
|
||||
- Vector search with Atlas Vector Search
|
||||
- Quality scoring and filtering
|
||||
- User-specific memory storage
|
||||
- Persistent memory across sessions
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Ensure OpenAI API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
raise ValueError("Please set the OPENAI_API_KEY environment variable")
|
||||
|
||||
def main():
|
||||
# MongoDB memory configuration
|
||||
mongodb_memory_config = {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": "mongodb://localhost:27017/", # Replace with your MongoDB connection string
|
||||
"database": "praisonai_memory",
|
||||
"use_vector_search": True, # Enable Atlas Vector Search (requires MongoDB Atlas)
|
||||
"max_pool_size": 50,
|
||||
"min_pool_size": 10,
|
||||
"server_selection_timeout": 5000
|
||||
}
|
||||
}
|
||||
|
||||
# Create a research agent with MongoDB memory
|
||||
research_agent = Agent(
|
||||
name="MongoDB Research Agent",
|
||||
role="Senior Research Specialist",
|
||||
goal="Research topics and maintain persistent memory using MongoDB",
|
||||
backstory="""You are an expert researcher who specializes in collecting,
|
||||
analyzing, and storing information using advanced MongoDB-based memory systems.
|
||||
You excel at maintaining context across multiple research sessions and can
|
||||
retrieve relevant information from your persistent memory store.""",
|
||||
memory=True,
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create research tasks
|
||||
research_tasks = [
|
||||
Task(
|
||||
description="""Research the latest developments in AI/ML for 2024:
|
||||
1. Identify major AI breakthroughs and trends
|
||||
2. Analyze the impact on various industries
|
||||
3. Store key findings in MongoDB memory with quality scores
|
||||
4. Identify emerging technologies and their potential applications
|
||||
""",
|
||||
expected_output="Comprehensive AI/ML research report with key findings stored in MongoDB",
|
||||
agent=research_agent
|
||||
),
|
||||
Task(
|
||||
description="""Research MongoDB Atlas Vector Search capabilities:
|
||||
1. Explore vector search features and use cases
|
||||
2. Compare with other vector databases
|
||||
3. Analyze performance and scalability
|
||||
4. Store technical insights in MongoDB memory
|
||||
""",
|
||||
expected_output="Technical analysis of MongoDB Atlas Vector Search with stored insights",
|
||||
agent=research_agent
|
||||
),
|
||||
Task(
|
||||
description="""Research AI agent frameworks and tools:
|
||||
1. Compare popular AI agent frameworks
|
||||
2. Analyze their strengths and weaknesses
|
||||
3. Identify best practices for agent development
|
||||
4. Store comparative analysis in MongoDB memory
|
||||
""",
|
||||
expected_output="Comparative analysis of AI agent frameworks with stored insights",
|
||||
agent=research_agent
|
||||
)
|
||||
]
|
||||
|
||||
# Initialize the multi-agent system with MongoDB memory
|
||||
print("🚀 Starting MongoDB Memory Research System...")
|
||||
print("=" * 60)
|
||||
|
||||
research_system = PraisonAIAgents(
|
||||
agents=[research_agent],
|
||||
tasks=research_tasks,
|
||||
memory=True,
|
||||
memory_config=mongodb_memory_config,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Execute the research pipeline
|
||||
try:
|
||||
results = research_system.start()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 Research System Results:")
|
||||
print("=" * 60)
|
||||
|
||||
for i, result in enumerate(results, 1):
|
||||
print(f"\n{i}. Task Result:")
|
||||
print(f" Output: {result.raw[:200]}...")
|
||||
print(f" Agent: {result.agent}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("💾 MongoDB Memory Integration Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
# Demonstrate memory retrieval
|
||||
print("\n🔍 Demonstrating Memory Retrieval:")
|
||||
print("-" * 40)
|
||||
|
||||
# Access the memory system directly
|
||||
memory_system = research_system.memory
|
||||
|
||||
# Search for AI-related memories
|
||||
ai_memories = memory_system.search_long_term("AI machine learning", limit=3)
|
||||
print(f"Found {len(ai_memories)} AI-related memories:")
|
||||
for memory in ai_memories:
|
||||
print(f" - {memory.get('text', '')[:100]}...")
|
||||
|
||||
# Search for MongoDB-related memories
|
||||
mongo_memories = memory_system.search_long_term("MongoDB vector search", limit=3)
|
||||
print(f"\nFound {len(mongo_memories)} MongoDB-related memories:")
|
||||
for memory in mongo_memories:
|
||||
print(f" - {memory.get('text', '')[:100]}...")
|
||||
|
||||
print("\n✅ MongoDB Memory Example Completed Successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error during execution: {e}")
|
||||
print("Please check your MongoDB connection and API keys.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("MongoDB Memory Example for PraisonAI Agents")
|
||||
print("=" * 50)
|
||||
print("This example demonstrates MongoDB integration for persistent memory")
|
||||
print("Make sure you have:")
|
||||
print("1. MongoDB server running (local or Atlas)")
|
||||
print("2. OpenAI API key set in environment variables")
|
||||
print("3. MongoDB dependencies installed: pip install 'praisonaiagents[mongodb]'")
|
||||
print("=" * 50)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MongoDB Tools Example for PraisonAI Agents
|
||||
|
||||
This example demonstrates how to use MongoDB tools with PraisonAI agents.
|
||||
Agents can perform MongoDB operations like inserting, querying, and vector search.
|
||||
|
||||
Prerequisites:
|
||||
- Install MongoDB dependencies: pip install 'praisonaiagents[mongodb]'
|
||||
- MongoDB server running (local or Atlas)
|
||||
- OpenAI API key for embeddings
|
||||
|
||||
Features demonstrated:
|
||||
- MongoDB tools integration
|
||||
- Document CRUD operations
|
||||
- Vector search with embeddings
|
||||
- Data analysis with MongoDB
|
||||
- Agent-driven database operations
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import mongodb_tools
|
||||
|
||||
# Ensure OpenAI API key is set
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
raise ValueError("Please set the OPENAI_API_KEY environment variable")
|
||||
|
||||
def main():
|
||||
# Create a MongoDB Database Agent with MongoDB tools
|
||||
db_agent = Agent(
|
||||
name="MongoDB Database Agent",
|
||||
role="Database Operations Specialist",
|
||||
goal="Manage MongoDB operations and data analysis",
|
||||
backstory="""You are an expert database administrator who specializes in
|
||||
MongoDB operations. You can perform complex database queries, manage
|
||||
collections, and analyze data patterns. You excel at using MongoDB tools
|
||||
for data management and retrieval.""",
|
||||
tools=[mongodb_tools],
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create a Data Analysis Agent
|
||||
analysis_agent = Agent(
|
||||
name="Data Analysis Agent",
|
||||
role="Data Analyst",
|
||||
goal="Analyze data stored in MongoDB and provide insights",
|
||||
backstory="""You are a data analyst who specializes in extracting insights
|
||||
from MongoDB databases. You can query data, perform aggregations, and
|
||||
identify patterns and trends in the stored information.""",
|
||||
tools=[mongodb_tools],
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create tasks for MongoDB operations
|
||||
mongodb_tasks = [
|
||||
Task(
|
||||
description="""Set up MongoDB collections and insert sample data:
|
||||
1. Create a collection called 'products' for e-commerce data
|
||||
2. Insert sample product documents with fields: name, price, category, description
|
||||
3. Create a collection called 'users' for user data
|
||||
4. Insert sample user documents with fields: name, email, age, preferences
|
||||
5. Create appropriate indexes for better query performance
|
||||
|
||||
Use the MongoDB tools to perform these operations.
|
||||
""",
|
||||
expected_output="MongoDB collections created with sample data and indexes",
|
||||
agent=db_agent
|
||||
),
|
||||
Task(
|
||||
description="""Perform vector search operations:
|
||||
1. Create a vector index for product descriptions
|
||||
2. Store products with text embeddings for vector search
|
||||
3. Demonstrate vector similarity search for product recommendations
|
||||
4. Show how to find similar products based on description embeddings
|
||||
|
||||
Use the MongoDB vector search tools for these operations.
|
||||
""",
|
||||
expected_output="Vector search implementation with product recommendations",
|
||||
agent=db_agent
|
||||
),
|
||||
Task(
|
||||
description="""Query and analyze the stored data:
|
||||
1. Find all products in a specific category
|
||||
2. Calculate average price by category
|
||||
3. Find users with specific preferences
|
||||
4. Perform text search on product descriptions
|
||||
5. Generate insights about the data patterns
|
||||
|
||||
Use MongoDB query tools to perform these analyses.
|
||||
""",
|
||||
expected_output="Data analysis report with insights from MongoDB queries",
|
||||
agent=analysis_agent
|
||||
),
|
||||
Task(
|
||||
description="""Demonstrate advanced MongoDB operations:
|
||||
1. Update product prices based on specific criteria
|
||||
2. Perform aggregation operations for statistical analysis
|
||||
3. Create reports on user demographics and preferences
|
||||
4. Implement data validation and error handling
|
||||
5. Show MongoDB best practices for data management
|
||||
|
||||
Use MongoDB tools for these advanced operations.
|
||||
""",
|
||||
expected_output="Advanced MongoDB operations report with best practices",
|
||||
agent=db_agent
|
||||
)
|
||||
]
|
||||
|
||||
# Initialize the multi-agent system with MongoDB tools
|
||||
print("🚀 Starting MongoDB Tools Demo System...")
|
||||
print("=" * 60)
|
||||
|
||||
mongodb_system = PraisonAIAgents(
|
||||
agents=[db_agent, analysis_agent],
|
||||
tasks=mongodb_tasks,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Execute the MongoDB operations pipeline
|
||||
try:
|
||||
results = mongodb_system.start()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 MongoDB Tools System Results:")
|
||||
print("=" * 60)
|
||||
|
||||
for i, result in enumerate(results, 1):
|
||||
print(f"\n{i}. Task Result:")
|
||||
print(f" Output: {result.raw[:200]}...")
|
||||
print(f" Agent: {result.agent}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("💾 MongoDB Tools Integration Complete!")
|
||||
print("=" * 60)
|
||||
|
||||
# Demonstrate direct MongoDB tools usage
|
||||
print("\n🔍 Demonstrating Direct MongoDB Tools Usage:")
|
||||
print("-" * 40)
|
||||
|
||||
# Connect to MongoDB
|
||||
mongo_client = mongodb_tools.connect_mongodb(
|
||||
"mongodb://localhost:27017/", # Replace with your connection string
|
||||
"praisonai_tools_demo"
|
||||
)
|
||||
|
||||
# Insert a sample document
|
||||
print("📄 Inserting sample document...")
|
||||
result = mongo_client.insert_document(
|
||||
"demo_collection",
|
||||
{
|
||||
"title": "MongoDB Tools Demo",
|
||||
"description": "Demonstration of MongoDB tools with PraisonAI",
|
||||
"category": "database",
|
||||
"tags": ["mongodb", "praisonai", "tools", "demo"]
|
||||
},
|
||||
metadata={"created_by": "demo", "version": "1.0"}
|
||||
)
|
||||
print(f" Result: {result}")
|
||||
|
||||
# Query documents
|
||||
print("\n🔍 Querying documents...")
|
||||
documents = mongo_client.find_documents(
|
||||
"demo_collection",
|
||||
{"category": "database"},
|
||||
limit=5
|
||||
)
|
||||
print(f" Found {len(documents)} documents")
|
||||
for doc in documents:
|
||||
print(f" - {doc.get('title', 'N/A')}: {doc.get('description', '')[:50]}...")
|
||||
|
||||
# Get collection statistics
|
||||
print("\n📈 Getting collection statistics...")
|
||||
stats = mongo_client.get_stats("demo_collection")
|
||||
print(f" Collection stats: {stats}")
|
||||
|
||||
print("\n✅ MongoDB Tools Example Completed Successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error during execution: {e}")
|
||||
print("Please check your MongoDB connection and API keys.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("MongoDB Tools Example for PraisonAI Agents")
|
||||
print("=" * 50)
|
||||
print("This example demonstrates MongoDB tools integration")
|
||||
print("Make sure you have:")
|
||||
print("1. MongoDB server running (local or Atlas)")
|
||||
print("2. OpenAI API key set in environment variables")
|
||||
print("3. MongoDB dependencies installed: pip install 'praisonaiagents[mongodb]'")
|
||||
print("=" * 50)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Basic example of using AI21 with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with AI21
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="ai21/j2-ultra",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a writing task?")
|
||||
|
||||
# Example with creative writing
|
||||
writing_task = """
|
||||
Write a short story about a time traveler who discovers
|
||||
they can only travel to moments of great historical significance.
|
||||
Make it engaging and about 200 words.
|
||||
"""
|
||||
|
||||
response = agent.start(writing_task)
|
||||
|
||||
# Example with reasoning
|
||||
reasoning_task = """
|
||||
Explain the concept of quantum entanglement in simple terms,
|
||||
and then discuss its potential applications in quantum computing.
|
||||
"""
|
||||
|
||||
response = agent.start(reasoning_task)
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Basic example of using AWS Bedrock with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with AWS Bedrock (Claude)
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a writing task?")
|
||||
|
||||
# Example with creative writing
|
||||
writing_task = """
|
||||
Write a short story about a time traveler who discovers
|
||||
they can only travel to moments of great historical significance.
|
||||
Make it engaging and about 200 words.
|
||||
"""
|
||||
|
||||
response = agent.start(writing_task)
|
||||
|
||||
# Example with reasoning
|
||||
reasoning_task = """
|
||||
Explain the concept of quantum entanglement in simple terms,
|
||||
and then discuss its potential applications in quantum computing.
|
||||
"""
|
||||
|
||||
response = agent.start(reasoning_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Azure OpenAI with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Azure OpenAI
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="azure/gpt-4",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a coding task?")
|
||||
|
||||
# Example with code generation
|
||||
coding_task = """
|
||||
Write a Python function that implements a binary search algorithm.
|
||||
Include proper documentation and error handling.
|
||||
"""
|
||||
|
||||
response = agent.start(coding_task)
|
||||
|
||||
# Example with analysis
|
||||
analysis_task = """
|
||||
Analyze the pros and cons of using microservices architecture
|
||||
for a large-scale e-commerce application.
|
||||
"""
|
||||
|
||||
response = agent.start(analysis_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Cloudflare AI with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Cloudflare AI
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="cloudflare/@cf/meta/llama-3.1-8b-instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a coding task?")
|
||||
|
||||
# Example with code generation
|
||||
coding_task = """
|
||||
Write a Python function that implements a binary search algorithm.
|
||||
Include proper documentation and error handling.
|
||||
"""
|
||||
|
||||
response = agent.start(coding_task)
|
||||
|
||||
# Example with analysis
|
||||
analysis_task = """
|
||||
Analyze the pros and cons of using microservices architecture
|
||||
for a large-scale e-commerce application.
|
||||
"""
|
||||
|
||||
response = agent.start(analysis_task)
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Basic example of using Databricks with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Databricks
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="databricks/databricks-llama-3.1-8b-instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a data analysis task?")
|
||||
|
||||
# Example with data analysis
|
||||
analysis_task = """
|
||||
Analyze the potential market opportunities for a new AI-powered
|
||||
productivity tool targeting remote workers. Include market size,
|
||||
competitive landscape, and go-to-market strategy recommendations.
|
||||
"""
|
||||
|
||||
response = agent.start(analysis_task)
|
||||
|
||||
# Example with business intelligence
|
||||
business_task = """
|
||||
Provide insights on how AI can transform customer service operations
|
||||
in the next 5 years, including specific use cases and implementation strategies.
|
||||
"""
|
||||
|
||||
response = agent.start(business_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Fireworks AI with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Fireworks AI
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="fireworks/accounts/fireworks/models/llama-v3-8b-instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a coding task?")
|
||||
|
||||
# Example with code generation
|
||||
coding_task = """
|
||||
Write a Python function that implements a binary search algorithm.
|
||||
Include proper documentation and error handling.
|
||||
"""
|
||||
|
||||
response = agent.start(coding_task)
|
||||
|
||||
# Example with creative writing
|
||||
creative_task = """
|
||||
Write a short story about a time traveler who discovers
|
||||
they can only travel to moments of great historical significance.
|
||||
"""
|
||||
|
||||
response = agent.start(creative_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Hugging Face with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Hugging Face
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="huggingface/meta-llama/Llama-3.1-8B-Instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a language translation task?")
|
||||
|
||||
# Example with language translation
|
||||
translation_task = """
|
||||
Translate the following text to French and explain any cultural nuances:
|
||||
"The early bird catches the worm, but the second mouse gets the cheese."
|
||||
"""
|
||||
|
||||
response = agent.start(translation_task)
|
||||
|
||||
# Example with creative content generation
|
||||
creative_task = """
|
||||
Write a haiku about artificial intelligence and its impact on society.
|
||||
Then explain the symbolism in your haiku.
|
||||
"""
|
||||
|
||||
response = agent.start(creative_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Moonshot AI with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Moonshot AI
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="moonshot/moonshot-v1-8k",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a research task?")
|
||||
|
||||
# Example with research and analysis
|
||||
research_task = """
|
||||
Research and provide insights on the latest developments in
|
||||
artificial intelligence, focusing on recent breakthroughs and trends.
|
||||
"""
|
||||
|
||||
response = agent.start(research_task)
|
||||
|
||||
# Example with creative content
|
||||
creative_task = """
|
||||
Write a haiku about artificial intelligence and its impact on society.
|
||||
Then explain the symbolism in your haiku.
|
||||
"""
|
||||
|
||||
response = agent.start(creative_task)
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Basic example of using OpenRouter with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with OpenRouter
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="openrouter/meta-llama/llama-3.1-8b-instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a creative writing task?")
|
||||
|
||||
# Example with creative writing
|
||||
writing_task = """
|
||||
Write a short story about a time traveler who discovers
|
||||
they can only travel to moments of great historical significance.
|
||||
Make it engaging and about 200 words.
|
||||
"""
|
||||
|
||||
response = agent.start(writing_task)
|
||||
|
||||
# Example with reasoning
|
||||
reasoning_task = """
|
||||
Explain the concept of quantum entanglement in simple terms,
|
||||
and then discuss its potential applications in quantum computing.
|
||||
"""
|
||||
|
||||
response = agent.start(reasoning_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Perplexity with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Perplexity
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="perplexity/llama-3.1-8b-instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a research task?")
|
||||
|
||||
# Example with research and analysis
|
||||
research_task = """
|
||||
Research and provide insights on the latest developments in
|
||||
artificial intelligence, focusing on recent breakthroughs and trends.
|
||||
"""
|
||||
|
||||
response = agent.start(research_task)
|
||||
|
||||
# Example with document analysis
|
||||
analysis_task = """
|
||||
Analyze the potential market opportunities for a new AI-powered
|
||||
productivity tool targeting remote workers.
|
||||
"""
|
||||
|
||||
response = agent.start(analysis_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Replicate with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Replicate
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="replicate/meta/llama-3.1-8b-instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a creative task?")
|
||||
|
||||
# Example with creative writing
|
||||
creative_task = """
|
||||
Write a short story about a robot learning to paint.
|
||||
Make it engaging and about 100 words.
|
||||
"""
|
||||
|
||||
response = agent.start(creative_task)
|
||||
|
||||
# Example with problem solving
|
||||
problem_task = """
|
||||
Solve this problem step by step:
|
||||
If a train travels at 60 mph for 2.5 hours, how far does it travel?
|
||||
"""
|
||||
|
||||
response = agent.start(problem_task)
|
||||
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Basic example of using AWS SageMaker with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with AWS SageMaker
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="sagemaker/meta-llama/Llama-3.1-8B-Instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a business analysis task?")
|
||||
|
||||
# Example with business analysis
|
||||
business_task = """
|
||||
Analyze the potential market opportunities for a new AI-powered
|
||||
productivity tool targeting remote workers. Include market size,
|
||||
competitive landscape, and go-to-market strategy recommendations.
|
||||
"""
|
||||
|
||||
response = agent.start(business_task)
|
||||
|
||||
# Example with document summarization
|
||||
summary_task = """
|
||||
Summarize the key points from this business proposal:
|
||||
Our company proposes to develop an AI-powered customer service chatbot
|
||||
that can handle 80% of common customer inquiries automatically.
|
||||
"""
|
||||
|
||||
response = agent.start(summary_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Together AI with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Together AI
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="together_ai/meta-llama/Llama-3.1-8B-Instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a research task?")
|
||||
|
||||
# Example with research and analysis
|
||||
research_task = """
|
||||
Research and provide insights on the latest developments in
|
||||
renewable energy technology, focusing on solar and wind power innovations.
|
||||
"""
|
||||
|
||||
response = agent.start(research_task)
|
||||
|
||||
# Example with creative content
|
||||
creative_task = """
|
||||
Write a haiku about artificial intelligence and its impact on society.
|
||||
Then explain the symbolism in your haiku.
|
||||
"""
|
||||
|
||||
response = agent.start(creative_task)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Basic example of using Google Vertex AI with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with Google Vertex AI
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="vertex_ai/gemini-1.5-pro",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a research task?")
|
||||
|
||||
# Example with research and analysis
|
||||
research_task = """
|
||||
Research and provide insights on the latest developments in
|
||||
renewable energy technology, focusing on solar and wind power innovations.
|
||||
"""
|
||||
|
||||
response = agent.start(research_task)
|
||||
|
||||
# Example with multimodal capabilities (text-based for now)
|
||||
multimodal_task = """
|
||||
Describe how you would analyze an image of a city skyline
|
||||
and provide insights about urban development patterns.
|
||||
"""
|
||||
|
||||
response = agent.start(multimodal_task)
|
||||
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Basic example of using vLLM with PraisonAI
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Initialize Agent with vLLM
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="vllm/meta-llama/Llama-3.1-8B-Instruct",
|
||||
)
|
||||
|
||||
# Example conversation
|
||||
response = agent.start("Hello! Can you help me with a mathematical problem?")
|
||||
|
||||
# Example with mathematical reasoning
|
||||
math_task = """
|
||||
Solve this calculus problem step by step:
|
||||
Find the derivative of f(x) = x^3 * e^(2x) using the product rule.
|
||||
"""
|
||||
|
||||
response = agent.start(math_task)
|
||||
|
||||
# Example with code optimization
|
||||
code_task = """
|
||||
Optimize this Python function for better performance:
|
||||
def find_duplicates(arr):
|
||||
duplicates = []
|
||||
for i in range(len(arr)):
|
||||
for j in range(i+1, len(arr)):
|
||||
if arr[i] == arr[j] and arr[i] not in duplicates:
|
||||
duplicates.append(arr[i])
|
||||
return duplicates
|
||||
"""
|
||||
|
||||
response = agent.start(code_task)
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from praisonaiagents import Agent, force_shutdown_telemetry
|
||||
|
||||
print("Creating agent...")
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print("Starting agent...")
|
||||
result = agent.start("Why is the sky blue?")
|
||||
|
||||
print("Agent completed successfully!")
|
||||
print(f"Result: {result}")
|
||||
|
||||
# Optional: Clean up telemetry system when script is completely done
|
||||
print("Cleaning up telemetry...")
|
||||
force_shutdown_telemetry()
|
||||
|
||||
print("Script finished normally.")
|
||||
@@ -4,5 +4,4 @@ agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
agent.start("Why sky is Blue?")
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Simple syntax checker
|
||||
filename = "praisonaiagents/agent/agent.py"
|
||||
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Try to compile the code
|
||||
compile(content, filename, 'exec')
|
||||
print(f"✅ {filename} has valid syntax")
|
||||
|
||||
except SyntaxError as e:
|
||||
print(f"❌ Syntax error in {filename}:")
|
||||
print(f" Line {e.lineno}: {e.text.strip() if e.text else 'N/A'}")
|
||||
print(f" Error: {e.msg}")
|
||||
if e.offset:
|
||||
print(f" {' ' * (e.offset-1)}^")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
@@ -22,7 +22,7 @@ def multiply(a: int, b: int) -> int:
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant. You can use the tools provided to you to help the user.",
|
||||
llm="gemini/gemini-2.5-pro",
|
||||
llm="gemini/gemini-2.5-flash",
|
||||
tools=[get_stock_price, multiply]
|
||||
)
|
||||
|
||||
|
||||
@@ -34,7 +34,13 @@ from .tools.tools import Tools
|
||||
from .agents.autoagents import AutoAgents
|
||||
from .knowledge.knowledge import Knowledge
|
||||
from .knowledge.chunking import Chunking
|
||||
from .mcp.mcp import MCP
|
||||
# MCP support (optional)
|
||||
try:
|
||||
from .mcp.mcp import MCP
|
||||
_mcp_available = True
|
||||
except ImportError:
|
||||
_mcp_available = False
|
||||
MCP = None
|
||||
from .session import Session
|
||||
from .memory.memory import Memory
|
||||
from .guardrails import GuardrailResult, LLMGuardrail
|
||||
@@ -124,7 +130,6 @@ __all__ = [
|
||||
'async_display_callbacks',
|
||||
'Knowledge',
|
||||
'Chunking',
|
||||
'MCP',
|
||||
'GuardrailResult',
|
||||
'LLMGuardrail',
|
||||
'Handoff',
|
||||
@@ -137,4 +142,8 @@ __all__ = [
|
||||
'disable_telemetry',
|
||||
'MinimalTelemetry',
|
||||
'TelemetryCollector'
|
||||
]
|
||||
]
|
||||
|
||||
# Add MCP to __all__ if available
|
||||
if _mcp_available:
|
||||
__all__.append('MCP')
|
||||
@@ -151,9 +151,23 @@ class ImageAgent(Agent):
|
||||
# Use the model name in config
|
||||
config['model'] = model_name
|
||||
|
||||
# Check if we're using a Gemini model and remove unsupported parameters
|
||||
if 'gemini' in model_name.lower():
|
||||
# Gemini models don't support response_format parameter
|
||||
# Filter parameters based on the provider to avoid unsupported parameter errors
|
||||
custom_llm_provider = None
|
||||
try:
|
||||
import litellm
|
||||
_, custom_llm_provider, _, _ = litellm.get_llm_provider(model=model_name)
|
||||
except (ImportError, AttributeError, ValueError, TypeError, Exception) as e:
|
||||
# Log the specific error for debugging but continue with string-based fallback
|
||||
# Include generic Exception to catch provider-specific errors like BadRequestError
|
||||
logging.debug(f"Provider detection failed for model '{model_name}': {e}")
|
||||
|
||||
if custom_llm_provider == "vertex_ai":
|
||||
# Vertex AI only supports 'n' and 'size' parameters for image generation
|
||||
supported_params = ['n', 'size', 'model']
|
||||
config = {k: v for k, v in config.items() if k in supported_params}
|
||||
elif custom_llm_provider == "gemini" or (custom_llm_provider is None and 'gemini' in model_name.lower()):
|
||||
# Gemini provider doesn't support response_format parameter
|
||||
# Apply this filter if provider is explicitly 'gemini' or as fallback for gemini models
|
||||
config.pop('response_format', None)
|
||||
|
||||
with Progress(
|
||||
@@ -165,9 +179,10 @@ class ImageAgent(Agent):
|
||||
# Add a task for image generation
|
||||
task = progress.add_task(f"[cyan]Generating image with {model_name}...", total=None)
|
||||
|
||||
# Use litellm's image generation
|
||||
# Use litellm's image generation with parameter dropping enabled as safety net
|
||||
response = self.litellm(
|
||||
prompt=prompt,
|
||||
drop_params=True,
|
||||
**config
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import logging
|
||||
import uuid
|
||||
import time
|
||||
from datetime import datetime
|
||||
from .chunking import Chunking
|
||||
from functools import cached_property
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
|
||||
@@ -47,6 +48,342 @@ class CustomMemory:
|
||||
"event": "ADD"
|
||||
}]
|
||||
|
||||
class MongoDBMemory:
|
||||
"""MongoDB-based memory store for knowledge management."""
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.vector_store_config = config.get("vector_store", {}).get("config", {})
|
||||
self.connection_string = self.vector_store_config.get("connection_string", "mongodb://localhost:27017/")
|
||||
self.database_name = self.vector_store_config.get("database", "praisonai")
|
||||
self.collection_name = self.vector_store_config.get("collection", "knowledge_base")
|
||||
self.use_vector_search = self.vector_store_config.get("use_vector_search", True)
|
||||
|
||||
# Initialize MongoDB client
|
||||
self._init_mongodb()
|
||||
|
||||
# Initialize embedding model
|
||||
self._init_embedding_model()
|
||||
|
||||
def _init_mongodb(self):
|
||||
"""Initialize MongoDB client and collection."""
|
||||
try:
|
||||
from pymongo import MongoClient
|
||||
|
||||
self.client = MongoClient(
|
||||
self.connection_string,
|
||||
maxPoolSize=50,
|
||||
retryWrites=True,
|
||||
retryReads=True
|
||||
)
|
||||
|
||||
# Test connection
|
||||
self.client.admin.command('ping')
|
||||
|
||||
# Setup database and collection
|
||||
self.db = self.client[self.database_name]
|
||||
self.collection = self.db[self.collection_name]
|
||||
|
||||
# Create indexes
|
||||
self._create_indexes()
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to initialize MongoDB: {e}")
|
||||
|
||||
def _init_embedding_model(self):
|
||||
"""Initialize embedding model from config."""
|
||||
try:
|
||||
# Set up embedding model based on config
|
||||
embedder_config = self.config.get("embedder", {})
|
||||
if embedder_config.get("provider") == "openai":
|
||||
import openai
|
||||
self.embedding_model = openai.OpenAI()
|
||||
self.embedding_model_name = embedder_config.get("config", {}).get("model", "text-embedding-3-small")
|
||||
else:
|
||||
# Default to OpenAI
|
||||
import openai
|
||||
self.embedding_model = openai.OpenAI()
|
||||
self.embedding_model_name = "text-embedding-3-small"
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to initialize embedding model: {e}")
|
||||
|
||||
def _get_embedding_dimensions(self, model_name: str) -> int:
|
||||
"""Get embedding dimensions based on model name."""
|
||||
# Common embedding model dimensions
|
||||
model_dimensions = {
|
||||
"text-embedding-3-small": 1536,
|
||||
"text-embedding-3-large": 3072,
|
||||
"text-embedding-ada-002": 1536,
|
||||
"text-embedding-002": 1536,
|
||||
# Add more models as needed
|
||||
}
|
||||
|
||||
# Check if model name contains known model identifiers
|
||||
for model_key, dimensions in model_dimensions.items():
|
||||
if model_key in model_name.lower():
|
||||
return dimensions
|
||||
|
||||
# Default to 1536 for unknown models (OpenAI standard)
|
||||
return 1536
|
||||
|
||||
def _create_indexes(self):
|
||||
"""Create necessary indexes for MongoDB."""
|
||||
try:
|
||||
# Text search index
|
||||
self.collection.create_index([("content", "text")])
|
||||
|
||||
# Metadata indexes
|
||||
self.collection.create_index([("metadata.filename", 1)])
|
||||
self.collection.create_index([("created_at", -1)])
|
||||
|
||||
# Vector search index for Atlas (if enabled)
|
||||
if self.use_vector_search:
|
||||
self._create_vector_index()
|
||||
|
||||
except Exception as e:
|
||||
logging.warning(f"Could not create MongoDB indexes: {e}")
|
||||
|
||||
def _create_vector_index(self):
|
||||
"""Create vector search index for Atlas Vector Search."""
|
||||
try:
|
||||
vector_index_def = {
|
||||
"mappings": {
|
||||
"dynamic": True,
|
||||
"fields": {
|
||||
"embedding": {
|
||||
"type": "knnVector",
|
||||
"dimensions": self._get_embedding_dimensions(self.embedding_model_name),
|
||||
"similarity": "cosine"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.collection.create_search_index(vector_index_def, "vector_index")
|
||||
|
||||
except Exception as e:
|
||||
logging.warning(f"Could not create vector search index: {e}")
|
||||
|
||||
def _get_embedding(self, text):
|
||||
"""Get embedding for text."""
|
||||
try:
|
||||
response = self.embedding_model.embeddings.create(
|
||||
input=text,
|
||||
model=self.embedding_model_name
|
||||
)
|
||||
return response.data[0].embedding
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting embedding: {e}")
|
||||
return None
|
||||
|
||||
def add(self, messages, user_id=None, agent_id=None, run_id=None, metadata=None):
|
||||
"""Add memory to MongoDB."""
|
||||
try:
|
||||
# Handle different message formats
|
||||
if isinstance(messages, list):
|
||||
content = "\n".join([msg.get("content", str(msg)) if isinstance(msg, dict) else str(msg) for msg in messages])
|
||||
else:
|
||||
content = str(messages)
|
||||
|
||||
# Generate embedding
|
||||
embedding = self._get_embedding(content) if self.use_vector_search else None
|
||||
|
||||
# Create document
|
||||
doc = {
|
||||
"content": content,
|
||||
"metadata": metadata or {},
|
||||
"user_id": user_id,
|
||||
"agent_id": agent_id,
|
||||
"run_id": run_id,
|
||||
"created_at": datetime.utcnow(),
|
||||
"memory_type": "knowledge"
|
||||
}
|
||||
|
||||
if embedding:
|
||||
doc["embedding"] = embedding
|
||||
|
||||
# Insert document
|
||||
result = self.collection.insert_one(doc)
|
||||
|
||||
return [{
|
||||
"id": str(result.inserted_id),
|
||||
"memory": content,
|
||||
"event": "ADD"
|
||||
}]
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error adding memory to MongoDB: {e}")
|
||||
return []
|
||||
|
||||
def search(self, query, user_id=None, agent_id=None, run_id=None, rerank=False, **kwargs):
|
||||
"""Search memories in MongoDB."""
|
||||
try:
|
||||
results = []
|
||||
|
||||
# Vector search if enabled
|
||||
if self.use_vector_search:
|
||||
embedding = self._get_embedding(query)
|
||||
if embedding:
|
||||
pipeline = [
|
||||
{
|
||||
"$vectorSearch": {
|
||||
"index": "vector_index",
|
||||
"path": "embedding",
|
||||
"queryVector": embedding,
|
||||
"numCandidates": kwargs.get("limit", 10) * 10,
|
||||
"limit": kwargs.get("limit", 10)
|
||||
}
|
||||
},
|
||||
{
|
||||
"$addFields": {
|
||||
"score": {"$meta": "vectorSearchScore"}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Add filters if provided
|
||||
if user_id or agent_id or run_id:
|
||||
match_filter = {}
|
||||
if user_id:
|
||||
match_filter["user_id"] = user_id
|
||||
if agent_id:
|
||||
match_filter["agent_id"] = agent_id
|
||||
if run_id:
|
||||
match_filter["run_id"] = run_id
|
||||
|
||||
pipeline.append({"$match": match_filter})
|
||||
|
||||
for doc in self.collection.aggregate(pipeline):
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"memory": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": doc.get("score", 1.0)
|
||||
})
|
||||
|
||||
# Fallback to text search
|
||||
if not results:
|
||||
search_filter = {"$text": {"$search": query}}
|
||||
|
||||
# Add additional filters
|
||||
if user_id:
|
||||
search_filter["user_id"] = user_id
|
||||
if agent_id:
|
||||
search_filter["agent_id"] = agent_id
|
||||
if run_id:
|
||||
search_filter["run_id"] = run_id
|
||||
|
||||
for doc in self.collection.find(search_filter).limit(kwargs.get("limit", 10)):
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"memory": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": 1.0
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error searching MongoDB: {e}")
|
||||
return []
|
||||
|
||||
def get_all(self, user_id=None, agent_id=None, run_id=None):
|
||||
"""Get all memories from MongoDB."""
|
||||
try:
|
||||
search_filter = {}
|
||||
if user_id:
|
||||
search_filter["user_id"] = user_id
|
||||
if agent_id:
|
||||
search_filter["agent_id"] = agent_id
|
||||
if run_id:
|
||||
search_filter["run_id"] = run_id
|
||||
|
||||
results = []
|
||||
for doc in self.collection.find(search_filter):
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"memory": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"created_at": doc.get("created_at")
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting all memories from MongoDB: {e}")
|
||||
return []
|
||||
|
||||
def get(self, memory_id):
|
||||
"""Get a specific memory by ID."""
|
||||
try:
|
||||
from bson import ObjectId
|
||||
doc = self.collection.find_one({"_id": ObjectId(memory_id)})
|
||||
if doc:
|
||||
return {
|
||||
"id": str(doc["_id"]),
|
||||
"memory": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"created_at": doc.get("created_at")
|
||||
}
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting memory from MongoDB: {e}")
|
||||
return None
|
||||
|
||||
def update(self, memory_id, data):
|
||||
"""Update a memory."""
|
||||
try:
|
||||
from bson import ObjectId
|
||||
result = self.collection.update_one(
|
||||
{"_id": ObjectId(memory_id)},
|
||||
{"$set": {"content": data, "updated_at": datetime.utcnow()}}
|
||||
)
|
||||
return result.modified_count > 0
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error updating memory in MongoDB: {e}")
|
||||
return False
|
||||
|
||||
def delete(self, memory_id):
|
||||
"""Delete a memory."""
|
||||
try:
|
||||
from bson import ObjectId
|
||||
result = self.collection.delete_one({"_id": ObjectId(memory_id)})
|
||||
return result.deleted_count > 0
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error deleting memory from MongoDB: {e}")
|
||||
return False
|
||||
|
||||
def delete_all(self, user_id=None, agent_id=None, run_id=None):
|
||||
"""Delete all memories."""
|
||||
try:
|
||||
search_filter = {}
|
||||
if user_id:
|
||||
search_filter["user_id"] = user_id
|
||||
if agent_id:
|
||||
search_filter["agent_id"] = agent_id
|
||||
if run_id:
|
||||
search_filter["run_id"] = run_id
|
||||
|
||||
result = self.collection.delete_many(search_filter)
|
||||
return result.deleted_count
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error deleting all memories from MongoDB: {e}")
|
||||
return 0
|
||||
|
||||
def reset(self):
|
||||
"""Reset all memories."""
|
||||
try:
|
||||
result = self.collection.delete_many({})
|
||||
return result.deleted_count
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error resetting MongoDB memories: {e}")
|
||||
return 0
|
||||
|
||||
class Knowledge:
|
||||
def __init__(self, config=None, verbose=None):
|
||||
self._config = config
|
||||
@@ -119,8 +456,20 @@ class Knowledge:
|
||||
if "vector_store" in self._config:
|
||||
if "provider" in self._config["vector_store"]:
|
||||
base_config["vector_store"]["provider"] = self._config["vector_store"]["provider"]
|
||||
|
||||
# Special handling for MongoDB vector store
|
||||
if self._config["vector_store"]["provider"] == "mongodb":
|
||||
base_config["vector_store"] = {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"connection_string": self._config["vector_store"]["config"].get("connection_string", "mongodb://localhost:27017/"),
|
||||
"database": self._config["vector_store"]["config"].get("database", "praisonai"),
|
||||
"collection": self._config["vector_store"]["config"].get("collection", "knowledge_base"),
|
||||
"use_vector_search": self._config["vector_store"]["config"].get("use_vector_search", True)
|
||||
}
|
||||
}
|
||||
|
||||
if "config" in self._config["vector_store"]:
|
||||
if "config" in self._config["vector_store"] and self._config["vector_store"]["provider"] != "mongodb":
|
||||
config_copy = self._config["vector_store"]["config"].copy()
|
||||
# Only exclude client as it's managed internally
|
||||
if "client" in config_copy:
|
||||
@@ -146,6 +495,16 @@ class Knowledge:
|
||||
|
||||
@cached_property
|
||||
def memory(self):
|
||||
# Check if MongoDB provider is specified
|
||||
if (self.config.get("vector_store", {}).get("provider") == "mongodb"):
|
||||
try:
|
||||
return MongoDBMemory(self.config)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize MongoDB memory: {e}")
|
||||
# Fall back to default memory
|
||||
pass
|
||||
|
||||
# Default Mem0 memory
|
||||
try:
|
||||
return CustomMemory.from_config(self.config)
|
||||
except (NotImplementedError, ValueError) as e:
|
||||
|
||||
@@ -1049,6 +1049,150 @@ class OpenAIClient:
|
||||
break
|
||||
|
||||
return final_response
|
||||
|
||||
def chat_completion_with_tools_stream(
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
model: str = "gpt-4o",
|
||||
temperature: float = 0.7,
|
||||
tools: Optional[List[Any]] = None,
|
||||
execute_tool_fn: Optional[Callable] = None,
|
||||
reasoning_steps: bool = False,
|
||||
verbose: bool = True,
|
||||
max_iterations: int = 10,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Create a streaming chat completion with tool support.
|
||||
|
||||
This method yields chunks of the response as they are generated,
|
||||
enabling real-time streaming to the user.
|
||||
|
||||
Args:
|
||||
messages: List of message dictionaries
|
||||
model: Model to use
|
||||
temperature: Temperature for generation
|
||||
tools: List of tools (can be callables, dicts, or strings)
|
||||
execute_tool_fn: Function to execute tools
|
||||
reasoning_steps: Whether to show reasoning
|
||||
verbose: Whether to show verbose output
|
||||
max_iterations: Maximum tool calling iterations
|
||||
**kwargs: Additional API parameters
|
||||
|
||||
Yields:
|
||||
String chunks of the response as they are generated
|
||||
"""
|
||||
# Format tools for OpenAI API
|
||||
formatted_tools = self.format_tools(tools)
|
||||
|
||||
# Continue tool execution loop until no more tool calls are needed
|
||||
iteration_count = 0
|
||||
|
||||
while iteration_count < max_iterations:
|
||||
try:
|
||||
# Create streaming response
|
||||
response_stream = self._sync_client.chat.completions.create(
|
||||
model=model,
|
||||
messages=messages,
|
||||
temperature=temperature,
|
||||
tools=formatted_tools if formatted_tools else None,
|
||||
stream=True,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
full_response_text = ""
|
||||
reasoning_content = ""
|
||||
chunks = []
|
||||
|
||||
# Stream the response chunk by chunk
|
||||
for chunk in response_stream:
|
||||
chunks.append(chunk)
|
||||
if chunk.choices and chunk.choices[0].delta.content:
|
||||
content = chunk.choices[0].delta.content
|
||||
full_response_text += content
|
||||
yield content
|
||||
|
||||
# Handle reasoning content if enabled
|
||||
if reasoning_steps and chunk.choices and hasattr(chunk.choices[0].delta, "reasoning_content"):
|
||||
rc = chunk.choices[0].delta.reasoning_content
|
||||
if rc:
|
||||
reasoning_content += rc
|
||||
yield f"[Reasoning: {rc}]"
|
||||
|
||||
# Process the complete response to check for tool calls
|
||||
final_response = process_stream_chunks(chunks)
|
||||
|
||||
if not final_response:
|
||||
return
|
||||
|
||||
# Check for tool calls
|
||||
tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None)
|
||||
|
||||
if tool_calls and execute_tool_fn:
|
||||
# Convert ToolCall dataclass objects to dict for JSON serialization
|
||||
serializable_tool_calls = []
|
||||
for tc in tool_calls:
|
||||
if isinstance(tc, ToolCall):
|
||||
# Convert dataclass to dict
|
||||
serializable_tool_calls.append({
|
||||
"id": tc.id,
|
||||
"type": tc.type,
|
||||
"function": tc.function
|
||||
})
|
||||
else:
|
||||
# Already an OpenAI object, keep as is
|
||||
serializable_tool_calls.append(tc)
|
||||
|
||||
messages.append({
|
||||
"role": "assistant",
|
||||
"content": final_response.choices[0].message.content,
|
||||
"tool_calls": serializable_tool_calls
|
||||
})
|
||||
|
||||
for tool_call in tool_calls:
|
||||
# Handle both ToolCall dataclass and OpenAI object
|
||||
try:
|
||||
if isinstance(tool_call, ToolCall):
|
||||
function_name = tool_call.function["name"]
|
||||
arguments = json.loads(tool_call.function["arguments"])
|
||||
else:
|
||||
function_name = tool_call.function.name
|
||||
arguments = json.loads(tool_call.function.arguments)
|
||||
except json.JSONDecodeError as e:
|
||||
if verbose:
|
||||
yield f"\n[Error parsing arguments for {function_name if 'function_name' in locals() else 'unknown function'}: {str(e)}]"
|
||||
continue
|
||||
|
||||
if verbose:
|
||||
yield f"\n[Calling function: {function_name}]"
|
||||
|
||||
# Execute the tool with error handling
|
||||
try:
|
||||
tool_result = execute_tool_fn(function_name, arguments)
|
||||
results_str = json.dumps(tool_result) if tool_result else "Function returned an empty output"
|
||||
except Exception as e:
|
||||
results_str = f"Error executing function: {str(e)}"
|
||||
if verbose:
|
||||
yield f"\n[Function error: {str(e)}]"
|
||||
|
||||
if verbose:
|
||||
yield f"\n[Function result: {results_str}]"
|
||||
|
||||
messages.append({
|
||||
"role": "tool",
|
||||
"tool_call_id": tool_call.id if hasattr(tool_call, 'id') else tool_call['id'],
|
||||
"content": results_str
|
||||
})
|
||||
|
||||
# Continue the loop to allow more tool calls
|
||||
iteration_count += 1
|
||||
else:
|
||||
# No tool calls, we're done
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
yield f"Error: {str(e)}"
|
||||
break
|
||||
|
||||
def parse_structured_output(
|
||||
self,
|
||||
|
||||
@@ -25,6 +25,8 @@ class MCPToolRunner(threading.Thread):
|
||||
self.initialized = threading.Event()
|
||||
self.tools = []
|
||||
self.timeout = timeout
|
||||
self._tool_timings = {}
|
||||
self._timings_lock = threading.Lock()
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
@@ -75,25 +77,63 @@ class MCPToolRunner(threading.Thread):
|
||||
|
||||
def call_tool(self, tool_name, arguments):
|
||||
"""Call an MCP tool and wait for the result."""
|
||||
# Import telemetry here to avoid circular imports
|
||||
try:
|
||||
from ..telemetry.telemetry import get_telemetry
|
||||
telemetry = get_telemetry()
|
||||
except (ImportError, AttributeError):
|
||||
telemetry = None
|
||||
|
||||
# Check initialization first (without timing)
|
||||
if not self.initialized.is_set():
|
||||
self.initialized.wait(timeout=self.timeout)
|
||||
if not self.initialized.is_set():
|
||||
# Track initialization timeout failure
|
||||
if telemetry:
|
||||
telemetry.track_tool_usage(tool_name, success=False, execution_time=0)
|
||||
return f"Error: MCP initialization timed out after {self.timeout} seconds"
|
||||
|
||||
# Put request in queue
|
||||
self.queue.put((tool_name, arguments))
|
||||
|
||||
# Wait for result
|
||||
success, result = self.result_queue.get()
|
||||
if not success:
|
||||
return f"Error: {result}"
|
||||
|
||||
# Process result
|
||||
if hasattr(result, 'content') and result.content:
|
||||
if hasattr(result.content[0], 'text'):
|
||||
return result.content[0].text
|
||||
return str(result.content[0])
|
||||
return str(result)
|
||||
# Start timing after initialization check
|
||||
start_time = time.time()
|
||||
is_success = False
|
||||
try:
|
||||
# Put request in queue
|
||||
self.queue.put((tool_name, arguments))
|
||||
|
||||
# Wait for result
|
||||
success, result = self.result_queue.get()
|
||||
if not success:
|
||||
return f"Error: {result}"
|
||||
|
||||
# Process result
|
||||
if hasattr(result, 'content') and result.content:
|
||||
if hasattr(result.content[0], 'text'):
|
||||
processed_result = result.content[0].text
|
||||
else:
|
||||
processed_result = str(result.content[0])
|
||||
else:
|
||||
processed_result = str(result)
|
||||
|
||||
is_success = True
|
||||
return processed_result
|
||||
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
finally:
|
||||
# Track timing regardless of success/failure
|
||||
end_time = time.time()
|
||||
execution_time = end_time - start_time
|
||||
|
||||
# Log timing information for debugging
|
||||
logging.debug(f"Tool '{tool_name}' execution time: {execution_time:.3f} seconds")
|
||||
|
||||
# Store timing in thread-safe manner
|
||||
with self._timings_lock:
|
||||
self._tool_timings[tool_name] = execution_time
|
||||
|
||||
# Track tool usage with timing information
|
||||
if telemetry:
|
||||
telemetry.track_tool_usage(tool_name, success=is_success, execution_time=execution_time)
|
||||
|
||||
def shutdown(self):
|
||||
"""Signal the thread to shut down."""
|
||||
|
||||
@@ -5,6 +5,7 @@ import time
|
||||
import shutil
|
||||
from typing import Any, Dict, List, Optional, Union, Literal
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
# Disable litellm telemetry before any imports
|
||||
os.environ["LITELLM_TELEMETRY"] = "False"
|
||||
@@ -39,6 +40,13 @@ try:
|
||||
except ImportError:
|
||||
LITELLM_AVAILABLE = False
|
||||
|
||||
try:
|
||||
import pymongo
|
||||
from pymongo import MongoClient
|
||||
PYMONGO_AVAILABLE = True
|
||||
except ImportError:
|
||||
PYMONGO_AVAILABLE = False
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +63,7 @@ class Memory:
|
||||
|
||||
Config example:
|
||||
{
|
||||
"provider": "rag" or "mem0" or "none",
|
||||
"provider": "rag" or "mem0" or "mongodb" or "none",
|
||||
"use_embedding": True,
|
||||
"short_db": "short_term.db",
|
||||
"long_db": "long_term.db",
|
||||
@@ -65,6 +73,15 @@ class Memory:
|
||||
"org_id": "...",
|
||||
"project_id": "...",
|
||||
|
||||
# MongoDB configuration (if provider is "mongodb")
|
||||
"connection_string": "mongodb://localhost:27017/" or "mongodb+srv://user:[email protected]/",
|
||||
"database": "praisonai",
|
||||
"use_vector_search": True, # Enable Atlas Vector Search
|
||||
"max_pool_size": 50,
|
||||
"min_pool_size": 10,
|
||||
"max_idle_time": 30000,
|
||||
"server_selection_timeout": 5000,
|
||||
|
||||
# Graph memory configuration (optional)
|
||||
"graph_store": {
|
||||
"provider": "neo4j" or "memgraph",
|
||||
@@ -115,6 +132,7 @@ class Memory:
|
||||
self.provider = self.cfg.get("provider", "rag")
|
||||
self.use_mem0 = (self.provider.lower() == "mem0") and MEM0_AVAILABLE
|
||||
self.use_rag = (self.provider.lower() == "rag") and CHROMADB_AVAILABLE and self.cfg.get("use_embedding", False)
|
||||
self.use_mongodb = (self.provider.lower() == "mongodb") and PYMONGO_AVAILABLE
|
||||
self.graph_enabled = False # Initialize graph support flag
|
||||
|
||||
# Extract embedding model from config
|
||||
@@ -127,6 +145,10 @@ class Memory:
|
||||
|
||||
self._log_verbose(f"Using embedding model: {self.embedding_model}")
|
||||
|
||||
# Determine embedding dimensions based on model
|
||||
self.embedding_dimensions = self._get_embedding_dimensions(self.embedding_model)
|
||||
self._log_verbose(f"Using embedding dimensions: {self.embedding_dimensions}")
|
||||
|
||||
# Create .praison directory if it doesn't exist
|
||||
os.makedirs(".praison", exist_ok=True)
|
||||
|
||||
@@ -138,9 +160,11 @@ class Memory:
|
||||
self.long_db = self.cfg.get("long_db", ".praison/long_term.db")
|
||||
self._init_ltm()
|
||||
|
||||
# Conditionally init Mem0 or local RAG
|
||||
# Conditionally init Mem0, MongoDB, or local RAG
|
||||
if self.use_mem0:
|
||||
self._init_mem0()
|
||||
elif self.use_mongodb:
|
||||
self._init_mongodb()
|
||||
elif self.use_rag:
|
||||
self._init_chroma()
|
||||
|
||||
@@ -261,6 +285,143 @@ class Memory:
|
||||
self._log_verbose(f"Failed to initialize ChromaDB: {e}", logging.ERROR)
|
||||
self.use_rag = False
|
||||
|
||||
def _init_mongodb(self):
|
||||
"""Initialize MongoDB client for memory storage."""
|
||||
try:
|
||||
mongo_cfg = self.cfg.get("config", {})
|
||||
self.connection_string = mongo_cfg.get("connection_string", "mongodb://localhost:27017/")
|
||||
self.database_name = mongo_cfg.get("database", "praisonai")
|
||||
self.use_vector_search = mongo_cfg.get("use_vector_search", False)
|
||||
|
||||
# Initialize MongoDB client
|
||||
self.mongo_client = MongoClient(
|
||||
self.connection_string,
|
||||
maxPoolSize=mongo_cfg.get("max_pool_size", 50),
|
||||
minPoolSize=mongo_cfg.get("min_pool_size", 10),
|
||||
maxIdleTimeMS=mongo_cfg.get("max_idle_time", 30000),
|
||||
serverSelectionTimeoutMS=mongo_cfg.get("server_selection_timeout", 5000),
|
||||
retryWrites=True,
|
||||
retryReads=True
|
||||
)
|
||||
|
||||
# Test connection
|
||||
self.mongo_client.admin.command('ping')
|
||||
|
||||
# Setup database and collections
|
||||
self.mongo_db = self.mongo_client[self.database_name]
|
||||
self.mongo_short_term = self.mongo_db.short_term_memory
|
||||
self.mongo_long_term = self.mongo_db.long_term_memory
|
||||
self.mongo_entities = self.mongo_db.entity_memory
|
||||
self.mongo_users = self.mongo_db.user_memory
|
||||
|
||||
# Create indexes for better performance
|
||||
self._create_mongodb_indexes()
|
||||
|
||||
self._log_verbose("MongoDB initialized successfully")
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Failed to initialize MongoDB: {e}", logging.ERROR)
|
||||
self.use_mongodb = False
|
||||
|
||||
def _create_mongodb_indexes(self):
|
||||
"""Create MongoDB indexes for better performance."""
|
||||
try:
|
||||
# Text search indexes
|
||||
self.mongo_short_term.create_index([("content", "text")])
|
||||
self.mongo_long_term.create_index([("content", "text")])
|
||||
|
||||
# Compound indexes for filtering
|
||||
self.mongo_short_term.create_index([("created_at", -1), ("metadata.quality", -1)])
|
||||
self.mongo_long_term.create_index([("created_at", -1), ("metadata.quality", -1)])
|
||||
|
||||
# User-specific indexes
|
||||
self.mongo_users.create_index([("user_id", 1), ("created_at", -1)])
|
||||
|
||||
# Entity indexes
|
||||
self.mongo_entities.create_index([("entity_name", 1), ("entity_type", 1)])
|
||||
|
||||
# Vector search indexes for Atlas (if enabled)
|
||||
if self.use_vector_search:
|
||||
self._create_vector_search_indexes()
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Warning: Could not create MongoDB indexes: {e}", logging.WARNING)
|
||||
|
||||
def _create_vector_search_indexes(self):
|
||||
"""Create vector search indexes for Atlas."""
|
||||
try:
|
||||
vector_index_def = {
|
||||
"mappings": {
|
||||
"dynamic": True,
|
||||
"fields": {
|
||||
"embedding": {
|
||||
"type": "knnVector",
|
||||
"dimensions": self.embedding_dimensions,
|
||||
"similarity": "cosine"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create vector indexes for both short and long term collections
|
||||
try:
|
||||
self.mongo_short_term.create_search_index(vector_index_def, "vector_index")
|
||||
self.mongo_long_term.create_search_index(vector_index_def, "vector_index")
|
||||
self._log_verbose("Vector search indexes created successfully")
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Could not create vector search indexes: {e}", logging.WARNING)
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error creating vector search indexes: {e}", logging.WARNING)
|
||||
|
||||
def _get_embedding(self, text: str) -> List[float]:
|
||||
"""Get embedding for text using available embedding services."""
|
||||
try:
|
||||
if LITELLM_AVAILABLE:
|
||||
# Use LiteLLM for consistency with the rest of the codebase
|
||||
import litellm
|
||||
|
||||
response = litellm.embedding(
|
||||
model=self.embedding_model,
|
||||
input=text
|
||||
)
|
||||
return response.data[0]["embedding"]
|
||||
elif OPENAI_AVAILABLE:
|
||||
# Fallback to OpenAI client
|
||||
from openai import OpenAI
|
||||
client = OpenAI()
|
||||
|
||||
response = client.embeddings.create(
|
||||
input=text,
|
||||
model=self.embedding_model
|
||||
)
|
||||
return response.data[0].embedding
|
||||
else:
|
||||
self._log_verbose("Neither litellm nor openai available for embeddings", logging.WARNING)
|
||||
return None
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error getting embedding: {e}", logging.ERROR)
|
||||
return None
|
||||
|
||||
def _get_embedding_dimensions(self, model_name: str) -> int:
|
||||
"""Get embedding dimensions based on model name."""
|
||||
# Common embedding model dimensions
|
||||
model_dimensions = {
|
||||
"text-embedding-3-small": 1536,
|
||||
"text-embedding-3-large": 3072,
|
||||
"text-embedding-ada-002": 1536,
|
||||
"text-embedding-002": 1536,
|
||||
# Add more models as needed
|
||||
}
|
||||
|
||||
# Check if model name contains known model identifiers
|
||||
for model_key, dimensions in model_dimensions.items():
|
||||
if model_key in model_name.lower():
|
||||
return dimensions
|
||||
|
||||
# Default to 1536 for unknown models (OpenAI standard)
|
||||
return 1536
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Basic Quality Score Computation
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -323,20 +484,40 @@ class Memory:
|
||||
)
|
||||
logger.info(f"Processed metadata: {metadata}")
|
||||
|
||||
# Existing store logic
|
||||
# Generate unique ID and timestamp once
|
||||
ident = str(time.time_ns())
|
||||
created_at = time.time()
|
||||
|
||||
# Store in MongoDB if enabled
|
||||
if self.use_mongodb and hasattr(self, "mongo_short_term"):
|
||||
try:
|
||||
doc = {
|
||||
"_id": ident,
|
||||
"content": text,
|
||||
"metadata": metadata,
|
||||
"created_at": datetime.utcnow(),
|
||||
"memory_type": "short_term"
|
||||
}
|
||||
self.mongo_short_term.insert_one(doc)
|
||||
logger.info(f"Successfully stored in MongoDB short-term memory with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in MongoDB short-term memory: {e}")
|
||||
raise
|
||||
|
||||
# Existing SQLite store logic
|
||||
try:
|
||||
conn = sqlite3.connect(self.short_db)
|
||||
ident = str(time.time_ns())
|
||||
conn.execute(
|
||||
"INSERT INTO short_mem (id, content, meta, created_at) VALUES (?,?,?,?)",
|
||||
(ident, text, json.dumps(metadata), time.time())
|
||||
(ident, text, json.dumps(metadata), created_at)
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
logger.info(f"Successfully stored in short-term memory with ID: {ident}")
|
||||
logger.info(f"Successfully stored in SQLite short-term memory with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in short-term memory: {e}")
|
||||
raise
|
||||
logger.error(f"Failed to store in SQLite short-term memory: {e}")
|
||||
if not self.use_mongodb: # Only raise if we're not using MongoDB as fallback
|
||||
raise
|
||||
|
||||
def search_short_term(
|
||||
self,
|
||||
@@ -358,6 +539,67 @@ class Memory:
|
||||
filtered = [r for r in results if r.get("score", 1.0) >= relevance_cutoff]
|
||||
return filtered
|
||||
|
||||
elif self.use_mongodb and hasattr(self, "mongo_short_term"):
|
||||
try:
|
||||
results = []
|
||||
|
||||
# If vector search is enabled and we have embeddings
|
||||
if self.use_vector_search and hasattr(self, "_get_embedding"):
|
||||
embedding = self._get_embedding(query)
|
||||
if embedding:
|
||||
# Vector search pipeline
|
||||
pipeline = [
|
||||
{
|
||||
"$vectorSearch": {
|
||||
"index": "vector_index",
|
||||
"path": "embedding",
|
||||
"queryVector": embedding,
|
||||
"numCandidates": limit * 10,
|
||||
"limit": limit
|
||||
}
|
||||
},
|
||||
{
|
||||
"$addFields": {
|
||||
"score": {"$meta": "vectorSearchScore"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"$match": {
|
||||
"metadata.quality": {"$gte": min_quality},
|
||||
"score": {"$gte": relevance_cutoff}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
for doc in self.mongo_short_term.aggregate(pipeline):
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"text": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": doc.get("score", 1.0)
|
||||
})
|
||||
|
||||
# Fallback to text search if no vector results
|
||||
if not results:
|
||||
search_filter = {
|
||||
"$text": {"$search": query},
|
||||
"metadata.quality": {"$gte": min_quality}
|
||||
}
|
||||
|
||||
for doc in self.mongo_short_term.find(search_filter).limit(limit):
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"text": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": 1.0 # Default score for text search
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error searching MongoDB short-term memory: {e}", logging.ERROR)
|
||||
return []
|
||||
|
||||
elif self.use_rag and hasattr(self, "chroma_col"):
|
||||
try:
|
||||
if LITELLM_AVAILABLE:
|
||||
@@ -481,6 +723,29 @@ class Memory:
|
||||
ident = str(time.time_ns())
|
||||
created = time.time()
|
||||
|
||||
# Store in MongoDB if enabled (first priority)
|
||||
if self.use_mongodb and hasattr(self, "mongo_long_term"):
|
||||
try:
|
||||
doc = {
|
||||
"_id": ident,
|
||||
"content": text,
|
||||
"metadata": metadata,
|
||||
"created_at": datetime.utcnow(),
|
||||
"memory_type": "long_term"
|
||||
}
|
||||
|
||||
# Add embedding if vector search is enabled
|
||||
if self.use_vector_search:
|
||||
embedding = self._get_embedding(text)
|
||||
if embedding:
|
||||
doc["embedding"] = embedding
|
||||
|
||||
self.mongo_long_term.insert_one(doc)
|
||||
logger.info(f"Successfully stored in MongoDB long-term memory with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in MongoDB long-term memory: {e}")
|
||||
# Continue to SQLite fallback
|
||||
|
||||
# Store in SQLite
|
||||
try:
|
||||
conn = sqlite3.connect(self.long_db)
|
||||
@@ -493,7 +758,9 @@ class Memory:
|
||||
logger.info(f"Successfully stored in SQLite with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing in SQLite: {e}")
|
||||
return
|
||||
if not (self.use_mongodb and hasattr(self, "mongo_long_term")):
|
||||
# Only raise if MongoDB is not available as fallback
|
||||
return
|
||||
|
||||
# Store in vector database if enabled
|
||||
if self.use_rag and hasattr(self, "chroma_col"):
|
||||
@@ -579,6 +846,76 @@ class Memory:
|
||||
logger.info(f"Found {len(filtered)} results in Mem0")
|
||||
return filtered
|
||||
|
||||
elif self.use_mongodb and hasattr(self, "mongo_long_term"):
|
||||
try:
|
||||
results = []
|
||||
|
||||
# If vector search is enabled and we have embeddings
|
||||
if self.use_vector_search:
|
||||
embedding = self._get_embedding(query)
|
||||
if embedding:
|
||||
# Vector search pipeline
|
||||
pipeline = [
|
||||
{
|
||||
"$vectorSearch": {
|
||||
"index": "vector_index",
|
||||
"path": "embedding",
|
||||
"queryVector": embedding,
|
||||
"numCandidates": limit * 10,
|
||||
"limit": limit
|
||||
}
|
||||
},
|
||||
{
|
||||
"$addFields": {
|
||||
"score": {"$meta": "vectorSearchScore"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"$match": {
|
||||
"metadata.quality": {"$gte": min_quality},
|
||||
"score": {"$gte": relevance_cutoff}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
for doc in self.mongo_long_term.aggregate(pipeline):
|
||||
text = doc["content"]
|
||||
# Add memory record citation
|
||||
if "(Memory record:" not in text:
|
||||
text = f"{text} (Memory record: {str(doc['_id'])})"
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"text": text,
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": doc.get("score", 1.0)
|
||||
})
|
||||
|
||||
# Fallback to text search if no vector results
|
||||
if not results:
|
||||
search_filter = {
|
||||
"$text": {"$search": query},
|
||||
"metadata.quality": {"$gte": min_quality}
|
||||
}
|
||||
|
||||
for doc in self.mongo_long_term.find(search_filter).limit(limit):
|
||||
text = doc["content"]
|
||||
# Add memory record citation
|
||||
if "(Memory record:" not in text:
|
||||
text = f"{text} (Memory record: {str(doc['_id'])})"
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"text": text,
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": 1.0 # Default score for text search
|
||||
})
|
||||
|
||||
logger.info(f"Found {len(results)} results in MongoDB")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error searching MongoDB long-term memory: {e}", logging.ERROR)
|
||||
# Fall through to SQLite search
|
||||
|
||||
elif self.use_rag and hasattr(self, "chroma_col"):
|
||||
try:
|
||||
if LITELLM_AVAILABLE:
|
||||
@@ -617,7 +954,7 @@ class Memory:
|
||||
metadata = resp["metadatas"][0][i] if "metadatas" in resp else {}
|
||||
text = resp["documents"][0][i]
|
||||
# Add memory record citation
|
||||
text = f"{text} (Memory record: {text})"
|
||||
text = f"{text} (Memory record: {resp['ids'][0][i]})"
|
||||
found.append({
|
||||
"id": resp["ids"][0][i],
|
||||
"text": text,
|
||||
@@ -673,7 +1010,7 @@ class Memory:
|
||||
return results[:limit]
|
||||
|
||||
def reset_long_term(self):
|
||||
"""Clear local LTM DB, plus Chroma or mem0 if in use."""
|
||||
"""Clear local LTM DB, plus Chroma, MongoDB, or mem0 if in use."""
|
||||
conn = sqlite3.connect(self.long_db)
|
||||
conn.execute("DELETE FROM long_mem")
|
||||
conn.commit()
|
||||
@@ -682,6 +1019,12 @@ class Memory:
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
# Mem0 has no universal reset API. Could implement partial or no-op.
|
||||
pass
|
||||
if self.use_mongodb and hasattr(self, "mongo_long_term"):
|
||||
try:
|
||||
self.mongo_long_term.delete_many({})
|
||||
self._log_verbose("MongoDB long-term memory cleared")
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error clearing MongoDB long-term memory: {e}", logging.ERROR)
|
||||
if self.use_rag and hasattr(self, "chroma_client"):
|
||||
self.chroma_client.reset() # entire DB
|
||||
self._init_chroma() # re-init fresh
|
||||
@@ -730,6 +1073,21 @@ class Memory:
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
self.mem0_client.add(text, user_id=user_id, metadata=meta)
|
||||
elif self.use_mongodb and hasattr(self, "mongo_users"):
|
||||
try:
|
||||
from datetime import datetime
|
||||
ident = str(time.time_ns())
|
||||
doc = {
|
||||
"_id": ident,
|
||||
"user_id": user_id,
|
||||
"content": text,
|
||||
"metadata": meta,
|
||||
"created_at": datetime.utcnow()
|
||||
}
|
||||
self.mongo_users.insert_one(doc)
|
||||
self._log_verbose(f"Successfully stored user memory for {user_id}")
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error storing user memory: {e}", logging.ERROR)
|
||||
else:
|
||||
self.store_long_term(text, metadata=meta)
|
||||
|
||||
@@ -742,6 +1100,26 @@ class Memory:
|
||||
search_params = {"query": query, "limit": limit, "user_id": user_id, "rerank": rerank}
|
||||
search_params.update(kwargs)
|
||||
return self.mem0_client.search(**search_params)
|
||||
elif self.use_mongodb and hasattr(self, "mongo_users"):
|
||||
try:
|
||||
results = []
|
||||
search_filter = {
|
||||
"user_id": user_id,
|
||||
"$text": {"$search": query}
|
||||
}
|
||||
|
||||
for doc in self.mongo_users.find(search_filter).limit(limit):
|
||||
results.append({
|
||||
"id": str(doc["_id"]),
|
||||
"text": doc["content"],
|
||||
"metadata": doc.get("metadata", {}),
|
||||
"score": 1.0
|
||||
})
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error searching MongoDB user memory: {e}", logging.ERROR)
|
||||
return []
|
||||
else:
|
||||
hits = self.search_long_term(query, limit=20)
|
||||
filtered = []
|
||||
@@ -790,7 +1168,7 @@ class Memory:
|
||||
|
||||
return self.mem0_client.search(**search_params)
|
||||
|
||||
# For local memory, use specific search methods
|
||||
# For MongoDB or local memory, use specific search methods
|
||||
if user_id:
|
||||
# Use user-specific search
|
||||
return self.search_user_memory(user_id, query, limit=limit, rerank=rerank, **kwargs)
|
||||
|
||||
@@ -24,6 +24,7 @@ __all__ = [
|
||||
'get_telemetry',
|
||||
'enable_telemetry',
|
||||
'disable_telemetry',
|
||||
'force_shutdown_telemetry',
|
||||
'MinimalTelemetry',
|
||||
'TelemetryCollector', # For backward compatibility
|
||||
]
|
||||
@@ -47,6 +48,12 @@ def disable_telemetry():
|
||||
_disable_telemetry()
|
||||
|
||||
|
||||
def force_shutdown_telemetry():
|
||||
"""Force shutdown of telemetry system with comprehensive cleanup."""
|
||||
from .telemetry import force_shutdown_telemetry as _force_shutdown_telemetry
|
||||
_force_shutdown_telemetry()
|
||||
|
||||
|
||||
# Auto-instrumentation and cleanup setup
|
||||
_initialized = False
|
||||
_atexit_registered = False
|
||||
|
||||
@@ -9,6 +9,7 @@ import os
|
||||
import time
|
||||
import platform
|
||||
import hashlib
|
||||
import threading
|
||||
from typing import Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
import logging
|
||||
@@ -40,6 +41,15 @@ class MinimalTelemetry:
|
||||
- Can be disabled via environment variables
|
||||
"""
|
||||
|
||||
# Common error phrases that indicate interpreter shutdown
|
||||
_SHUTDOWN_ERROR_PHRASES = [
|
||||
'cannot schedule new futures',
|
||||
'interpreter shutdown',
|
||||
'atexit after shutdown',
|
||||
'event loop closed',
|
||||
'runtime is shutting down'
|
||||
]
|
||||
|
||||
def __init__(self, enabled: bool = None):
|
||||
"""
|
||||
Initialize the minimal telemetry collector.
|
||||
@@ -55,6 +65,10 @@ class MinimalTelemetry:
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
# Add shutdown tracking to prevent double shutdown
|
||||
self._shutdown_complete = False
|
||||
self._shutdown_lock = threading.Lock()
|
||||
|
||||
if not self.enabled:
|
||||
self.logger.debug("Telemetry is disabled")
|
||||
return
|
||||
@@ -70,6 +84,8 @@ class MinimalTelemetry:
|
||||
"tool_calls": 0,
|
||||
"errors": 0,
|
||||
}
|
||||
self._metrics_lock = threading.Lock()
|
||||
self._max_timing_entries = 1000 # Limit to prevent memory leaks
|
||||
|
||||
# Collect basic environment info (anonymous)
|
||||
self._environment = {
|
||||
@@ -100,7 +116,7 @@ class MinimalTelemetry:
|
||||
try:
|
||||
from .. import __version__
|
||||
return __version__
|
||||
except ImportError:
|
||||
except (ImportError, KeyError, AttributeError):
|
||||
return "unknown"
|
||||
|
||||
def track_agent_execution(self, agent_name: str = None, success: bool = True):
|
||||
@@ -114,7 +130,8 @@ class MinimalTelemetry:
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
self._metrics["agent_executions"] += 1
|
||||
with self._metrics_lock:
|
||||
self._metrics["agent_executions"] += 1
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
@@ -140,7 +157,8 @@ class MinimalTelemetry:
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
self._metrics["task_completions"] += 1
|
||||
with self._metrics_lock:
|
||||
self._metrics["task_completions"] += 1
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
@@ -155,33 +173,60 @@ class MinimalTelemetry:
|
||||
|
||||
self.logger.debug(f"Task completion tracked: success={success}")
|
||||
|
||||
def track_tool_usage(self, tool_name: str, success: bool = True):
|
||||
def track_tool_usage(self, tool_name: str, success: bool = True, execution_time: float = None):
|
||||
"""
|
||||
Track tool usage event.
|
||||
Track tool usage event with optional timing.
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool being used
|
||||
success: Whether the tool call was successful
|
||||
execution_time: Time in seconds the tool took to execute (optional)
|
||||
"""
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
self._metrics["tool_calls"] += 1
|
||||
with self._metrics_lock:
|
||||
self._metrics["tool_calls"] += 1
|
||||
|
||||
# Add timing metrics if provided (with memory management)
|
||||
if execution_time is not None:
|
||||
if "tool_execution_times" not in self._metrics:
|
||||
self._metrics["tool_execution_times"] = []
|
||||
|
||||
timing_list = self._metrics["tool_execution_times"]
|
||||
timing_list.append({
|
||||
"tool_name": tool_name,
|
||||
"execution_time": execution_time,
|
||||
"success": success
|
||||
})
|
||||
|
||||
# Prevent memory leaks by limiting stored entries
|
||||
if len(timing_list) > self._max_timing_entries:
|
||||
timing_list[:] = timing_list[-self._max_timing_entries:]
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
properties = {
|
||||
'tool_name': tool_name,
|
||||
'success': success,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
|
||||
# Include execution time if available
|
||||
if execution_time is not None:
|
||||
properties['execution_time'] = execution_time
|
||||
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='tool_usage',
|
||||
properties={
|
||||
'tool_name': tool_name,
|
||||
'success': success,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
properties=properties
|
||||
)
|
||||
|
||||
# Only track tool name, not arguments or results
|
||||
self.logger.debug(f"Tool usage tracked: {tool_name}, success={success}")
|
||||
debug_msg = f"Tool usage tracked: {tool_name}, success={success}"
|
||||
if execution_time is not None:
|
||||
debug_msg += f", execution_time={execution_time:.3f}s"
|
||||
self.logger.debug(debug_msg)
|
||||
|
||||
def track_error(self, error_type: str = None):
|
||||
"""
|
||||
@@ -193,7 +238,8 @@ class MinimalTelemetry:
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
self._metrics["errors"] += 1
|
||||
with self._metrics_lock:
|
||||
self._metrics["errors"] += 1
|
||||
|
||||
# Send event to PostHog
|
||||
if self._posthog:
|
||||
@@ -243,10 +289,13 @@ class MinimalTelemetry:
|
||||
if not self.enabled:
|
||||
return {"enabled": False}
|
||||
|
||||
with self._metrics_lock:
|
||||
metrics_copy = self._metrics.copy()
|
||||
|
||||
return {
|
||||
"enabled": True,
|
||||
"session_id": self.session_id,
|
||||
"metrics": self._metrics.copy(),
|
||||
"metrics": metrics_copy,
|
||||
"environment": self._environment.copy(),
|
||||
}
|
||||
|
||||
@@ -281,28 +330,174 @@ class MinimalTelemetry:
|
||||
pass
|
||||
|
||||
# Reset counters
|
||||
for key in self._metrics:
|
||||
if isinstance(self._metrics[key], int):
|
||||
self._metrics[key] = 0
|
||||
with self._metrics_lock:
|
||||
for key in self._metrics:
|
||||
if isinstance(self._metrics[key], int):
|
||||
self._metrics[key] = 0
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
Shutdown telemetry and ensure all events are sent.
|
||||
Forces proper cleanup of background threads to prevent hanging.
|
||||
"""
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
# Use lock to prevent concurrent shutdown calls
|
||||
with self._shutdown_lock:
|
||||
if self._shutdown_complete:
|
||||
return
|
||||
self._shutdown_complete = True
|
||||
|
||||
# Final flush
|
||||
self.flush()
|
||||
|
||||
# Shutdown PostHog if available
|
||||
if hasattr(self, '_posthog') and self._posthog:
|
||||
posthog_client = getattr(self, '_posthog', None)
|
||||
if posthog_client:
|
||||
try:
|
||||
# Force a synchronous flush before shutdown
|
||||
self._posthog.flush()
|
||||
self._posthog.shutdown()
|
||||
except:
|
||||
pass
|
||||
# Check if Python interpreter is shutting down
|
||||
if self._is_interpreter_shutting_down():
|
||||
self.logger.debug("Interpreter shutting down, skipping PostHog operations")
|
||||
return
|
||||
|
||||
# Use a timeout-based flush to prevent hanging
|
||||
import threading
|
||||
import time
|
||||
import concurrent.futures
|
||||
|
||||
# Use ThreadPoolExecutor for better control
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
|
||||
flush_future = executor.submit(self._safe_flush_posthog, posthog_client)
|
||||
|
||||
try:
|
||||
flush_future.result(timeout=5.0) # 5 second timeout
|
||||
self.logger.debug("PostHog flush completed successfully")
|
||||
except concurrent.futures.TimeoutError:
|
||||
self.logger.warning("PostHog flush timed out")
|
||||
flush_future.cancel()
|
||||
except Exception as e:
|
||||
self.logger.error(f"PostHog flush failed: {e}")
|
||||
|
||||
# Cleanup PostHog threads safely
|
||||
self._shutdown_posthog_threads(posthog_client)
|
||||
|
||||
# Standard shutdown - with interpreter shutdown check
|
||||
if not self._is_interpreter_shutting_down():
|
||||
posthog_client.shutdown()
|
||||
else:
|
||||
self.logger.debug("Skipping PostHog shutdown call due to interpreter shutdown")
|
||||
|
||||
except Exception as e:
|
||||
# Handle specific shutdown-related errors gracefully
|
||||
if self._is_shutdown_related_error(e):
|
||||
self.logger.debug(f"PostHog shutdown prevented due to interpreter shutdown: {e}")
|
||||
else:
|
||||
self.logger.error(f"Error during PostHog shutdown: {e}")
|
||||
finally:
|
||||
self._posthog = None
|
||||
|
||||
def _is_shutdown_related_error(self, error: Exception) -> bool:
|
||||
"""
|
||||
Check if an error is related to interpreter shutdown.
|
||||
|
||||
Args:
|
||||
error: The exception to check
|
||||
|
||||
Returns:
|
||||
True if the error is shutdown-related, False otherwise
|
||||
"""
|
||||
error_msg = str(error).lower()
|
||||
return any(phrase in error_msg for phrase in self._SHUTDOWN_ERROR_PHRASES)
|
||||
|
||||
def _is_interpreter_shutting_down(self) -> bool:
|
||||
"""
|
||||
Check if the Python interpreter is shutting down.
|
||||
|
||||
Returns:
|
||||
True if interpreter is shutting down, False otherwise
|
||||
"""
|
||||
try:
|
||||
import sys
|
||||
|
||||
# Check if the interpreter is in shutdown mode
|
||||
if hasattr(sys, 'is_finalizing') and sys.is_finalizing():
|
||||
return True
|
||||
|
||||
# Check if we can create new threads (fails during shutdown)
|
||||
try:
|
||||
test_thread = threading.Thread(target=lambda: None)
|
||||
test_thread.daemon = True
|
||||
test_thread.start()
|
||||
test_thread.join(timeout=0.001)
|
||||
return False
|
||||
except (RuntimeError, threading.ThreadError):
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
# If we can't determine state, assume we're shutting down to be safe
|
||||
return True
|
||||
|
||||
def _safe_flush_posthog(self, posthog_client):
|
||||
"""Safely flush PostHog data with error handling."""
|
||||
try:
|
||||
# Skip flush if interpreter is shutting down
|
||||
if self._is_interpreter_shutting_down():
|
||||
self.logger.debug("Skipping PostHog flush due to interpreter shutdown")
|
||||
return False
|
||||
|
||||
posthog_client.flush()
|
||||
return True
|
||||
except Exception as e:
|
||||
if self._is_shutdown_related_error(e):
|
||||
self.logger.debug(f"PostHog flush prevented due to interpreter shutdown: {e}")
|
||||
else:
|
||||
self.logger.debug(f"PostHog flush error: {e}")
|
||||
return False
|
||||
|
||||
def _shutdown_posthog_threads(self, posthog_client):
|
||||
"""Safely shutdown PostHog background threads."""
|
||||
try:
|
||||
# Skip thread cleanup if interpreter is shutting down
|
||||
if self._is_interpreter_shutting_down():
|
||||
self.logger.debug("Skipping PostHog thread cleanup due to interpreter shutdown")
|
||||
return
|
||||
|
||||
# Access thread pool safely (fix double shutdown issue)
|
||||
thread_pool = getattr(posthog_client, '_thread_pool', None)
|
||||
if thread_pool:
|
||||
try:
|
||||
# Single shutdown call with timeout
|
||||
if hasattr(thread_pool, 'shutdown'):
|
||||
thread_pool.shutdown(wait=False)
|
||||
# Wait briefly for graceful shutdown
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
except Exception as e:
|
||||
if self._is_shutdown_related_error(e):
|
||||
self.logger.debug(f"Thread pool shutdown prevented due to interpreter shutdown: {e}")
|
||||
else:
|
||||
self.logger.debug(f"Thread pool shutdown error: {e}")
|
||||
|
||||
# Clean up consumer
|
||||
consumer = getattr(posthog_client, '_consumer', None)
|
||||
if consumer:
|
||||
try:
|
||||
if hasattr(consumer, 'flush'):
|
||||
consumer.flush()
|
||||
if hasattr(consumer, 'shutdown'):
|
||||
consumer.shutdown()
|
||||
except Exception as e:
|
||||
if self._is_shutdown_related_error(e):
|
||||
self.logger.debug(f"Consumer shutdown prevented due to interpreter shutdown: {e}")
|
||||
else:
|
||||
self.logger.debug(f"Consumer shutdown error: {e}")
|
||||
|
||||
except Exception as e:
|
||||
if self._is_shutdown_related_error(e):
|
||||
self.logger.debug(f"PostHog thread cleanup prevented due to interpreter shutdown: {e}")
|
||||
else:
|
||||
self.logger.debug(f"Error during PostHog thread cleanup: {e}")
|
||||
|
||||
|
||||
# Global telemetry instance
|
||||
@@ -331,6 +526,41 @@ def disable_telemetry():
|
||||
_telemetry_instance = MinimalTelemetry(enabled=False)
|
||||
|
||||
|
||||
def force_shutdown_telemetry():
|
||||
"""
|
||||
Force shutdown of telemetry system with comprehensive cleanup.
|
||||
This function ensures proper termination of all background threads.
|
||||
"""
|
||||
global _telemetry_instance
|
||||
if _telemetry_instance:
|
||||
_telemetry_instance.shutdown()
|
||||
|
||||
# Additional cleanup - wait for all threads to finish
|
||||
import threading
|
||||
import time
|
||||
|
||||
# Wait up to 3 seconds for any remaining threads to finish
|
||||
max_wait = 3.0
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time < max_wait:
|
||||
# Check for any analytics/telemetry related threads
|
||||
analytics_threads = [
|
||||
t for t in threading.enumerate()
|
||||
if t != threading.current_thread()
|
||||
and not t.daemon
|
||||
and any(keyword in t.name.lower() for keyword in ['posthog', 'analytics', 'telemetry', 'consumer'])
|
||||
]
|
||||
|
||||
if not analytics_threads:
|
||||
break
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
# Reset the global instance
|
||||
_telemetry_instance = None
|
||||
|
||||
|
||||
def enable_telemetry():
|
||||
"""Programmatically enable telemetry (if not disabled by environment)."""
|
||||
global _telemetry_instance
|
||||
|
||||
@@ -49,6 +49,20 @@ TOOL_MAPPINGS = {
|
||||
'analyze_data': ('.duckdb_tools', None),
|
||||
'duckdb_tools': ('.duckdb_tools', None),
|
||||
|
||||
# MongoDB Tools
|
||||
'insert_document': ('.mongodb_tools', None),
|
||||
'insert_documents': ('.mongodb_tools', None),
|
||||
'find_documents': ('.mongodb_tools', None),
|
||||
'update_document': ('.mongodb_tools', None),
|
||||
'delete_document': ('.mongodb_tools', None),
|
||||
'create_vector_index': ('.mongodb_tools', None),
|
||||
'vector_search': ('.mongodb_tools', None),
|
||||
'store_with_embedding': ('.mongodb_tools', None),
|
||||
'text_search': ('.mongodb_tools', None),
|
||||
'get_stats': ('.mongodb_tools', None),
|
||||
'connect_mongodb': ('.mongodb_tools', None),
|
||||
'mongodb_tools': ('.mongodb_tools', None),
|
||||
|
||||
# Shell Tools
|
||||
'execute_command': ('.shell_tools', None),
|
||||
'list_processes': ('.shell_tools', None),
|
||||
@@ -180,12 +194,14 @@ def __getattr__(name: str) -> Any:
|
||||
'get_article', 'get_news_sources', 'get_articles_from_source', 'get_trending_topics',
|
||||
'scrape_page', 'extract_links', 'crawl', 'extract_text',
|
||||
'query', 'create_table', 'load_data', 'export_data', 'get_table_info', 'analyze_data',
|
||||
'insert_document', 'insert_documents', 'find_documents', 'update_document', 'delete_document',
|
||||
'create_vector_index', 'vector_search', 'store_with_embedding', 'text_search', 'get_stats', 'connect_mongodb',
|
||||
'execute_command', 'list_processes', 'kill_process', 'get_system_info',
|
||||
'evaluate', 'solve_equation', 'convert_units', 'calculate_statistics', 'calculate_financial'
|
||||
]:
|
||||
return getattr(module, name)
|
||||
if name in ['file_tools', 'pandas_tools', 'wikipedia_tools',
|
||||
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools', 'cot_tools']:
|
||||
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'mongodb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools', 'cot_tools']:
|
||||
return module # Returns the callable module
|
||||
return getattr(module, name)
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
"""Tools for working with MongoDB databases and Atlas Vector Search.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import mongodb_tools
|
||||
result = mongodb_tools.insert_document("my_collection", {"name": "test", "value": 42})
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import connect_mongodb, insert_document, vector_search
|
||||
client = connect_mongodb("mongodb://localhost:27017/")
|
||||
result = insert_document(client, "my_collection", {"name": "test"})
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Any, Optional, Union, TYPE_CHECKING
|
||||
from importlib import util
|
||||
from datetime import datetime
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import pymongo
|
||||
from pymongo import MongoClient
|
||||
from pymongo.collection import Collection
|
||||
|
||||
class MongoDBTools:
|
||||
"""Tools for working with MongoDB databases and Atlas Vector Search."""
|
||||
|
||||
def __init__(self, connection_string: str = "mongodb://localhost:27017/", database_name: str = "praisonai"):
|
||||
"""Initialize MongoDBTools.
|
||||
|
||||
Args:
|
||||
connection_string: MongoDB connection string
|
||||
database_name: Name of the database to use
|
||||
"""
|
||||
self.connection_string = connection_string
|
||||
self.database_name = database_name
|
||||
self._client = None
|
||||
self._db = None
|
||||
|
||||
def _get_pymongo(self) -> Optional['pymongo']:
|
||||
"""Get pymongo module, with helpful error if not installed"""
|
||||
if util.find_spec('pymongo') is None:
|
||||
error_msg = "pymongo package is not available. Please install it using: pip install 'praisonaiagents[mongodb]'"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import pymongo
|
||||
return pymongo
|
||||
|
||||
def _get_motor(self) -> Optional[Any]:
|
||||
"""Get motor module for async operations"""
|
||||
if util.find_spec('motor') is None:
|
||||
error_msg = "motor package is not available. Please install it using: pip install 'praisonaiagents[mongodb]'"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import motor.motor_asyncio
|
||||
return motor.motor_asyncio
|
||||
|
||||
def _get_client(self) -> Optional['MongoClient']:
|
||||
"""Get or create MongoDB client"""
|
||||
if self._client is None:
|
||||
pymongo = self._get_pymongo()
|
||||
if pymongo is None:
|
||||
return None
|
||||
try:
|
||||
self._client = pymongo.MongoClient(
|
||||
self.connection_string,
|
||||
maxPoolSize=50,
|
||||
minPoolSize=10,
|
||||
maxIdleTimeMS=30000,
|
||||
serverSelectionTimeoutMS=5000,
|
||||
retryWrites=True,
|
||||
retryReads=True
|
||||
)
|
||||
# Test connection
|
||||
self._client.admin.command('ping')
|
||||
self._db = self._client[self.database_name]
|
||||
except Exception as e:
|
||||
error_msg = f"Error connecting to MongoDB: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
return self._client
|
||||
|
||||
def _get_database(self):
|
||||
"""Get database instance"""
|
||||
if self._db is None:
|
||||
client = self._get_client()
|
||||
if client is None:
|
||||
return None
|
||||
self._db = client[self.database_name]
|
||||
return self._db
|
||||
|
||||
def _get_collection(self, collection_name: str) -> Optional['Collection']:
|
||||
"""Get collection instance"""
|
||||
db = self._get_database()
|
||||
if db is None:
|
||||
return None
|
||||
return db[collection_name]
|
||||
|
||||
def insert_document(
|
||||
self,
|
||||
collection_name: str,
|
||||
document: Dict[str, Any],
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Insert a document into a collection.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
document: Document to insert
|
||||
metadata: Optional metadata to include
|
||||
|
||||
Returns:
|
||||
Result dict with success status and inserted_id
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
# Add metadata and timestamp
|
||||
doc_to_insert = document.copy()
|
||||
if metadata:
|
||||
doc_to_insert.update(metadata)
|
||||
doc_to_insert["_created_at"] = datetime.utcnow()
|
||||
|
||||
result = collection.insert_one(doc_to_insert)
|
||||
return {
|
||||
"success": True,
|
||||
"inserted_id": str(result.inserted_id),
|
||||
"message": "Document inserted successfully"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error inserting document: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def insert_documents(
|
||||
self,
|
||||
collection_name: str,
|
||||
documents: List[Dict[str, Any]],
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Insert multiple documents into a collection.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
documents: List of documents to insert
|
||||
metadata: Optional metadata to include in all documents
|
||||
|
||||
Returns:
|
||||
Result dict with success status and inserted_ids
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
# Add metadata and timestamp to all documents
|
||||
docs_to_insert = []
|
||||
for doc in documents:
|
||||
doc_copy = doc.copy()
|
||||
if metadata:
|
||||
doc_copy.update(metadata)
|
||||
doc_copy["_created_at"] = datetime.utcnow()
|
||||
docs_to_insert.append(doc_copy)
|
||||
|
||||
result = collection.insert_many(docs_to_insert)
|
||||
return {
|
||||
"success": True,
|
||||
"inserted_ids": [str(oid) for oid in result.inserted_ids],
|
||||
"count": len(result.inserted_ids),
|
||||
"message": f"Successfully inserted {len(result.inserted_ids)} documents"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error inserting documents: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def find_documents(
|
||||
self,
|
||||
collection_name: str,
|
||||
query: Dict[str, Any] = None,
|
||||
limit: int = 10,
|
||||
sort: Optional[List[tuple]] = None,
|
||||
projection: Optional[Dict[str, int]] = None
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""Find documents in a collection.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
query: Query filter (default: empty dict for all documents)
|
||||
limit: Maximum number of documents to return
|
||||
sort: Sort specification as list of (field, direction) tuples
|
||||
projection: Fields to include/exclude
|
||||
|
||||
Returns:
|
||||
List of documents or error dict
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
query = query or {}
|
||||
cursor = collection.find(query, projection)
|
||||
|
||||
if sort:
|
||||
cursor = cursor.sort(sort)
|
||||
|
||||
cursor = cursor.limit(limit)
|
||||
|
||||
# Convert ObjectIds to strings for JSON serialization
|
||||
results = []
|
||||
for doc in cursor:
|
||||
doc["_id"] = str(doc["_id"])
|
||||
results.append(doc)
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error finding documents: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def update_document(
|
||||
self,
|
||||
collection_name: str,
|
||||
query: Dict[str, Any],
|
||||
update: Dict[str, Any],
|
||||
upsert: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
"""Update a document in a collection.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
query: Query to find the document
|
||||
update: Update operations
|
||||
upsert: Whether to insert if document doesn't exist
|
||||
|
||||
Returns:
|
||||
Result dict with success status
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
# Add timestamp to update
|
||||
if "$set" not in update:
|
||||
update["$set"] = {}
|
||||
update["$set"]["_updated_at"] = datetime.utcnow()
|
||||
|
||||
result = collection.update_one(query, update, upsert=upsert)
|
||||
return {
|
||||
"success": True,
|
||||
"matched_count": result.matched_count,
|
||||
"modified_count": result.modified_count,
|
||||
"upserted_id": str(result.upserted_id) if result.upserted_id else None,
|
||||
"message": "Document updated successfully"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error updating document: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def delete_document(
|
||||
self,
|
||||
collection_name: str,
|
||||
query: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
"""Delete a document from a collection.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
query: Query to find the document to delete
|
||||
|
||||
Returns:
|
||||
Result dict with success status
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
result = collection.delete_one(query)
|
||||
return {
|
||||
"success": True,
|
||||
"deleted_count": result.deleted_count,
|
||||
"message": f"Deleted {result.deleted_count} document(s)"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error deleting document: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def create_vector_index(
|
||||
self,
|
||||
collection_name: str,
|
||||
vector_field: str = "embedding",
|
||||
dimensions: int = 1536,
|
||||
similarity: str = "cosine",
|
||||
index_name: str = "vector_index"
|
||||
) -> Dict[str, Any]:
|
||||
"""Create a vector search index for Atlas Vector Search.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
vector_field: Name of the field containing vectors
|
||||
dimensions: Number of dimensions in the vectors
|
||||
similarity: Similarity metric ('cosine', 'euclidean', 'dotProduct')
|
||||
index_name: Name of the index
|
||||
|
||||
Returns:
|
||||
Result dict with success status
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
# Create Atlas Vector Search index
|
||||
index_definition = {
|
||||
"mappings": {
|
||||
"dynamic": True,
|
||||
"fields": {
|
||||
vector_field: {
|
||||
"type": "knnVector",
|
||||
"dimensions": dimensions,
|
||||
"similarity": similarity
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
collection.create_search_index(index_definition, index_name)
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Vector search index '{index_name}' created successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
if "already exists" in str(e).lower():
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Vector search index '{index_name}' already exists"
|
||||
}
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error creating vector index: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def vector_search(
|
||||
self,
|
||||
collection_name: str,
|
||||
query_vector: List[float],
|
||||
vector_field: str = "embedding",
|
||||
limit: int = 10,
|
||||
num_candidates: int = 100,
|
||||
score_threshold: float = 0.0,
|
||||
filter_query: Optional[Dict[str, Any]] = None,
|
||||
index_name: str = "vector_index"
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""Perform vector similarity search using Atlas Vector Search.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
query_vector: Vector to search for
|
||||
vector_field: Name of the field containing vectors
|
||||
limit: Maximum number of results to return
|
||||
num_candidates: Number of candidates to consider
|
||||
score_threshold: Minimum similarity score
|
||||
filter_query: Optional additional filter
|
||||
index_name: Name of the vector index to use
|
||||
|
||||
Returns:
|
||||
List of search results or error dict
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
# Build aggregation pipeline for vector search
|
||||
pipeline = [
|
||||
{
|
||||
"$vectorSearch": {
|
||||
"index": index_name,
|
||||
"path": vector_field,
|
||||
"queryVector": query_vector,
|
||||
"numCandidates": num_candidates,
|
||||
"limit": limit
|
||||
}
|
||||
},
|
||||
{
|
||||
"$addFields": {
|
||||
"score": {"$meta": "vectorSearchScore"}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Add score threshold filter if specified
|
||||
if score_threshold > 0:
|
||||
pipeline.append({
|
||||
"$match": {
|
||||
"score": {"$gte": score_threshold}
|
||||
}
|
||||
})
|
||||
|
||||
# Add additional filter if specified
|
||||
if filter_query:
|
||||
pipeline.append({
|
||||
"$match": filter_query
|
||||
})
|
||||
|
||||
# Execute search
|
||||
results = []
|
||||
for doc in collection.aggregate(pipeline):
|
||||
doc["_id"] = str(doc["_id"])
|
||||
results.append(doc)
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error performing vector search: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def store_with_embedding(
|
||||
self,
|
||||
collection_name: str,
|
||||
text: str,
|
||||
embedding: List[float],
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Store text with its embedding vector.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
text: Text content
|
||||
embedding: Vector embedding
|
||||
metadata: Optional metadata
|
||||
|
||||
Returns:
|
||||
Result dict with success status
|
||||
"""
|
||||
document = {
|
||||
"text": text,
|
||||
"embedding": embedding,
|
||||
"metadata": metadata or {},
|
||||
"created_at": datetime.utcnow()
|
||||
}
|
||||
|
||||
return self.insert_document(collection_name, document)
|
||||
|
||||
def text_search(
|
||||
self,
|
||||
collection_name: str,
|
||||
query: str,
|
||||
limit: int = 10,
|
||||
text_field: str = "text"
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""Perform text search using MongoDB text indexes.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
query: Search query
|
||||
limit: Maximum number of results
|
||||
text_field: Field to search in
|
||||
|
||||
Returns:
|
||||
List of search results or error dict
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
# Create text index if it doesn't exist
|
||||
try:
|
||||
collection.create_index([(text_field, "text")])
|
||||
except Exception:
|
||||
pass # Index might already exist
|
||||
|
||||
# Perform text search
|
||||
results = []
|
||||
for doc in collection.find(
|
||||
{"$text": {"$search": query}},
|
||||
{"score": {"$meta": "textScore"}}
|
||||
).sort([("score", {"$meta": "textScore"})]).limit(limit):
|
||||
doc["_id"] = str(doc["_id"])
|
||||
results.append(doc)
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error performing text search: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_stats(self, collection_name: str) -> Dict[str, Any]:
|
||||
"""Get collection statistics.
|
||||
|
||||
Args:
|
||||
collection_name: Name of the collection
|
||||
|
||||
Returns:
|
||||
Dict with collection statistics
|
||||
"""
|
||||
try:
|
||||
collection = self._get_collection(collection_name)
|
||||
if collection is None:
|
||||
return {"error": "Could not connect to MongoDB"}
|
||||
|
||||
stats = collection.estimated_document_count()
|
||||
return {
|
||||
"success": True,
|
||||
"collection_name": collection_name,
|
||||
"document_count": stats,
|
||||
"database_name": self.database_name
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting collection stats: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def close(self):
|
||||
"""Close MongoDB connection."""
|
||||
if self._client:
|
||||
self._client.close()
|
||||
self._client = None
|
||||
self._db = None
|
||||
|
||||
# Create default instance for direct function access
|
||||
_mongodb_tools = MongoDBTools()
|
||||
|
||||
# Export functions for direct use
|
||||
insert_document = _mongodb_tools.insert_document
|
||||
insert_documents = _mongodb_tools.insert_documents
|
||||
find_documents = _mongodb_tools.find_documents
|
||||
update_document = _mongodb_tools.update_document
|
||||
delete_document = _mongodb_tools.delete_document
|
||||
create_vector_index = _mongodb_tools.create_vector_index
|
||||
vector_search = _mongodb_tools.vector_search
|
||||
store_with_embedding = _mongodb_tools.store_with_embedding
|
||||
text_search = _mongodb_tools.text_search
|
||||
get_stats = _mongodb_tools.get_stats
|
||||
|
||||
def connect_mongodb(connection_string: str, database_name: str = "praisonai") -> MongoDBTools:
|
||||
"""Create a new MongoDB connection.
|
||||
|
||||
Args:
|
||||
connection_string: MongoDB connection string
|
||||
database_name: Database name to use
|
||||
|
||||
Returns:
|
||||
MongoDBTools instance
|
||||
"""
|
||||
return MongoDBTools(connection_string, database_name)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("MongoDB Tools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Test basic operations
|
||||
print("1. Testing Document Operations")
|
||||
print("------------------------------")
|
||||
|
||||
# Insert a document
|
||||
result = insert_document("test_collection", {
|
||||
"name": "Test Document",
|
||||
"value": 42,
|
||||
"tags": ["test", "mongodb"]
|
||||
})
|
||||
print(f"Insert result: {result}")
|
||||
|
||||
# Find documents
|
||||
results = find_documents("test_collection", {"name": "Test Document"})
|
||||
print(f"Find results: {results}")
|
||||
|
||||
print("\n2. Testing Vector Operations")
|
||||
print("------------------------------")
|
||||
|
||||
# Create vector index
|
||||
index_result = create_vector_index("vector_collection", dimensions=3)
|
||||
print(f"Vector index result: {index_result}")
|
||||
|
||||
# Store document with embedding
|
||||
embedding_result = store_with_embedding(
|
||||
"vector_collection",
|
||||
"This is a test document for vector search",
|
||||
[0.1, 0.2, 0.3],
|
||||
{"category": "test"}
|
||||
)
|
||||
print(f"Store with embedding result: {embedding_result}")
|
||||
|
||||
print("\n3. Testing Statistics")
|
||||
print("------------------------------")
|
||||
|
||||
stats = get_stats("test_collection")
|
||||
print(f"Collection stats: {stats}")
|
||||
|
||||
print("\n==================================================")
|
||||
print("MongoDB Tools Demonstration Complete")
|
||||
print("==================================================\n")
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.142"
|
||||
version = "0.0.144"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
@@ -60,6 +60,12 @@ telemetry = [
|
||||
"posthog>=3.0.0"
|
||||
]
|
||||
|
||||
# MongoDB dependencies
|
||||
mongodb = [
|
||||
"pymongo>=4.6.3",
|
||||
"motor>=3.4.0"
|
||||
]
|
||||
|
||||
|
||||
# Combined features
|
||||
all = [
|
||||
@@ -69,7 +75,8 @@ all = [
|
||||
"praisonaiagents[llm]",
|
||||
"praisonaiagents[mcp]",
|
||||
"praisonaiagents[api]",
|
||||
"praisonaiagents[telemetry]"
|
||||
"praisonaiagents[telemetry]",
|
||||
"praisonaiagents[mongodb]"
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
|
||||
@@ -2,11 +2,12 @@ from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
self_reflect=False,
|
||||
verbose=True,
|
||||
verbose=False,
|
||||
stream=True
|
||||
)
|
||||
|
||||
result = agent.start("Write a long report on about the history of the world")
|
||||
print(result)
|
||||
# Use return_generator=True to get the raw generator for custom streaming handling
|
||||
for chunk in agent.start("Write a report on about the history of the world"):
|
||||
print(chunk, end="", flush=True)
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple test to verify the termination fix
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to the path so we can import praisonaiagents
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
print("Testing agent termination fix...")
|
||||
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Create agent with minimal setup
|
||||
agent = Agent(instructions="You are a helpful AI assistant")
|
||||
|
||||
# Run the same test as in the issue
|
||||
print("Running agent.start() ...")
|
||||
response = agent.start("Write a movie script about a robot on Mars")
|
||||
|
||||
print("Agent completed successfully!")
|
||||
print(f"Response type: {type(response)}")
|
||||
print(f"Response length: {len(str(response)) if response else 'None'}")
|
||||
|
||||
# If we get here, the fix worked
|
||||
print("SUCCESS: Program should terminate properly!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: Exception occurred: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
print("Test completed - program should exit now.")
|
||||
@@ -0,0 +1,25 @@
|
||||
import ast
|
||||
import sys
|
||||
|
||||
def check_syntax(filename):
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Try to parse the AST
|
||||
ast.parse(content, filename=filename)
|
||||
print(f"✅ {filename} has valid syntax")
|
||||
return True
|
||||
|
||||
except SyntaxError as e:
|
||||
print(f"❌ Syntax error in {filename}:")
|
||||
print(f" Line {e.lineno}: {e.text.strip() if e.text else 'N/A'}")
|
||||
print(f" {' ' * (e.offset-1 if e.offset else 0)}^")
|
||||
print(f" Error: {e.msg}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Error reading {filename}: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_syntax("praisonaiagents/agent/agent.py")
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print("Creating agent...")
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print("Starting agent...")
|
||||
result = agent.start("Why is the sky blue?")
|
||||
|
||||
print("Agent completed successfully!")
|
||||
print(f"Result: {result}")
|
||||
print("Script finished normally.")
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify the termination fix works
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
import time
|
||||
from threading import Timer
|
||||
|
||||
# Add the src directory to the path so we can import praisonaiagents
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
# Set up timeout mechanism
|
||||
def timeout_handler(signum, frame):
|
||||
print("ERROR: Test timed out - program is still hanging!")
|
||||
sys.exit(1)
|
||||
|
||||
# Set up signal handler for timeout
|
||||
signal.signal(signal.SIGALRM, timeout_handler)
|
||||
signal.alarm(30) # 30 second timeout
|
||||
|
||||
try:
|
||||
# Import here to avoid issues with path setup
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print("Testing agent termination fix...")
|
||||
|
||||
# Create agent with minimal setup
|
||||
agent = Agent(instructions="You are a helpful AI assistant")
|
||||
|
||||
# Run the same test as in the issue
|
||||
print("Running agent.start() ...")
|
||||
response = agent.start("Write a short hello world message")
|
||||
|
||||
print(f"Agent completed successfully!")
|
||||
print(f"Response (truncated): {str(response)[:100]}...")
|
||||
|
||||
# If we get here, the fix worked
|
||||
print("SUCCESS: Program terminated properly without hanging!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: Exception occurred: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
# Cancel the alarm
|
||||
signal.alarm(0)
|
||||
@@ -0,0 +1,17 @@
|
||||
try:
|
||||
print("Testing import...")
|
||||
from praisonaiagents import Agent
|
||||
print("✅ Import successful - no syntax errors!")
|
||||
|
||||
print("Creating agent...")
|
||||
agent = Agent(instructions="Test agent", llm="gpt-4o-mini")
|
||||
print("✅ Agent created successfully!")
|
||||
|
||||
except SyntaxError as e:
|
||||
print(f"❌ SyntaxError: {e}")
|
||||
print(f" File: {e.filename}")
|
||||
print(f" Line {e.lineno}: {e.text}")
|
||||
except Exception as e:
|
||||
print(f"❌ Other error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Direct test of MongoDB tools"""
|
||||
import sys
|
||||
sys.path.insert(0, 'src/praisonai-agents')
|
||||
|
||||
try:
|
||||
# Test direct import of MongoDB tools
|
||||
from praisonaiagents.tools.mongodb_tools import MongoDBTools
|
||||
print('MongoDB tools import successful')
|
||||
|
||||
# Test basic initialization
|
||||
tools = MongoDBTools()
|
||||
print('MongoDB tools initialization successful')
|
||||
|
||||
# Test the _get_pymongo method which should handle missing dependency gracefully
|
||||
pymongo = tools._get_pymongo()
|
||||
if pymongo is None:
|
||||
print('pymongo not available - graceful fallback working')
|
||||
else:
|
||||
print('pymongo available')
|
||||
|
||||
# Test client connection (should fail gracefully)
|
||||
client = tools._get_client()
|
||||
if client is None:
|
||||
print('MongoDB client connection failed gracefully (expected without pymongo)')
|
||||
else:
|
||||
print('MongoDB client connection successful')
|
||||
|
||||
print('All MongoDB direct tests passed')
|
||||
except Exception as e:
|
||||
print(f'MongoDB direct test failed: {e}')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test MongoDB integration"""
|
||||
import sys
|
||||
sys.path.insert(0, 'src/praisonai-agents')
|
||||
|
||||
try:
|
||||
from praisonaiagents.tools.mongodb_tools import MongoDBTools
|
||||
print('MongoDB tools import successful')
|
||||
|
||||
# Test basic initialization (should work even without MongoDB running)
|
||||
tools = MongoDBTools()
|
||||
print('MongoDB tools initialization successful')
|
||||
|
||||
# Test function imports
|
||||
from praisonaiagents.tools import mongodb_tools
|
||||
print('MongoDB tools module import successful')
|
||||
|
||||
# Test individual function imports
|
||||
from praisonaiagents.tools import insert_document, find_documents, vector_search
|
||||
print('Individual MongoDB function imports successful')
|
||||
|
||||
print('All MongoDB integration tests passed')
|
||||
except Exception as e:
|
||||
print(f'MongoDB integration test failed: {e}')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Isolated test of MongoDB tools"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src/praisonai-agents'))
|
||||
|
||||
# Test if the MongoDB tools can be directly imported and used
|
||||
try:
|
||||
# Read the mongodb_tools.py file content and check for issues
|
||||
with open('src/praisonai-agents/praisonaiagents/tools/mongodb_tools.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Check for potential syntax or import issues
|
||||
print("Checking MongoDB tools file...")
|
||||
|
||||
# Check imports
|
||||
if 'from importlib import util' in content:
|
||||
print('✓ importlib import present')
|
||||
if 'from typing import' in content:
|
||||
print('✓ typing imports present')
|
||||
if 'TYPE_CHECKING' in content:
|
||||
print('✓ TYPE_CHECKING conditional imports present')
|
||||
|
||||
# Check class structure
|
||||
if 'class MongoDBTools:' in content:
|
||||
print('✓ MongoDBTools class present')
|
||||
if 'def _get_pymongo' in content:
|
||||
print('✓ _get_pymongo method present')
|
||||
if 'def _get_client' in content:
|
||||
print('✓ _get_client method present')
|
||||
|
||||
# Check MongoDB operations
|
||||
if 'def insert_document' in content:
|
||||
print('✓ insert_document method present')
|
||||
if 'def vector_search' in content:
|
||||
print('✓ vector_search method present')
|
||||
if 'def create_vector_index' in content:
|
||||
print('✓ create_vector_index method present')
|
||||
|
||||
# Check error handling
|
||||
if 'try:' in content and 'except' in content:
|
||||
print('✓ Error handling present')
|
||||
|
||||
# Check graceful fallback
|
||||
if 'if util.find_spec' in content:
|
||||
print('✓ Graceful dependency checking present')
|
||||
|
||||
print('MongoDB tools file structure looks good!')
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error checking MongoDB tools: {e}')
|
||||
|
||||
# Check if imports work correctly
|
||||
try:
|
||||
# Test individual components
|
||||
import logging
|
||||
from typing import List, Dict, Any, Optional, Union
|
||||
from importlib import util
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
print('✓ All required imports for MongoDB tools available')
|
||||
|
||||
# Test util.find_spec functionality
|
||||
if util.find_spec('pymongo') is None:
|
||||
print('✓ pymongo not available - graceful fallback should work')
|
||||
else:
|
||||
print('✓ pymongo available')
|
||||
|
||||
print('MongoDB tools dependencies check passed')
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error with MongoDB tools dependencies: {e}')
|
||||
@@ -1,148 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Simple test to check if the fixed files work correctly
|
||||
"""
|
||||
Simple test to verify the streaming implementation works
|
||||
"""
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
import os
|
||||
|
||||
def test_fixed_files():
|
||||
"""Test the files that had .dict() usage to ensure they work with .model_dump()"""
|
||||
print("Testing fixed files...")
|
||||
# Add the src directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src/praisonai-agents'))
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def test_streaming_basic():
|
||||
print("Testing basic streaming implementation...")
|
||||
|
||||
try:
|
||||
# Test 1: Import and test the reasoning extraction functionality
|
||||
print("Testing reasoning-extraction.py imports...")
|
||||
import json
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Create a dummy ChainOfThought class to test our fix
|
||||
class ChainOfThought(BaseModel):
|
||||
problem: str
|
||||
reasoning_steps: list = []
|
||||
solution: str = ""
|
||||
|
||||
def save_reasoning_chain(chain: ChainOfThought) -> str:
|
||||
"""Save a chain of thought reasoning to analyze patterns."""
|
||||
filename = f"reasoning_{chain.problem[:20].replace(' ', '_')}.json"
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(chain.model_dump(), f, indent=2) # This was the fix
|
||||
return f"Reasoning chain saved to {filename}"
|
||||
|
||||
# Test the fixed function
|
||||
test_chain = ChainOfThought(
|
||||
problem="Test problem for validation",
|
||||
reasoning_steps=["Step 1", "Step 2"],
|
||||
solution="Test solution"
|
||||
)
|
||||
result = save_reasoning_chain(test_chain)
|
||||
print(f"✅ reasoning-extraction.py fix works: {result}")
|
||||
|
||||
# Test 2: Test the API fix (MarketResearchRequest simulation)
|
||||
print("Testing secondary-market-research-api.py fix...")
|
||||
|
||||
class MarketResearchRequest(BaseModel):
|
||||
company: str
|
||||
geography: str
|
||||
market_segment: str = "general"
|
||||
|
||||
def test_api_serialization(request: MarketResearchRequest):
|
||||
"""Test the fixed API serialization"""
|
||||
from datetime import datetime
|
||||
job_status = {
|
||||
"status": "queued",
|
||||
"progress": 0,
|
||||
"message": "Report generation queued",
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"updated_at": datetime.now().isoformat(),
|
||||
"config": request.model_dump() # This was the fix
|
||||
}
|
||||
return job_status
|
||||
|
||||
test_request = MarketResearchRequest(
|
||||
company="Test Company",
|
||||
geography="Global",
|
||||
market_segment="tech"
|
||||
)
|
||||
api_result = test_api_serialization(test_request)
|
||||
print(f"✅ secondary-market-research-api.py fix works: {api_result['config']}")
|
||||
|
||||
print("\n✅ All Pydantic .dict() → .model_dump() fixes are working correctly!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error testing fixes: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def test_import_praisonaiagents():
|
||||
"""Test basic PraisonAI Agents import"""
|
||||
try:
|
||||
print("Testing PraisonAI Agents import...")
|
||||
from praisonaiagents import Agent
|
||||
print("✅ Successfully imported PraisonAI Agents")
|
||||
|
||||
# Try to create an agent without LLM call
|
||||
agent = Agent(
|
||||
instructions="Test agent for validation",
|
||||
tools=[]
|
||||
)
|
||||
print("✅ Successfully created agent instance")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Error importing or creating agent: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def test_litellm_deprecation():
|
||||
"""Test for litellm deprecation warnings"""
|
||||
try:
|
||||
# Capture warnings to see if we've reduced them
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
|
||||
# Try to import litellm
|
||||
import litellm
|
||||
print("✅ LiteLLM imported successfully")
|
||||
|
||||
# Check for any deprecation warnings caught
|
||||
pydantic_warnings = [warning for warning in w if 'pydantic' in str(warning.message).lower() and 'dict' in str(warning.message).lower()]
|
||||
|
||||
if pydantic_warnings:
|
||||
print(f"⚠️ Found {len(pydantic_warnings)} Pydantic dict() deprecation warnings:")
|
||||
for warning in pydantic_warnings:
|
||||
print(f" - {warning.message}")
|
||||
else:
|
||||
print("✅ No Pydantic dict() deprecation warnings detected during import")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error testing litellm: {e}")
|
||||
# Test 1: Create agent with streaming enabled
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="mock", # Use mock model for testing
|
||||
self_reflect=False,
|
||||
verbose=False,
|
||||
stream=True
|
||||
)
|
||||
|
||||
# Test 2: Check that start() returns a generator
|
||||
result = agent.start("Say hello")
|
||||
print(f"✅ agent.start() returns: {type(result)}")
|
||||
|
||||
if hasattr(result, '__iter__') and not isinstance(result, str):
|
||||
print("✅ SUCCESS: The result is iterable (generator)")
|
||||
print("✅ The user's code pattern will work:")
|
||||
print(" for chunk in agent.start(prompt):")
|
||||
print(" print(chunk, end='', flush=True)")
|
||||
else:
|
||||
print("❌ FAILED: Result is not iterable")
|
||||
return False
|
||||
|
||||
# Test 3: Check backward compatibility
|
||||
print("\nTesting backward compatibility...")
|
||||
|
||||
# Agent with streaming disabled
|
||||
agent_no_stream = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="mock", # Use mock model for testing
|
||||
self_reflect=False,
|
||||
verbose=False,
|
||||
stream=False
|
||||
)
|
||||
|
||||
result_no_stream = agent_no_stream.start("Say hello")
|
||||
print(f"✅ agent.start() with stream=False returns: {type(result_no_stream)}")
|
||||
|
||||
# Agent with streaming enabled but overridden in start()
|
||||
result_override = agent.start("Say hello", stream=False)
|
||||
print(f"✅ agent.start(stream=False) returns: {type(result_override)}")
|
||||
|
||||
print("\n🎉 All tests passed! The streaming implementation is working correctly.")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("TESTING DEPRECATION WARNING FIXES")
|
||||
print("=" * 60)
|
||||
|
||||
success = True
|
||||
|
||||
# Test our fixed files
|
||||
success &= test_fixed_files()
|
||||
print()
|
||||
|
||||
# Test basic imports
|
||||
success &= test_import_praisonaiagents()
|
||||
print()
|
||||
|
||||
# Test for remaining warnings
|
||||
success &= test_litellm_deprecation()
|
||||
print()
|
||||
|
||||
success = test_streaming_basic()
|
||||
if success:
|
||||
print("✅ All tests passed! Deprecation warnings should be resolved.")
|
||||
sys.exit(0)
|
||||
print("\n📋 Summary:")
|
||||
print("- ✅ Streaming implementation added successfully")
|
||||
print("- ✅ agent.start() returns a generator when stream=True")
|
||||
print("- ✅ Backward compatibility maintained")
|
||||
print("- ✅ User's code example will now work")
|
||||
else:
|
||||
print("❌ Some tests failed. Please check the output above.")
|
||||
sys.exit(1)
|
||||
print("\n❌ Tests failed")
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Test script for the streaming fix implementation
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src/praisonai-agents'))
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def test_streaming():
|
||||
print("Testing streaming functionality...")
|
||||
|
||||
# Create agent with streaming enabled - use a simpler test without API calls
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="mock", # Use mock model for testing
|
||||
self_reflect=False,
|
||||
verbose=False,
|
||||
stream=True
|
||||
)
|
||||
|
||||
# Test the streaming functionality
|
||||
print("Testing streaming with short response...")
|
||||
prompt = "Write a short sentence about the weather"
|
||||
|
||||
# Test if start() returns a generator when streaming
|
||||
result = agent.start(prompt)
|
||||
print(f"Type of result: {type(result)}")
|
||||
|
||||
if hasattr(result, '__iter__') and not isinstance(result, str):
|
||||
print("Success! Result is iterable (generator)")
|
||||
print("The streaming implementation is working correctly")
|
||||
else:
|
||||
print("Result is not iterable - this means streaming is not working")
|
||||
print(f"Result: {result}")
|
||||
|
||||
def test_backward_compatibility():
|
||||
print("\nTesting backward compatibility...")
|
||||
|
||||
# Test with streaming disabled
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="mock",
|
||||
self_reflect=False,
|
||||
verbose=False,
|
||||
stream=False
|
||||
)
|
||||
|
||||
result = agent.start("Say hello", stream=False)
|
||||
print(f"Type of result with stream=False: {type(result)}")
|
||||
|
||||
# Test with streaming enabled but stream=False in start()
|
||||
agent2 = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="mock",
|
||||
self_reflect=False,
|
||||
verbose=False,
|
||||
stream=True
|
||||
)
|
||||
|
||||
result2 = agent2.start("Say hello", stream=False)
|
||||
print(f"Type of result with agent.stream=True but start(stream=False): {type(result2)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_streaming()
|
||||
test_backward_compatibility()
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
import ast
|
||||
import sys
|
||||
|
||||
def check_syntax(filename):
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Try to parse the content
|
||||
ast.parse(content)
|
||||
print(f"✅ Syntax is valid for {filename}")
|
||||
return True
|
||||
|
||||
except SyntaxError as e:
|
||||
print(f"❌ SyntaxError in {filename}:")
|
||||
print(f" Line {e.lineno}: {e.text}")
|
||||
print(f" Error: {e.msg}")
|
||||
print(f" Position: {' ' * (e.offset - 1 if e.offset else 0)}^")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Error reading {filename}: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
filename = "praisonaiagents/agent/agent.py"
|
||||
success = check_syntax(filename)
|
||||
sys.exit(0 if success else 1)
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
print("Testing syntax fix...")
|
||||
|
||||
try:
|
||||
# Test basic import
|
||||
import ast
|
||||
|
||||
# Parse the agent.py file to check for syntax errors
|
||||
with open('praisonaiagents/agent/agent.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# This will raise SyntaxError if there are issues
|
||||
ast.parse(content)
|
||||
print("✅ agent.py syntax is valid")
|
||||
|
||||
# Test actual import
|
||||
from praisonaiagents.agent.agent import Agent
|
||||
print("✅ Agent import successful")
|
||||
|
||||
print("🎉 All syntax checks passed!")
|
||||
|
||||
except SyntaxError as e:
|
||||
print(f"❌ Syntax error: {e}")
|
||||
print(f" Line {e.lineno}: {e.text}")
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Import error: {e}")
|
||||
exit(1)
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test the telemetry cleanup fix
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from datetime import datetime
|
||||
|
||||
# Add signal handler for timeout
|
||||
def timeout_handler(signum, frame):
|
||||
print("SUCCESS: Program terminated within timeout - fix is working!")
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGALRM, timeout_handler)
|
||||
signal.alarm(15) # 15 second timeout
|
||||
|
||||
try:
|
||||
# Set environment variable to disable telemetry (for testing)
|
||||
os.environ['PRAISONAI_TELEMETRY_DISABLED'] = 'true'
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print(f"[{datetime.now()}] Starting agent termination test...")
|
||||
|
||||
# Create agent with minimal setup
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print(f"[{datetime.now()}] Agent created successfully")
|
||||
|
||||
# Test the start method (which was hanging)
|
||||
print(f"[{datetime.now()}] Running agent.start()...")
|
||||
response = agent.start("Hello, just say hi back!")
|
||||
|
||||
print(f"[{datetime.now()}] Agent completed successfully!")
|
||||
print(f"Response: {response}")
|
||||
|
||||
# If we get here, the fix worked
|
||||
print(f"[{datetime.now()}] SUCCESS: Program should terminate properly now!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: Exception occurred: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
# Cancel the alarm
|
||||
signal.alarm(0)
|
||||
|
||||
print("Test completed - program should exit now.")
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Test script to reproduce the termination issue
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to the path so we can import praisonaiagents
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def test_termination():
|
||||
"""Test that the agent terminates properly after execution"""
|
||||
print("Testing agent termination...")
|
||||
|
||||
agent = Agent(instructions="You are a helpful AI assistant")
|
||||
response = agent.start("Write a short hello world message")
|
||||
|
||||
print(f"Agent response: {response}")
|
||||
print("Agent execution completed. Testing if program terminates...")
|
||||
|
||||
# If this completes without hanging, the fix works
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
result = test_termination()
|
||||
if result:
|
||||
print("SUCCESS: Program terminated properly")
|
||||
else:
|
||||
print("FAILURE: Program did not terminate")
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Test script that exactly matches the user's code example
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src/praisonai-agents'))
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def test_user_example():
|
||||
print("Testing user's exact code example...")
|
||||
|
||||
# This is the exact code from the user's issue
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
llm="mock", # Use mock model for testing
|
||||
self_reflect=False,
|
||||
verbose=False,
|
||||
stream=True
|
||||
)
|
||||
|
||||
# Test the streaming functionality
|
||||
print("Testing: for chunk in agent.start(prompt):")
|
||||
prompt = "Write a report on about the history of the world"
|
||||
|
||||
# This is the exact code the user wants to work
|
||||
result = agent.start(prompt)
|
||||
print(f"Type of result: {type(result)}")
|
||||
|
||||
if hasattr(result, '__iter__') and not isinstance(result, str):
|
||||
print("✅ SUCCESS: agent.start() returns a generator!")
|
||||
print("The user's code will now work:")
|
||||
print("for chunk in agent.start(prompt):")
|
||||
print(" print(chunk, end='', flush=True)")
|
||||
print("\nSimulating the iteration (first 5 chunks):")
|
||||
|
||||
# Test the first few chunks
|
||||
count = 0
|
||||
for chunk in result:
|
||||
print(f"Chunk {count}: {chunk}")
|
||||
count += 1
|
||||
if count >= 5: # Just show first 5 chunks to avoid long output
|
||||
break
|
||||
|
||||
print(f"\n✅ Streaming is working! Generated {count} chunks.")
|
||||
else:
|
||||
print("❌ FAILED: agent.start() does not return a generator")
|
||||
print(f"Result type: {type(result)}")
|
||||
print(f"Result: {result}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_user_example()
|
||||
Generated
+88
-2
@@ -616,6 +616,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "durationpy"
|
||||
version = "0.9"
|
||||
@@ -1721,6 +1730,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/d3/f7e6d7d062b8d7072c3989a528d9d47486ee5d5ae75250f6e26b4976d098/mmh3-5.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:122fa9ec148383f9124292962bda745f192b47bfd470b2af5fe7bb3982b17896", size = 36539, upload-time = "2024-09-22T16:46:19.332Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "motor"
|
||||
version = "3.7.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pymongo" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/93/ae/96b88362d6a84cb372f7977750ac2a8aed7b2053eed260615df08d5c84f4/motor-3.7.1.tar.gz", hash = "sha256:27b4d46625c87928f331a6ca9d7c51c2f518ba0e270939d395bc1ddc89d64526", size = 280997, upload-time = "2025-05-14T18:56:33.653Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/9a/35e053d4f442addf751ed20e0e922476508ee580786546d699b0567c4c67/motor-3.7.1-py3-none-any.whl", hash = "sha256:8a63b9049e38eeeb56b4fdd57c3312a6d1f25d01db717fe7d82222393c410298", size = 74996, upload-time = "2025-05-14T18:56:31.665Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mpmath"
|
||||
version = "1.3.0"
|
||||
@@ -2375,7 +2396,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.142"
|
||||
version = "0.0.144"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -2394,8 +2415,10 @@ all = [
|
||||
{ name = "markitdown", extra = ["all"] },
|
||||
{ name = "mcp" },
|
||||
{ name = "mem0ai", extra = ["graph"] },
|
||||
{ name = "motor" },
|
||||
{ name = "posthog" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pymongo" },
|
||||
{ name = "uvicorn" },
|
||||
]
|
||||
api = [
|
||||
@@ -2425,6 +2448,10 @@ memory = [
|
||||
{ name = "chromadb" },
|
||||
{ name = "litellm" },
|
||||
]
|
||||
mongodb = [
|
||||
{ name = "motor" },
|
||||
{ name = "pymongo" },
|
||||
]
|
||||
telemetry = [
|
||||
{ name = "posthog" },
|
||||
]
|
||||
@@ -2444,6 +2471,7 @@ requires-dist = [
|
||||
{ name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.6.0" },
|
||||
{ name = "mem0ai", marker = "extra == 'knowledge'", specifier = ">=0.1.0" },
|
||||
{ name = "mem0ai", extras = ["graph"], marker = "extra == 'graph'", specifier = ">=0.1.0" },
|
||||
{ name = "motor", marker = "extra == 'mongodb'", specifier = ">=3.4.0" },
|
||||
{ name = "openai" },
|
||||
{ name = "posthog", specifier = ">=3.0.0" },
|
||||
{ name = "posthog", marker = "extra == 'telemetry'", specifier = ">=3.0.0" },
|
||||
@@ -2453,14 +2481,16 @@ requires-dist = [
|
||||
{ name = "praisonaiagents", extras = ["llm"], marker = "extra == 'all'" },
|
||||
{ name = "praisonaiagents", extras = ["mcp"], marker = "extra == 'all'" },
|
||||
{ name = "praisonaiagents", extras = ["memory"], marker = "extra == 'all'" },
|
||||
{ name = "praisonaiagents", extras = ["mongodb"], marker = "extra == 'all'" },
|
||||
{ name = "praisonaiagents", extras = ["telemetry"], marker = "extra == 'all'" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic", marker = "extra == 'llm'", specifier = ">=2.4.2" },
|
||||
{ name = "pymongo", marker = "extra == 'mongodb'", specifier = ">=4.6.3" },
|
||||
{ name = "rich" },
|
||||
{ name = "uvicorn", marker = "extra == 'api'", specifier = ">=0.34.0" },
|
||||
{ name = "uvicorn", marker = "extra == 'mcp'", specifier = ">=0.34.0" },
|
||||
]
|
||||
provides-extras = ["mcp", "memory", "knowledge", "graph", "llm", "api", "telemetry", "all"]
|
||||
provides-extras = ["mcp", "memory", "knowledge", "graph", "llm", "api", "telemetry", "mongodb", "all"]
|
||||
|
||||
[[package]]
|
||||
name = "propcache"
|
||||
@@ -2713,6 +2743,62 @@ crypto = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pymongo"
|
||||
version = "4.13.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "dnspython" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/5a/d664298bf54762f0c89b8aa2c276868070e06afb853b4a8837de5741e5f9/pymongo-4.13.2.tar.gz", hash = "sha256:0f64c6469c2362962e6ce97258ae1391abba1566a953a492562d2924b44815c2", size = 2167844, upload-time = "2025-06-16T18:16:30.685Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/a8/293dfd3accda06ae94c54e7c15ac5108614d31263708236b4743554ad6ee/pymongo-4.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:01065eb1838e3621a30045ab14d1a60ee62e01f65b7cf154e69c5c722ef14d2f", size = 802768, upload-time = "2025-06-16T18:14:39.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/7f/2cbc897dd2867b9b5f8e9e6587dc4bf23e3777a4ddd712064ed21aea99e0/pymongo-4.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ab0325d436075f5f1901cde95afae811141d162bc42d9a5befb647fda585ae6", size = 803053, upload-time = "2025-06-16T18:14:43.318Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/da/07cdbaf507cccfdac837f612ea276523d2cdd380c5253c86ceae0369f0e2/pymongo-4.13.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdd8041902963c84dc4e27034fa045ac55fabcb2a4ba5b68b880678557573e70", size = 1180427, upload-time = "2025-06-16T18:14:44.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/5c/5f61269c87e565a6f4016e644e2bd20473b4b5a47c362ad3d57a1428ef33/pymongo-4.13.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b00ab04630aa4af97294e9abdbe0506242396269619c26f5761fd7b2524ef501", size = 1214655, upload-time = "2025-06-16T18:14:46.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/51/757ee06299e2bb61c0ae7b886ca845a78310cf94fc95bbc044bbe7892392/pymongo-4.13.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16440d0da30ba804c6c01ea730405fdbbb476eae760588ea09e6e7d28afc06de", size = 1197586, upload-time = "2025-06-16T18:14:48.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/a8/9ddf0ad0884046c34c5eb3de9a944c47d37e39989ae782ded2b207462a97/pymongo-4.13.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad9a2d1357aed5d6750deb315f62cb6f5b3c4c03ffb650da559cb09cb29e6fe8", size = 1183599, upload-time = "2025-06-16T18:14:49.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/57/61b289b440e77524e4b0d6881f6c6f50cf9a55a72b5ba2adaa43d70531e6/pymongo-4.13.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c793223aef21a8c415c840af1ca36c55a05d6fa3297378da35de3fb6661c0174", size = 1162761, upload-time = "2025-06-16T18:14:51.558Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/22/bd328cedc79768ab03942fd828f0cd1d50a3ae2c3caf3aebad65a644eb75/pymongo-4.13.2-cp310-cp310-win32.whl", hash = "sha256:8ef6ae029a3390565a0510c872624514dde350007275ecd8126b09175aa02cca", size = 790062, upload-time = "2025-06-16T18:14:53.024Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/70/2d8bbdac28e869cebb8081a43f8b16c6dd2384f6aef28fcc6ec0693a7042/pymongo-4.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:66f168f8c5b1e2e3d518507cf9f200f0c86ac79e2b2be9e7b6c8fd1e2f7d7824", size = 800198, upload-time = "2025-06-16T18:14:54.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/df/4c4ef17b48c70120f834ba7151860c300924915696c4a57170cb5b09787f/pymongo-4.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7af8c56d0a7fcaf966d5292e951f308fb1f8bac080257349e14742725fd7990d", size = 857145, upload-time = "2025-06-16T18:14:56.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/41/480ca82b3b3320fc70fe699a01df28db15a4ea154c8759ab4a437a74c808/pymongo-4.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad24f5864706f052b05069a6bc59ff875026e28709548131448fe1e40fc5d80f", size = 857437, upload-time = "2025-06-16T18:14:58.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/d4/eb74e98ea980a5e1ec4f06f383ec6c52ab02076802de24268f477ef616d2/pymongo-4.13.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a10069454195d1d2dda98d681b1dbac9a425f4b0fe744aed5230c734021c1cb9", size = 1426516, upload-time = "2025-06-16T18:15:00.589Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/fe/c5960c0e6438bd489367261e5ef1a5db01e34349f0dbf7529fb938d3d2ef/pymongo-4.13.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e20862b81e3863bcd72334e3577a3107604553b614a8d25ee1bb2caaea4eb90", size = 1477477, upload-time = "2025-06-16T18:15:02.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/9f/ef4395175fc97876978736c8493d8ffa4d13aa7a4e12269a2cb0d52a1246/pymongo-4.13.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b4d5794ca408317c985d7acfb346a60f96f85a7c221d512ff0ecb3cce9d6110", size = 1451921, upload-time = "2025-06-16T18:15:04.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/b9/397cb2a3ec03f880e882102eddcb46c3d516c6cf47a05f44db48067924d9/pymongo-4.13.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8e0420fb4901006ae7893e76108c2a36a343b4f8922466d51c45e9e2ceb717", size = 1431045, upload-time = "2025-06-16T18:15:06.392Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/0d/e150a414e5cb07f2fefca817fa071a6da8d96308469a85a777244c8c4337/pymongo-4.13.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:239b5f83b83008471d54095e145d4c010f534af99e87cc8877fc6827736451a0", size = 1399697, upload-time = "2025-06-16T18:15:08.975Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/29/5190eafb994721c30a38a8a62df225c47a9da364ab5c8cffe90aabf6a54e/pymongo-4.13.2-cp311-cp311-win32.whl", hash = "sha256:6bceb524110c32319eb7119422e400dbcafc5b21bcc430d2049a894f69b604e5", size = 836261, upload-time = "2025-06-16T18:15:10.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/da/30bdcc83b23fc4f2996b39b41b2ff0ff2184230a78617c7b8636aac4d81d/pymongo-4.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:ab87484c97ae837b0a7bbdaa978fa932fbb6acada3f42c3b2bee99121a594715", size = 851451, upload-time = "2025-06-16T18:15:12.181Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/e0/0e187750e23eed4227282fcf568fdb61f2b53bbcf8cbe3a71dde2a860d12/pymongo-4.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ec89516622dfc8b0fdff499612c0bd235aa45eeb176c9e311bcc0af44bf952b6", size = 912004, upload-time = "2025-06-16T18:15:14.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/c2/9b79795382daaf41e5f7379bffdef1880d68160adea352b796d6948cb5be/pymongo-4.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f30eab4d4326df54fee54f31f93e532dc2918962f733ee8e115b33e6fe151d92", size = 911698, upload-time = "2025-06-16T18:15:16.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/e4/f04dc9ed5d1d9dbc539dc2d8758dd359c5373b0e06fcf25418b2c366737c/pymongo-4.13.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cce9428d12ba396ea245fc4c51f20228cead01119fcc959e1c80791ea45f820", size = 1690357, upload-time = "2025-06-16T18:15:18.358Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/de/41478a7d527d38f1b98b084f4a78bbb805439a6ebd8689fbbee0a3dfacba/pymongo-4.13.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac9241b727a69c39117c12ac1e52d817ea472260dadc66262c3fdca0bab0709b", size = 1754593, upload-time = "2025-06-16T18:15:20.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/d9/8fa2eb110291e154f4312779b1a5b815090b8b05a59ecb4f4a32427db1df/pymongo-4.13.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3efc4c515b371a9fa1d198b6e03340985bfe1a55ae2d2b599a714934e7bc61ab", size = 1723637, upload-time = "2025-06-16T18:15:22.048Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/7b/9863fa60a4a51ea09f5e3cd6ceb231af804e723671230f2daf3bd1b59c2b/pymongo-4.13.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f57a664aa74610eb7a52fa93f2cf794a1491f4f76098343485dd7da5b3bcff06", size = 1693613, upload-time = "2025-06-16T18:15:24.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/89/a42efa07820a59089836f409a63c96e7a74e33313e50dc39c554db99ac42/pymongo-4.13.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dcb0b8cdd499636017a53f63ef64cf9b6bd3fd9355796c5a1d228e4be4a4c94", size = 1652745, upload-time = "2025-06-16T18:15:27.078Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/cf/2c77d1acda61d281edd3e3f00d5017d3fac0c29042c769efd3b8018cb469/pymongo-4.13.2-cp312-cp312-win32.whl", hash = "sha256:bf43ae07804d7762b509f68e5ec73450bb8824e960b03b861143ce588b41f467", size = 883232, upload-time = "2025-06-16T18:15:29.169Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/4f/727f59156e3798850c3c2901f106804053cb0e057ed1bd9883f5fa5aa8fa/pymongo-4.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:812a473d584bcb02ab819d379cd5e752995026a2bb0d7713e78462b6650d3f3a", size = 903304, upload-time = "2025-06-16T18:15:31.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/95/b44b8e24b161afe7b244f6d43c09a7a1f93308cad04198de1c14c67b24ce/pymongo-4.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6044ca0eb74d97f7d3415264de86a50a401b7b0b136d30705f022f9163c3124", size = 966232, upload-time = "2025-06-16T18:15:33.057Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/fc/d4d59799a52033acb187f7bd1f09bc75bebb9fd12cef4ba2964d235ad3f9/pymongo-4.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dd326bcb92d28d28a3e7ef0121602bad78691b6d4d1f44b018a4616122f1ba8b", size = 965935, upload-time = "2025-06-16T18:15:34.826Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/a8/67502899d89b317ea9952e4769bc193ca15efee561b24b38a86c59edde6f/pymongo-4.13.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfb0c21bdd58e58625c9cd8de13e859630c29c9537944ec0a14574fdf88c2ac4", size = 1954070, upload-time = "2025-06-16T18:15:36.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/3b/0dac5d81d1af1b96b3200da7ccc52fc261a35efb7d2ac493252eb40a2b11/pymongo-4.13.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9c7d345d57f17b1361008aea78a37e8c139631a46aeb185dd2749850883c7ba", size = 2031424, upload-time = "2025-06-16T18:15:38.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/ed/7a5af49a153224ca7e31e9915703e612ad9c45808cc39540e9dd1a2a7537/pymongo-4.13.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8860445a8da1b1545406fab189dc20319aff5ce28e65442b2b4a8f4228a88478", size = 1995339, upload-time = "2025-06-16T18:15:40.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/e9/9c72eceae8439c4f1bdebc4e6b290bf035e3f050a80eeb74abb5e12ef8e2/pymongo-4.13.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01c184b612f67d5a4c8f864ae7c40b6cc33c0e9bb05e39d08666f8831d120504", size = 1956066, upload-time = "2025-06-16T18:15:42.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/79/9b019c47923395d5fced03856996465fb9340854b0f5a2ddf16d47e2437c/pymongo-4.13.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ea8c62d5f3c6529407c12471385d9a05f9fb890ce68d64976340c85cd661b", size = 1905642, upload-time = "2025-06-16T18:15:43.978Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/2f/ebf56c7fa9298fa2f9716e7b66cf62b29e7fc6e11774f3b87f55d214d466/pymongo-4.13.2-cp313-cp313-win32.whl", hash = "sha256:d13556e91c4a8cb07393b8c8be81e66a11ebc8335a40fa4af02f4d8d3b40c8a1", size = 930184, upload-time = "2025-06-16T18:15:46.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/2f/49c35464cbd5d116d950ff5d24b4b20491aaae115d35d40b945c33b29250/pymongo-4.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:cfc69d7bc4d4d5872fd1e6de25e6a16e2372c7d5556b75c3b8e2204dce73e3fb", size = 955111, upload-time = "2025-06-16T18:15:48.85Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/56/b17c8b5329b1842b7847cf0fa224ef0a272bf2e5126360f4da8065c855a1/pymongo-4.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a457d2ac34c05e9e8a6bb724115b093300bf270f0655fb897df8d8604b2e3700", size = 1022735, upload-time = "2025-06-16T18:15:50.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/e6/66fec65a7919bf5f35be02e131b4dc4bf3152b5e8d78cd04b6d266a44514/pymongo-4.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:02f131a6e61559613b1171b53fbe21fed64e71b0cb4858c47fc9bc7c8e0e501c", size = 1022740, upload-time = "2025-06-16T18:15:53.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/92/cda7383df0d5e71dc007f172c1ecae6313d64ea05d82bbba06df7f6b3e49/pymongo-4.13.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c942d1c6334e894271489080404b1a2e3b8bd5de399f2a0c14a77d966be5bc9", size = 2282430, upload-time = "2025-06-16T18:15:55.356Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/da/285e05eb1d617b30dc7a7a98ebeb264353a8903e0e816a4eec6487c81f18/pymongo-4.13.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:850168d115680ab66a0931a6aa9dd98ed6aa5e9c3b9a6c12128049b9a5721bc5", size = 2369470, upload-time = "2025-06-16T18:15:57.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c0/c0d5eae236de9ca293497dc58fc1e4872382223c28ec223f76afc701392c/pymongo-4.13.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af7dfff90647ee77c53410f7fe8ca4fe343f8b768f40d2d0f71a5602f7b5a541", size = 2328857, upload-time = "2025-06-16T18:15:59.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/5a/d8639fba60def128ce9848b99c56c54c8a4d0cd60342054cd576f0bfdf26/pymongo-4.13.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8057f9bc9c94a8fd54ee4f5e5106e445a8f406aff2df74746f21c8791ee2403", size = 2280053, upload-time = "2025-06-16T18:16:02.166Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/69/d56f0897cc4932a336820c5d2470ffed50be04c624b07d1ad6ea75aaa975/pymongo-4.13.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51040e1ba78d6671f8c65b29e2864483451e789ce93b1536de9cc4456ede87fa", size = 2219378, upload-time = "2025-06-16T18:16:04.108Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/1e/427e7f99801ee318b6331062d682d3816d7e1d6b6013077636bd75d49c87/pymongo-4.13.2-cp313-cp313t-win32.whl", hash = "sha256:7ab86b98a18c8689514a9f8d0ec7d9ad23a949369b31c9a06ce4a45dcbffcc5e", size = 979460, upload-time = "2025-06-16T18:16:06.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/9c/00301a6df26f0f8d5c5955192892241e803742e7c3da8c2c222efabc0df6/pymongo-4.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c38168263ed94a250fc5cf9c6d33adea8ab11c9178994da1c3481c2a49d235f8", size = 1011057, upload-time = "2025-06-16T18:16:07.917Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pypdf"
|
||||
version = "5.6.0"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import ast
|
||||
|
||||
try:
|
||||
with open('praisonaiagents/agent/agent.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
ast.parse(content)
|
||||
print("SUCCESS: agent.py syntax is valid")
|
||||
except SyntaxError as e:
|
||||
print(f"SYNTAX ERROR: {e}")
|
||||
print(f"Line {e.lineno}: {e.text}")
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}")
|
||||
@@ -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.70.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.70.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.71.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.71.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -57,7 +57,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.70 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.71 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.70"
|
||||
version = "2.2.71"
|
||||
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.142",
|
||||
"praisonaiagents>=0.0.144",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -102,7 +102,7 @@ autogen-v4 = [
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.70"
|
||||
version = "2.2.71"
|
||||
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 = ""
|
||||
@@ -120,7 +120,7 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.142"
|
||||
praisonaiagents = ">=0.0.144"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
|
||||
Generated
+5
-5
@@ -4048,7 +4048,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonai"
|
||||
version = "2.2.70"
|
||||
version = "2.2.71"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "instructor" },
|
||||
@@ -4202,7 +4202,7 @@ requires-dist = [
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.142" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.144" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = "==0.2.29" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4257,7 +4257,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.142"
|
||||
version = "0.0.144"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -4266,9 +4266,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/51/2b/d341e8f774adc84c273e57f7bb6c4ec8af5f974b4d5ec0a1811ab96ef5c5/praisonaiagents-0.0.142.tar.gz", hash = "sha256:9f6dfd26d5c334eda3f0ba86c821616cb673ef230c15d57e8230e4f9bb7108a8", size = 205694, upload-time = "2025-07-17T00:31:09.554Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b4/3a/bf0925b182699f4fe9d8b645772ac76126af536755058682922955efe2b1/praisonaiagents-0.0.144.tar.gz", hash = "sha256:5b3870d8bd6679a27a38793d7444f4636fd0bb21b880e4866706dc9f8da783fe", size = 217748, upload-time = "2025-07-18T21:26:38.632Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/71/c9/79f2124287cc99c0d50f23b20ff2958e08eb8b0b496198607f343dfc2245/praisonaiagents-0.0.142-py3-none-any.whl", hash = "sha256:0887cf92acda4be650dd8e7a110a16e4e7624eae12a7c10e8ca8d32f1ac282cf", size = 224783, upload-time = "2025-07-17T00:31:07.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/73/8a89fcb4d8cd17c7dbe43505a671644c283af859ce3984660ad71dbb71d0/praisonaiagents-0.0.144-py3-none-any.whl", hash = "sha256:9907cc4e02d35a20c99a171c723625384937a02f725f00f20a43b7d09ceb7d14", size = 237310, upload-time = "2025-07-18T21:26:37.074Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user