Compare commits

...
9 Commits
Author SHA1 Message Date
Mervin Praison fe4550d2bc Merge pull request #315 from MervinPraison/develop
2.0.54
2025-01-21 15:01:57 +00:00
MervinPraison db09a94425 Update PraisonAI to version 2.0.54 and enhance agent functionality
- Bumped PraisonAI version from 2.0.53 to 2.0.54 in `Dockerfile`, `pyproject.toml`, and `uv.lock` to reflect the latest updates.
- Updated the `praisonai.rb` formula to point to the new version's tarball.
- Updated the `praisonaiagents` dependency version from `0.0.44` to `0.0.45` across relevant files for improved functionality.
- Removed the obsolete `knowledge-embedding-agents.py` file and introduced a new `vector-db-agents.py` file to enhance knowledge retrieval capabilities.
- Added new documentation for RAG (Retrieval Augmented Generation) agents, detailing their configuration and usage in `docs/features/rag.mdx`.
- Updated deployment scripts to ensure compatibility with the new version and improved agent functionalities.

These changes improve the overall functionality and usability of the PraisonAI framework, particularly in knowledge management and retrieval capabilities.
2025-01-21 15:01:12 +00:00
MervinPraison 2db36dc932 Enhance Knowledge Agent Configuration and Update Version
- Updated the Knowledge Agent configuration to include a new versioning system and improved vector store settings.
- Changed the collection name from "custom_knowledge" to "praison" for consistency across files.
- Added user_id parameter to the Knowledge Agent for better user tracking.
- Enhanced the embedding database configuration to support dynamic context management.
- Bumped the version of the `praisonaiagents` package from 0.0.44 to 0.0.45 in `pyproject.toml` to reflect these updates.

These changes improve the functionality and usability of the Knowledge Agent within the PraisonAI framework, particularly in knowledge management and retrieval capabilities.
2025-01-21 13:09:41 +00:00
Mervin Praison 8c0e4277a4 Merge pull request #313 from MervinPraison/develop
Update PraisonAI to version 2.0.53 and enhance agent functionality
2025-01-20 10:04:38 +00:00
MervinPraison 41448617d5 Update PraisonAI to version 2.0.53 and enhance agent functionality
- Bumped PraisonAI version from 2.0.52 to 2.0.53 in `Dockerfile`, `pyproject.toml`, and `uv.lock` to reflect the latest updates.
- Updated the `praisonai.rb` formula to point to the new version's tarball.
- Updated the `praisonaiagents` dependency version from `0.0.43` to `0.0.44` across relevant files for improved functionality.
- Enhanced the `PraisonAIAgents` class to include a `max_iter` parameter, allowing for more control over agent iterations.
- Added new tasks to `agents/tasks.csv` to expand the capabilities of the agents in handling various writing tasks.
- Updated documentation to reflect changes in agent functionality and new task examples.

These changes improve the overall functionality and usability of the PraisonAI framework, particularly in task execution and agent capabilities.
2025-01-20 10:04:20 +00:00
Mervin Praison 49ea23c676 Merge pull request #312 from MervinPraison/develop
Introducing Repetitive Agents
2025-01-20 07:18:19 +00:00
MervinPraison bb0dc63680 Update PraisonAI to version 2.0.52 and enhance repetitive agents functionality
- Bumped PraisonAI version from 2.0.51 to 2.0.52 in `Dockerfile`, `pyproject.toml`, and `uv.lock` to reflect the latest updates.
- Updated the `praisonai.rb` formula to point to the new version's tarball.
- Updated the `praisonaiagents` dependency version from `0.0.42` to `0.0.43` across relevant files for improved functionality.
- Introduced new files for repetitive agents in `agents/repetitive-agents.py` and `examples/concepts/repetitive-agents.py`, enabling automated task loops.
- Enhanced documentation with a new section on repetitive agents in `docs/features/repetitive.mdx` and updated `mint.json` to include the new feature.

These changes improve the overall functionality and usability of the PraisonAI framework, particularly in handling repetitive tasks and enhancing agent capabilities.
2025-01-20 07:17:43 +00:00
MervinPraison 7774f2c565 Add loop agent functionality and update task management in PraisonAI agents
- Introduced a new loop agent in `loop_agents.py` to create and manage a loop of tasks from a CSV file.
- Added a new file `repetitive-manual-agents.py` that implements task identification, status checking, and updating functionalities.
- Updated `process.py` to enhance task handling by allowing dynamic input files for loop tasks.
- Removed the obsolete `repeat_agents.py` file to streamline the codebase.
- Bumped the version of the `praisonaiagents` package from 0.0.42 to 0.0.43 in `pyproject.toml` and `uv.lock` to reflect these changes.

These updates improve the task execution capabilities of the PraisonAI framework, particularly in handling repetitive tasks and enhancing agent functionality.
2025-01-20 06:56:33 +00:00
MervinPraison 5533fd3476 Enhance logging and improve decision/loop task handling in PraisonAI agents
- Suppressed debug logs from the markdown parser to reduce noise in the logging output.
- Refactored decision-making logic to utilize Pydantic models for better type safety and clarity.
- Updated loop task handling to ensure proper context management and error handling.
- Introduced dynamic literal types for decision and loop tasks to enhance flexibility in task definitions.

These changes improve the overall robustness and usability of the PraisonAI framework, particularly in task execution and logging clarity.
2025-01-19 05:58:46 +00:00
35 changed files with 1792 additions and 118 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.51 gunicorn markdown
RUN pip install flask praisonai==2.0.54 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
+43
View File
@@ -0,0 +1,43 @@
FROM mcr.microsoft.com/devcontainers/python:3.11
# Prevent apt from showing prompts
ENV DEBIAN_FRONTEND=noninteractive
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Create a non-root user
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID
# Create the user and group only if they don't exist
RUN if ! getent group $USER_GID > /dev/null 2>&1; then \
groupadd --gid $USER_GID $USERNAME; \
fi && \
if ! getent passwd $USER_UID > /dev/null 2>&1; then \
useradd --uid $USER_UID --gid $USER_GID -m $USERNAME; \
fi && \
apt-get update && \
apt-get install -y sudo && \
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \
chmod 0440 /etc/sudoers.d/$USERNAME
# Set Python environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# Install Python dependencies
COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
&& rm -rf /tmp/pip-tmp
# Switch to non-root user
USER $USERNAME
# Set the default shell to bash
ENV SHELL=/bin/bash
+36
View File
@@ -0,0 +1,36 @@
{
"name": "PraisonAI Development",
"dockerFile": "Dockerfile",
"context": "..",
"runArgs": [
"--env-file",
".env",
"--network=host"
],
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"ms-toolsai.jupyter",
"yzhang.markdown-all-in-one",
"streetsidesoftware.code-spell-checker"
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.formatting.provider": "black",
"editor.formatOnSave": true,
"editor.rulers": [88],
"files.trimTrailingWhitespace": true
}
}
},
"remoteUser": "vscode",
"updateRemoteUserUID": false,
"forwardPorts": [],
"appPort": ["56040:56040"],
"overrideCommand": false,
"init": true
}
+28 -6
View File
@@ -1,6 +1,7 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
import logging
import os
import json
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -8,11 +9,29 @@ logger = logging.getLogger(__name__)
# Define the configuration for the Knowledge instance
config = {
"version": "v1.1",
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "knowledge_test",
"path": ".praison",
"collection_name": "praison",
"path": os.path.abspath("./.praison"),
"host": None,
"port": None
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small",
"embedding_dims": 1536
}
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-4o-mini",
"temperature": 0,
"max_tokens": 1000
}
}
}
@@ -23,17 +42,20 @@ knowledge_agent = Agent(
role="Information Specialist",
goal="Store and retrieve knowledge efficiently",
backstory="Expert in managing and utilizing stored knowledge",
knowledge=["sample.pdf"],
knowledge_config=config,
verbose=True
)
# Define a task for the agent
knowledge_task = Task(
name="knowledge_task",
description="Who is Mervin Praison?",
description="KAG",
expected_output="Answer to the question",
agent=knowledge_agent
agent=knowledge_agent,
context=[
{
"embedding_db_config": json.dumps(config["vector_store"])
}
]
)
# Create and start the agents
+3 -2
View File
@@ -4,7 +4,7 @@ config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "custom_knowledge",
"collection_name": "praison",
"path": ".praison",
}
}
@@ -14,7 +14,8 @@ agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["large.pdf"],
knowledge_config=config
knowledge_config=config,
user_id="user1"
)
agent.start("What is KAG in one line?")
+44
View File
@@ -0,0 +1,44 @@
import logging
from praisonaiagents.knowledge import Knowledge
import os
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
config = {
"version": "v1.1",
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": os.path.abspath("./.praison"),
"host": None,
"port": None
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small",
"embedding_dims": 1536
}
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-4o-mini",
"temperature": 0,
"max_tokens": 1000
}
}
}
knowledge = Knowledge(config)
# Search for memories based on a query
query = "KAG"
logger.info(f"Searching for memories with query: {query}")
search_results = knowledge.search(query, user_id="user1")
logger.info(f"Search results: {search_results}")
print(f"\nSearch results for '{query}':")
print(search_results)
+220
View File
@@ -0,0 +1,220 @@
from mem0 import Memory
import logging
import json
import os
import time
from datetime import datetime
from typing import Dict, Any, Optional
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class MemoryManager:
def __init__(self):
"""Initialize Mem0 Memory manager with configuration"""
# Define the complete configuration with only supported fields
config = {
"version": "v1.1",
"custom_prompt": None,
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": os.path.abspath("./.praison/storage"),
"host": None,
"port": None
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small",
"embedding_dims": 1536
}
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-4o-mini",
"temperature": 0,
"max_tokens": 1000
}
}
}
# Ensure storage directory exists
storage_path = os.path.abspath("./.praison/storage")
if os.path.exists(storage_path):
logger.info(f"Clearing existing storage at {storage_path}")
import shutil
shutil.rmtree(storage_path)
os.makedirs(storage_path, exist_ok=True)
logger.info(f"Created storage path: {storage_path}")
# Initialize memory
self.memory = Memory.from_config(config)
logger.info("Memory manager initialized")
def pretty_print(self, data: Any) -> None:
"""Helper function to print memory data in a readable format"""
if isinstance(data, (dict, list)):
print(json.dumps(data, indent=2))
else:
print(data)
def extract_memory_id(self, result: Any) -> Optional[str]:
"""Extract memory ID from the result, handling different formats"""
try:
if isinstance(result, dict):
# v1.1 format
memories = result.get('results', [])
return memories[0]['id'] if memories else None
elif isinstance(result, list):
# v1.0 format
return result[0]['id'] if result else None
return None
except (KeyError, IndexError) as e:
logger.warning(f"Could not extract memory ID from result: {e}")
return None
def prepare_metadata(self, metadata: Optional[Dict]) -> Dict:
"""Prepare metadata by converting values to acceptable types"""
if not metadata:
return {}
clean_metadata = {}
for key, value in metadata.items():
if isinstance(value, (str, int, float, bool)):
clean_metadata[key] = value
elif isinstance(value, list):
clean_metadata[key] = ','.join(map(str, value))
elif isinstance(value, dict):
clean_metadata[key] = json.dumps(value)
else:
clean_metadata[key] = str(value)
return clean_metadata
def verify_memory_addition(self, user_id: str, max_retries: int = 3) -> bool:
"""Verify that memory was added successfully"""
for attempt in range(max_retries):
try:
time.sleep(0.5) # Brief pause to allow indexing
memories = self.memory.get_all(user_id=user_id)
if len(memories.get('results', [])) > 0:
return True
logger.warning(f"Memory not found on attempt {attempt + 1}")
except Exception as e:
logger.warning(f"Verification attempt {attempt + 1} failed: {e}")
if attempt < max_retries - 1:
time.sleep(1)
return False
def add_memory(self, text: str, user_id: Optional[str] = None, metadata: Optional[Dict] = None) -> Dict:
"""Add a new memory with error handling and verification"""
try:
logger.info(f"Adding memory for user {user_id}")
clean_metadata = self.prepare_metadata(metadata)
logger.debug(f"Prepared metadata: {clean_metadata}")
messages = [{"role": "user", "content": text}]
result = self.memory.add(
messages=messages,
user_id=user_id,
metadata=clean_metadata
)
if self.verify_memory_addition(user_id):
logger.info("Memory added and verified successfully")
else:
logger.warning("Memory addition could not be verified")
return result
except Exception as e:
logger.error(f"Failed to add memory: {str(e)}")
raise
def get_all_memories(self, user_id: Optional[str] = None) -> Dict:
"""Retrieve all memories for a user"""
try:
logger.info(f"Retrieving all memories for user {user_id}")
memories = self.memory.get_all(user_id=user_id)
count = len(memories.get('results', []))
logger.info(f"Retrieved {count} memories")
return memories
except Exception as e:
logger.error(f"Failed to retrieve memories: {str(e)}")
raise
def search_memories(self, query: str, user_id: Optional[str] = None, limit: int = 5) -> Dict:
"""Search memories with a query"""
try:
logger.info(f"Searching memories with query: {query}")
results = self.memory.search(
query=query,
user_id=user_id,
limit=limit
)
count = len(results.get('results', []))
logger.info(f"Found {count} matching memories")
return results
except Exception as e:
logger.error(f"Search failed: {str(e)}")
raise
def main():
try:
# Initialize memory manager
manager = MemoryManager()
# Add a test memory
print("\n1. Adding a new memory:")
metadata = {
"category": "hobbies",
"timestamp": datetime.now().isoformat(),
"tags": "tennis,learning",
"priority": 1,
"source": "user_input"
}
result = manager.add_memory(
text="I am working on improving my tennis skills. Suggest some online courses.",
user_id="alice",
metadata=metadata
)
manager.pretty_print(result)
# Wait briefly for indexing
time.sleep(1)
print("\n2. Retrieving all memories:")
all_memories = manager.get_all_memories(user_id="alice")
manager.pretty_print(all_memories)
print("\n3. Searching memories:")
search_results = manager.search_memories(
query="What are Alice's hobbies?",
user_id="alice",
limit=5
)
manager.pretty_print(search_results)
# Print summary
print("\nMemory Store Summary:")
total_memories = len(all_memories.get('results', []))
print(f"Total memories stored: {total_memories}")
if total_memories > 0:
print("Available memories:")
for mem in all_memories['results']:
print(f"- {mem.get('memory', 'No content')}")
except Exception as e:
logger.error(f"Main process failed: {str(e)}", exc_info=True)
raise
if __name__ == "__main__":
main()
+143
View File
@@ -0,0 +1,143 @@
from mem0 import Memory
import logging
import json
import os
from typing import Dict, Any, Optional
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class MemorySearcher:
def __init__(self):
"""Initialize Mem0 Memory searcher with configuration"""
# Define the configuration
config = {
"version": "v1.1",
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": os.path.abspath("./.praison/storage"),
"host": None,
"port": None
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small",
"embedding_dims": 1536
}
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-4o-mini",
"temperature": 0,
"max_tokens": 1000
}
}
}
# Initialize memory
self.memory = Memory.from_config(config)
logger.info("Memory searcher initialized")
def pretty_print(self, data: Any) -> None:
"""Helper function to print data in a readable format"""
if isinstance(data, (dict, list)):
print(json.dumps(data, indent=2))
else:
print(data)
def get_all_memories(self, user_id: Optional[str] = None) -> Dict:
"""Retrieve all memories for a user"""
try:
logger.info(f"Retrieving all memories for user {user_id}")
memories = self.memory.get_all(user_id=user_id)
count = len(memories.get('results', []))
logger.info(f"Retrieved {count} memories")
return memories
except Exception as e:
logger.error(f"Failed to retrieve memories: {str(e)}")
raise
def search_memories(self, query: str, user_id: Optional[str] = None, limit: int = 5) -> Dict:
"""Search memories with a query"""
try:
logger.info(f"Searching memories with query: {query}")
results = self.memory.search(
query=query,
user_id=user_id,
limit=limit
)
count = len(results.get('results', []))
logger.info(f"Found {count} matching memories")
return results
except Exception as e:
logger.error(f"Search failed: {str(e)}")
raise
def list_unique_users(self) -> list:
"""List all unique users in the memory store"""
try:
all_memories = self.memory.get_all()
users = set()
for memory in all_memories.get('results', []):
if 'user_id' in memory:
users.add(memory['user_id'])
return sorted(list(users))
except Exception as e:
logger.error(f"Failed to list users: {str(e)}")
raise
def main():
try:
# Initialize memory searcher
searcher = MemorySearcher()
# Get list of users
print("\n1. Available users in the memory store:")
users = searcher.list_unique_users()
searcher.pretty_print(users)
if not users:
print("No users found in the memory store. Please add some memories first.")
return
# Example user (using first user found)
example_user = users[0]
print(f"\n2. All memories for user '{example_user}':")
all_memories = searcher.get_all_memories(user_id=example_user)
searcher.pretty_print(all_memories)
print(f"\n3. Searching memories for user '{example_user}':")
search_results = searcher.search_memories(
query="What are their hobbies?",
user_id=example_user,
limit=5
)
searcher.pretty_print(search_results)
# Print summary
print("\nMemory Store Summary:")
total_memories = len(all_memories.get('results', []))
print(f"Total memories for {example_user}: {total_memories}")
if total_memories > 0:
print("Available memories:")
for mem in all_memories['results']:
print(f"- {mem.get('memory', 'No content')}")
if 'metadata' in mem:
print(f" Metadata: {mem['metadata']}")
except Exception as e:
logger.error(f"Main process failed: {str(e)}", exc_info=True)
raise
if __name__ == "__main__":
main()
+26
View File
@@ -0,0 +1,26 @@
from mem0 import Memory
import os
import json
# Basic configuration
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison",
}
}
}
# Initialize memory
memory = Memory.from_config(config)
# Search Alice's hobbies
search_results = memory.search(
query="KAG",
user_id="user1",
limit=5
)
print(search_results)
+46
View File
@@ -0,0 +1,46 @@
from mem0 import Memory
import os
import json
# Basic configuration
config = {
"version": "v1.1",
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": os.path.abspath("./.praison/storage")
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small"
}
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-4o-mini"
}
}
}
# Initialize memory
memory = Memory.from_config(config)
# Search Alice's hobbies
search_results = memory.search(
query="What are Alice's hobbies?",
user_id="alice",
limit=5
)
# Print results in a readable format
print("\nAlice's Memories:")
for memory in search_results['results']:
print(f"\nMemory: {memory['memory']}")
print(f"Score: {memory['score']}")
if 'metadata' in memory:
print("Metadata:")
print(json.dumps(memory['metadata'], indent=2))
+43 -4
View File
@@ -45,12 +45,13 @@ def process_video(video_path: str, seconds_per_frame=2):
return base64_frames
class PraisonAIAgents:
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None, memory=False, memory_config=None, embedder=None, user_id=None):
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None, memory=False, memory_config=None, embedder=None, user_id=None, max_iter=10):
if not agents:
raise ValueError("At least one agent must be provided")
self.run_id = str(uuid.uuid4()) # Auto-generate run_id
self.user_id = user_id # Optional user_id
self.user_id = user_id or "praison" # Optional user_id
self.max_iter = max_iter # Add max_iter parameter
# Pass user_id to each agent
for agent in agents:
@@ -244,6 +245,24 @@ Expected Output: {task.expected_output}.
context_results.append(
f"Previous task {context_item.name if context_item.name else context_item.description} has no result yet."
)
elif isinstance(context_item, dict) and "vector_store" in context_item:
from ..knowledge.knowledge import Knowledge
try:
# Handle both string and dict configs
cfg = context_item["vector_store"]
if isinstance(cfg, str):
cfg = json.loads(cfg)
knowledge = Knowledge(config={"vector_store": cfg}, verbose=self.verbose)
# Only use user_id as filter
db_results = knowledge.search(
task.description,
user_id=self.user_id if self.user_id else None
)
context_results.append(f"[DB Context]: {str(db_results)}")
except Exception as e:
context_results.append(f"[Vector DB Error]: {e}")
# Join unique context results
unique_contexts = list(dict.fromkeys(context_results)) # Remove duplicates
@@ -402,7 +421,8 @@ Context:
tasks=self.tasks,
agents=self.agents,
manager_llm=self.manager_llm,
verbose=self.verbose
verbose=self.verbose,
max_iter=self.max_iter
)
if self.process == "workflow":
@@ -521,6 +541,24 @@ Expected Output: {task.expected_output}.
context_results.append(
f"Previous task {context_item.name if context_item.name else context_item.description} has no result yet."
)
elif isinstance(context_item, dict) and "vector_store" in context_item:
from ..knowledge.knowledge import Knowledge
try:
# Handle both string and dict configs
cfg = context_item["vector_store"]
if isinstance(cfg, str):
cfg = json.loads(cfg)
knowledge = Knowledge(config={"vector_store": cfg}, verbose=self.verbose)
# Only use user_id as filter
db_results = knowledge.search(
task.description,
user_id=self.user_id if self.user_id else None
)
context_results.append(f"[DB Context]: {str(db_results)}")
except Exception as e:
context_results.append(f"[Vector DB Error]: {e}")
# Join unique context results
unique_contexts = list(dict.fromkeys(context_results)) # Remove duplicates
@@ -710,7 +748,8 @@ Context:
tasks=self.tasks,
agents=self.agents,
manager_llm=self.manager_llm,
verbose=self.verbose
verbose=self.verbose,
max_iter=self.max_iter
)
if self.process == "workflow":
+29 -11
View File
@@ -84,8 +84,8 @@ class Knowledge:
@cached_property
def config(self):
# Generate unique collection name for each instance
collection_name = f"test_{int(time.time())}_{str(uuid.uuid4())[:8]}"
# Generate unique collection name for each instance (only if not provided in config)
default_collection = f"test_{int(time.time())}_{str(uuid.uuid4())[:8]}"
persist_dir = ".praison"
# Create persistent client config
@@ -93,9 +93,11 @@ class Knowledge:
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": collection_name,
"collection_name": default_collection,
"path": persist_dir,
"client": self._deps['chromadb'].PersistentClient(path=persist_dir)
"client": self._deps['chromadb'].PersistentClient(path=persist_dir),
"host": None,
"port": None
}
},
"version": "v1.1",
@@ -104,13 +106,29 @@ class Knowledge:
# If config is provided, merge it with base config
if self._config:
if "vector_store" in self._config and "config" in self._config["vector_store"]:
config_copy = self._config["vector_store"]["config"].copy()
for key in ["collection_name", "client"]:
if key in config_copy:
del config_copy[key]
base_config["vector_store"]["config"].update(config_copy)
# Merge version if provided
if "version" in self._config:
base_config["version"] = self._config["version"]
# Merge vector_store config
if "vector_store" in self._config:
if "provider" in self._config["vector_store"]:
base_config["vector_store"]["provider"] = self._config["vector_store"]["provider"]
if "config" in self._config["vector_store"]:
config_copy = self._config["vector_store"]["config"].copy()
# Only exclude client as it's managed internally
if "client" in config_copy:
del config_copy["client"]
base_config["vector_store"]["config"].update(config_copy)
# Merge embedder config if provided
if "embedder" in self._config:
base_config["embedder"] = self._config["embedder"]
# Merge llm config if provided
if "llm" in self._config:
base_config["llm"] = self._config["llm"]
return base_config
@cached_property
+4
View File
@@ -23,6 +23,10 @@ logging.basicConfig(
handlers=[RichHandler(rich_tracebacks=True)]
)
# Add these lines to suppress markdown parser debug logs
logging.getLogger('markdown_it').setLevel(logging.WARNING)
logging.getLogger('rich.markdown').setLevel(logging.WARNING)
# Global list to store error logs
error_logs = []
+157 -76
View File
@@ -5,6 +5,8 @@ from pydantic import BaseModel
from ..agent.agent import Agent
from ..task.task import Task
from ..main import display_error, client
import csv
import os
class LoopItems(BaseModel):
items: List[Any]
@@ -62,46 +64,47 @@ class Process:
if prev_task and prev_task.result:
# Handle loop data
if current_task.task_type == "loop":
# create a loop manager Agent
loop_manager = Agent(
name="Loop Manager",
role="Loop data processor",
goal="Process loop data and convert it to list format",
backstory="Expert at handling loop data and converting it to proper format",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True
)
# # create a loop manager Agent
# loop_manager = Agent(
# name="Loop Manager",
# role="Loop data processor",
# goal="Process loop data and convert it to list format",
# backstory="Expert at handling loop data and converting it to proper format",
# llm=self.manager_llm,
# verbose=self.verbose,
# markdown=True
# )
# get the loop data convert it to list using calling Agent class chat
loop_prompt = f"""
Process this data into a list format:
{prev_task.result.raw}
# # get the loop data convert it to list using calling Agent class chat
# loop_prompt = f"""
# Process this data into a list format:
# {prev_task.result.raw}
Return a JSON object with an 'items' array containing the items to process.
"""
if current_task.async_execution:
loop_data_str = await loop_manager.achat(
prompt=loop_prompt,
output_json=LoopItems
)
else:
loop_data_str = loop_manager.chat(
prompt=loop_prompt,
output_json=LoopItems
)
# Return a JSON object with an 'items' array containing the items to process.
# """
# if current_task.async_execution:
# loop_data_str = await loop_manager.achat(
# prompt=loop_prompt,
# output_json=LoopItems
# )
# else:
# loop_data_str = loop_manager.chat(
# prompt=loop_prompt,
# output_json=LoopItems
# )
try:
# The response will already be parsed into LoopItems model
loop_data[f"loop_{current_task.name}"] = {
"items": loop_data_str.items,
"index": 0,
"remaining": len(loop_data_str.items)
}
context += f"\nCurrent loop item: {loop_data_str.items[0]}"
except Exception as e:
display_error(f"Failed to process loop data: {e}")
context += f"\n{prev_name}: {prev_task.result.raw}"
# try:
# # The response will already be parsed into LoopItems model
# loop_data[f"loop_{current_task.name}"] = {
# "items": loop_data_str.items,
# "index": 0,
# "remaining": len(loop_data_str.items)
# }
# context += f"\nCurrent loop item: {loop_data_str.items[0]}"
# except Exception as e:
# display_error(f"Failed to process loop data: {e}")
# context += f"\n{prev_name}: {prev_task.result.raw}"
context += f"\n{prev_name}: {prev_task.result.raw}"
else:
context += f"\n{prev_name}: {prev_task.result.raw}"
@@ -144,13 +147,17 @@ Return a JSON object with an 'items' array containing the items to process.
next_task = None
if current_task and current_task.result:
if current_task.task_type in ["decision", "loop"]:
result = current_task.result.raw.lower()
# MINIMAL CHANGE: use pydantic decision if present
decision_str = current_task.result.raw.lower()
if current_task.result.pydantic and hasattr(current_task.result.pydantic, "decision"):
decision_str = current_task.result.pydantic.decision.lower()
# Check conditions
for condition, tasks in current_task.condition.items():
if condition.lower() in result:
if condition.lower() == decision_str:
# Handle both list and direct string values
task_value = tasks[0] if isinstance(tasks, list) else tasks
if not task_value or task_value == "exit": # If empty or explicit exit
if not task_value or task_value == "exit":
logging.info("Workflow exit condition met, ending workflow")
current_task = None
break
@@ -322,7 +329,76 @@ Provide a JSON with the structure:
if not start_task:
start_task = list(self.tasks.values())[0]
logging.info("No start task marked, using first task")
# If loop type and no input_file, default to tasks.csv
if start_task and start_task.task_type == "loop" and not start_task.input_file:
start_task.input_file = "tasks.csv"
# --- If loop + input_file, read file & create tasks
if start_task and start_task.task_type == "loop" and getattr(start_task, "input_file", None):
try:
file_ext = os.path.splitext(start_task.input_file)[1].lower()
new_tasks = []
if file_ext == ".csv":
# existing CSV reading logic
with open(start_task.input_file, "r", encoding="utf-8") as f:
# Try as simple CSV first
reader = csv.reader(f)
previous_task = None
for i, row in enumerate(reader):
if row: # Skip empty rows
task_desc = row[0] # Take first column
row_task = Task(
description=task_desc, # Keep full row as description
agent=start_task.agent,
name=task_desc, # Use first column as name
is_start=(i == 0),
task_type="task",
condition={
"complete": ["next"],
"retry": ["current"]
}
)
self.tasks[row_task.id] = row_task
new_tasks.append(row_task)
if previous_task:
previous_task.next_tasks = [row_task.name]
previous_task.condition["complete"] = [row_task.name]
previous_task = row_task
else:
# If not CSV, read lines
with open(start_task.input_file, "r", encoding="utf-8") as f:
lines = f.read().splitlines()
previous_task = None
for i, line in enumerate(lines):
row_task = Task(
description=line.strip(),
agent=start_task.agent,
name=line.strip(),
is_start=(i == 0),
task_type="task",
condition={
"complete": ["next"],
"retry": ["current"]
}
)
self.tasks[row_task.id] = row_task
new_tasks.append(row_task)
if previous_task:
previous_task.next_tasks = [row_task.name]
previous_task.condition["complete"] = [row_task.name]
previous_task = row_task
if new_tasks:
start_task = new_tasks[0]
logging.info(f"Created {len(new_tasks)} tasks from: {start_task.input_file}")
except Exception as e:
logging.error(f"Failed to read file tasks: {e}")
# end of the new block
current_task = start_task
visited_tasks = set()
loop_data = {} # Store loop-specific data
@@ -346,40 +422,41 @@ Provide a JSON with the structure:
if prev_task and prev_task.result:
# Handle loop data
if current_task.task_type == "loop":
# create a loop manager Agent
loop_manager = Agent(
name="Loop Manager",
role="Loop data processor",
goal="Process loop data and convert it to list format",
backstory="Expert at handling loop data and converting it to proper format",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True
)
# # create a loop manager Agent
# loop_manager = Agent(
# name="Loop Manager",
# role="Loop data processor",
# goal="Process loop data and convert it to list format",
# backstory="Expert at handling loop data and converting it to proper format",
# llm=self.manager_llm,
# verbose=self.verbose,
# markdown=True
# )
# get the loop data convert it to list using calling Agent class chat
loop_prompt = f"""
Process this data into a list format:
{prev_task.result.raw}
# # get the loop data convert it to list using calling Agent class chat
# loop_prompt = f"""
# Process this data into a list format:
# {prev_task.result.raw}
Return a JSON object with an 'items' array containing the items to process.
"""
loop_data_str = loop_manager.chat(
prompt=loop_prompt,
output_json=LoopItems
)
# Return a JSON object with an 'items' array containing the items to process.
# """
# loop_data_str = loop_manager.chat(
# prompt=loop_prompt,
# output_json=LoopItems
# )
try:
# The response will already be parsed into LoopItems model
loop_data[f"loop_{current_task.name}"] = {
"items": loop_data_str.items,
"index": 0,
"remaining": len(loop_data_str.items)
}
context += f"\nCurrent loop item: {loop_data_str.items[0]}"
except Exception as e:
display_error(f"Failed to process loop data: {e}")
context += f"\n{prev_name}: {prev_task.result.raw}"
# try:
# # The response will already be parsed into LoopItems model
# loop_data[f"loop_{current_task.name}"] = {
# "items": loop_data_str.items,
# "index": 0,
# "remaining": len(loop_data_str.items)
# }
# context += f"\nCurrent loop item: {loop_data_str.items[0]}"
# except Exception as e:
# display_error(f"Failed to process loop data: {e}")
# context += f"\n{prev_name}: {prev_task.result.raw}"
context += f"\n{prev_name}: {prev_task.result.raw}"
else:
context += f"\n{prev_name}: {prev_task.result.raw}"
@@ -396,7 +473,7 @@ Return a JSON object with an 'items' array containing the items to process.
yield task_id
visited_tasks.add(task_id)
# Reset completed task to "not started" so it can run again
# Reset completed task to "not started" so it can run again: Only for workflow because some tasks may be revisited
if self.tasks[task_id].status == "completed":
logging.debug(f"Task {task_id} was completed, resetting to 'not started' for next iteration.")
self.tasks[task_id].status = "not started"
@@ -422,13 +499,17 @@ Return a JSON object with an 'items' array containing the items to process.
next_task = None
if current_task and current_task.result:
if current_task.task_type in ["decision", "loop"]:
result = current_task.result.raw.lower()
# MINIMAL CHANGE: use pydantic decision if present
decision_str = current_task.result.raw.lower()
if current_task.result.pydantic and hasattr(current_task.result.pydantic, "decision"):
decision_str = current_task.result.pydantic.decision.lower()
# Check conditions
for condition, tasks in current_task.condition.items():
if condition.lower() in result:
if condition.lower() == decision_str:
# Handle both list and direct string values
task_value = tasks[0] if isinstance(tasks, list) else tasks
if not task_value or task_value == "exit": # If empty or explicit exit
if not task_value or task_value == "exit":
logging.info("Workflow exit condition met, ending workflow")
current_task = None
break
+45 -2
View File
@@ -1,6 +1,6 @@
import logging
import asyncio
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine, Literal
from pydantic import BaseModel
from ..main import TaskOutput
from ..agent.agent import Agent
@@ -37,8 +37,10 @@ class Task:
is_start: bool = False,
loop_state: Optional[Dict[str, Union[str, int]]] = None,
memory=None,
quality_check=True
quality_check=True,
input_file: Optional[str] = None
):
self.input_file = input_file
self.id = str(uuid.uuid4()) if id is None else str(id)
self.name = name
self.description = description
@@ -83,6 +85,47 @@ class Task:
# Track previous tasks based on next_tasks relationships
self.previous_tasks = []
# If task_type="decision" and output_pydantic is not set
if self.task_type == "decision" and not self.output_pydantic:
from pydantic import BaseModel
from typing import Literal
# Gather condition keys for the "decision" field
condition_keys = list(self.condition.keys())
if not condition_keys:
# Fall back to placeholders if nothing is specified
condition_keys = ["next_task", "exit"]
# Create a dynamic literal type from condition keys
DecisionLiteral = Literal.__getitem__(tuple(condition_keys))
class DecisionModel(BaseModel):
response: str
decision: DecisionLiteral
self.output_pydantic = DecisionModel
# If task_type="loop" and output_pydantic is not set
if self.task_type == "loop" and not self.output_pydantic:
from pydantic import BaseModel
from typing import Literal
# Gather condition keys for the "decision" field
condition_keys = list(self.condition.keys())
if not condition_keys:
# Fall back to placeholders if nothing is specified
condition_keys = ["next_item", "exit"]
# Create a dynamic literal type
LoopLiteral = Literal.__getitem__(tuple(condition_keys))
class LoopModel(BaseModel):
response: str
decision: LoopLiteral
loop_id: str # Additional field for loop
self.output_pydantic = LoopModel
def __str__(self):
return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.42"
version = "0.0.45"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
authors = [
{ name="Mervin Praison" }
+22
View File
@@ -0,0 +1,22 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
agent = Agent(
instructions="You are a loop agent that creating a loop of tasks.",
llm="gpt-4o-mini"
)
task = Task(
description="Create the list of tasks to be looped through.",
agent=agent,
task_type="loop",
input_file="tasks.csv"
)
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="workflow",
max_iter=30
)
agents.start()
+139
View File
@@ -0,0 +1,139 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import read_csv, write_csv
import pandas as pd
def read_csv_smart(csv_path: str) -> pd.DataFrame:
"""Read CSV and use first column as task, handling both header/no-header cases"""
# Try reading with headers first
df = pd.read_csv(csv_path)
# Check if first row looks like data
if not any(col.lower() in ['task', 'description', 'status', 'state'] for col in df.columns):
# If no header-like names found, read without headers
df = pd.read_csv(csv_path, header=None)
# Rename columns consistently
df.columns = [*['task'], *[f'col_{i}' for i in range(1, len(df.columns))]]
if 'status' not in df.columns:
df['status'] = 'pending'
if 'loop_id' not in df.columns:
# Start loop_id from 1 instead of 0
df['loop_id'] = (df.index + 1).astype(str)
return df[['task', 'status', 'loop_id']]
def has_pending_tasks(df: pd.DataFrame) -> bool:
"""Check if there are any pending tasks in the DataFrame"""
return not df[df['status'] == 'pending'].empty
def check_task_status(csv_path: str) -> dict:
"""Read task status from CSV file"""
try:
# Always read directly from CSV file
df = pd.read_csv(csv_path, keep_default_na=False)
# Get only pending tasks from original status
pending_tasks = df[df['status'].str.lower() == 'pending']
if not pending_tasks.empty:
task_data = pending_tasks.iloc[0].to_dict()
return {
'has_pending': True,
'next_task': task_data
}
return {'has_pending': False, 'next_task': None}
except Exception as e:
print(f"Error reading CSV: {e}")
return {'has_pending': False, 'next_task': None, 'error': str(e)}
def get_next_task() -> dict:
"""Get the next pending task from CSV"""
status = check_task_status("tasks.csv")
if status['has_pending'] and status['next_task']:
task = status['next_task']
if task['status'].lower() == 'pending':
return {
'loop_data': {
'task': task['task'],
'loop_id': task['loop_id'],
'status': task['status']
}
}
return None
def update_task_status(loop_id: str, new_status: str) -> bool:
"""Update task status in CSV file using loop_id as identifier"""
try:
# Read directly from CSV file
df = pd.read_csv("tasks.csv", keep_default_na=False)
df['loop_id'] = df['loop_id'].astype(str)
mask = df['loop_id'] == str(loop_id)
if mask.any():
df.loc[mask, 'status'] = new_status
df.to_csv("tasks.csv", index=False)
return True
print(f"No task found with loop_id: {loop_id}")
return False
except Exception as e:
print(f"Error updating status: {e}")
return False
identify_agent = Agent(
name="Identify Agent",
role="Pending Task Identifier",
goal="Identify the next pending task",
tools=[get_next_task],
llm="gpt-4o-mini"
)
# Create the repetitive agent
repetitive_agent = Agent(
name="TaskProcessor",
role="Task Processing Specialist",
goal="Complete the task response",
llm="gpt-4o-mini"
)
update_status_agent = Agent(
name="UpdateStatusAgent",
role="Status Updater",
goal="Update the status of the task in CSV",
tools=[update_task_status],
llm="gpt-4o-mini"
)
# Define the repetitive task
identify_task = Task(
name="identify_task",
description="Identify the next pending task. If there are no pending tasks decision is completed.",
agent=identify_agent,
task_type="loop",
condition={
"pending": ["process_task"],
"completed": "exit"
}
)
process_task = Task(
name="process_task",
description="Complete the task mentioned in the response",
agent=repetitive_agent,
context=[identify_task],
task_type="loop",
next_tasks=["update_task"]
)
update_task = Task(
name="update_task",
description="Update the task status",
agent=update_status_agent,
next_tasks= ["identify_task"]
)
# Create and run agents manager
agents = PraisonAIAgents(
agents=[identify_agent, repetitive_agent],
tasks=[identify_task, process_task, update_task],
process="workflow"
)
agents.start()
+15
View File
@@ -0,0 +1,15 @@
# Core dependencies
pydantic
rich
openai>=1.30.0
# Memory dependencies
chromadb>=0.5.23
# Knowledge dependencies
mem0ai>=0.1.0
markitdown
chonkie
# Development dependencies
python-dotenv>=0.19.0
+100
View File
@@ -0,0 +1,100 @@
Write a product description for a smartwatch in 2 lines.
Create a user manual for a blender in 2 lines.
Draft an apology email for delayed delivery in 2 lines.
Write a blog post on "Benefits of Meditation" in 2 lines.
Create a privacy policy for a website in 2 lines.
Write instructions for setting up a Wi-Fi router in 2 lines.
Draft an email to follow up after a job interview in 2 lines.
Create a FAQ page for an e-commerce store in 2 lines.
Write an invitation email for a webinar in 2 lines.
Create a terms and conditions page for an app in 2 lines.
Write a press release for a product launch in 2 lines.
Create a checklist for house cleaning tasks in 2 lines.
Draft a resignation letter template in 2 lines.
Write a cover letter for a software developer role in 2 lines.
Create onboarding instructions for new employees in 2 lines.
Write a memo announcing a new policy in a company in 2 lines.
Create a safety guidelines document for a workplace in 2 lines.
Write a pitch email to potential investors in 2 lines.
Draft a thank-you email for attending a conference in 2 lines.
Create a refund policy template for an online store in 2 lines.
Write a blog on "How to Improve Time Management" in 2 lines.
Create step-by-step instructions for changing a car tyre in 2 lines.
Write a welcome email for new subscribers in 2 lines.
Draft an event agenda template in 2 lines.
Create a code of conduct for an online forum in 2 lines.
Write a guide for beginners to start a blog in 2 lines.
Create a checklist for organising a party in 2 lines.
Draft an email to request a product review in 2 lines.
Write a policy for remote work in a company in 2 lines.
Create a simple tutorial for using Excel formulas in 2 lines.
Write a thank-you letter for a donation in 2 lines.
Draft a meeting minutes template in 2 lines.
Create an FAQ for a gym membership in 2 lines.
Write a blog post about "Healthy Snacks for Kids" in 2 lines.
Create a guide for securing your social media accounts in 2 lines.
Draft an email to request a testimonial from a client in 2 lines.
Write a safety checklist for camping trips in 2 lines.
Create a script for a customer service call in 2 lines.
Write a tutorial for creating a simple PowerPoint presentation in 2 lines.
Create a packing list for a beach vacation in 2 lines.
Draft an email to cancel a service subscription in 2 lines.
Write a personal statement for university admission in 2 lines.
Create a template for tracking monthly expenses in 2 lines.
Write a guide for planting and caring for succulents in 2 lines.
Create a simple user guide for a mobile app in 2 lines.
Draft an email to announce a team-building activity in 2 lines.
Write a step-by-step guide for baking a cake in 2 lines.
Create a list of house maintenance tasks in 2 lines.
Draft a partnership proposal email in 2 lines.
Write a guide for basic photography tips in 2 lines.
Create a feedback form template for an online course in 2 lines.
Write a blog on "Top 5 Budget Travel Destinations" in 2 lines.
Draft a letter of recommendation for a colleague in 2 lines.
Create a workout plan for beginners in 2 lines.
Write a guide for troubleshooting common tech issues in 2 lines.
Draft a reminder email for an upcoming appointment in 2 lines.
Create a guide for setting personal goals in 2 lines.
Write a blog post about "How to Start Freelancing" in 2 lines.
Create a template for event planning in 2 lines.
Draft an email to notify users about system maintenance in 2 lines.
Write a guide for choosing the right laptop in 2 lines.
Create a checklist for moving to a new house in 2 lines.
Draft an email to update a client on project status in 2 lines.
Write a guide for reducing screen time in 2 lines.
Create a shopping list template for weekly groceries in 2 lines.
Draft a thank-you email for completing a survey in 2 lines.
Write a blog on "Simple DIY Crafts for Beginners" in 2 lines.
Create a checklist for starting a small business in 2 lines.
Draft an email to apologise for a customer complaint in 2 lines.
Write a guide for setting up a personal budget in 2 lines.
Create a policy for using company-provided equipment in 2 lines.
Draft an email to notify about a price increase in 2 lines.
Write a tutorial for creating a resume using Word in 2 lines.
Create a simple guide for organising a charity event in 2 lines.
Draft a formal invitation letter for a conference in 2 lines.
Write a blog post about "Top Productivity Apps" in 2 lines.
Create a checklist for first-time pet owners in 2 lines.
Draft an email to confirm a service appointment in 2 lines.
Write a beginner’s guide for learning to code in 2 lines.
Create a guide for assembling a basic toolkit in 2 lines.
Draft a welcome email for a new team member in 2 lines.
Write a guide for preparing a home for winter in 2 lines.
Create a schedule template for meal planning in 2 lines.
Draft an email to request feedback from a customer in 2 lines.
Write a guide on "How to Build a Morning Routine" in 2 lines.
Create a checklist for hosting a virtual meeting in 2 lines.
Draft a newsletter for a non-profit organisation in 2 lines.
Write a guide for beginners to use Canva in 2 lines.
Create a guide for organising your workspace in 2 lines.
Draft an email to invite users to a beta program in 2 lines.
Write a step-by-step guide for creating a podcast in 2 lines.
Create a simple guide for buying your first car in 2 lines.
Draft an email to announce a new team member in 2 lines.
Write a tutorial on creating basic charts in Excel in 2 lines.
Create a guide for teaching basic first aid in 2 lines.
Draft an email to follow up after a sales call in 2 lines.
Write a blog post on "How to Save Money on Utilities" in 2 lines.
Create a guide for designing a basic website in 2 lines.
Draft a thank-you email for a business referral in 2 lines.
Write a guide for setting up a new smartphone in 2 lines.
Can't render this file because it contains an unexpected character in line 4 and column 22.
+1 -1
View File
@@ -1558,7 +1558,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.42"
version = "0.0.45"
source = { editable = "." }
dependencies = [
{ name = "openai" },
+39
View File
@@ -0,0 +1,39 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
# Define the configuration for the Knowledge instance
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison"
}
}
}
# Create an agent
rag_agent = Agent(
name="RAG Agent",
role="Information Specialist",
goal="Retrieve knowledge efficiently",
llm="gpt-4o-mini"
)
# Define a task for the agent
rag_task = Task(
name="RAG Task",
description="What is KAG?",
expected_output="Answer to the question",
agent=rag_agent,
context=[config] # Vector Database provided as context
)
# Build Agents
agents = PraisonAIAgents(
agents=[rag_agent],
tasks=[rag_task],
user_id="user1"
)
# Start Agents
agents.start()
+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.0.51 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.54 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;)
+352
View File
@@ -0,0 +1,352 @@
---
title: "RAG Agents"
description: "Learn how to create AI agents that can efficiently interact with vector databases for knowledge retrieval and storage."
icon: "database"
---
```mermaid
flowchart LR
In[Input] --> RAGAgent[("RAG Agent")]
VDB[(Vector DB)] --> RAGAgent
RAGAgent --> Task[Knowledge Task]
Task --> |Query| VDB
Task --> Out[Output]
style In fill:#8B0000,color:#fff
style RAGAgent fill:#2E8B57,color:#fff,shape:circle
style VDB fill:#4169E1,color:#fff,shape:cylinder
style Task fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
A knowledge-centric workflow where RAG (Retrieval Augmented Generation) agents interact with vector databases to store and retrieve information efficiently, enabling sophisticated question-answering and information retrieval capabilities.
## Quick Start
<Steps>
<Step title="Install Package">
Install PraisonAI Agents with knowledge support:
```bash
pip install "praisonaiagents[knowledge]"
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key:
```bash
export OPENAI_API_KEY=xxxxx
```
</Step>
<Step title="Create Script">
Create a new file `app.py`:
```python
from praisonaiagents import Agent
agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["small.pdf"], # Indexing
)
agent.start("What is KAG in one line?") # Retrieval
```
</Step>
</Steps>
## Data Indexing and Retrieval Agents
<Note>
Indexing and Ingestion are relatively the same.
</Note>
```mermaid
flowchart LR
subgraph In[Input]
PDF[PDF]
TXT[TXT]
MD[MD]
end
subgraph Router[Vector Store]
DB[(Vector DB)]
end
subgraph Out[Agents]
A1[Agent 1]
A2[Agent 2]
A3[Agent 3]
end
In --> Router
Router --> A1
Router --> A2
Router --> A3
style In fill:#8B0000,color:#fff
style Router fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
The simplest way to create a knowledge-based agent is without any configuration:
```python
from praisonaiagents import Agent
agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["small.pdf"] # Indexing
)
agent.start("What is KAG in one line?") # Retrieval
```
### Advanced Configuration
For more control over the knowledge base, you can specify a configuration:
```python
from praisonaiagents import Agent
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison",
}
}
}
agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["small.pdf"], # Indexing
knowledge_config=config # Configuration
)
agent.start("What is KAG in one line?") # Retrieval
```
### Multi-Agent Knowledge System
For more complex scenarios, you can create a knowledge-based system with multiple agents:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
import logging
import os
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Define the configuration for the Knowledge instance
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison",
}
}
}
# Create an agent with knowledge capabilities
knowledge_agent = Agent(
name="KnowledgeAgent",
role="Information Specialist",
goal="Store and retrieve knowledge efficiently",
backstory="Expert in managing and utilizing stored knowledge",
knowledge=["sample.pdf"], # Indexing
knowledge_config=config, # Configuration
verbose=True
)
# Define a task for the agent
knowledge_task = Task(
name="knowledge_task",
description="Who is Mervin Praison?",
expected_output="Answer to the question",
agent=knowledge_agent # Agent
)
# Create and start the agents
agents = PraisonAIAgents(
agents=[knowledge_agent],
tasks=[knowledge_task],
process="sequential",
user_id="user1" # User ID
)
# Start execution
result = agents.start() # Retrieval
```
## Retrieval Agents
<Note>
Retrieval is the process of querying the vector database for information.
Considering there is data already in the Vector Database.
</Note>
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key as an environment variable in your terminal:
```bash
export OPENAI_API_KEY=your_api_key_here
```
</Step>
<Step title="Create a file">
Create a new file `rag_agent.py` with the basic setup:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
# Define the configuration for the Knowledge instance
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison"
}
}
}
# Create an agent
rag_agent = Agent(
name="RAG Agent",
role="Information Specialist",
goal="Retrieve knowledge efficiently",
llm="gpt-4o-mini"
)
# Define a task for the agent
rag_task = Task(
name="RAG Task",
description="What is KAG?",
expected_output="Answer to the question",
agent=rag_agent,
context=[config] # Retrieval : Vector Database provided as context
)
# Build Agents
agents = PraisonAIAgents(
agents=[rag_agent],
tasks=[rag_task],
user_id="user1"
)
# Start Agents
agents.start()
```
</Step>
<Step title="Start Agents">
Type this in your terminal to run your agents:
```bash
python rag_agent.py
```
</Step>
</Steps>
<Note>
**Requirements**
- Python 3.10 or higher
- OpenAI API key. Generate OpenAI API key [here](https://platform.openai.com/api-keys). Use Other models using [this guide](/models).
- ChromaDB or other supported vector database
</Note>
### Adding Knowledge to RAG Agents
```python
from praisonaiagents import Agent
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison",
}
}
}
agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["small.pdf"], # Indexing
knowledge_config=config # Configuration
)
agent.start("What is KAG in one line?") # Retrieval
```
## Understanding RAG Agents
<Card title="What are RAG Agents?" icon="question">
RAG (Retrieval Augmented Generation) agents enable:
- Efficient knowledge retrieval
- Semantic search capabilities
- Persistent knowledge storage
- Context-aware responses
</Card>
## Features
<CardGroup cols={2}>
<Card title="RAG Architecture" icon="database">
Store and manage vector embeddings efficiently.
</Card>
<Card title="Semantic Search" icon="magnifying-glass">
Find relevant information using semantic similarity.
</Card>
<Card title="Knowledge Integration" icon="brain">
Seamlessly integrate with existing knowledge bases.
</Card>
<Card title="Context Management" icon="layer-group">
Handle complex contextual queries and responses.
</Card>
</CardGroup>
## Troubleshooting
<CardGroup cols={2}>
<Card title="RAG Issues" icon="triangle-exclamation">
If RAG system isn't working:
- Check database configuration
- Verify connection settings
- Enable verbose mode for debugging
</Card>
<Card title="Query Issues" icon="gauge-high">
If queries aren't returning expected results:
- Check embedding quality
- Verify search parameters
- Monitor similarity thresholds
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="AutoAgents" icon="robot" href="./autoagents">
Learn about automatically created and managed AI agents
</Card>
<Card title="Mini Agents" icon="microchip" href="./mini">
Explore lightweight, focused AI agents
</Card>
</CardGroup>
<Note>
For optimal results, ensure your vector database is properly configured and indexed for your specific use case.
</Note>
+138
View File
@@ -0,0 +1,138 @@
---
title: "Repetitive Agents"
description: "Learn how to create AI agents that can efficiently handle repetitive tasks through automated loops."
icon: "repeat"
---
```mermaid
flowchart LR
In[Input] --> LoopAgent[("Looping Agent")]
LoopAgent --> Task[Task]
Task --> |Next iteration| LoopAgent
Task --> |Done| Out[Output]
style In fill:#8B0000,color:#fff
style LoopAgent fill:#2E8B57,color:#fff,shape:circle
style Task fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
A workflow optimization pattern where agents handle repetitive tasks through automated loops, processing multiple instances efficiently while maintaining consistency.
## Quick Start
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key as an environment variable in your terminal:
```bash
export OPENAI_API_KEY=your_api_key_here
```
</Step>
<Step title="Create a file">
Create a new file `repetitive_agent.py` with the basic setup:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
agent = Agent(
instructions="You are a loop agent that creating a loop of tasks."
)
task = Task(
description="Create the list of tasks to be looped through.",
agent=agent,
task_type="loop",
input_file="tasks.csv"
)
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="workflow",
max_iter=30
)
agents.start()
```
</Step>
<Step title="Start Agents">
Type this in your terminal to run your agents:
```bash
python repetitive_agent.py
```
</Step>
</Steps>
<Note>
**Requirements**
- Python 3.10 or higher
- OpenAI API key. Generate OpenAI API key [here](https://platform.openai.com/api-keys). Use Other models using [this guide](/models).
</Note>
## Understanding Repetitive Agents
<Card title="What are Repetitive Agents?" icon="question">
Repetitive agents enable:
- Automated task loops
- Batch processing
- Consistent task execution
- Efficient handling of multiple similar tasks
</Card>
## Features
<CardGroup cols={2}>
<Card title="Task Looping" icon="repeat">
Process multiple tasks through automated loops.
</Card>
<Card title="Batch Processing" icon="layer-group">
Handle multiple similar tasks efficiently.
</Card>
<Card title="Input Management" icon="file-csv">
Process tasks from structured input files.
</Card>
<Card title="Progress Tracking" icon="chart-line">
Monitor task completion and progress.
</Card>
</CardGroup>
## Troubleshooting
<CardGroup cols={2}>
<Card title="Loop Issues" icon="triangle-exclamation">
If loops aren't working as expected:
- Verify input file format
- Check task configurations
- Enable verbose mode for debugging
</Card>
<Card title="Performance Issues" icon="gauge-high">
If processing is slow:
- Check batch sizes
- Verify resource allocation
- Monitor memory usage
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="AutoAgents" icon="robot" href="./autoagents">
Learn about automatically created and managed AI agents
</Card>
<Card title="Mini Agents" icon="microchip" href="./mini">
Explore lightweight, focused AI agents
</Card>
</CardGroup>
<Note>
For optimal results, ensure your input files are properly formatted and your task configurations are appropriate for your use case.
</Note>
+3 -1
View File
@@ -108,7 +108,8 @@
"features/autonomous-workflow",
"features/parallelisation",
"features/promptchaining",
"features/evaluator-optimiser"
"features/evaluator-optimiser",
"features/repetitive"
]
},
{
@@ -116,6 +117,7 @@
"pages": [
"features/autoagents",
"features/selfreflection",
"features/rag",
"features/reasoning",
"features/multimodal",
"features/langchain",
+40
View File
@@ -12,6 +12,46 @@ export REDDIT_CLIENT_SECRET=xxxxxx
export REDDIT_CLIENT_ID=xxxxxx
```
[USER] in this format script:APP:v1.0(by/u/USERNAME)
```bash
pip install praisonai langchain_community praw
```
```bash
export OPENAI_API_KEY="Enter your API key"
```
## Code
### Single Agent
```python
from praisonaiagents import Agent
from langchain_community.tools.reddit_search.tool import RedditSearchRun
agent = Agent(name="RedditSearchAgent",instructions="Search Reddit for information", tools=[RedditSearchRun],)
agent.start("Search Reddit for information about the latest AI advancements in subreddit all")
```
### Multi Agents
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from langchain_community.tools.reddit_search.tool import RedditSearchRun
agent = Agent(name="RedditSearchAgent",instructions="Search Reddit for information", tools=[RedditSearchRun],)
task = Task(description="Search Reddit for information about the latest AI news in subreddit all", agent=agent)
agents = PraisonAIAgents(agents=[agent],tasks=[task])
agents.start()
```
## No Code
tools.py
```python
+1 -1
View File
@@ -4,7 +4,7 @@ config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "custom_knowledge",
"collection_name": "praison",
"path": ".praison",
}
}
+39
View File
@@ -0,0 +1,39 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
# Define the configuration for the Knowledge instance
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison"
}
}
}
# Create an agent
rag_agent = Agent(
name="RAG Agent",
role="Information Specialist",
goal="Retrieve knowledge efficiently",
llm="gpt-4o-mini"
)
# Define a task for the agent
rag_task = Task(
name="RAG Task",
description="What is KAG?",
expected_output="Answer to the question",
agent=rag_agent,
context=[config] # Vector Database provided as context
)
# Build Agents
agents = PraisonAIAgents(
agents=[rag_agent],
tasks=[rag_task],
user_id="user1"
)
# Start Agents
agents.start()
+22
View File
@@ -0,0 +1,22 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
agent = Agent(
instructions="You are a loop agent that creating a loop of tasks.",
llm="gpt-4o-mini"
)
task = Task(
description="Create the list of tasks to be looped through.",
agent=agent,
task_type="loop",
input_file="tasks.csv"
)
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="workflow",
max_iter=30
)
agents.start()
Binary file not shown.
+1 -1
View File
@@ -3,7 +3,7 @@ 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/2.0.51.tar.gz"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.54.tar.gz"
sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum
license "MIT"
+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.0.51 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.0.54 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.0.51"
version = "2.0.54"
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.42",
"praisonaiagents>=0.0.45",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -84,7 +84,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.7", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.0.51"
version = "2.0.54"
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 = ""
@@ -102,7 +102,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.42"
praisonaiagents = ">=0.0.45"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
Generated
+5 -5
View File
@@ -3060,7 +3060,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.0.51"
version = "2.0.54"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -3197,7 +3197,7 @@ requires-dist = [
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.7" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.7" },
{ name = "praisonaiagents", specifier = ">=0.0.42" },
{ name = "praisonaiagents", specifier = ">=0.0.45" },
{ 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" },
@@ -3250,16 +3250,16 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.42"
version = "0.0.45"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openai" },
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/da/06/c38a7889fb3ed80786591f25c4852acd1505cafafe59b9994b67255a1282/praisonaiagents-0.0.42.tar.gz", hash = "sha256:7266b13b091f79bdb3b868599eb6b2e4cf11808ecaf3c107a1b33c908e4d003d", size = 75885 }
sdist = { url = "https://files.pythonhosted.org/packages/47/05/9fa3e9c20677bab481df2275d0ce66062a052953fd50aa6aa032379fc090/praisonaiagents-0.0.45.tar.gz", hash = "sha256:4d21caa34dc6e046128bcf17ab0cf37088d1b9c04ac060b08cf8f2a1b61ab0c1", size = 77600 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bf/48/dd42c2887d982ce02b7b7c990bb295ef045db454b277d477dbbd95e9e4cf/praisonaiagents-0.0.42-py3-none-any.whl", hash = "sha256:27cf3c5de305fe54b2192d7fa3d14700b3722852d712fa554314ac62b6d85399", size = 93626 },
{ url = "https://files.pythonhosted.org/packages/c8/eb/cf5477768b740610015e743b9573fd918653267bc567d5c09b11419b22fe/praisonaiagents-0.0.45-py3-none-any.whl", hash = "sha256:b2feba4ff86a06a7010751e28f0b258a297e4219527bf58e38f2c740694c268d", size = 95181 },
]
[[package]]