Compare commits

...
15 Commits
Author SHA1 Message Date
Mervin Praison 3be4c4c70e Merge pull request #571 from MervinPraison/develop
chore: update PraisonAI version to 2.2.24 across Dockerfiles and docu…
2025-06-01 05:38:39 +01:00
MervinPraison 709640240f chore: update PraisonAI version to 2.2.24 across Dockerfiles and documentation
- Updated the version of PraisonAI from 2.2.23 to 2.2.24 in all relevant Dockerfiles.
- Adjusted version references in README.md and various documentation files.
- Ensured consistency in package versioning for improved stability and performance.
2025-06-01 05:36:48 +01:00
Mervin Praison 93d526eb7b Merge pull request #570 from MervinPraison/claude/issue-122-20250601_041558
feat: add --merge CLI parameter for merging agents.yaml files
2025-06-01 05:33:20 +01:00
Mervin Praison 624a2cfa17 Merge pull request #567 from MervinPraison/claude/issue-121-20250531_210355
fix: prevent CLI conflicts when PraisonAI is used as library
2025-06-01 05:30:06 +01:00
Mervin Praison 4d2a6a2e2e Merge pull request #569 from MervinPraison/claude/issue-466-20250601_041120
feat: Add Mem0 built-in reranker support to Knowledge Agent
2025-06-01 05:29:01 +01:00
claude[bot]andMervinPraison a1bdb056a0 refactor: use argparse.Namespace instead of custom DefaultArgs class
Replace the custom DefaultArgs class with argparse.Namespace for better 
maintainability and consistency with argparse conventions. This addresses
the code review suggestion to use idiomatic Python patterns and avoid
duplication of argument handling logic.

Co-authored-by: MervinPraison <[email protected]>
2025-06-01 04:27:44 +00:00
Mervin Praison b4a6401bae Merge pull request #568 from MervinPraison/claude/issue-460-20250601_040421
fix: resolve MCP function signature parameter ordering issue
2025-06-01 05:25:20 +01:00
claude[bot]andMervinPraison 772ce7d2e2 feat: add --merge CLI parameter for merging agents.yaml files
- Add --merge flag to CLI with description for merging behavior
- Default behavior remains overwrite (backwards compatible)
- When --merge is used, preserves existing agents and adds new ones
- Implements intelligent conflict resolution by renaming duplicates
- Combines topics when both existing and new topics are present
- Added comprehensive error handling for invalid YAML files

Usage:
- praisonai --init "topic" (overwrites existing agents.yaml)
- praisonai --init "topic" --merge (merges with existing agents.yaml)

Co-authored-by: MervinPraison <[email protected]>
2025-06-01 04:19:11 +00:00
claude[bot]andMervinPraison 20c2d28f35 feat: Add Mem0 built-in reranker support to Knowledge Agent
- Add rerank parameter to Knowledge.search() method with backward compatibility
- Update memory search methods to pass rerank parameter to Mem0
- Add reranker configuration options with enabled/default_rerank settings
- Support all Mem0 advanced retrieval options (keyword_search, filter_memories)
- Create comprehensive example demonstrating reranker usage and performance
- Maintains full backward compatibility - existing code works unchanged
- Addresses issue #466 for improved RAG relevance with minimal latency impact

Co-authored-by: MervinPraison <[email protected]>
2025-06-01 04:14:24 +00:00
claude[bot]andMervinPraison f8589e4363 fix: resolve MCP function signature parameter ordering issue
Fix ValueError "non-default argument follows default argument" in MCP tool 
wrapper creation by ensuring required parameters come before optional 
parameters in function signatures.

- Separate required and optional parameters during signature creation
- Maintain backward compatibility and preserve all functionality
- Resolves GitHub MCP server initialization failures

Fixes #460

Co-authored-by: MervinPraison <[email protected]>
2025-06-01 04:05:36 +00:00
Mervin Praison 49d9d40199 Merge pull request #565 from MervinPraison/claude/issue-97-20250531_205330
fix: implement litellm handling in code.py same as llm.py
2025-06-01 04:27:46 +01:00
claude[bot]andMervinPraison 445cc037ce fix: prevent CLI conflicts when PraisonAI is used as library
Adds minimal detection in parse_args() to skip CLI parsing when imported
as a library (e.g., by Fabric). Returns default args instead of trying
to parse external application's CLI arguments.

- Only 20 lines added to fix the specific Fabric compatibility issue
- Zero breaking changes - all existing CLI and library usage preserved
- Addresses the root cause from issue #121

Co-authored-by: MervinPraison <[email protected]>
2025-05-31 21:07:12 +00:00
claude[bot]andMervinPraison 9e7c172f55 fix: implement litellm handling in code.py same as llm.py
Apply minimal changes to fix LiteLLM provider error:
- Add litellm configuration with drop_params and modify_params
- Add _build_completion_params() helper function
- Update acompletion calls to use proper parameter handling

This prevents "list index out of range" errors when using model names
like "gemini" without provider prefix by leveraging litellm's built-in
parameter validation and modification capabilities.

Fixes #97

Co-authored-by: MervinPraison <[email protected]>
2025-05-31 20:55:14 +00:00
Mervin Praison d89570b085 Merge pull request #564 from MervinPraison/develop
chore: update PraisonAI version to 2.2.23 across Dockerfiles and docu…
2025-05-31 17:09:32 +01:00
MervinPraison ee5dfddccd chore: update PraisonAI version to 2.2.23 across Dockerfiles and documentation
- Updated the PraisonAI version from 2.2.22 to 2.2.23 in all relevant Dockerfiles.
- Adjusted version references in README.md, local-development.mdx, and other documentation files.
- Ensured consistency in versioning for deployment scripts and project files.

This change maintains compatibility with the latest features and fixes in PraisonAI.
2025-05-31 17:08:37 +01:00
22 changed files with 406 additions and 72 deletions

No files matched your search

+1 -1
View File
@@ -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
+1 -1
View File
@@ -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]"
+1 -1
View File
@@ -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]" \
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
file.write(&#34;FROM python:3.11-slim\n&#34;)
file.write(&#34;WORKDIR /app\n&#34;)
file.write(&#34;COPY . .\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.2.22 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.2.24 gunicorn markdown\n&#34;)
file.write(&#34;EXPOSE 8080\n&#34;)
file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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 = []
+1 -1
View File
@@ -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 = [
+1 -1
View File
@@ -2233,7 +2233,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.93"
version = "0.0.95"
source = { editable = "." }
dependencies = [
{ name = "mcp" },
+2 -2
View File
@@ -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]"
+98 -25
View File
@@ -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.
+33 -2
View File
@@ -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:
+1 -1
View File
@@ -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')
+32 -9
View File
@@ -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}")
+4 -4
View File
@@ -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"
+5 -5
View File
@@ -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]]