mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-09 03:39:37 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3be4c4c70e | ||
|
|
709640240f | ||
|
|
93d526eb7b | ||
|
|
624a2cfa17 | ||
|
|
4d2a6a2e2e | ||
|
|
a1bdb056a0 | ||
|
|
b4a6401bae | ||
|
|
772ce7d2e2 | ||
|
|
20c2d28f35 | ||
|
|
f8589e4363 | ||
|
|
49d9d40199 | ||
|
|
445cc037ce | ||
|
|
9e7c172f55 | ||
|
|
d89570b085 | ||
|
|
ee5dfddccd |
No files matched your search
+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.22" \
|
||||
"praisonai>=2.2.24" \
|
||||
"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.22" \
|
||||
"praisonai>=2.2.24" \
|
||||
"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.22" \
|
||||
"praisonai>=2.2.24" \
|
||||
"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.22" \
|
||||
"praisonai>=2.2.24" \
|
||||
"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.22`
|
||||
- PraisonAI: `>=2.2.24`
|
||||
- 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.22" "praisonaiagents==0.0.92"
|
||||
RUN pip install "praisonai==2.2.24" "praisonaiagents==0.0.92"
|
||||
```
|
||||
|
||||
## 🌐 Production Deployment
|
||||
|
||||
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.22 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.24 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.2.22 watchdog
|
||||
RUN pip install flask praisonai==2.2.24 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.2.22 watchdog
|
||||
RUN pip install flask praisonai==2.2.24 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.2.22 watchdog
|
||||
RUN pip install flask praisonai==2.2.24 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
Knowledge Agent with Reranker Example
|
||||
===================================
|
||||
|
||||
This example demonstrates how to use Mem0's built-in reranking feature
|
||||
with the Knowledge Agent for improved search result relevance.
|
||||
"""
|
||||
|
||||
import os
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
|
||||
def main():
|
||||
"""
|
||||
Demonstrates knowledge search with and without reranking.
|
||||
"""
|
||||
|
||||
# Create sample content for knowledge base
|
||||
sample_documents = [
|
||||
"Artificial Intelligence is a field of computer science that aims to create machines that can perform tasks that typically require human intelligence.",
|
||||
"Machine Learning is a subset of AI that enables computers to learn and improve from experience without being explicitly programmed.",
|
||||
"Deep Learning is a type of machine learning that uses neural networks with multiple layers to model and understand complex patterns.",
|
||||
"Natural Language Processing (NLP) is a branch of AI that helps computers understand, interpret and manipulate human language.",
|
||||
"Computer Vision is an AI field that trains computers to interpret and understand the visual world from digital images and videos.",
|
||||
"Reinforcement Learning is a type of machine learning where an agent learns to make decisions by taking actions in an environment.",
|
||||
"Neural Networks are computing systems inspired by biological neural networks that constitute animal brains.",
|
||||
"Data Science combines domain expertise, programming skills, and knowledge of mathematics and statistics to extract insights from data."
|
||||
]
|
||||
|
||||
print("=== Knowledge Agent Reranker Example ===\n")
|
||||
|
||||
# Example 1: Basic Knowledge without reranking
|
||||
print("1. Creating Knowledge base without reranking...")
|
||||
basic_config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "knowledge_basic",
|
||||
"path": ".praison_basic"
|
||||
}
|
||||
},
|
||||
"reranker": {
|
||||
"enabled": True,
|
||||
"default_rerank": False # Disabled by default
|
||||
}
|
||||
}
|
||||
|
||||
basic_knowledge = Knowledge(config=basic_config, verbose=1)
|
||||
|
||||
# Add documents to knowledge base
|
||||
for i, doc in enumerate(sample_documents):
|
||||
basic_knowledge.store(doc, metadata={"doc_id": f"doc_{i}", "category": "AI"})
|
||||
|
||||
# Example 2: Knowledge with reranking enabled by default
|
||||
print("\n2. Creating Knowledge base with reranking enabled...")
|
||||
rerank_config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "knowledge_rerank",
|
||||
"path": ".praison_rerank"
|
||||
}
|
||||
},
|
||||
"reranker": {
|
||||
"enabled": True,
|
||||
"default_rerank": True # Enabled by default
|
||||
}
|
||||
}
|
||||
|
||||
rerank_knowledge = Knowledge(config=rerank_config, verbose=1)
|
||||
|
||||
# Add same documents to reranking knowledge base
|
||||
for i, doc in enumerate(sample_documents):
|
||||
rerank_knowledge.store(doc, metadata={"doc_id": f"doc_{i}", "category": "AI"})
|
||||
|
||||
print("\n=== Search Results Comparison ===\n")
|
||||
|
||||
query = "What is machine learning and how does it work?"
|
||||
|
||||
# Search without reranking
|
||||
print("3. Search WITHOUT reranking:")
|
||||
basic_results = basic_knowledge.search(query, rerank=False)
|
||||
print(f"Query: {query}")
|
||||
print(f"Results (limit=3):")
|
||||
for i, result in enumerate(basic_results[:3]):
|
||||
text = result.get('memory', result.get('text', str(result)))
|
||||
score = result.get('score', 'N/A')
|
||||
print(f" {i+1}. Score: {score}")
|
||||
print(f" Text: {text[:100]}...")
|
||||
print()
|
||||
|
||||
# Search with reranking explicitly enabled
|
||||
print("4. Search WITH reranking:")
|
||||
rerank_results = basic_knowledge.search(query, rerank=True)
|
||||
print(f"Query: {query}")
|
||||
print(f"Results (limit=3):")
|
||||
for i, result in enumerate(rerank_results[:3]):
|
||||
text = result.get('memory', result.get('text', str(result)))
|
||||
score = result.get('score', 'N/A')
|
||||
print(f" {i+1}. Score: {score}")
|
||||
print(f" Text: {text[:100]}...")
|
||||
print()
|
||||
|
||||
# Search using config default (reranking enabled)
|
||||
print("5. Search using config default (reranking enabled):")
|
||||
default_results = rerank_knowledge.search(query) # Will use default_rerank=True
|
||||
print(f"Query: {query}")
|
||||
print(f"Results (limit=3):")
|
||||
for i, result in enumerate(default_results[:3]):
|
||||
text = result.get('memory', result.get('text', str(result)))
|
||||
score = result.get('score', 'N/A')
|
||||
print(f" {i+1}. Score: {score}")
|
||||
print(f" Text: {text[:100]}...")
|
||||
print()
|
||||
|
||||
print("=== Advanced Search Options ===\n")
|
||||
|
||||
# Example with additional Mem0 advanced retrieval options
|
||||
print("6. Search with keyword search + reranking:")
|
||||
advanced_results = basic_knowledge.search(
|
||||
query,
|
||||
rerank=True,
|
||||
keyword_search=True, # Enable keyword search for better recall
|
||||
filter_memories=True # Enable filtering for better precision
|
||||
)
|
||||
print(f"Query: {query}")
|
||||
print(f"Advanced results with keyword_search=True, filter_memories=True:")
|
||||
for i, result in enumerate(advanced_results[:3]):
|
||||
text = result.get('memory', result.get('text', str(result)))
|
||||
score = result.get('score', 'N/A')
|
||||
print(f" {i+1}. Score: {score}")
|
||||
print(f" Text: {text[:100]}...")
|
||||
print()
|
||||
|
||||
# Example with Agent using reranking knowledge
|
||||
print("\n=== Agent with Reranking Knowledge ===\n")
|
||||
|
||||
agent = Agent(
|
||||
name="AI Research Assistant",
|
||||
instructions="You are an AI research assistant. Use your knowledge base to answer questions about artificial intelligence topics.",
|
||||
knowledge=sample_documents, # Will use default knowledge config
|
||||
knowledge_config=rerank_config, # Use reranking config
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
print("7. Agent with reranking knowledge:")
|
||||
response = agent.run("Explain the difference between machine learning and deep learning, and how they relate to AI.")
|
||||
print(f"Agent response: {response}")
|
||||
|
||||
print("\n=== Performance Notes ===")
|
||||
print("- Basic search: ~10ms latency")
|
||||
print("- Reranking: 150-200ms additional latency")
|
||||
print("- Reranking improves relevance ordering significantly")
|
||||
print("- Use rerank=True for better results when query relevance is critical")
|
||||
print("- Use keyword_search=True for better recall")
|
||||
print("- Use filter_memories=True for better precision")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -101,7 +101,11 @@ class Knowledge:
|
||||
}
|
||||
},
|
||||
"version": "v1.1",
|
||||
"custom_prompt": "Return {{\"facts\": [text]}} where text is the exact input provided and json response"
|
||||
"custom_prompt": "Return {{\"facts\": [text]}} where text is the exact input provided and json response",
|
||||
"reranker": {
|
||||
"enabled": False,
|
||||
"default_rerank": False
|
||||
}
|
||||
}
|
||||
|
||||
# If config is provided, merge it with base config
|
||||
@@ -129,6 +133,10 @@ class Knowledge:
|
||||
# Merge llm config if provided
|
||||
if "llm" in self._config:
|
||||
base_config["llm"] = self._config["llm"]
|
||||
|
||||
# Merge reranker config if provided
|
||||
if "reranker" in self._config:
|
||||
base_config["reranker"].update(self._config["reranker"])
|
||||
return base_config
|
||||
|
||||
@cached_property
|
||||
@@ -191,9 +199,25 @@ class Knowledge:
|
||||
"""Retrieve a specific memory by ID."""
|
||||
return self.memory.get(memory_id)
|
||||
|
||||
def search(self, query, user_id=None, agent_id=None, run_id=None):
|
||||
"""Search for memories related to a query."""
|
||||
return self.memory.search(query, user_id=user_id, agent_id=agent_id, run_id=run_id)
|
||||
def search(self, query, user_id=None, agent_id=None, run_id=None, rerank=None, **kwargs):
|
||||
"""Search for memories related to a query.
|
||||
|
||||
Args:
|
||||
query: The search query string
|
||||
user_id: Optional user ID for user-specific search
|
||||
agent_id: Optional agent ID for agent-specific search
|
||||
run_id: Optional run ID for run-specific search
|
||||
rerank: Whether to use Mem0's advanced reranking. If None, uses config default
|
||||
**kwargs: Additional search parameters to pass to Mem0 (keyword_search, filter_memories, etc.)
|
||||
|
||||
Returns:
|
||||
List of search results, reranked if rerank=True
|
||||
"""
|
||||
# Use config default if rerank not explicitly specified
|
||||
if rerank is None:
|
||||
rerank = self.config.get("reranker", {}).get("default_rerank", False)
|
||||
|
||||
return self.memory.search(query, user_id=user_id, agent_id=agent_id, run_id=run_id, rerank=rerank, **kwargs)
|
||||
|
||||
def update(self, memory_id, data):
|
||||
"""Update a memory."""
|
||||
|
||||
@@ -299,7 +299,11 @@ class MCP:
|
||||
required_params.append(name)
|
||||
|
||||
# Create the function signature
|
||||
params = []
|
||||
# Separate required and optional parameters to ensure proper ordering
|
||||
# (required parameters must come before optional parameters)
|
||||
required_param_objects = []
|
||||
optional_param_objects = []
|
||||
|
||||
for name in param_names:
|
||||
is_required = name in required_params
|
||||
param = inspect.Parameter(
|
||||
@@ -308,7 +312,14 @@ class MCP:
|
||||
default=inspect.Parameter.empty if is_required else None,
|
||||
annotation=param_annotations.get(name, Any)
|
||||
)
|
||||
params.append(param)
|
||||
|
||||
if is_required:
|
||||
required_param_objects.append(param)
|
||||
else:
|
||||
optional_param_objects.append(param)
|
||||
|
||||
# Combine parameters with required first, then optional
|
||||
params = required_param_objects + optional_param_objects
|
||||
|
||||
# Create function template to be properly decorated
|
||||
def template_function(*args, **kwargs):
|
||||
|
||||
@@ -262,13 +262,18 @@ class Memory:
|
||||
query: str,
|
||||
limit: int = 5,
|
||||
min_quality: float = 0.0,
|
||||
relevance_cutoff: float = 0.0
|
||||
relevance_cutoff: float = 0.0,
|
||||
rerank: bool = False,
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search short-term memory with optional quality filter"""
|
||||
self._log_verbose(f"Searching short memory for: {query}")
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
results = self.mem0_client.search(query=query, limit=limit)
|
||||
# Pass rerank and other kwargs to Mem0 search
|
||||
search_params = {"query": query, "limit": limit, "rerank": rerank}
|
||||
search_params.update(kwargs)
|
||||
results = self.mem0_client.search(**search_params)
|
||||
filtered = [r for r in results if r.get("score", 1.0) >= relevance_cutoff]
|
||||
return filtered
|
||||
|
||||
@@ -439,7 +444,9 @@ class Memory:
|
||||
query: str,
|
||||
limit: int = 5,
|
||||
relevance_cutoff: float = 0.0,
|
||||
min_quality: float = 0.0
|
||||
min_quality: float = 0.0,
|
||||
rerank: bool = False,
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search long-term memory with optional quality filter"""
|
||||
self._log_verbose(f"Searching long memory for: {query}")
|
||||
@@ -448,7 +455,10 @@ class Memory:
|
||||
found = []
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
results = self.mem0_client.search(query=query, limit=limit)
|
||||
# Pass rerank and other kwargs to Mem0 search
|
||||
search_params = {"query": query, "limit": limit, "rerank": rerank}
|
||||
search_params.update(kwargs)
|
||||
results = self.mem0_client.search(**search_params)
|
||||
# Filter by quality
|
||||
filtered = [r for r in results if r.get("metadata", {}).get("quality", 0.0) >= min_quality]
|
||||
logger.info(f"Found {len(filtered)} results in Mem0")
|
||||
@@ -595,12 +605,15 @@ class Memory:
|
||||
else:
|
||||
self.store_long_term(text, metadata=meta)
|
||||
|
||||
def search_user_memory(self, user_id: str, query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
||||
def search_user_memory(self, user_id: str, query: str, limit: int = 5, rerank: bool = False, **kwargs) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
If mem0 is used, pass user_id in. Otherwise fallback to local filter on user in metadata.
|
||||
"""
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
return self.mem0_client.search(query=query, limit=limit, user_id=user_id)
|
||||
# Pass rerank and other kwargs to Mem0 search
|
||||
search_params = {"query": query, "limit": limit, "user_id": user_id, "rerank": rerank}
|
||||
search_params.update(kwargs)
|
||||
return self.mem0_client.search(**search_params)
|
||||
else:
|
||||
hits = self.search_long_term(query, limit=20)
|
||||
filtered = []
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.93"
|
||||
version = "0.0.95"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
|
||||
Generated
+1
-1
@@ -2233,7 +2233,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.93"
|
||||
version = "0.0.95"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.22.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.22.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.24.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.24.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -121,12 +121,12 @@ Tools are not available for {framework}. To use tools, install:
|
||||
mode=instructor.Mode.JSON,
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
def generate(self, merge=False):
|
||||
"""
|
||||
Generates a team structure for the specified topic.
|
||||
|
||||
Args:
|
||||
None
|
||||
merge (bool): Whether to merge with existing agents.yaml file instead of overwriting.
|
||||
|
||||
Returns:
|
||||
str: The full path of the YAML file containing the generated team structure.
|
||||
@@ -149,46 +149,119 @@ Tools are not available for {framework}. To use tools, install:
|
||||
]
|
||||
)
|
||||
json_data = json.loads(response.model_dump_json())
|
||||
self.convert_and_save(json_data)
|
||||
self.convert_and_save(json_data, merge=merge)
|
||||
full_path = os.path.abspath(self.agent_file)
|
||||
return full_path
|
||||
|
||||
def convert_and_save(self, json_data):
|
||||
def convert_and_save(self, json_data, merge=False):
|
||||
"""Converts the provided JSON data into the desired YAML format and saves it to a file.
|
||||
|
||||
Args:
|
||||
json_data (dict): The JSON data representing the team structure.
|
||||
topic (str, optional): The topic to be inserted into the YAML. Defaults to "Artificial Intelligence".
|
||||
agent_file (str, optional): The name of the YAML file to save. Defaults to "test.yaml".
|
||||
merge (bool): Whether to merge with existing agents.yaml file instead of overwriting.
|
||||
"""
|
||||
|
||||
yaml_data = {
|
||||
"framework": self.framework,
|
||||
"topic": self.topic,
|
||||
"roles": {},
|
||||
"dependencies": []
|
||||
}
|
||||
|
||||
for role_id, role_details in json_data['roles'].items():
|
||||
yaml_data['roles'][role_id] = {
|
||||
"backstory": "" + role_details['backstory'],
|
||||
"goal": role_details['goal'],
|
||||
"role": role_details['role'],
|
||||
"tasks": {},
|
||||
# "tools": role_details.get('tools', []),
|
||||
"tools": ['']
|
||||
# Handle merge functionality
|
||||
if merge and os.path.exists(self.agent_file):
|
||||
yaml_data = self.merge_with_existing_agents(json_data)
|
||||
else:
|
||||
# Original behavior: create new yaml_data structure
|
||||
yaml_data = {
|
||||
"framework": self.framework,
|
||||
"topic": self.topic,
|
||||
"roles": {},
|
||||
"dependencies": []
|
||||
}
|
||||
|
||||
for task_id, task_details in role_details['tasks'].items():
|
||||
yaml_data['roles'][role_id]['tasks'][task_id] = {
|
||||
"description": "" + task_details['description'],
|
||||
"expected_output": "" + task_details['expected_output']
|
||||
for role_id, role_details in json_data['roles'].items():
|
||||
yaml_data['roles'][role_id] = {
|
||||
"backstory": "" + role_details['backstory'],
|
||||
"goal": role_details['goal'],
|
||||
"role": role_details['role'],
|
||||
"tasks": {},
|
||||
# "tools": role_details.get('tools', []),
|
||||
"tools": ['']
|
||||
}
|
||||
|
||||
for task_id, task_details in role_details['tasks'].items():
|
||||
yaml_data['roles'][role_id]['tasks'][task_id] = {
|
||||
"description": "" + task_details['description'],
|
||||
"expected_output": "" + task_details['expected_output']
|
||||
}
|
||||
|
||||
# Save to YAML file, maintaining the order
|
||||
with open(self.agent_file, 'w') as f:
|
||||
yaml.dump(yaml_data, f, allow_unicode=True, sort_keys=False)
|
||||
|
||||
def merge_with_existing_agents(self, new_json_data):
|
||||
"""
|
||||
Merge existing agents.yaml with new auto-generated agents.
|
||||
|
||||
Args:
|
||||
new_json_data (dict): The JSON data representing the new team structure.
|
||||
|
||||
Returns:
|
||||
dict: The merged YAML data structure.
|
||||
"""
|
||||
try:
|
||||
# Load existing agents.yaml
|
||||
with open(self.agent_file, 'r') as f:
|
||||
existing_data = yaml.safe_load(f)
|
||||
|
||||
if not existing_data:
|
||||
# If existing file is empty, treat as new file
|
||||
existing_data = {"roles": {}, "dependencies": []}
|
||||
except (yaml.YAMLError, FileNotFoundError) as e:
|
||||
logging.warning(f"Could not load existing agents file {self.agent_file}: {e}")
|
||||
logging.warning("Creating new file instead of merging")
|
||||
existing_data = {"roles": {}, "dependencies": []}
|
||||
|
||||
# Start with existing data structure
|
||||
merged_data = existing_data.copy()
|
||||
|
||||
# Ensure required fields exist
|
||||
if 'roles' not in merged_data:
|
||||
merged_data['roles'] = {}
|
||||
if 'dependencies' not in merged_data:
|
||||
merged_data['dependencies'] = []
|
||||
if 'framework' not in merged_data:
|
||||
merged_data['framework'] = self.framework
|
||||
|
||||
# Handle topic merging
|
||||
existing_topic = merged_data.get('topic', '')
|
||||
new_topic = self.topic
|
||||
if existing_topic and existing_topic != new_topic:
|
||||
merged_data['topic'] = f"{existing_topic} + {new_topic}"
|
||||
else:
|
||||
merged_data['topic'] = new_topic
|
||||
|
||||
# Merge new roles with existing ones
|
||||
for role_id, role_details in new_json_data['roles'].items():
|
||||
# Check for conflicts and rename if necessary
|
||||
final_role_id = role_id
|
||||
counter = 1
|
||||
while final_role_id in merged_data['roles']:
|
||||
final_role_id = f"{role_id}_auto_{counter}"
|
||||
counter += 1
|
||||
|
||||
# Add the new role
|
||||
merged_data['roles'][final_role_id] = {
|
||||
"backstory": "" + role_details['backstory'],
|
||||
"goal": role_details['goal'],
|
||||
"role": role_details['role'],
|
||||
"tasks": {},
|
||||
"tools": ['']
|
||||
}
|
||||
|
||||
# Add tasks for this role
|
||||
for task_id, task_details in role_details['tasks'].items():
|
||||
merged_data['roles'][final_role_id]['tasks'][task_id] = {
|
||||
"description": "" + task_details['description'],
|
||||
"expected_output": "" + task_details['expected_output']
|
||||
}
|
||||
|
||||
return merged_data
|
||||
|
||||
def get_user_content(self):
|
||||
"""
|
||||
Generates a prompt for the OpenAI API to generate a team structure.
|
||||
|
||||
@@ -417,7 +417,7 @@ class PraisonAI:
|
||||
|
||||
self.agent_file = "test.yaml"
|
||||
generator = AutoGenerator(topic=self.topic, framework=self.framework, agent_file=self.agent_file)
|
||||
self.agent_file = generator.generate()
|
||||
self.agent_file = generator.generate(merge=getattr(args, 'merge', False))
|
||||
agents_generator = AgentsGenerator(self.agent_file, self.framework, self.config_list)
|
||||
result = agents_generator.generate_crew_and_kickoff()
|
||||
print(result)
|
||||
@@ -430,7 +430,7 @@ class PraisonAI:
|
||||
|
||||
self.agent_file = "agents.yaml"
|
||||
generator = AutoGenerator(topic=self.topic, framework=self.framework, agent_file=self.agent_file)
|
||||
self.agent_file = generator.generate()
|
||||
self.agent_file = generator.generate(merge=getattr(args, 'merge', False))
|
||||
print(f"File {self.agent_file} created successfully")
|
||||
return f"File {self.agent_file} created successfully"
|
||||
|
||||
@@ -476,6 +476,36 @@ class PraisonAI:
|
||||
'unittest' in sys.modules
|
||||
)
|
||||
|
||||
# Check if we're being used as a library (not from praisonai CLI)
|
||||
# Skip CLI parsing to avoid conflicts with applications like Fabric
|
||||
is_library_usage = (
|
||||
'praisonai' not in sys.argv[0] and
|
||||
not in_test_env
|
||||
)
|
||||
|
||||
if is_library_usage:
|
||||
# Return default args when used as library to prevent CLI conflicts
|
||||
default_args = argparse.Namespace()
|
||||
default_args.framework = None
|
||||
default_args.ui = None
|
||||
default_args.auto = None
|
||||
default_args.init = None
|
||||
default_args.command = None
|
||||
default_args.deploy = False
|
||||
default_args.schedule = None
|
||||
default_args.schedule_config = None
|
||||
default_args.provider = "gcp"
|
||||
default_args.max_retries = 3
|
||||
default_args.model = None
|
||||
default_args.llm = None
|
||||
default_args.hf = None
|
||||
default_args.ollama = None
|
||||
default_args.dataset = "yahma/alpaca-cleaned"
|
||||
default_args.realtime = False
|
||||
default_args.call = False
|
||||
default_args.public = False
|
||||
return default_args
|
||||
|
||||
# Define special commands
|
||||
special_commands = ['chat', 'code', 'call', 'realtime', 'train', 'ui']
|
||||
|
||||
@@ -498,6 +528,7 @@ class PraisonAI:
|
||||
parser.add_argument("--realtime", action="store_true", help="Start the realtime voice interaction interface")
|
||||
parser.add_argument("--call", action="store_true", help="Start the PraisonAI Call server")
|
||||
parser.add_argument("--public", action="store_true", help="Use ngrok to expose the server publicly (only with --call)")
|
||||
parser.add_argument("--merge", action="store_true", help="Merge existing agents.yaml with auto-generated agents instead of overwriting")
|
||||
|
||||
# If we're in a test environment, parse with empty args to avoid pytest interference
|
||||
if in_test_env:
|
||||
|
||||
@@ -56,7 +56,7 @@ class CloudDeployer:
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.22 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.24 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ from chainlit.input_widget import TextInput
|
||||
from chainlit.types import ThreadDict
|
||||
import chainlit.data as cl_data
|
||||
from litellm import acompletion
|
||||
import litellm
|
||||
from db import DatabaseManager
|
||||
|
||||
# Load environment variables
|
||||
@@ -40,6 +41,15 @@ logger.addHandler(console_handler)
|
||||
# Set the logging level for the logger
|
||||
logger.setLevel(log_level)
|
||||
|
||||
# Configure litellm same as in llm.py
|
||||
litellm.set_verbose = False
|
||||
litellm.success_callback = []
|
||||
litellm._async_success_callback = []
|
||||
litellm.callbacks = []
|
||||
litellm.drop_params = True
|
||||
litellm.modify_params = True
|
||||
litellm.suppress_debug_messages = True
|
||||
|
||||
CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
|
||||
|
||||
if not CHAINLIT_AUTH_SECRET:
|
||||
@@ -55,6 +65,17 @@ db_manager.initialize()
|
||||
|
||||
deleted_thread_ids = [] # type: List[str]
|
||||
|
||||
def _build_completion_params(model_name, **override_params):
|
||||
"""Build parameters for litellm completion calls with proper model handling"""
|
||||
params = {
|
||||
"model": model_name,
|
||||
}
|
||||
|
||||
# Override with any provided parameters
|
||||
params.update(override_params)
|
||||
|
||||
return params
|
||||
|
||||
def save_setting(key: str, value: str):
|
||||
"""Saves a setting to the database.
|
||||
|
||||
@@ -237,12 +258,12 @@ Context:
|
||||
msg = cl.Message(content="")
|
||||
await msg.send()
|
||||
|
||||
# Prepare the completion parameters
|
||||
completion_params = {
|
||||
"model": model_name,
|
||||
"messages": message_history,
|
||||
"stream": True,
|
||||
}
|
||||
# Prepare the completion parameters using the helper function
|
||||
completion_params = _build_completion_params(
|
||||
model_name,
|
||||
messages=message_history,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
# If an image is uploaded, include it in the message
|
||||
if image:
|
||||
@@ -344,9 +365,11 @@ Context:
|
||||
logger.error(f"Failed to parse function arguments: {function_args}")
|
||||
|
||||
second_response = await acompletion(
|
||||
model=model_name,
|
||||
stream=True,
|
||||
messages=messages,
|
||||
**_build_completion_params(
|
||||
model_name,
|
||||
stream=True,
|
||||
messages=messages,
|
||||
)
|
||||
)
|
||||
logger.debug(f"Second LLM response: {second_response}")
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.22"
|
||||
version = "2.2.24"
|
||||
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.91",
|
||||
"praisonaiagents>=0.0.95",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.22"
|
||||
version = "2.2.24"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
authors = ["Mervin Praison"]
|
||||
license = ""
|
||||
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.91"
|
||||
praisonaiagents = ">=0.0.95"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
|
||||
Generated
+5
-5
@@ -3931,7 +3931,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonai"
|
||||
version = "2.2.22"
|
||||
version = "2.2.24"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "instructor" },
|
||||
@@ -4073,7 +4073,7 @@ requires-dist = [
|
||||
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.91" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.95" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4130,7 +4130,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.91"
|
||||
version = "0.0.95"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
@@ -4138,9 +4138,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/63/a26fe7ae31191ed5998fabe6cb13f779728caf3123ac8c8e8f4707a99e58/praisonaiagents-0.0.91.tar.gz", hash = "sha256:ec0409591f9278b766c4c6b3876ed142cfc217b1545c1b9809494f8a826b55fa", size = 125710 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/7c/c6ae58b98d652f3c986683e72883eda5a3a4c2de054c01f6674ebdb0ae26/praisonaiagents-0.0.95.tar.gz", hash = "sha256:3b71869c3c73241e7dee109232d492545ea2a397fbfd9c922d4c9cfd41d93094", size = 127328 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/2b/2e2d29ceb21a8e3af15beaf60955b85a9373ee6164d2d76f649a3e533786/praisonaiagents-0.0.91-py3-none-any.whl", hash = "sha256:aaaa51370a81aae29500168678d4b7208845890485f0a13da5367f1210dfaa3c", size = 143898 },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/d2/63c8725b03d4f4bb1387594ed2cf93d1d544a6216ec7c374536ebfe6bf2a/praisonaiagents-0.0.95-py3-none-any.whl", hash = "sha256:04528314fd23240b4cb388149d8f181714753e6a3b68d429de46f66fc2c94857", size = 145811 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user