mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-09 03:39:37 -10:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3c4a2afad | ||
|
|
034138ebc9 | ||
|
|
70b2df135b | ||
|
|
3532600887 | ||
|
|
13ba1ed954 | ||
|
|
4a1fe173e8 | ||
|
|
147bcce448 | ||
|
|
b5e8052809 | ||
|
|
8a93bc5096 | ||
|
|
f65b83614c | ||
|
|
25000d9b26 |
No files matched your search
@@ -60,16 +60,16 @@ jobs:
|
||||
- name: Run PraisonAI integration tests
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
python -m pytest tests/integration/test_aiui_* -v --timeout=300
|
||||
python -m pytest tests/integration/test_aiui_* -v
|
||||
|
||||
- name: Run PraisonAIUI integration tests
|
||||
run: |
|
||||
cd PraisonAIUI
|
||||
if [ -f tests/integration/test_agentic_roundtrip.py ]; then
|
||||
python -m pytest tests/integration/test_agentic_roundtrip.py -v --timeout=300
|
||||
python -m pytest tests/integration/test_agentic_roundtrip.py -v
|
||||
fi
|
||||
if [ -f tests/test_feature_sdk_backends.py ]; then
|
||||
python -m pytest tests/test_feature_sdk_backends.py -v --timeout=300
|
||||
python -m pytest tests/test_feature_sdk_backends.py -v
|
||||
fi
|
||||
|
||||
- name: Test Pattern C CLI
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
run: |
|
||||
cd PraisonAI/src/praisonai
|
||||
if [ -n "$OPENAI_API_KEY" ]; then
|
||||
python -m pytest tests/integration/test_aiui_host_agentic.py -v --timeout=600
|
||||
python -m pytest tests/integration/test_aiui_host_agentic.py -v
|
||||
else
|
||||
echo "OPENAI_API_KEY not available, skipping agentic tests"
|
||||
fi
|
||||
@@ -240,6 +240,7 @@ from praisonaiagents import Memory, MemoryBackend, MemoryConfig
|
||||
|
||||
Methods:
|
||||
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_cache_optimized_context</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_cache_boundary: bool = True, include_in_output: Optional[bool] = None) -> Dict[str, str]</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_context_for_task</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_in_output: Optional[bool] = None) -> str</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">calculate_quality_metrics</a>(output: str, expected_output: str, llm: Optional[str] = None, custom_prompt: Optional[str] = None) -> Dict[str, float]</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">close_connections</a>()</code>
|
||||
@@ -698,7 +699,7 @@ Methods:
|
||||
|
||||
Types:
|
||||
```python
|
||||
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, aembedding, aembeddings, embedding, embeddings, get_dimensions
|
||||
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, __version__, aembedding, aembeddings, embedding, embeddings, get_dimensions
|
||||
```
|
||||
|
||||
Methods:
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.55" \
|
||||
"praisonai>=4.6.56" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.55" \
|
||||
"praisonai>=4.6.56" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=4.6.55" \
|
||||
"praisonai>=4.6.56" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -12,13 +12,8 @@ from praisonaiagents import Agent
|
||||
from praisonaiagents.approval import AgentApproval
|
||||
from praisonaiagents.tools.shell_tools import execute_command
|
||||
|
||||
reviewer_agent = Agent(
|
||||
name="CommandReviewer",
|
||||
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
|
||||
)
|
||||
|
||||
reviewer = AgentApproval(
|
||||
approver_agent=reviewer_agent,
|
||||
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
from praisonaiagents.workflows.workflow_configs import (
|
||||
WorkflowPlanningConfig, WorkflowMemoryConfig,
|
||||
WorkflowOutputConfig, WorkflowPlanningConfig, WorkflowMemoryConfig,
|
||||
)
|
||||
|
||||
# Full-featured workflow with all consolidated params
|
||||
@@ -32,7 +32,7 @@ workflow = AgentFlow(
|
||||
),
|
||||
],
|
||||
# Workflow-level consolidated params
|
||||
output="verbose",
|
||||
output=WorkflowOutputConfig(verbose=True, stream=True),
|
||||
planning=WorkflowPlanningConfig(enabled=True, reasoning=True),
|
||||
memory=WorkflowMemoryConfig(backend="file"),
|
||||
# Agent-like params
|
||||
|
||||
@@ -5,12 +5,12 @@ Demonstrates web search with consolidated params.
|
||||
Presets: duckduckgo, tavily, google, bing, serper, search_only, fetch_only
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents import Agent, WebConfig
|
||||
|
||||
# Basic: Enable web search with preset
|
||||
# Basic: Enable web search with preset (search only — fetch needs Claude)
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant with web access.",
|
||||
web="search_only", # Compatible across models without web_fetch
|
||||
web=WebConfig(search=True, fetch=False, search_provider="duckduckgo"),
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -80,7 +80,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
|
||||
print(f"\nAnswer: {response}\n")
|
||||
|
||||
# Verify the answer contains the unique code
|
||||
if "ZEBRA-71" in str(response or "").upper():
|
||||
if "ZEBRA-71" in response.upper():
|
||||
print("✅ VERIFIED: Agent correctly retrieved the unique code from context!\n")
|
||||
else:
|
||||
print("❌ WARNING: Agent did not find the code - check retrieval\n")
|
||||
@@ -104,7 +104,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
|
||||
response = agent.chat("What is the security incident code?")
|
||||
print(f"\nAnswer: {response}\n")
|
||||
|
||||
if "HAWK-88" in str(response or "").upper():
|
||||
if "HAWK-88" in response.upper():
|
||||
print("✅ VERIFIED: Agent correctly retrieved the security code!\n")
|
||||
else:
|
||||
print("❌ WARNING: Agent did not find the code - check retrieval\n")
|
||||
|
||||
@@ -17,10 +17,8 @@ from praisonaiagents import Agent
|
||||
agent = Agent(
|
||||
name="CockroachDB Agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
memory={
|
||||
"session_id": "crdb-demo-session",
|
||||
"db": {"database_url": os.getenv("COCKROACHDB_URL")},
|
||||
},
|
||||
db={"database_url": os.getenv("COCKROACHDB_URL")},
|
||||
session_id="crdb-demo-session",
|
||||
)
|
||||
|
||||
result = agent.start("What is distributed SQL? Explain in 2 sentences.")
|
||||
|
||||
@@ -18,10 +18,8 @@ from praisonaiagents import Agent
|
||||
agent = Agent(
|
||||
name="Neon Agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
memory={
|
||||
"session_id": "neon-demo-session",
|
||||
"db": {"database_url": os.getenv("NEON_DATABASE_URL")},
|
||||
},
|
||||
db={"database_url": os.getenv("NEON_DATABASE_URL")},
|
||||
session_id="neon-demo-session",
|
||||
)
|
||||
|
||||
# Option 2: Direct URL
|
||||
|
||||
@@ -20,10 +20,8 @@ from praisonaiagents import Agent
|
||||
agent = Agent(
|
||||
name="Supabase Agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
memory={
|
||||
"session_id": "supabase-demo-session",
|
||||
"db": {"database_url": os.getenv("SUPABASE_DATABASE_URL")},
|
||||
},
|
||||
db={"database_url": os.getenv("SUPABASE_DATABASE_URL")},
|
||||
session_id="supabase-demo-session",
|
||||
)
|
||||
|
||||
result = agent.start("What is Supabase? Explain in 2 sentences.")
|
||||
|
||||
@@ -18,13 +18,11 @@ from praisonaiagents import Agent
|
||||
agent = Agent(
|
||||
name="Turso Agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
memory={
|
||||
"session_id": "turso-demo-session",
|
||||
"db": {
|
||||
"database_url": os.getenv("TURSO_DATABASE_URL"),
|
||||
"auth_token": os.getenv("TURSO_AUTH_TOKEN"),
|
||||
},
|
||||
db={
|
||||
"database_url": os.getenv("TURSO_DATABASE_URL"),
|
||||
"auth_token": os.getenv("TURSO_AUTH_TOKEN"),
|
||||
},
|
||||
session_id="turso-demo-session",
|
||||
)
|
||||
|
||||
result = agent.start("What is edge computing? Explain in 2 sentences.")
|
||||
|
||||
@@ -10,7 +10,8 @@ from praisonaiagents import Agent
|
||||
agent = Agent(
|
||||
name="FlexBot",
|
||||
instructions="You are a helpful assistant.",
|
||||
llm={"model": "gpt-4o-mini", "configurable": True},
|
||||
llm="gpt-4o-mini",
|
||||
llm_config={"configurable": True}
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -21,7 +21,7 @@ agent = Agent(
|
||||
name="ContextBot",
|
||||
instructions="You help show context information.",
|
||||
tools=[show_context],
|
||||
memory={"session_id": "my-session-123"},
|
||||
session_id="my-session-123"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -19,16 +19,6 @@ from praisonaiagents import Agent, AgentTeam
|
||||
# Initialize observability
|
||||
obs.init()
|
||||
|
||||
|
||||
def set_span_attr(span, key, value):
|
||||
"""Set span attribute safely across observability implementations."""
|
||||
if hasattr(span, "set_attribute"):
|
||||
span.set_attribute(key, value)
|
||||
return
|
||||
attrs = getattr(span, "attributes", None)
|
||||
if attrs is not None:
|
||||
attrs[key] = value
|
||||
|
||||
# Create agents
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
@@ -47,13 +37,13 @@ with obs.trace("content-creation", metadata={"workflow": "research-write"}):
|
||||
|
||||
# Research phase
|
||||
with obs.span("research-phase", kind=SpanKind.AGENT) as research_span:
|
||||
set_span_attr(research_span, "agent", "Researcher")
|
||||
research_span.attributes["agent"] = "Researcher"
|
||||
research_result = researcher.chat("Research the benefits of AI in healthcare")
|
||||
|
||||
# Writing phase
|
||||
with obs.span("writing-phase", kind=SpanKind.AGENT) as write_span:
|
||||
set_span_attr(write_span, "agent", "Writer")
|
||||
set_span_attr(write_span, "input_length", len(str(research_result)))
|
||||
write_span.attributes["agent"] = "Writer"
|
||||
write_span.attributes["input_length"] = len(str(research_result))
|
||||
final_content = writer.chat(f"Write a blog post based on: {research_result}")
|
||||
|
||||
print("Final Content:")
|
||||
|
||||
@@ -52,10 +52,8 @@ agent = Agent(
|
||||
instructions="You are a helpful assistant with access to technical documentation.",
|
||||
knowledge={
|
||||
"sources": [doc_path],
|
||||
"vector_store": {
|
||||
"provider": "qdrant",
|
||||
"url": "http://localhost:6333",
|
||||
},
|
||||
"vector_store": "qdrant",
|
||||
"url": "http://localhost:6333",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ Expected output:
|
||||
- Session can be resumed later with same session_id
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.db import db
|
||||
from praisonaiagents import Agent, db
|
||||
|
||||
# Create database connection (PostgreSQL + Redis)
|
||||
my_db = db(
|
||||
@@ -26,12 +25,10 @@ my_db = db(
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant. Be concise.",
|
||||
memory={
|
||||
"db": my_db,
|
||||
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
|
||||
# Set explicitly for session continuity across runs:
|
||||
"session_id": "my-persistent-session",
|
||||
},
|
||||
db=my_db,
|
||||
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
|
||||
# Set explicitly for session continuity across runs:
|
||||
session_id="my-persistent-session"
|
||||
)
|
||||
|
||||
# Chat - messages are automatically persisted
|
||||
|
||||
@@ -12,8 +12,7 @@ Expected output:
|
||||
- Export shows full session data
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.db import db
|
||||
from praisonaiagents import Agent, db
|
||||
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get weather for a city."""
|
||||
@@ -30,10 +29,8 @@ agent = Agent(
|
||||
name="WeatherBot",
|
||||
instructions="You help with weather. Use the get_weather tool when asked.",
|
||||
tools=[get_weather],
|
||||
memory={
|
||||
"db": my_db,
|
||||
"session_id": "weather-session-001",
|
||||
},
|
||||
db=my_db,
|
||||
session_id="weather-session-001"
|
||||
)
|
||||
|
||||
# First chat - triggers run tracking
|
||||
|
||||
@@ -13,8 +13,7 @@ Expected output:
|
||||
Agent responds with state persisted to Redis
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.db import db
|
||||
from praisonaiagents import Agent, db
|
||||
|
||||
print("=== Redis State Store (Agent-First) ===")
|
||||
|
||||
@@ -27,10 +26,8 @@ my_db = db(
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant.",
|
||||
memory={
|
||||
"db": my_db,
|
||||
"session_id": "redis-state-session",
|
||||
},
|
||||
db=my_db,
|
||||
session_id="redis-state-session"
|
||||
)
|
||||
|
||||
# Chat - state is automatically managed via Redis
|
||||
|
||||
@@ -11,8 +11,7 @@ Expected output:
|
||||
- Second run: Resumes with history
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.db import db
|
||||
from praisonaiagents import Agent, db
|
||||
|
||||
# Create database
|
||||
my_db = db(
|
||||
@@ -26,10 +25,8 @@ SESSION_ID = "resume-demo-session"
|
||||
agent = Agent(
|
||||
name="MemoryBot",
|
||||
instructions="You remember our conversation. Be helpful and concise.",
|
||||
memory={
|
||||
"db": my_db,
|
||||
"session_id": SESSION_ID,
|
||||
},
|
||||
db=my_db,
|
||||
session_id=SESSION_ID
|
||||
)
|
||||
|
||||
# Check if this is a new or resumed session
|
||||
|
||||
@@ -26,8 +26,7 @@ Expected Output:
|
||||
Messages persisted: 2
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.db import db
|
||||
from praisonaiagents import Agent, db
|
||||
|
||||
# Create database adapter (simplified import)
|
||||
db_instance = db(
|
||||
@@ -38,10 +37,8 @@ db_instance = db(
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant. Keep responses brief.",
|
||||
memory={
|
||||
"db": db_instance,
|
||||
"session_id": "demo-session-001", # Same session_id = resume conversation
|
||||
},
|
||||
db=db_instance,
|
||||
session_id="demo-session-001", # Same session_id = resume conversation
|
||||
output="silent"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ Expected output:
|
||||
- Data persisted to local SQLite file
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.db import db
|
||||
from praisonaiagents import Agent, db
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
@@ -26,10 +25,8 @@ print(f"Database: {db_path}")
|
||||
agent = Agent(
|
||||
name="LocalBot",
|
||||
instructions="You are a helpful assistant.",
|
||||
memory={
|
||||
"db": my_db,
|
||||
"session_id": "local-session",
|
||||
},
|
||||
db=my_db,
|
||||
session_id="local-session"
|
||||
)
|
||||
|
||||
# Chat
|
||||
|
||||
@@ -57,8 +57,8 @@ def main():
|
||||
agent = Agent(
|
||||
name="SecureAgent",
|
||||
instructions="You are a file management assistant.",
|
||||
policy=engine
|
||||
)
|
||||
agent.policy = engine
|
||||
|
||||
print("\n--- Agent with Policy Enforcement Created ---")
|
||||
print(f"Agent: {agent.name}")
|
||||
|
||||
@@ -60,10 +60,9 @@ agents_system = AgentTeam(
|
||||
process="sequential", output="verbose"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting hierarchical agent generation...")
|
||||
result = agents_system.start()
|
||||
print("Starting hierarchical agent generation...")
|
||||
result = agents_system.start()
|
||||
|
||||
print(f"\nHierarchical Result: {str(result)[:200]}...")
|
||||
print("\n✅ AutoAgents hierarchical generation complete!")
|
||||
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
|
||||
print(f"\nHierarchical Result: {result[:200]}...")
|
||||
print("\n✅ AutoAgents hierarchical generation complete!")
|
||||
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
|
||||
@@ -6,10 +6,18 @@ It includes self-reflection for improving code quality and multiple specialized
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import CodeInterpreter
|
||||
|
||||
# Initialize code execution tool
|
||||
code_interpreter = CodeInterpreter()
|
||||
def run_python_code(code: str) -> str:
|
||||
"""Execute Python code safely and return stdout or errors."""
|
||||
import io
|
||||
import contextlib
|
||||
try:
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf):
|
||||
exec(code, {"__builtins__": __builtins__})
|
||||
return buf.getvalue() or "Executed successfully (no output)"
|
||||
except Exception as e:
|
||||
return f"Execution error: {e}"
|
||||
|
||||
# Custom code analysis tool
|
||||
def analyze_code_complexity(code: str) -> str:
|
||||
@@ -32,9 +40,7 @@ code_writer = Agent(
|
||||
goal="Write clean, efficient, and well-documented Python code",
|
||||
backstory="You are a senior Python developer with 10+ years of experience in writing production-quality code.",
|
||||
instructions="Write Python code that follows PEP 8 standards, includes proper error handling, and has clear documentation.",
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
code_reviewer = Agent(
|
||||
@@ -52,7 +58,7 @@ code_executor = Agent(
|
||||
goal="Safely execute and test Python code",
|
||||
backstory="You are responsible for running code and reporting results or errors.",
|
||||
instructions="Execute the provided code safely and report the output or any errors encountered.",
|
||||
tools=[code_interpreter]
|
||||
tools=[run_python_code]
|
||||
)
|
||||
|
||||
# Example tasks
|
||||
@@ -87,7 +93,7 @@ def demonstrate_code_agent():
|
||||
workflow = AgentTeam(
|
||||
agents=[code_writer, code_reviewer, code_executor],
|
||||
tasks=[write_task, review_task, execute_task],
|
||||
process="sequential", output="verbose"
|
||||
process="sequential", output="silent"
|
||||
)
|
||||
|
||||
# Run the workflow
|
||||
@@ -106,10 +112,8 @@ general_code_agent = Agent(
|
||||
3. Explain code concepts
|
||||
4. Refactor code for better performance
|
||||
5. Add tests and documentation""",
|
||||
tools=[code_interpreter, analyze_code_complexity],
|
||||
reflection=True,
|
||||
|
||||
|
||||
tools=[run_python_code, analyze_code_complexity],
|
||||
output="silent",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -41,21 +41,25 @@ def calculate_statistics(numbers: list) -> str:
|
||||
"""Calculate basic statistics for a list of numbers."""
|
||||
if not numbers:
|
||||
return "No numbers provided"
|
||||
|
||||
n = len(numbers)
|
||||
mean = sum(numbers) / n
|
||||
sorted_nums = sorted(numbers)
|
||||
try:
|
||||
nums = [float(x) for x in numbers]
|
||||
except (TypeError, ValueError):
|
||||
return "Error: all values must be numbers"
|
||||
|
||||
n = len(nums)
|
||||
mean = sum(nums) / n
|
||||
sorted_nums = sorted(nums)
|
||||
median = sorted_nums[n//2] if n % 2 else (sorted_nums[n//2-1] + sorted_nums[n//2]) / 2
|
||||
variance = sum((x - mean)**2 for x in numbers) / n
|
||||
variance = sum((x - mean)**2 for x in nums) / n
|
||||
std_dev = math.sqrt(variance)
|
||||
|
||||
|
||||
return f"""Statistics:
|
||||
- Count: {n}
|
||||
- Mean: {mean:.4f}
|
||||
- Median: {median:.4f}
|
||||
- Standard Deviation: {std_dev:.4f}
|
||||
- Min: {min(numbers)}
|
||||
- Max: {max(numbers)}"""
|
||||
- Min: {min(nums)}
|
||||
- Max: {max(nums)}"""
|
||||
|
||||
# Create specialized math agents
|
||||
calculation_agent = Agent(
|
||||
@@ -66,8 +70,7 @@ calculation_agent = Agent(
|
||||
instructions="Perform the requested calculations step by step, showing your work clearly.",
|
||||
tools=[basic_calculator, solve_quadratic, calculate_statistics],
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
problem_solver_agent = Agent(
|
||||
@@ -82,8 +85,7 @@ problem_solver_agent = Agent(
|
||||
4. Show each step of the solution
|
||||
5. Verify the answer makes sense""",
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
math_tutor_agent = Agent(
|
||||
@@ -91,7 +93,8 @@ math_tutor_agent = Agent(
|
||||
role="Mathematics tutor and explainer",
|
||||
goal="Explain mathematical concepts clearly and help users understand math",
|
||||
backstory="You are a patient math tutor who can explain concepts at any level from elementary to advanced.",
|
||||
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding."
|
||||
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding.",
|
||||
output="silent",
|
||||
)
|
||||
|
||||
# Example workflow for solving complex problems
|
||||
@@ -126,7 +129,8 @@ def solve_math_problem_workflow():
|
||||
workflow = AgentTeam(
|
||||
agents=[problem_solver_agent, calculation_agent, math_tutor_agent],
|
||||
tasks=[understand_task, solve_task, verify_task],
|
||||
process="sequential", output="verbose"
|
||||
process="sequential",
|
||||
output="silent",
|
||||
)
|
||||
|
||||
return workflow.start()
|
||||
@@ -148,8 +152,7 @@ math_assistant = Agent(
|
||||
Always show your work step by step.""",
|
||||
tools=[basic_calculator, solve_quadratic, calculate_statistics],
|
||||
reflection=True,
|
||||
|
||||
|
||||
output="silent",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
if __name__ == "__main__":
|
||||
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
|
||||
agent.launch(port=8080, protocol="mcp", path="/")
|
||||
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
|
||||
agent.launch(port=8080, protocol="mcp")
|
||||
@@ -90,18 +90,9 @@ def main():
|
||||
|
||||
if result:
|
||||
print("\n🔍 Analysis Results:")
|
||||
task_results = result.get("task_results") if isinstance(result, dict) else result
|
||||
if isinstance(task_results, dict):
|
||||
iterable = task_results.items()
|
||||
elif isinstance(task_results, list):
|
||||
iterable = enumerate(task_results)
|
||||
else:
|
||||
iterable = [("result", task_results)]
|
||||
|
||||
for task_id, task_result in iterable:
|
||||
for task_id, task_result in result["task_results"].items():
|
||||
print(f"\nTask {task_id}:")
|
||||
raw_value = getattr(task_result, "raw", None)
|
||||
print(raw_value if raw_value is not None else str(task_result))
|
||||
print(task_result.raw)
|
||||
else:
|
||||
print("❌ Failed to capture and analyze camera feed")
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class ContinuousCameraMonitor:
|
||||
agents=[self.vision_agent],
|
||||
tasks=[task],
|
||||
process="sequential",
|
||||
output="silent", # Reduced verbosity for continuous monitoring
|
||||
verbose=0 # Reduced verbosity for continuous monitoring
|
||||
)
|
||||
|
||||
return agents.start()
|
||||
|
||||
@@ -148,19 +148,10 @@ def main():
|
||||
"scene_analysis": "🌍 SCENE ANALYSIS"
|
||||
}
|
||||
|
||||
task_results = result.get("task_results") if isinstance(result, dict) else result
|
||||
if isinstance(task_results, dict):
|
||||
iterable = task_results.items()
|
||||
elif isinstance(task_results, list):
|
||||
iterable = enumerate(task_results)
|
||||
else:
|
||||
iterable = [("result", task_results)]
|
||||
|
||||
for task_id, task_result in iterable:
|
||||
for task_id, task_result in result["task_results"].items():
|
||||
print(f"\n{agent_names.get(task_id, f'Task {task_id}')}:")
|
||||
print("-" * 40)
|
||||
raw_value = getattr(task_result, "raw", None)
|
||||
print(raw_value if raw_value is not None else str(task_result))
|
||||
print(task_result.raw)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✅ Multi-agent analysis complete!")
|
||||
|
||||
@@ -34,5 +34,5 @@ print(f"\nChanged to: {handler.get_mode()}")
|
||||
print("File edits now auto-approved, shell commands still require approval")
|
||||
|
||||
# Get statistics
|
||||
stats = manager.get_stats()
|
||||
stats = handler.get_stats()
|
||||
print(f"\nStats: {stats}")
|
||||
@@ -31,4 +31,4 @@ print(f"Total cost: ${summary['total_cost']:.4f}")
|
||||
print(f"Avg cost/request: ${summary['avg_cost_per_request']:.4f}")
|
||||
|
||||
# Display formatted (uses Rich if available)
|
||||
handler.print_summary()
|
||||
handler.display_summary()
|
||||
@@ -41,9 +41,9 @@ def my_custom_handler(args, context):
|
||||
custom_cmd = SlashCommand(
|
||||
name="mycommand",
|
||||
description="My custom command",
|
||||
action=my_custom_handler,
|
||||
kind=CommandKind.CUSTOM,
|
||||
alt_names=["mc"]
|
||||
handler=my_custom_handler,
|
||||
kind=CommandKind.ACTION,
|
||||
aliases=["mc"]
|
||||
)
|
||||
|
||||
handler.register(custom_cmd)
|
||||
|
||||
@@ -16,8 +16,7 @@ agents = AgentTeam(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="workflow",
|
||||
execution={"max_iter": 30},
|
||||
max_iter=30
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
agents.start()
|
||||
agents.start()
|
||||
@@ -5,8 +5,7 @@ This example demonstrates self-reflection capabilities using PraisonAI's
|
||||
built-in reflection features for iterative improvement and quality optimization.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task
|
||||
from praisonaiagents.agents import AgentTeam
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import internet_search
|
||||
|
||||
print("=== Self-Reflection Optimization Patterns Example ===\n")
|
||||
@@ -34,16 +33,9 @@ reflection_task = Task(
|
||||
)
|
||||
|
||||
# Run with self-reflection optimization
|
||||
if __name__ == "__main__":
|
||||
print("Starting self-reflection optimization demonstration...")
|
||||
agents = AgentTeam(
|
||||
agents=[reflection_agent],
|
||||
tasks=[reflection_task],
|
||||
process="sequential",
|
||||
output="verbose",
|
||||
)
|
||||
result = agents.start()
|
||||
print("Starting self-reflection optimization demonstration...")
|
||||
result = reflection_agent.execute_task(reflection_task)
|
||||
|
||||
print(f"\nSelf-Reflection Result: {str(result)[:200]}...")
|
||||
print("\n✅ Self-reflection optimization complete!")
|
||||
print("Agent demonstrated iterative improvement through built-in self-reflection capabilities.")
|
||||
print(f"\nSelf-Reflection Result: {result[:200]}...")
|
||||
print("\n✅ Self-reflection optimization complete!")
|
||||
print("Agent demonstrated iterative improvement through built-in self-reflection capabilities.")
|
||||
@@ -61,6 +61,7 @@ def example_with_wrappers():
|
||||
role="Data Analyst",
|
||||
goal="Analyze data and provide insights",
|
||||
llm="gpt-4o-mini",
|
||||
request_id="request_2" # Unique request ID
|
||||
)
|
||||
|
||||
# Operations are automatically tracked
|
||||
|
||||
@@ -132,9 +132,7 @@ agents = AgentTeam(
|
||||
agents=[qa_generator, total_questions_evaluator, cot_generator, upload_to_huggingface],
|
||||
tasks=[generate_task, evaluate_total_questions_task, generate_cot_task, upload_to_huggingface_task],
|
||||
process="workflow",
|
||||
execution={"max_iter": 30},
|
||||
output="minimal",
|
||||
max_iter=30, output="minimal"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
agents.start()
|
||||
agents.start()
|
||||
@@ -18,7 +18,7 @@ openai_profile = AuthProfile(
|
||||
provider="openai",
|
||||
api_key=os.environ.get("OPENAI_API_KEY", "sk-..."),
|
||||
priority=1, # Highest priority
|
||||
rate_limit_rpm=100,
|
||||
rate_limit=100,
|
||||
)
|
||||
|
||||
anthropic_profile = AuthProfile(
|
||||
@@ -26,7 +26,7 @@ anthropic_profile = AuthProfile(
|
||||
provider="anthropic",
|
||||
api_key=os.environ.get("ANTHROPIC_API_KEY", "sk-ant-..."),
|
||||
priority=2, # Second priority
|
||||
rate_limit_rpm=50,
|
||||
rate_limit=50,
|
||||
)
|
||||
|
||||
groq_profile = AuthProfile(
|
||||
@@ -34,7 +34,7 @@ groq_profile = AuthProfile(
|
||||
provider="groq",
|
||||
api_key=os.environ.get("GROQ_API_KEY", "gsk-..."),
|
||||
priority=3, # Third priority (fast but limited)
|
||||
rate_limit_rpm=30,
|
||||
rate_limit=30,
|
||||
)
|
||||
|
||||
# Configure failover behavior
|
||||
|
||||
@@ -105,13 +105,13 @@ def main():
|
||||
agents=[researcher, retriever],
|
||||
tasks=[store_task, verify_task, query_task, query_both_task], # Use same verbose level as memory
|
||||
memory={
|
||||
"backend": "sqlite",
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small"
|
||||
}
|
||||
},
|
||||
"config": {"provider": "sqlite"},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -49,20 +49,10 @@ agents = AgentTeam(
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run all tasks
|
||||
result = agents.start()
|
||||
# Run all tasks
|
||||
result = agents.start()
|
||||
|
||||
# Print results
|
||||
task_results = result.get("task_results") if isinstance(result, dict) else result
|
||||
if isinstance(task_results, dict):
|
||||
iterable = task_results.items()
|
||||
elif isinstance(task_results, list):
|
||||
iterable = enumerate(task_results)
|
||||
else:
|
||||
iterable = [("result", task_results)]
|
||||
|
||||
for task_id, task_result in iterable:
|
||||
print(f"\nTask {task_id} Result:")
|
||||
raw_value = getattr(task_result, "raw", None)
|
||||
print(raw_value if raw_value is not None else str(task_result))
|
||||
# Print results
|
||||
for task_id, task_result in result["task_results"].items():
|
||||
print(f"\nTask {task_id} Result:")
|
||||
print(task_result.raw)
|
||||
@@ -6,7 +6,6 @@ built-in validation and safety mechanisms for secure agent operations.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.config.feature_configs import GuardrailConfig
|
||||
from praisonaiagents.tools import internet_search
|
||||
|
||||
print("=== Production Guardrails Patterns Example ===\n")
|
||||
@@ -39,8 +38,8 @@ production_agent = Agent(
|
||||
goal="Generate safe, compliant content for production use",
|
||||
backstory="Production-ready agent with built-in safety and compliance checks",
|
||||
tools=[internet_search],
|
||||
guardrails=GuardrailConfig(validator=content_guardrail, max_retries=3),
|
||||
)
|
||||
guardrails=content_guardrail,
|
||||
max_retries=3)
|
||||
|
||||
# Create secure task with validation
|
||||
secure_task = Task(
|
||||
|
||||
@@ -40,7 +40,7 @@ agent = Agent(
|
||||
"execute_command"
|
||||
],
|
||||
memory=True,
|
||||
web=True,
|
||||
web_search=True,
|
||||
auto_approve_tools=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -14,33 +14,30 @@ agent = Agent(name="env-mgmt", backend=managed)
|
||||
# First call creates agent + environment + session
|
||||
agent.start("Say hello briefly")
|
||||
|
||||
if hasattr(managed, "_get_client"):
|
||||
# Access the underlying Anthropic client for environment management
|
||||
client = managed._get_client()
|
||||
env_id = managed.environment_id
|
||||
print(f"Environment ID: {env_id}")
|
||||
# Access the underlying Anthropic client for environment management
|
||||
client = managed._get_client()
|
||||
env_id = managed.environment_id
|
||||
print(f"Environment ID: {env_id}")
|
||||
|
||||
# List all environments
|
||||
environments = client.beta.environments.list()
|
||||
print(f"\nTotal environments: {len(environments.data)}")
|
||||
for env in environments.data[:5]:
|
||||
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
|
||||
# List all environments
|
||||
environments = client.beta.environments.list()
|
||||
print(f"\nTotal environments: {len(environments.data)}")
|
||||
for env in environments.data[:5]:
|
||||
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
|
||||
|
||||
# Retrieve a specific environment
|
||||
env = client.beta.environments.retrieve(env_id)
|
||||
print(f"\nRetrieved: {env.id} | {env.name}")
|
||||
# Retrieve a specific environment
|
||||
env = client.beta.environments.retrieve(env_id)
|
||||
print(f"\nRetrieved: {env.id} | {env.name}")
|
||||
|
||||
# Archive (read-only, existing sessions continue)
|
||||
archived = client.beta.environments.archive(env_id)
|
||||
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
|
||||
# Archive (read-only, existing sessions continue)
|
||||
archived = client.beta.environments.archive(env_id)
|
||||
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
|
||||
|
||||
# Delete (only if no sessions reference it — may fail if sessions exist)
|
||||
try:
|
||||
client.beta.environments.delete(env_id)
|
||||
print(f"Deleted: {env_id}")
|
||||
except Exception as e:
|
||||
print(f"Delete skipped (expected if sessions exist): {e}")
|
||||
else:
|
||||
print("Environment management demo skipped: backend does not expose _get_client().")
|
||||
# Delete (only if no sessions reference it — may fail if sessions exist)
|
||||
try:
|
||||
client.beta.environments.delete(env_id)
|
||||
print(f"Deleted: {env_id}")
|
||||
except Exception as e:
|
||||
print(f"Delete skipped (expected if sessions exist): {e}")
|
||||
|
||||
print("\nEnvironment management complete.")
|
||||
@@ -15,6 +15,13 @@ Protocol: MCP 2025-11-25
|
||||
|
||||
from praisonaiagents import Agent, MCP
|
||||
|
||||
# Agent with resumability support (automatic)
|
||||
agent = Agent(
|
||||
name="Resumable Assistant",
|
||||
instructions="You are a helpful assistant with resumable connections.",
|
||||
tools=MCP("https://api.example.com/mcp")
|
||||
)
|
||||
|
||||
# Resumability is automatic - the MCP client:
|
||||
# 1. Tracks event IDs from SSE streams (id: field)
|
||||
# 2. Stores last event ID per stream
|
||||
@@ -22,12 +29,6 @@ from praisonaiagents import Agent, MCP
|
||||
# 4. Respects retry delay from server (retry: field)
|
||||
|
||||
if __name__ == "__main__":
|
||||
agent = Agent(
|
||||
name="Resumable Assistant",
|
||||
instructions="You are a helpful assistant with resumable connections.",
|
||||
tools=MCP("https://api.example.com/mcp"),
|
||||
)
|
||||
|
||||
print("MCP Resumability Example")
|
||||
print("=" * 40)
|
||||
print()
|
||||
|
||||
@@ -15,6 +15,13 @@ Protocol: MCP 2025-11-25
|
||||
|
||||
from praisonaiagents import Agent, MCP
|
||||
|
||||
# Basic agent with automatic session management
|
||||
agent = Agent(
|
||||
name="Session Assistant",
|
||||
instructions="You are a helpful assistant with session support.",
|
||||
tools=MCP("https://api.example.com/mcp")
|
||||
)
|
||||
|
||||
# Session management is automatic - the MCP client:
|
||||
# 1. Receives Mcp-Session-Id from server during initialization
|
||||
# 2. Includes session ID in all subsequent requests
|
||||
@@ -22,12 +29,6 @@ from praisonaiagents import Agent, MCP
|
||||
# 4. Can terminate session explicitly when done
|
||||
|
||||
if __name__ == "__main__":
|
||||
agent = Agent(
|
||||
name="Session Assistant",
|
||||
instructions="You are a helpful assistant with session support.",
|
||||
tools=MCP("https://api.example.com/mcp"),
|
||||
)
|
||||
|
||||
print("MCP Session Management Example")
|
||||
print("=" * 40)
|
||||
print()
|
||||
|
||||
@@ -16,31 +16,34 @@ Protocol: MCP 2025-11-25
|
||||
|
||||
from praisonaiagents import Agent, MCP
|
||||
|
||||
# Basic Streamable HTTP connection
|
||||
agent = Agent(
|
||||
name="HTTP Stream Assistant",
|
||||
instructions="You are a helpful assistant connected via Streamable HTTP.",
|
||||
tools=MCP("https://api.example.com/mcp")
|
||||
)
|
||||
|
||||
# With authentication headers
|
||||
agent_auth = Agent(
|
||||
name="Authenticated Assistant",
|
||||
instructions="You are a helpful assistant with authenticated connection.",
|
||||
tools=MCP(
|
||||
"https://api.example.com/mcp",
|
||||
headers={"Authorization": "Bearer your-token"}
|
||||
)
|
||||
)
|
||||
|
||||
# With timeout and debug
|
||||
agent_debug = Agent(
|
||||
name="Debug Assistant",
|
||||
tools=MCP(
|
||||
"https://api.example.com/mcp",
|
||||
timeout=120,
|
||||
debug=True
|
||||
)
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
agent = Agent(
|
||||
name="HTTP Stream Assistant",
|
||||
instructions="You are a helpful assistant connected via Streamable HTTP.",
|
||||
tools=MCP("https://api.example.com/mcp"),
|
||||
)
|
||||
|
||||
agent_auth = Agent(
|
||||
name="Authenticated Assistant",
|
||||
instructions="You are a helpful assistant with authenticated connection.",
|
||||
tools=MCP(
|
||||
"https://api.example.com/mcp",
|
||||
headers={"Authorization": "Bearer your-token"},
|
||||
),
|
||||
)
|
||||
|
||||
agent_debug = Agent(
|
||||
name="Debug Assistant",
|
||||
tools=MCP(
|
||||
"https://api.example.com/mcp",
|
||||
timeout=120,
|
||||
debug=True,
|
||||
),
|
||||
)
|
||||
|
||||
print("Streamable HTTP MCP Transport Example")
|
||||
print("=" * 40)
|
||||
print("This example requires a Streamable HTTP MCP server running.")
|
||||
|
||||
@@ -16,33 +16,36 @@ Protocol: SEP-1288 (WebSocket Transport)
|
||||
|
||||
from praisonaiagents import Agent, MCP
|
||||
|
||||
# Basic WebSocket connection
|
||||
agent = Agent(
|
||||
name="WebSocket Assistant",
|
||||
instructions="You are a helpful assistant connected via WebSocket.",
|
||||
tools=MCP("ws://localhost:8080/mcp")
|
||||
)
|
||||
|
||||
# Secure WebSocket with authentication
|
||||
agent_secure = Agent(
|
||||
name="Secure WebSocket Assistant",
|
||||
instructions="You are a helpful assistant with secure WebSocket connection.",
|
||||
tools=MCP(
|
||||
"wss://api.example.com/mcp",
|
||||
auth_token="Bearer your-secret-token",
|
||||
timeout=60
|
||||
)
|
||||
)
|
||||
|
||||
# WebSocket with custom options
|
||||
agent_custom = Agent(
|
||||
name="Custom WebSocket Assistant",
|
||||
tools=MCP(
|
||||
"wss://api.example.com/mcp",
|
||||
auth_token="your-token",
|
||||
timeout=120,
|
||||
debug=True
|
||||
)
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
agent = Agent(
|
||||
name="WebSocket Assistant",
|
||||
instructions="You are a helpful assistant connected via WebSocket.",
|
||||
tools=MCP("ws://localhost:8080/mcp"),
|
||||
)
|
||||
|
||||
agent_secure = Agent(
|
||||
name="Secure WebSocket Assistant",
|
||||
instructions="You are a helpful assistant with secure WebSocket connection.",
|
||||
tools=MCP(
|
||||
"wss://api.example.com/mcp",
|
||||
auth_token="Bearer your-secret-token",
|
||||
timeout=60,
|
||||
),
|
||||
)
|
||||
|
||||
agent_custom = Agent(
|
||||
name="Custom WebSocket Assistant",
|
||||
tools=MCP(
|
||||
"wss://api.example.com/mcp",
|
||||
auth_token="your-token",
|
||||
timeout=120,
|
||||
debug=True,
|
||||
),
|
||||
)
|
||||
|
||||
# Note: Requires a running WebSocket MCP server
|
||||
# Example server: https://github.com/modelcontextprotocol/servers
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ print("=== Advanced Graph Memory Integration Example ===\n")
|
||||
|
||||
# Configure simple graph memory
|
||||
memory_config = {
|
||||
"provider": "mem0",
|
||||
"config": {
|
||||
"provider": "mem0",
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
|
||||
@@ -12,8 +12,8 @@ print("=== Advanced Memory Search and Optimization Example ===\n")
|
||||
|
||||
# Configure optimized memory settings
|
||||
memory_config = {
|
||||
"provider": "mem0",
|
||||
"config": {
|
||||
"provider": "mem0",
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
|
||||
@@ -86,13 +86,13 @@ def main():
|
||||
agents=[researcher, retriever],
|
||||
tasks=[store_task, retrieve_task],
|
||||
memory={
|
||||
"backend": "sqlite",
|
||||
"embedder": {
|
||||
"provider": "gemini",
|
||||
"config": {
|
||||
"model": "text-embedding-004",
|
||||
}
|
||||
},
|
||||
"config": {"provider": "sqlite"},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -30,11 +30,7 @@ config = {
|
||||
agent = Agent(
|
||||
name="Knowledge Agent",
|
||||
instructions="You answer questions based on the provided knowledge.",
|
||||
knowledge={
|
||||
"sources": ["kag-research-paper.pdf"],
|
||||
"vector_store": config["vector_store"],
|
||||
"embedder": config["embedder"],
|
||||
},
|
||||
knowledge={**config, "sources": ["kag-research-paper.pdf"]},
|
||||
memory={"user_id": "user1"},
|
||||
llm="deepseek-r1"
|
||||
)
|
||||
|
||||
@@ -31,9 +31,10 @@ llm_config = {
|
||||
agent = Agent(
|
||||
instructions="You are a helpful Assistant specialized in scientific explanations. "
|
||||
"Provide clear, accurate, and engaging responses.",
|
||||
llm=llm_config,
|
||||
output={"markdown": True},
|
||||
reflection=True,
|
||||
llm=llm_config, # Pass the detailed configuration
|
||||
# Enable detailed output
|
||||
markdown=True, # Format responses in markdown
|
||||
reflection=True # Enable self-reflection
|
||||
)
|
||||
|
||||
# Test the agent
|
||||
|
||||
@@ -28,8 +28,8 @@ if not os.environ.get("OPENAI_API_KEY"):
|
||||
def main():
|
||||
# MongoDB memory configuration
|
||||
mongodb_memory_config = {
|
||||
"provider": "mongodb",
|
||||
"config": {
|
||||
"provider": "mongodb",
|
||||
"connection_string": "mongodb://localhost:27017/", # Replace with your MongoDB connection string
|
||||
"database": "praisonai_memory",
|
||||
"use_vector_search": True, # Enable Atlas Vector Search (requires MongoDB Atlas)
|
||||
|
||||
@@ -4,7 +4,7 @@ Example 3: Save Agent Output Using Workflow output_file
|
||||
Workflow step output is automatically saved with variable substitution.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, AgentFlow, Task
|
||||
from praisonaiagents import AgentFlow
|
||||
|
||||
# Define workflow with output_file
|
||||
workflow_config = {
|
||||
@@ -33,30 +33,9 @@ workflow_config = {
|
||||
]
|
||||
}
|
||||
|
||||
# Build agent and workflow from config dict
|
||||
writer_cfg = workflow_config["agents"]["writer"]
|
||||
writer = Agent(
|
||||
name="writer",
|
||||
role=writer_cfg["role"],
|
||||
goal=writer_cfg["goal"],
|
||||
llm=writer_cfg["llm"],
|
||||
)
|
||||
|
||||
step_cfg = workflow_config["steps"][0]
|
||||
workflow = AgentFlow(
|
||||
name=workflow_config["metadata"]["name"],
|
||||
variables=workflow_config["variables"],
|
||||
steps=[
|
||||
Task(
|
||||
name="write_tutorial",
|
||||
description=step_cfg["action"],
|
||||
expected_output=step_cfg["expected_output"],
|
||||
output_file=step_cfg["output_file"],
|
||||
agent=writer,
|
||||
)
|
||||
],
|
||||
)
|
||||
result = workflow.start()
|
||||
# Run workflow
|
||||
workflow = AgentFlow(config=workflow_config)
|
||||
result = workflow.run()
|
||||
|
||||
print("✅ Workflow completed!")
|
||||
print("Output saved to: generated/tutorial.md")
|
||||
@@ -28,7 +28,7 @@ def example_basic_persistence():
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
memory={"session_id": session_id},
|
||||
session_id=session_id,
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ def example_session_restoration():
|
||||
agent1 = Agent(
|
||||
name="MemoryBot",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
memory={"session_id": session_id},
|
||||
session_id=session_id,
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
@@ -72,7 +72,7 @@ def example_session_restoration():
|
||||
agent2 = Agent(
|
||||
name="MemoryBot",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
memory={"session_id": session_id},
|
||||
session_id=session_id,
|
||||
output="minimal",
|
||||
)
|
||||
|
||||
|
||||
@@ -69,10 +69,9 @@ agents_system = AgentTeam(
|
||||
memory=True, output="verbose"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting state management demonstration...")
|
||||
result = agents_system.start()
|
||||
print("Starting state management demonstration...")
|
||||
result = agents_system.start()
|
||||
|
||||
print(f"\nState Management Result: {str(result)[:200]}...")
|
||||
print("\n✅ State management patterns complete!")
|
||||
print("Agent demonstrated persistent state tracking across multiple tasks.")
|
||||
print(f"\nState Management Result: {result[:200]}...")
|
||||
print("\n✅ State management patterns complete!")
|
||||
print("Agent demonstrated persistent state tracking across multiple tasks.")
|
||||
@@ -179,7 +179,7 @@ def main():
|
||||
|
||||
context = memory.build_context_for_task(
|
||||
task_descr="Explain AI alignment challenges",
|
||||
user_id="quality_demo_user",
|
||||
memory={"user_id": "quality_demo_user"},
|
||||
max_items=3
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ def main():
|
||||
# Create a session with persistent storage
|
||||
session = Session(
|
||||
session_id="demo_chat_001",
|
||||
user_id="demo_user",
|
||||
memory={"user_id": "demo_user"},
|
||||
)
|
||||
|
||||
print(f"📝 Created session: {session}")
|
||||
|
||||
@@ -1,55 +1,284 @@
|
||||
"""
|
||||
Production Telemetry Example
|
||||
|
||||
Demonstrates compatible telemetry setup with `enable_telemetry()` and
|
||||
`TelemetryCollector` patterns.
|
||||
This example demonstrates comprehensive telemetry and monitoring setup for
|
||||
production AI agent systems, including metrics collection, performance tracking,
|
||||
and observability.
|
||||
|
||||
Features demonstrated:
|
||||
- OpenTelemetry integration for distributed tracing
|
||||
- Custom metrics collection and monitoring
|
||||
- Performance tracking and analytics
|
||||
- Error tracking and alerting
|
||||
- Usage analytics and reporting
|
||||
"""
|
||||
|
||||
from contextlib import nullcontext
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
from praisonaiagents.telemetry import enable_telemetry, TelemetryCollector
|
||||
from praisonaiagents.telemetry import enable_telemetry
|
||||
import time
|
||||
import random
|
||||
from datetime import datetime
|
||||
|
||||
# Initialize telemetry for production monitoring
|
||||
print("="*80)
|
||||
print("PRODUCTION TELEMETRY SETUP")
|
||||
print("="*80)
|
||||
|
||||
def main() -> None:
|
||||
print("=" * 80)
|
||||
print("PRODUCTION TELEMETRY SETUP")
|
||||
print("=" * 80)
|
||||
# Configure telemetry (in production, you'd use actual telemetry backends)
|
||||
telemetry = enable_telemetry(
|
||||
backend="opentelemetry",
|
||||
config={
|
||||
"service_name": "praisonai_production_agents",
|
||||
"service_version": "1.0.0",
|
||||
"environment": "production",
|
||||
"export_console": True, # For demo purposes
|
||||
"export_file": True, # Export to file for analysis
|
||||
"sample_rate": 1.0, # 100% sampling for demo
|
||||
"attributes": {
|
||||
"deployment": "cloud",
|
||||
"region": "us-east-1",
|
||||
"cluster": "main"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# New API: no args
|
||||
enable_telemetry()
|
||||
collector = TelemetryCollector()
|
||||
# Create agents for production workflow with telemetry
|
||||
customer_service_agent = Agent(
|
||||
name="CustomerServiceAgent",
|
||||
role="Customer Service Representative",
|
||||
goal="Provide excellent customer service with fast response times",
|
||||
backstory="You are a customer service representative focused on resolving customer issues quickly and effectively.",
|
||||
tools=[duckduckgo],
|
||||
instructions="Provide helpful, accurate customer service responses. Research solutions when needed."
|
||||
)
|
||||
|
||||
customer_service_agent = Agent(
|
||||
name="CustomerServiceAgent",
|
||||
role="Customer Service Representative",
|
||||
goal="Provide excellent customer service with fast response times",
|
||||
backstory="Customer service expert focused on quick issue resolution.",
|
||||
tools=[duckduckgo],
|
||||
instructions="Provide accurate customer-service responses.",
|
||||
)
|
||||
technical_support_agent = Agent(
|
||||
name="TechnicalSupportAgent",
|
||||
role="Technical Support Specialist",
|
||||
goal="Resolve technical issues with detailed solutions",
|
||||
backstory="You are a technical support specialist who provides detailed technical solutions and troubleshooting.",
|
||||
instructions="Provide detailed technical solutions with step-by-step instructions."
|
||||
)
|
||||
|
||||
requests = [
|
||||
"Customer asking about monthly bill charges",
|
||||
"Customer experiencing connectivity issues",
|
||||
quality_assurance_agent = Agent(
|
||||
name="QualityAssuranceAgent",
|
||||
role="Quality Assurance Specialist",
|
||||
goal="Ensure all customer interactions meet quality standards",
|
||||
backstory="You review and assess the quality of customer service interactions to ensure high standards.",
|
||||
instructions="Review customer service interactions and provide quality scores with detailed feedback."
|
||||
)
|
||||
|
||||
# Production workflow simulation with telemetry tracking
|
||||
def simulate_customer_requests():
|
||||
"""Simulate various customer requests for telemetry demonstration"""
|
||||
|
||||
customer_requests = [
|
||||
{
|
||||
"type": "billing_inquiry",
|
||||
"complexity": "low",
|
||||
"description": "Customer asking about their monthly bill charges",
|
||||
"expected_duration": 30
|
||||
},
|
||||
{
|
||||
"type": "technical_issue",
|
||||
"complexity": "high",
|
||||
"description": "Customer experiencing connectivity issues with the service",
|
||||
"expected_duration": 120
|
||||
},
|
||||
{
|
||||
"type": "account_setup",
|
||||
"complexity": "medium",
|
||||
"description": "New customer needs help setting up their account",
|
||||
"expected_duration": 60
|
||||
},
|
||||
{
|
||||
"type": "feature_question",
|
||||
"complexity": "low",
|
||||
"description": "Customer asking about product features and capabilities",
|
||||
"expected_duration": 45
|
||||
}
|
||||
]
|
||||
|
||||
return customer_requests
|
||||
|
||||
for idx, description in enumerate(requests, start=1):
|
||||
trace_ctx = getattr(collector, "trace", None)
|
||||
trace_cm = trace_ctx(f"customer_request_{idx}") if callable(trace_ctx) else nullcontext()
|
||||
with trace_cm:
|
||||
task = Task(
|
||||
name=f"customer_request_{idx}",
|
||||
description=description,
|
||||
expected_output="Professional customer service response with solution",
|
||||
agent=customer_service_agent,
|
||||
)
|
||||
agents = AgentTeam(agents=[customer_service_agent], tasks=[task], output="minimal")
|
||||
result = agents.start()
|
||||
print(f"✅ Request {idx} completed, response length={len(str(result))}")
|
||||
# Execute production workflow with comprehensive telemetry
|
||||
print("Starting production workflow with telemetry tracking...")
|
||||
|
||||
print("\nTelemetry demonstration completed.")
|
||||
customer_requests = simulate_customer_requests()
|
||||
|
||||
for i, request in enumerate(customer_requests):
|
||||
print(f"\n{'='*60}")
|
||||
print(f"PROCESSING CUSTOMER REQUEST {i+1}: {request['type'].upper()}")
|
||||
print(f"Complexity: {request['complexity']} | Expected Duration: {request['expected_duration']}s")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# Start telemetry trace for this customer interaction
|
||||
with telemetry.trace_operation(f"customer_request_{request['type']}") as trace:
|
||||
|
||||
# Add custom attributes to the trace
|
||||
trace.set_attributes({
|
||||
"request.type": request['type'],
|
||||
"request.complexity": request['complexity'],
|
||||
"request.expected_duration": request['expected_duration'],
|
||||
"customer.id": f"customer_{random.randint(1000, 9999)}",
|
||||
"agent.assigned": "CustomerServiceAgent"
|
||||
})
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Create customer service task with telemetry
|
||||
customer_task = Task(
|
||||
name=f"customer_request_{i+1}",
|
||||
description=request['description'],
|
||||
expected_output="Professional customer service response with solution",
|
||||
agent=customer_service_agent
|
||||
)
|
||||
|
||||
# Execute with telemetry tracking
|
||||
try:
|
||||
# Customer service response
|
||||
with telemetry.trace_agent_execution("CustomerServiceAgent") as agent_trace:
|
||||
agents = AgentTeam(
|
||||
agents=[customer_service_agent],
|
||||
tasks=[customer_task],
|
||||
output="minimal" # Reduce verbosity for telemetry demo
|
||||
)
|
||||
|
||||
cs_result = agents.start()
|
||||
agent_trace.set_attributes({
|
||||
"agent.response_length": len(str(cs_result)),
|
||||
"agent.tools_used": len(customer_service_agent.tools) if customer_service_agent.tools else 0
|
||||
})
|
||||
|
||||
# Technical support (if needed for high complexity)
|
||||
if request['complexity'] == 'high':
|
||||
with telemetry.trace_agent_execution("TechnicalSupportAgent") as tech_trace:
|
||||
tech_task = Task(
|
||||
name=f"technical_support_{i+1}",
|
||||
description=f"Provide detailed technical support for: {request['description']}",
|
||||
expected_output="Detailed technical solution with troubleshooting steps",
|
||||
agent=technical_support_agent,
|
||||
context=[customer_task]
|
||||
)
|
||||
|
||||
tech_agents = AgentTeam(
|
||||
agents=[technical_support_agent],
|
||||
tasks=[tech_task], output="minimal"
|
||||
)
|
||||
|
||||
tech_result = tech_agents.start()
|
||||
tech_trace.set_attributes({
|
||||
"agent.response_length": len(str(tech_result)),
|
||||
"agent.escalation": True
|
||||
})
|
||||
|
||||
# Quality assurance check
|
||||
with telemetry.trace_agent_execution("QualityAssuranceAgent") as qa_trace:
|
||||
qa_task = Task(
|
||||
name=f"quality_check_{i+1}",
|
||||
description=f"Review the quality of customer service provided for: {request['description']}",
|
||||
expected_output="Quality assessment score and feedback",
|
||||
agent=quality_assurance_agent
|
||||
)
|
||||
|
||||
qa_agents = AgentTeam(
|
||||
agents=[quality_assurance_agent],
|
||||
tasks=[qa_task], output="minimal"
|
||||
)
|
||||
|
||||
qa_result = qa_agents.start()
|
||||
|
||||
# Extract quality score (simulated)
|
||||
quality_score = random.uniform(0.8, 1.0) # Simulated score
|
||||
qa_trace.set_attributes({
|
||||
"qa.score": quality_score,
|
||||
"qa.passed": quality_score >= 0.85
|
||||
})
|
||||
|
||||
# Calculate performance metrics
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
|
||||
# Record custom metrics
|
||||
telemetry.record_metric("request_duration", duration, {
|
||||
"request_type": request['type'],
|
||||
"complexity": request['complexity']
|
||||
})
|
||||
|
||||
telemetry.record_metric("request_success", 1, {
|
||||
"request_type": request['type']
|
||||
})
|
||||
|
||||
# Set final trace attributes
|
||||
trace.set_attributes({
|
||||
"request.duration_seconds": duration,
|
||||
"request.status": "completed",
|
||||
"request.quality_score": quality_score,
|
||||
"request.escalated": request['complexity'] == 'high'
|
||||
})
|
||||
|
||||
print(f"✅ Request completed successfully in {duration:.2f}s (Quality: {quality_score:.2f})")
|
||||
|
||||
except Exception as e:
|
||||
# Record error metrics
|
||||
telemetry.record_metric("request_error", 1, {
|
||||
"request_type": request['type'],
|
||||
"error_type": type(e).__name__
|
||||
})
|
||||
|
||||
trace.set_attributes({
|
||||
"request.status": "error",
|
||||
"request.error": str(e)
|
||||
})
|
||||
|
||||
print(f"❌ Request failed: {str(e)}")
|
||||
|
||||
# Add some delay between requests
|
||||
time.sleep(2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# Generate telemetry summary
|
||||
print(f"\n{'='*80}")
|
||||
print("TELEMETRY SUMMARY AND ANALYTICS")
|
||||
print(f"{'='*80}")
|
||||
|
||||
# Simulated telemetry analytics (in production, this would come from your telemetry backend)
|
||||
print("📊 Performance Metrics:")
|
||||
print("- Total Requests Processed: 4")
|
||||
print("- Average Response Time: 3.2s")
|
||||
print("- Success Rate: 100%")
|
||||
print("- Quality Score Average: 0.91")
|
||||
print("- High Complexity Requests: 25%")
|
||||
|
||||
print("\n📈 Usage Analytics:")
|
||||
print("- Most Common Request Type: technical_issue")
|
||||
print("- Peak Performance Window: All requests within SLA")
|
||||
print("- Agent Utilization: CustomerService (100%), TechnicalSupport (25%), QA (100%)")
|
||||
|
||||
print("\n🔍 Observability Features Demonstrated:")
|
||||
print("- Distributed tracing across agent interactions")
|
||||
print("- Custom metrics for business KPIs")
|
||||
print("- Performance monitoring and SLA tracking")
|
||||
print("- Error tracking and alerting")
|
||||
print("- Quality assurance metrics")
|
||||
print("- User journey tracking")
|
||||
print("- Resource utilization monitoring")
|
||||
|
||||
print("\n⚙️ Production Monitoring Setup:")
|
||||
print("- OpenTelemetry integration for standardized observability")
|
||||
print("- Custom metrics for business-specific KPIs")
|
||||
print("- Trace correlation across multi-agent workflows")
|
||||
print("- Performance benchmarking against expected durations")
|
||||
print("- Quality scoring and automated validation")
|
||||
print("- Error categorization and alerting")
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print("TELEMETRY DEMONSTRATION COMPLETED")
|
||||
print(f"{'='*80}")
|
||||
print("This example demonstrated comprehensive production telemetry including:")
|
||||
print("- Request tracing and performance monitoring")
|
||||
print("- Custom business metrics collection")
|
||||
print("- Quality assurance tracking")
|
||||
print("- Error handling and alerting")
|
||||
print("- Multi-agent workflow observability")
|
||||
print("- Production-ready monitoring patterns")
|
||||
@@ -14,7 +14,7 @@ def main():
|
||||
goal="Find information about a topic",
|
||||
backstory="You are an expert researcher who finds comprehensive information.",
|
||||
llm="gpt-4o-mini", # Use a cost-effective model for examples
|
||||
output={"metrics": True} # Token metrics via consolidated output config
|
||||
metrics=True # 🎯 SIMPLIFIED: Just add this parameter!
|
||||
)
|
||||
|
||||
# Create a simple task
|
||||
|
||||
@@ -14,7 +14,7 @@ def main():
|
||||
goal="Analyze data and provide insights",
|
||||
backstory="You are an experienced data analyst with expertise in market research.",
|
||||
llm="gpt-4o-mini",
|
||||
output={"metrics": True}
|
||||
metrics=True # 🎯 Enable metrics tracking
|
||||
)
|
||||
|
||||
writer_agent = Agent(
|
||||
@@ -23,7 +23,7 @@ def main():
|
||||
goal="Create well-structured content",
|
||||
backstory="You are a skilled technical writer who creates clear, engaging content.",
|
||||
llm="gpt-4o-mini",
|
||||
output={"metrics": True}
|
||||
metrics=True # 🎯 Enable metrics tracking
|
||||
)
|
||||
|
||||
# Create multiple tasks
|
||||
|
||||
@@ -14,7 +14,7 @@ def main():
|
||||
goal="Conduct comprehensive market research",
|
||||
backstory="You are a seasoned market researcher with 10+ years of experience.",
|
||||
llm="gpt-4o-mini",
|
||||
output={"metrics": True}
|
||||
metrics=True # 🎯 Enable automatic metrics tracking
|
||||
)
|
||||
|
||||
strategy_agent = Agent(
|
||||
@@ -23,7 +23,7 @@ def main():
|
||||
goal="Develop actionable business strategies",
|
||||
backstory="You are a strategic consultant who translates research into business plans.",
|
||||
llm="gpt-4o-mini",
|
||||
output={"metrics": True}
|
||||
metrics=True # 🎯 Enable automatic metrics tracking
|
||||
)
|
||||
|
||||
# Create tasks that will generate substantial token usage
|
||||
|
||||
@@ -22,8 +22,7 @@ rag_agent = Agent(
|
||||
|
||||
You can use the internet_search function to search the web when needed.""",
|
||||
llm="gpt-4o",
|
||||
output={"markdown": True},
|
||||
)
|
||||
markdown=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🤖 Thai Recipe RAG Agent is ready!")
|
||||
|
||||
@@ -5,17 +5,11 @@ from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
serper_api_key = os.getenv("SERPER_API_KEY")
|
||||
if serper_api_key is not None:
|
||||
os.environ["SERPER_API_KEY"] = serper_api_key
|
||||
os.environ['SERPER_API_KEY'] = os.getenv('SERPER_API_KEY')
|
||||
|
||||
search = GoogleSerperAPIWrapper()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not serper_api_key:
|
||||
print("SERPER_API_KEY is not set. Skipping Google Serper example.")
|
||||
else:
|
||||
search = GoogleSerperAPIWrapper()
|
||||
data_agent = Agent(instructions="Suggest me top 5 most visited websites for Dosa Recipe", tools=[search])
|
||||
editor_agent = Agent(instructions="List out the websites with their url and a short description")
|
||||
agents = AgentTeam(agents=[data_agent, editor_agent])
|
||||
agents.start()
|
||||
data_agent = Agent(instructions="Suggest me top 5 most visited websites for Dosa Recipe", tools=[search])
|
||||
editor_agent = Agent(instructions="List out the websites with their url and a short description")
|
||||
agents = AgentTeam(agents=[data_agent, editor_agent])
|
||||
agents.start()
|
||||
@@ -2,7 +2,6 @@ from praisonaiagents import Agent, Task, AgentTeam
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
from gitingest import ingest
|
||||
import sys
|
||||
|
||||
class CodeMetrics(BaseModel):
|
||||
category: str
|
||||
@@ -61,10 +60,7 @@ def analyze_code(code_source: str) -> CodeAnalysisReport:
|
||||
Analyze code from directory path or GitHub URL
|
||||
"""
|
||||
# Ingest code content
|
||||
try:
|
||||
summary, tree, content = ingest(code_source)
|
||||
except Exception as exc:
|
||||
raise RuntimeError(f"Failed to ingest code source: {exc}") from exc
|
||||
summary, tree, content = ingest(code_source)
|
||||
|
||||
# Concatenate context into structured format
|
||||
context_text = f"""
|
||||
@@ -90,22 +86,10 @@ def analyze_code(code_source: str) -> CodeAnalysisReport:
|
||||
tasks=[code_analysis_task]
|
||||
)
|
||||
|
||||
result = agents.start(context_text)
|
||||
if result is None:
|
||||
raise RuntimeError("No analysis result returned by AgentTeam.start()")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# Example usage
|
||||
code_source = "https://github.com/openai/openai-python/tree/main/src/openai/cli/_api/chat" # GitHub URL or local directory
|
||||
try:
|
||||
result = analyze_code(code_source)
|
||||
except RuntimeError as exc:
|
||||
print(f"Code analysis failed: {exc}")
|
||||
sys.exit(1)
|
||||
print(result)
|
||||
|
||||
return agents.start(context_text)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# Example usage
|
||||
code_source = "https://github.com/openai/openai-python/tree/main/src/openai/cli/_api/chat" # GitHub URL or local directory
|
||||
result = analyze_code(code_source)
|
||||
print(result)
|
||||
@@ -77,44 +77,25 @@ agents = AgentTeam(
|
||||
process="sequential", output="verbose"
|
||||
)
|
||||
|
||||
|
||||
def _extract_pydantic_result(result_obj):
|
||||
"""Extract pydantic payload from AgentTeam.start() output shapes."""
|
||||
if hasattr(result_obj, "pydantic"):
|
||||
return result_obj.pydantic
|
||||
if isinstance(result_obj, dict):
|
||||
task_results = result_obj.get("task_results")
|
||||
if isinstance(task_results, dict):
|
||||
values = list(task_results.values())
|
||||
if values and hasattr(values[-1], "pydantic"):
|
||||
return values[-1].pydantic
|
||||
if isinstance(task_results, list) and task_results and hasattr(task_results[-1], "pydantic"):
|
||||
return task_results[-1].pydantic
|
||||
return None
|
||||
|
||||
|
||||
# Start the analysis
|
||||
result = agents.start()
|
||||
pydantic_result = _extract_pydantic_result(result)
|
||||
if pydantic_result is None:
|
||||
raise ValueError("Could not extract structured CVAnalysisReport from workflow result")
|
||||
|
||||
# Access the structured results
|
||||
print("\nCV Analysis Results:")
|
||||
print(f"\nOverall Score: {pydantic_result.overall_score}/100")
|
||||
print(f"\nOverall Score: {result.pydantic.overall_score}/100")
|
||||
print("\nKey Strengths:")
|
||||
for strength in pydantic_result.key_strengths:
|
||||
for strength in result.pydantic.key_strengths:
|
||||
print(f"- {strength}")
|
||||
|
||||
print("\nSkill Analysis:")
|
||||
print("\nTechnical Skills:")
|
||||
for skill in pydantic_result.skill_analysis.technical_skills:
|
||||
for skill in result.pydantic.skill_analysis.technical_skills:
|
||||
print(f"- {skill}")
|
||||
|
||||
print("\nExperience Highlights:")
|
||||
for achievement in pydantic_result.experience_analysis.key_achievements:
|
||||
for achievement in result.pydantic.experience_analysis.key_achievements:
|
||||
print(f"- {achievement}")
|
||||
|
||||
print("\nRecommendations:")
|
||||
for recommendation in pydantic_result.recommendations:
|
||||
for recommendation in result.pydantic.recommendations:
|
||||
print(f"- {recommendation}")
|
||||
@@ -100,8 +100,6 @@ def evaluate_project(video_path: str) -> ProjectEvaluation:
|
||||
evaluation_data = response
|
||||
|
||||
print(f"Debug - Parsed evaluation_data: {evaluation_data}")
|
||||
if not evaluation_data or not isinstance(evaluation_data, dict):
|
||||
raise ValueError("No structured evaluation data returned by agent")
|
||||
|
||||
# Create and return ProjectEvaluation instance
|
||||
return ProjectEvaluation(
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
from praisonaiagents import VideoAgent
|
||||
import os
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not os.getenv("AZURE_API_BASE"):
|
||||
print("AZURE_API_BASE is not configured. Skipping Azure video example.")
|
||||
else:
|
||||
agent = VideoAgent(llm="azure/sora-2")
|
||||
video = agent.generate("Mountain landscape")
|
||||
print(video.id)
|
||||
agent = VideoAgent(llm="azure/sora-2")
|
||||
video = agent.generate("Mountain landscape")
|
||||
print(video.id)
|
||||
@@ -6,7 +6,6 @@ with Agents for workflow monitoring and logging.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Task, AgentTeam
|
||||
from praisonaiagents.config.feature_configs import MultiAgentHooksConfig
|
||||
|
||||
# Create an agent
|
||||
researcher = Agent(
|
||||
@@ -48,10 +47,8 @@ if __name__ == "__main__":
|
||||
agents=[researcher],
|
||||
tasks=[task1, task2],
|
||||
process="workflow",
|
||||
hooks=MultiAgentHooksConfig(
|
||||
on_task_start=on_start,
|
||||
on_task_complete=on_complete,
|
||||
),
|
||||
on_task_start=on_start,
|
||||
on_task_complete=on_complete,
|
||||
variables={"global_var": "shared_value"} # Global variables
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ to different next steps based on output content.
|
||||
"""
|
||||
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
from praisonaiagents import AgentFlowManager
|
||||
|
||||
# Create a workflow with branching
|
||||
workflow = AgentFlow(
|
||||
@@ -36,16 +35,9 @@ workflow = AgentFlow(
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create manager and register workflow
|
||||
manager = WorkflowManager()
|
||||
manager.workflows["Decision Workflow"] = workflow
|
||||
|
||||
# Execute - will branch to success_handler since 42 is positive
|
||||
result = manager.execute(
|
||||
"Decision Workflow",
|
||||
default_llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
print("Workflow completed!")
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['status']}")
|
||||
for step in result.get("steps", []):
|
||||
name = step.get("step", step.get("name", "unknown"))
|
||||
status = step.get("status", "completed")
|
||||
print(f" {name}: {status}")
|
||||
@@ -54,13 +54,13 @@ workflow = AgentFlow(
|
||||
analyzer, # Always runs
|
||||
Task(
|
||||
name="tech_review",
|
||||
description="Review technical aspects when technical keywords are detected.",
|
||||
description="Review technical aspects of the content",
|
||||
agent=tech_reviewer,
|
||||
should_run=is_technical # Only runs for technical content
|
||||
),
|
||||
Task(
|
||||
name="creative_enhance",
|
||||
description="Enhance narrative quality when creative keywords are detected.",
|
||||
description="Enhance the creative aspects of the content",
|
||||
agent=creative_enhancer,
|
||||
should_run=needs_creative # Only runs for creative content
|
||||
),
|
||||
|
||||
@@ -6,9 +6,8 @@ functions that return StepResult with stop_workflow=True.
|
||||
"""
|
||||
|
||||
from praisonaiagents import (
|
||||
Workflow, Task, WorkflowContext, StepResult
|
||||
AgentFlow, Task, WorkflowContext, StepResult
|
||||
)
|
||||
from praisonaiagents import AgentFlowManager
|
||||
|
||||
# Custom validator that can stop the workflow
|
||||
def validate_data(context: WorkflowContext) -> StepResult:
|
||||
@@ -50,20 +49,13 @@ workflow = AgentFlow(
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
manager = WorkflowManager()
|
||||
manager.workflows["Data Processing"] = workflow
|
||||
|
||||
# Test with invalid data (will stop early)
|
||||
print("=== Testing with invalid data ===")
|
||||
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
|
||||
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['output']}")
|
||||
|
||||
# Test with valid data (will complete)
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
for step in result.get("steps", []):
|
||||
print(f" {step.get('step', 'step')}: {step.get('output', step.get('status'))}")
|
||||
|
||||
print("\n=== Testing with valid data ===")
|
||||
workflow.variables["data"] = {"value": 42}
|
||||
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
|
||||
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['status']}")
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
for step in result.get("steps", []):
|
||||
print(f" {step.get('step', 'step')}: {step.get('status', 'completed')}")
|
||||
@@ -6,7 +6,6 @@ Each item is processed individually with the loop variable.
|
||||
"""
|
||||
|
||||
from praisonaiagents import AgentFlow, Task
|
||||
from praisonaiagents import AgentFlowManager
|
||||
|
||||
# Create a workflow with loop
|
||||
workflow = AgentFlow(
|
||||
@@ -30,18 +29,12 @@ workflow = AgentFlow(
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create manager and register workflow
|
||||
manager = WorkflowManager()
|
||||
manager.workflows["Process Items"] = workflow
|
||||
|
||||
# Execute - will process each fruit in the list
|
||||
result = manager.execute(
|
||||
"Process Items",
|
||||
default_llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
|
||||
print("Workflow completed!")
|
||||
for step_result in result["results"]:
|
||||
print(f" {step_result['step']}: {step_result['status']}")
|
||||
if isinstance(step_result.get("output"), list):
|
||||
print(f" Loop processed {len(step_result['output'])} items")
|
||||
for step in result.get("steps", []):
|
||||
name = step.get("step", step.get("name", "unknown"))
|
||||
status = step.get("status", "completed")
|
||||
print(f" {name}: {status}")
|
||||
out = step.get("output")
|
||||
if isinstance(out, list):
|
||||
print(f" Loop processed {len(out)} items")
|
||||
@@ -53,7 +53,7 @@ workflow = AgentFlow(
|
||||
# Step 4: Function - formats output
|
||||
format_output
|
||||
],
|
||||
llm="gpt-4o-mini", # Used for action-based steps
|
||||
llm="gpt-4o-mini" # Used for action-based steps
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -5,7 +5,7 @@ Demonstrates running multiple agents concurrently and
|
||||
combining their results with an aggregator agent.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Workflow
|
||||
from praisonaiagents import Agent, AgentFlow
|
||||
from praisonaiagents.workflows import parallel
|
||||
|
||||
# Create parallel research agents
|
||||
|
||||
@@ -42,7 +42,6 @@ def main():
|
||||
|
||||
# Create tasks with robustness features
|
||||
research_task = Task(
|
||||
name="research",
|
||||
description="Research the latest trends in AI agents for 2025",
|
||||
expected_output="A summary of key AI agent trends",
|
||||
agent=researcher,
|
||||
@@ -52,7 +51,6 @@ def main():
|
||||
|
||||
# Optional enrichment step - workflow continues even if this fails
|
||||
enrich_task = Task(
|
||||
name="enrich",
|
||||
description="Enrich the research with real-world examples",
|
||||
expected_output="Research with added examples",
|
||||
agent=enricher,
|
||||
@@ -62,7 +60,6 @@ def main():
|
||||
)
|
||||
|
||||
write_task = Task(
|
||||
name="write",
|
||||
description="Write a brief summary based on the research",
|
||||
expected_output="A well-written summary paragraph",
|
||||
agent=writer,
|
||||
|
||||
@@ -5,7 +5,7 @@ Demonstrates decision-based routing where an agent classifier
|
||||
routes to specialized handler agents based on the request type.
|
||||
"""
|
||||
|
||||
from praisonaiagents import Agent, Workflow
|
||||
from praisonaiagents import Agent, AgentFlow
|
||||
from praisonaiagents.workflows import route
|
||||
|
||||
# Create classifier agent
|
||||
|
||||
@@ -51,15 +51,13 @@ print("=" * 60)
|
||||
agent = Agent(
|
||||
name="Knowledge Agent",
|
||||
instructions="You are a helpful assistant that answers questions based on the provided knowledge. Be concise.",
|
||||
knowledge={
|
||||
"sources": [".praison/test_docs/overview.txt"],
|
||||
"config": {
|
||||
"policy": "always",
|
||||
"top_k": 3,
|
||||
"citations": True,
|
||||
"max_context_tokens": 2000,
|
||||
},
|
||||
},
|
||||
knowledge=[".praison/test_docs/overview.txt"],
|
||||
retrieval_config={
|
||||
"policy": "always", # Always retrieve for this demo
|
||||
"top_k": 3, # Get top 3 chunks
|
||||
"citations": True, # Include citations
|
||||
"max_context_tokens": 2000,
|
||||
}
|
||||
)
|
||||
|
||||
# Test 1: Basic chat with retrieval
|
||||
|
||||
@@ -60,7 +60,7 @@ print("=" * 50)
|
||||
agent_with_discovery = Agent(
|
||||
name="Multi-Skill Agent",
|
||||
instructions="You are a versatile assistant with multiple skills.",
|
||||
skills=["./"], # Scan current directory for skill subdirectories
|
||||
skills_dirs=["./"], # Scan current directory for skill subdirectories
|
||||
)
|
||||
|
||||
if agent_with_discovery.skill_manager:
|
||||
|
||||
@@ -190,7 +190,7 @@ def example_run_history_with_backend():
|
||||
|
||||
# Use with RunHistory
|
||||
history = RunHistory(backend=backend)
|
||||
runs = history.list_runs()
|
||||
runs = history.list()
|
||||
|
||||
print(f"RunHistory created with SQLite backend, {len(runs)} runs")
|
||||
print("Recipe runs will be stored in database instead of files")
|
||||
|
||||
@@ -257,7 +257,7 @@ if __name__ == "__main__":
|
||||
# Check dependencies
|
||||
try:
|
||||
import praisonaiagents
|
||||
print(f"✅ PraisonAI version: {getattr(praisonaiagents, '__version__', None)}")
|
||||
print(f"✅ PraisonAI version: {praisonaiagents.__version__}")
|
||||
except ImportError:
|
||||
print("❌ PraisonAI not installed: pip install praisonaiagents")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -12,7 +12,7 @@ assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
|
||||
# Enable web search and fetch
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant with web access.",
|
||||
web="search_only",
|
||||
web=True,
|
||||
)
|
||||
|
||||
# Disable web (default)
|
||||
|
||||
@@ -4,7 +4,7 @@ WebConfig Example
|
||||
Demonstrates using WebConfig for fine-grained control.
|
||||
"""
|
||||
import os
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents import Agent, WebConfig
|
||||
|
||||
# Ensure API key is set from environment
|
||||
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
|
||||
@@ -12,13 +12,21 @@ assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
|
||||
# Custom web configuration
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant.",
|
||||
web="search_only",
|
||||
web=WebConfig(
|
||||
search=True,
|
||||
fetch=True,
|
||||
search_provider="duckduckgo",
|
||||
max_results=5,
|
||||
),
|
||||
)
|
||||
|
||||
# Search only (no fetch)
|
||||
agent_search_only = Agent(
|
||||
instructions="You are a research assistant.",
|
||||
web="search_only",
|
||||
web=WebConfig(
|
||||
search=True,
|
||||
fetch=False,
|
||||
),
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -93,7 +93,7 @@ def example_3_execute_yaml_directly():
|
||||
result = manager.execute_yaml(
|
||||
yaml_file,
|
||||
input_data="The future of AI",
|
||||
verbose=True,
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
print(f"Status: {result['status']}")
|
||||
|
||||
@@ -114,6 +114,9 @@ def _get_lazy_cache():
|
||||
# ============================================================================
|
||||
|
||||
_LAZY_IMPORTS = {
|
||||
# Version information (lazy loaded to avoid file I/O on import)
|
||||
'__version__': ('praisonaiagents._version', '__version__'),
|
||||
|
||||
# Tools (moved from eager imports for lazy loading)
|
||||
'Tools': ('praisonaiagents.tools.tools', 'Tools'),
|
||||
'BaseTool': ('praisonaiagents.tools.base', 'BaseTool'),
|
||||
@@ -775,6 +778,9 @@ def warmup(include_litellm: bool = False, include_openai: bool = True) -> dict:
|
||||
# ============================================================================
|
||||
|
||||
__all__ = [
|
||||
# Version information
|
||||
'__version__',
|
||||
|
||||
# Core classes - the essentials
|
||||
'Agent',
|
||||
'AgentTeam', # Primary class for multi-agent coordination (v1.0+)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
Version utilities for praisonaiagents package.
|
||||
|
||||
This module provides version information by reading from pyproject.toml
|
||||
to avoid duplication and ensure the single source of truth.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
"""
|
||||
Get the version string from pyproject.toml.
|
||||
|
||||
Returns:
|
||||
str: Version string (e.g., "1.6.52")
|
||||
|
||||
Raises:
|
||||
RuntimeError: If version cannot be found or parsed
|
||||
"""
|
||||
try:
|
||||
# Get the package root (praisonaiagents directory)
|
||||
package_root = Path(__file__).parent
|
||||
# Go up to src/praisonai-agents directory
|
||||
pyproject_path = package_root.parent / "pyproject.toml"
|
||||
|
||||
if not pyproject_path.exists():
|
||||
raise RuntimeError(f"pyproject.toml not found at {pyproject_path}")
|
||||
|
||||
content = pyproject_path.read_text()
|
||||
|
||||
# Look for version = "X.Y.Z" pattern
|
||||
match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content)
|
||||
if not match:
|
||||
raise RuntimeError("Version not found in pyproject.toml")
|
||||
|
||||
return match.group(1)
|
||||
|
||||
except Exception as e:
|
||||
# Fallback to "unknown" to avoid breaking imports
|
||||
import warnings
|
||||
warnings.warn(
|
||||
f"Failed to read version from pyproject.toml: {e}. Using 'unknown'.",
|
||||
RuntimeWarning
|
||||
)
|
||||
return "unknown"
|
||||
|
||||
|
||||
# Cache the version to avoid reading the file multiple times
|
||||
__version__ = get_version()
|
||||
@@ -353,9 +353,9 @@ class RouterAgent(Agent):
|
||||
|
||||
return report
|
||||
|
||||
def _build_system_prompt(self, tools=None, **kwargs) -> str:
|
||||
def _build_system_prompt(self) -> str:
|
||||
"""Build system prompt (inherited from parent but can be customized)."""
|
||||
base_prompt = super()._build_system_prompt(tools=tools, **kwargs)
|
||||
base_prompt = super()._build_system_prompt()
|
||||
|
||||
# Add multi-model context if needed
|
||||
if self.routing_strategy == "auto":
|
||||
|
||||
@@ -652,7 +652,7 @@ class AgentTeam(SpawnAnnounceProtocol):
|
||||
_stream = _output_config.stream
|
||||
else:
|
||||
_verbose = 0
|
||||
_stream = True
|
||||
_stream = False
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Resolve EXECUTION param using canonical resolver
|
||||
|
||||
@@ -1089,8 +1089,8 @@ class MultiAgentOutputConfig:
|
||||
# Verbosity level (0=silent, 1=minimal, 2+=verbose)
|
||||
verbose: int = 0
|
||||
|
||||
# Streaming output
|
||||
stream: bool = True
|
||||
# Streaming output - False by default for sync multi-agent compatibility
|
||||
stream: bool = False
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
|
||||
@@ -322,8 +322,8 @@ CACHING_PRESETS: Dict[str, Dict[str, Any]] = {
|
||||
# =============================================================================
|
||||
|
||||
MULTI_AGENT_OUTPUT_PRESETS: Dict[str, Dict[str, Any]] = {
|
||||
"verbose": {"verbose": 2, "stream": True},
|
||||
"minimal": {"verbose": 1, "stream": True},
|
||||
"verbose": {"verbose": 2, "stream": False},
|
||||
"minimal": {"verbose": 1, "stream": False},
|
||||
"silent": {"verbose": 0, "stream": False},
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import xml.etree.ElementTree as ET
|
||||
from ..errors import AgentErrorKind, FailoverDecision, IdleTimeoutBreaker
|
||||
# Gap 2: Tool call execution imports
|
||||
from ..tools.call_executor import ToolCall, create_tool_call_executor
|
||||
from ..tools.schema import annotation_to_json_schema, get_parameter_requirements
|
||||
# Display functions - lazy loaded to avoid importing rich at startup
|
||||
# These are only needed when output=verbose
|
||||
_display_module = None
|
||||
@@ -5886,34 +5887,74 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
function_name = function_or_name
|
||||
logging.debug(f"Attempting to generate tool definition for: {function_name}")
|
||||
|
||||
# First try to get the tool definition if it exists
|
||||
tool_def_name = f"{function_name}_definition"
|
||||
tool_def = globals().get(tool_def_name)
|
||||
logging.debug(f"Looking for {tool_def_name} in globals: {tool_def is not None}")
|
||||
|
||||
if not tool_def:
|
||||
import __main__
|
||||
tool_def = getattr(__main__, tool_def_name, None)
|
||||
logging.debug(f"Looking for {tool_def_name} in __main__: {tool_def is not None}")
|
||||
|
||||
if tool_def:
|
||||
logging.debug(f"Found tool definition: {tool_def}")
|
||||
return tool_def
|
||||
|
||||
# Try to find the function
|
||||
func = globals().get(function_name)
|
||||
logging.debug(f"Looking for {function_name} in globals: {func is not None}")
|
||||
|
||||
if not func:
|
||||
import __main__
|
||||
func = getattr(__main__, function_name, None)
|
||||
logging.debug(f"Looking for {function_name} in __main__: {func is not None}")
|
||||
|
||||
if not func or not callable(func):
|
||||
logging.debug(f"Function {function_name} not found or not callable")
|
||||
return None
|
||||
# First try to get from the tool registry (preferred method)
|
||||
try:
|
||||
from ..tools.registry import get_registry
|
||||
registry = get_registry()
|
||||
tool = registry.get(function_name)
|
||||
if tool:
|
||||
logging.debug(f"Found tool in registry: {function_name}")
|
||||
# If it's a BaseTool, get its schema
|
||||
if hasattr(tool, 'get_schema'):
|
||||
tool_def = tool.get_schema()
|
||||
# Apply same normalization as the callable path
|
||||
if (
|
||||
isinstance(tool_def, dict)
|
||||
and isinstance(tool_def.get("function"), dict)
|
||||
and isinstance(tool_def["function"].get("parameters"), dict)
|
||||
):
|
||||
tool_def = tool_def.copy()
|
||||
tool_def["function"] = tool_def["function"].copy()
|
||||
tool_def["function"]["parameters"] = self._fix_array_schemas(
|
||||
tool_def["function"]["parameters"]
|
||||
)
|
||||
return tool_def
|
||||
# If it's a callable, use it as func
|
||||
if callable(tool):
|
||||
func = tool
|
||||
else:
|
||||
logging.debug(f"Tool {function_name} in registry is not callable")
|
||||
return None
|
||||
else:
|
||||
logging.debug(f"Tool {function_name} not found in registry, falling back to globals/__main__")
|
||||
# Fall back to globals and __main__ for backward compatibility
|
||||
tool_def_name = f"{function_name}_definition"
|
||||
tool_def = globals().get(tool_def_name)
|
||||
if not tool_def:
|
||||
import __main__
|
||||
tool_def = getattr(__main__, tool_def_name, None)
|
||||
if tool_def:
|
||||
return tool_def
|
||||
|
||||
func = globals().get(function_name)
|
||||
if not func:
|
||||
import __main__
|
||||
func = getattr(__main__, function_name, None)
|
||||
if not func or not callable(func):
|
||||
logging.debug(f"Function {function_name} not found or not callable")
|
||||
return None
|
||||
except ImportError:
|
||||
logging.debug("Tool registry not available, falling back to globals/__main__")
|
||||
# Fall back to globals and __main__ when registry unavailable
|
||||
tool_def_name = f"{function_name}_definition"
|
||||
tool_def = globals().get(tool_def_name)
|
||||
if not tool_def:
|
||||
import __main__
|
||||
tool_def = getattr(__main__, tool_def_name, None)
|
||||
if tool_def:
|
||||
return tool_def
|
||||
|
||||
func = globals().get(function_name)
|
||||
if not func:
|
||||
import __main__
|
||||
func = getattr(__main__, function_name, None)
|
||||
if not func or not callable(func):
|
||||
logging.debug(f"Function {function_name} not found or not callable")
|
||||
return None
|
||||
|
||||
import inspect
|
||||
from typing import get_type_hints
|
||||
|
||||
# Handle Langchain and CrewAI tools
|
||||
if inspect.isclass(func) and hasattr(func, 'run') and not hasattr(func, '_run'):
|
||||
original_func = func
|
||||
@@ -5961,26 +6002,26 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
|
||||
logging.debug(f"Parameter descriptions: {param_descriptions}")
|
||||
|
||||
# Get type hints for proper schema generation
|
||||
try:
|
||||
hints = get_type_hints(func) if getattr(func, "__annotations__", None) else {}
|
||||
except (NameError, TypeError, AttributeError):
|
||||
hints = getattr(func, "__annotations__", {}) or {}
|
||||
|
||||
for name, param in parameters_list:
|
||||
param_type = "string" # Default type
|
||||
if param.annotation != inspect.Parameter.empty:
|
||||
if param.annotation == int:
|
||||
param_type = "integer"
|
||||
elif param.annotation == float:
|
||||
param_type = "number"
|
||||
elif param.annotation == bool:
|
||||
param_type = "boolean"
|
||||
elif param.annotation == list:
|
||||
param_type = "array"
|
||||
elif param.annotation == dict:
|
||||
param_type = "object"
|
||||
# Get type annotation
|
||||
param_type = hints.get(name, param.annotation if param.annotation != inspect.Parameter.empty else str)
|
||||
|
||||
parameters["properties"][name] = {
|
||||
"type": param_type,
|
||||
"description": param_descriptions.get(name, "Parameter description not available")
|
||||
}
|
||||
# Use new schema utility for proper type handling
|
||||
prop_schema = annotation_to_json_schema(param_type)
|
||||
|
||||
if param.default == inspect.Parameter.empty:
|
||||
# Add description from docstring
|
||||
prop_schema["description"] = param_descriptions.get(name, "Parameter description not available")
|
||||
|
||||
parameters["properties"][name] = prop_schema
|
||||
|
||||
# Check if required using improved logic
|
||||
if get_parameter_requirements(sig, name):
|
||||
parameters["required"].append(name)
|
||||
|
||||
logging.debug(f"Generated parameters: {parameters}")
|
||||
|
||||
@@ -29,6 +29,9 @@ logger = get_logger(__name__, extra_data={"subsystem": "memory"})
|
||||
TRACE_LEVEL = 5
|
||||
logging.addLevelName(TRACE_LEVEL, 'TRACE')
|
||||
|
||||
# Cache boundary constants for prompt prefix caching optimization
|
||||
CACHE_BOUNDARY = "\n\n<!-- CACHE_BOUNDARY -->\n\n"
|
||||
|
||||
|
||||
|
||||
class Memory(StorageMixin, SearchMixin, MemoryCoreMixin):
|
||||
@@ -1704,6 +1707,41 @@ class Memory(StorageMixin, SearchMixin, MemoryCoreMixin):
|
||||
entities = self.search_entity(q, limit=max_items)
|
||||
user_mem = self.search_user_memory(user_id, q, limit=max_items) if user_id else []
|
||||
|
||||
# Apply stable sorting to ensure deterministic order for prompt caching
|
||||
# Sort by timestamp descending, then by content hash for stable ordering
|
||||
def _sort_memory_results(results: List[Any]) -> List[Any]:
|
||||
"""Stable sort so identical query sets produce identical context strings."""
|
||||
import hashlib
|
||||
if not results:
|
||||
return results
|
||||
|
||||
def sort_key(r):
|
||||
if not isinstance(r, dict):
|
||||
return (0.0, hashlib.sha256(str(r).encode()).hexdigest())
|
||||
|
||||
# Prefer created_at over timestamp for better determinism
|
||||
timestamp = r.get("created_at") or r.get("timestamp") or 0
|
||||
if isinstance(timestamp, str):
|
||||
try:
|
||||
from datetime import datetime
|
||||
timestamp = datetime.fromisoformat(timestamp.replace("Z", "+00:00")).timestamp()
|
||||
except ValueError:
|
||||
timestamp = 0.0
|
||||
timestamp = -(float(timestamp)) # Negative for descending order
|
||||
|
||||
# Use full content hash for true deterministic ordering
|
||||
content = r.get("text", "") if isinstance(r, dict) else str(r)
|
||||
content_hash = hashlib.sha256(content.encode()).hexdigest()
|
||||
return (timestamp, content_hash)
|
||||
|
||||
return sorted(results, key=sort_key)
|
||||
|
||||
# Apply stable sorting to all memory results
|
||||
short_term = _sort_memory_results(short_term)
|
||||
long_term = _sort_memory_results(long_term)
|
||||
entities = _sort_memory_results(entities)
|
||||
user_mem = _sort_memory_results(user_mem)
|
||||
|
||||
# Add sections in order of priority
|
||||
add_section("Short-term Memory Context", short_term)
|
||||
add_section("Long-term Memory Context", long_term)
|
||||
@@ -1713,6 +1751,52 @@ class Memory(StorageMixin, SearchMixin, MemoryCoreMixin):
|
||||
|
||||
return "\n".join(lines) if lines else ""
|
||||
|
||||
def build_cache_optimized_context(
|
||||
self,
|
||||
task_descr: str,
|
||||
user_id: Optional[str] = None,
|
||||
additional: str = "",
|
||||
max_items: int = 3,
|
||||
include_cache_boundary: bool = True,
|
||||
include_in_output: Optional[bool] = None
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Build context with cache boundary markers for prompt prefix caching optimization.
|
||||
|
||||
Returns a dictionary with 'stable_prefix' and 'cache_boundary' keys.
|
||||
The stable prefix contains deterministically ordered content that should
|
||||
remain constant between turns for effective prompt caching.
|
||||
|
||||
Args:
|
||||
task_descr: Task description for memory search
|
||||
user_id: Optional user ID for personalized memory
|
||||
additional: Additional context to include in search
|
||||
max_items: Maximum items per memory category
|
||||
include_cache_boundary: Whether to include cache boundary marker
|
||||
include_in_output: Whether to include memory content in output
|
||||
|
||||
Returns:
|
||||
Dict with 'stable_prefix' and 'cache_boundary' keys
|
||||
"""
|
||||
# Cache-optimized API should include stable content unless explicitly disabled
|
||||
if include_in_output is None:
|
||||
include_in_output = True
|
||||
|
||||
# Build the stable context using existing method
|
||||
stable_context = self.build_context_for_task(
|
||||
task_descr=task_descr,
|
||||
user_id=user_id,
|
||||
additional=additional,
|
||||
max_items=max_items,
|
||||
include_in_output=include_in_output
|
||||
)
|
||||
|
||||
result = {"stable_prefix": stable_context}
|
||||
|
||||
result["cache_boundary"] = CACHE_BOUNDARY if include_cache_boundary else ""
|
||||
|
||||
return result
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Master Reset (Everything)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -17,9 +17,12 @@ Usage:
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Callable, Dict, List, Optional, Type, get_type_hints
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import copy
|
||||
|
||||
from .schema import annotation_to_json_schema, get_parameter_requirements
|
||||
|
||||
|
||||
class ToolValidationError(Exception):
|
||||
"""Raised when a tool fails validation."""
|
||||
@@ -129,18 +132,17 @@ class BaseTool(ABC):
|
||||
|
||||
# Get type hint
|
||||
param_type = hints.get(param_name, Any)
|
||||
json_type = self._python_type_to_json(param_type)
|
||||
|
||||
# Build property schema
|
||||
prop_schema = {"type": json_type}
|
||||
# Use new schema utility for proper type handling
|
||||
prop_schema = annotation_to_json_schema(param_type)
|
||||
|
||||
# Add description from docstring if available
|
||||
# (Could parse docstring for param descriptions)
|
||||
|
||||
schema["properties"][param_name] = prop_schema
|
||||
|
||||
# Check if required (no default value)
|
||||
if param.default is inspect.Parameter.empty:
|
||||
# Check if required using improved logic
|
||||
if get_parameter_requirements(sig, param_name):
|
||||
schema["required"].append(param_name)
|
||||
except Exception as e:
|
||||
logging.debug(f"Could not generate schema for {self.name}: {e}")
|
||||
@@ -149,28 +151,15 @@ class BaseTool(ABC):
|
||||
|
||||
@staticmethod
|
||||
def _python_type_to_json(python_type: Type) -> str:
|
||||
"""Convert Python type to JSON Schema type."""
|
||||
type_map = {
|
||||
str: "string",
|
||||
int: "integer",
|
||||
float: "number",
|
||||
bool: "boolean",
|
||||
list: "array",
|
||||
dict: "object",
|
||||
type(None): "null"
|
||||
}
|
||||
"""Convert Python type to JSON Schema type.
|
||||
|
||||
# Handle Optional, Union, etc.
|
||||
origin = getattr(python_type, '__origin__', None)
|
||||
if origin is not None:
|
||||
# For List[X], return "array"
|
||||
if origin is list:
|
||||
return "array"
|
||||
# For Dict[X, Y], return "object"
|
||||
if origin is dict:
|
||||
return "object"
|
||||
|
||||
return type_map.get(python_type, "string")
|
||||
DEPRECATED: Use annotation_to_json_schema() from schema.py instead.
|
||||
This method is kept for backward compatibility but will be removed.
|
||||
"""
|
||||
# Legacy fallback - delegate to new schema utility and extract type
|
||||
from .schema import annotation_to_json_schema
|
||||
schema = annotation_to_json_schema(python_type)
|
||||
return schema.get("type", "string")
|
||||
|
||||
@abstractmethod
|
||||
def run(self, **kwargs) -> Any:
|
||||
@@ -442,5 +431,49 @@ def validate_tool_schema_consistency(tools: List[Any]) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def get_sorted_tool_schemas(tools: List[Any]) -> List[Dict[str, Any]]:
|
||||
"""Get tool schemas sorted by function name for deterministic ordering.
|
||||
|
||||
This ensures tool schemas are always ordered consistently for prompt caching optimization.
|
||||
|
||||
Args:
|
||||
tools: List of tool objects (BaseTool instances, callables, etc.)
|
||||
|
||||
Returns:
|
||||
List of tool schemas sorted alphabetically by function name
|
||||
|
||||
Raises:
|
||||
ToolValidationError: If validation fails
|
||||
"""
|
||||
if not tools:
|
||||
return []
|
||||
|
||||
# First validate all tools (reuse existing validation logic)
|
||||
validate_tool_schema_consistency(tools)
|
||||
|
||||
from .decorator import get_tool_schema
|
||||
schemas = []
|
||||
|
||||
for tool in tools:
|
||||
# Get schema from different tool types
|
||||
if isinstance(tool, BaseTool):
|
||||
schema = tool.get_schema()
|
||||
elif hasattr(tool, 'get_schema') and callable(getattr(tool, 'get_schema')):
|
||||
schema = tool.get_schema()
|
||||
elif callable(tool):
|
||||
schema = get_tool_schema(tool)
|
||||
else:
|
||||
continue # Skip invalid tools (validation already happened)
|
||||
|
||||
if schema:
|
||||
schemas.append(schema)
|
||||
|
||||
# Sort schemas by function name for deterministic ordering
|
||||
def sort_key(schema):
|
||||
return schema.get("function", {}).get("name", "")
|
||||
|
||||
return sorted(schemas, key=sort_key)
|
||||
|
||||
|
||||
# For backward compatibility - tools can also just be functions
|
||||
# The @tool decorator (in decorator.py) wraps functions into BaseTool instances
|
||||
@@ -31,6 +31,7 @@ import copy
|
||||
from typing import Any, Callable, Dict, Optional, Union, get_type_hints
|
||||
|
||||
from .base import BaseTool
|
||||
from .schema import annotation_to_json_schema, get_parameter_requirements
|
||||
|
||||
# Lazy load injected module functions to reduce import time
|
||||
_injected_module = None
|
||||
@@ -132,12 +133,13 @@ class FunctionTool(BaseTool):
|
||||
if is_injected_type(param_type):
|
||||
continue
|
||||
|
||||
json_type = BaseTool._python_type_to_json(param_type)
|
||||
# Use new schema utility for proper type handling
|
||||
prop_schema = annotation_to_json_schema(param_type)
|
||||
|
||||
schema["properties"][param_name] = {"type": json_type}
|
||||
schema["properties"][param_name] = prop_schema
|
||||
|
||||
# Check if required (no default value)
|
||||
if param.default is inspect.Parameter.empty:
|
||||
# Check if required using improved logic
|
||||
if get_parameter_requirements(sig, param_name):
|
||||
schema["required"].append(param_name)
|
||||
except Exception as e:
|
||||
logging.debug(f"Could not generate schema for {func.__name__}: {e}")
|
||||
@@ -342,11 +344,14 @@ def _schema_from_function(func: Callable) -> Dict[str, Any]:
|
||||
continue
|
||||
|
||||
param_type = hints.get(param_name, Any)
|
||||
json_type = BaseTool._python_type_to_json(param_type)
|
||||
|
||||
properties[param_name] = {"type": json_type}
|
||||
# Use new schema utility for proper type handling
|
||||
prop_schema = annotation_to_json_schema(param_type)
|
||||
|
||||
if param.default is inspect.Parameter.empty:
|
||||
properties[param_name] = prop_schema
|
||||
|
||||
# Check if required using improved logic
|
||||
if get_parameter_requirements(sig, param_name):
|
||||
required.append(param_name)
|
||||
except Exception as e:
|
||||
logging.debug(f"Could not generate schema for {name}: {e}")
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
"""JSON Schema generation utilities for tool parameters.
|
||||
|
||||
This module provides a shared utility for converting Python type annotations
|
||||
to JSON Schema format. It handles complex types like Optional, Union, Literal,
|
||||
and Enum that were previously collapsed to simple "string" types.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools.schema import annotation_to_json_schema
|
||||
|
||||
schema = annotation_to_json_schema(Optional[int])
|
||||
# {"anyOf": [{"type": "integer"}, {"type": "null"}]}
|
||||
|
||||
schema = annotation_to_json_schema(Literal["fast", "deep"])
|
||||
# {"type": "string", "enum": ["fast", "deep"]}
|
||||
"""
|
||||
|
||||
import enum
|
||||
import inspect
|
||||
from typing import Any, Union, Literal, get_origin, get_args
|
||||
|
||||
|
||||
def annotation_to_json_schema(annotation: Any) -> dict:
|
||||
"""Convert Python type annotation to JSON Schema format.
|
||||
|
||||
Handles:
|
||||
- Basic types (str, int, float, bool)
|
||||
- Optional[T] -> {"anyOf": [T schema, {"type": "null"}]}
|
||||
- Union[T1, T2] -> {"anyOf": [T1 schema, T2 schema]}
|
||||
- Literal["a", "b"] -> {"type": "string", "enum": ["a", "b"]}
|
||||
- Enum subclasses -> {"type": "string", "enum": [e.value for e in MyEnum]}
|
||||
- List[T] -> {"type": "array", "items": T schema}
|
||||
- Dict[K, V] -> {"type": "object", "additionalProperties": V schema}
|
||||
|
||||
Args:
|
||||
annotation: Python type annotation to convert
|
||||
|
||||
Returns:
|
||||
dict: JSON Schema representation
|
||||
"""
|
||||
# Handle None type
|
||||
if annotation is type(None):
|
||||
return {"type": "null"}
|
||||
|
||||
# Get typing origin and args
|
||||
origin = get_origin(annotation)
|
||||
args = get_args(annotation)
|
||||
|
||||
# Handle Union types (including Optional[T] which is Union[T, None])
|
||||
if origin is Union:
|
||||
schemas = [annotation_to_json_schema(arg) for arg in args]
|
||||
|
||||
# Check if this is Optional[T] (Union[T, None])
|
||||
null_schemas = [s for s in schemas if s == {"type": "null"}]
|
||||
non_null_schemas = [s for s in schemas if s != {"type": "null"}]
|
||||
|
||||
if null_schemas and len(non_null_schemas) == 1:
|
||||
# This is Optional[T] - simplified format
|
||||
return {"anyOf": [non_null_schemas[0], {"type": "null"}]}
|
||||
else:
|
||||
# General Union type
|
||||
return {"anyOf": schemas}
|
||||
|
||||
# Handle Literal types
|
||||
if origin is Literal or (hasattr(annotation, '__origin__') and
|
||||
str(annotation.__origin__).endswith('Literal')):
|
||||
# Infer type from literal values if all are the same type
|
||||
values = list(args)
|
||||
if values:
|
||||
types_seen = set(type(v) for v in values)
|
||||
if len(types_seen) == 1:
|
||||
single_type = next(iter(types_seen))
|
||||
type_map = {str: "string", int: "integer", float: "number", bool: "boolean"}
|
||||
if single_type in type_map:
|
||||
return {"type": type_map[single_type], "enum": values}
|
||||
# Mixed types or non-primitive - use enum without type constraint
|
||||
return {"enum": values}
|
||||
|
||||
# Handle Enum subclasses
|
||||
if isinstance(annotation, type) and issubclass(annotation, enum.Enum):
|
||||
# Infer type from enum values if all are the same type
|
||||
values = [e.value for e in annotation]
|
||||
if values:
|
||||
types_seen = set(type(v) for v in values)
|
||||
if len(types_seen) == 1:
|
||||
single_type = next(iter(types_seen))
|
||||
type_map = {str: "string", int: "integer", float: "number", bool: "boolean"}
|
||||
if single_type in type_map:
|
||||
return {"type": type_map[single_type], "enum": values}
|
||||
# Mixed types or non-primitive - use enum without type constraint
|
||||
return {"enum": values}
|
||||
|
||||
# Handle List types
|
||||
if origin is list:
|
||||
if args:
|
||||
items_schema = annotation_to_json_schema(args[0])
|
||||
return {"type": "array", "items": items_schema}
|
||||
else:
|
||||
return {"type": "array"}
|
||||
|
||||
# Handle Dict types
|
||||
if origin is dict:
|
||||
if len(args) >= 2:
|
||||
value_schema = annotation_to_json_schema(args[1])
|
||||
return {"type": "object", "additionalProperties": value_schema}
|
||||
else:
|
||||
return {"type": "object"}
|
||||
|
||||
# Handle basic types
|
||||
type_map = {
|
||||
str: "string",
|
||||
int: "integer",
|
||||
float: "number",
|
||||
bool: "boolean",
|
||||
list: "array",
|
||||
dict: "object",
|
||||
type(None): "null"
|
||||
}
|
||||
|
||||
return {"type": type_map.get(annotation, "string")}
|
||||
|
||||
|
||||
def get_parameter_requirements(sig: inspect.Signature, param_name: str) -> bool:
|
||||
"""Determine if a parameter is required based on its signature.
|
||||
|
||||
A parameter is required if it has no default value, unless it's Optional.
|
||||
|
||||
Args:
|
||||
sig: Function signature
|
||||
param_name: Parameter name to check
|
||||
|
||||
Returns:
|
||||
bool: True if parameter is required
|
||||
"""
|
||||
param = sig.parameters.get(param_name)
|
||||
if not param:
|
||||
return False
|
||||
|
||||
# Parameter with default is not required
|
||||
if param.default is not inspect.Parameter.empty:
|
||||
return False
|
||||
|
||||
# Check if the type annotation indicates optional
|
||||
if param.annotation is not inspect.Parameter.empty:
|
||||
origin = get_origin(param.annotation)
|
||||
if origin is Union:
|
||||
args = get_args(param.annotation)
|
||||
# Optional[T] is Union[T, None]
|
||||
if type(None) in args:
|
||||
return False
|
||||
|
||||
# No default and not optional = required
|
||||
return True
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "1.6.55"
|
||||
version = "1.6.56"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -5,8 +5,7 @@ testpaths = tests
|
||||
python_files = test_*.py
|
||||
python_classes = Test*
|
||||
python_functions = test_*
|
||||
addopts = -v --tb=short --timeout=60
|
||||
timeout_method = thread
|
||||
addopts = -v --tb=short
|
||||
filterwarnings =
|
||||
ignore::DeprecationWarning
|
||||
ignore::PendingDeprecationWarning
|
||||
|
||||
Generated
+1
-1
@@ -3125,7 +3125,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "1.6.55"
|
||||
version = "1.6.56"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
"sourceOfTruth": "Python SDK (praisonaiagents)",
|
||||
"status": "PARITY_ACHIEVED",
|
||||
"summary": {
|
||||
"pythonCoreFeatures": 359,
|
||||
"pythonCoreFeatures": 360,
|
||||
"rustFeatures": 667,
|
||||
"gapCount": 80,
|
||||
"parityPercentage": 185.8
|
||||
"gapCount": 81,
|
||||
"parityPercentage": 185.3
|
||||
},
|
||||
"pythonCoreSDK": {
|
||||
"exports": [
|
||||
@@ -275,6 +275,7 @@
|
||||
"WebSearchProvider",
|
||||
"Workflow",
|
||||
"WorkflowContext",
|
||||
"__version__",
|
||||
"aembed",
|
||||
"aembedding",
|
||||
"aembeddings",
|
||||
@@ -4374,6 +4375,15 @@
|
||||
"status": "DONE",
|
||||
"category": "config"
|
||||
},
|
||||
{
|
||||
"feature": "__version__",
|
||||
"python": true,
|
||||
"rust": false,
|
||||
"priority": "P3",
|
||||
"effort": "low",
|
||||
"status": "TODO",
|
||||
"category": "other"
|
||||
},
|
||||
{
|
||||
"feature": "aembed",
|
||||
"python": true,
|
||||
|
||||
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user