Compare commits

...
6 Commits
Author SHA1 Message Date
Mervin Praison 9d29c796c7 Release v3.9.7 2026-01-16 12:05:38 +00:00
Mervin Praison f058ce018f Release v3.9.6 2026-01-16 09:18:27 +00:00
Mervin Praison 09671d4bd3 Release v3.9.5 2026-01-16 08:00:39 +00:00
Mervin Praison c1dead194c Release v3.9.4 2026-01-16 06:33:39 +00:00
Mervin Praison 5d67f0fdcf Release v3.9.3 2026-01-16 05:23:44 +00:00
github-actions[bot] fb93b7d6d5 docs: auto-update api.md [skip ci] 2026-01-16 04:38:07 +00:00
139 changed files with 5548 additions and 619 deletions

No files matched your search

+4
View File
@@ -148,6 +148,10 @@ Methods:
* <code title="cli">praisonai agents info <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai agents list <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai audit agent-centric <a href="./src/praisonai/praisonai/cli/commands/audit.py">--help</a></code>
* <code title="cli">praisonai batch batch-run <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch list <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch report <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch stats <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai benchmark agent <a href="./src/praisonai/praisonai/cli/commands/benchmark.py">--help</a></code>
* <code title="cli">praisonai benchmark benchmark-callback <a href="./src/praisonai/praisonai/cli/commands/benchmark.py">--help</a></code>
* <code title="cli">praisonai benchmark cli <a href="./src/praisonai/praisonai/cli/commands/benchmark.py">--help</a></code>
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=3.9.2" \
"praisonai>=3.9.7" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=3.9.2" \
"praisonai>=3.9.7" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=3.9.2" \
"praisonai>=3.9.7" \
"praisonai[ui]" \
"praisonai[crewai]"
+3 -3
View File
@@ -35,7 +35,7 @@ def run_quick_comparison():
prompt="What is 2+2?",
iterations=2,
paths=["openai_sdk", "praisonai_agent"],
verbose=True
output="verbose"
)
# Print comparison table
@@ -56,7 +56,7 @@ def run_full_benchmark():
report = handler.run_full_benchmark(
prompt="Hi",
iterations=3,
verbose=True
output="verbose"
)
# Print full report
@@ -78,7 +78,7 @@ def run_agent_benchmark():
prompt="Explain Python in one sentence",
iterations=3,
paths=["openai_sdk", "praisonai_agent"],
verbose=True
output="verbose"
)
# Show timeline diagrams
+1 -1
View File
@@ -22,7 +22,7 @@ custom_modes = {
}
# Create handler with verbose output
handler = CompareHandler(verbose=True)
handler = CompareHandler(output="verbose")
# Example: Manually create comparison results for analysis
# (useful for testing without API calls)
+1 -1
View File
@@ -22,7 +22,7 @@ modes = parse_modes("basic,tools,planning")
print(f"\nParsed modes: {modes}")
# Create compare handler
handler = CompareHandler(verbose=True)
handler = CompareHandler(output="verbose")
# Compare modes (this will run actual agent calls)
# Uncomment to run:
@@ -32,7 +32,7 @@ workflow = Workflow(
),
],
# Workflow-level consolidated params
output=WorkflowOutputConfig(verbose=True, stream=True),
output=WorkflowOutputConfig(output="verbose", stream=True),
planning=WorkflowPlanningConfig(enabled=True, reasoning=True),
memory=WorkflowMemoryConfig(backend="file"),
# Agent-like params
+1 -1
View File
@@ -37,7 +37,7 @@ def main():
name="CodeAssistant",
instructions="You are a helpful code assistant.",
context=config,
verbose=False
output="silent"
)
print(f" Agent name: {agent.name}")
@@ -105,7 +105,7 @@ def example_agent_with_default_tools():
name="InteractiveAgent",
instructions="You are a helpful coding assistant with file and code analysis capabilities.",
tools=tools,
verbose=False
output="silent"
)
print(f"Agent created with {len(tools)} tools")
@@ -68,7 +68,7 @@ Answer questions based ONLY on the provided knowledge context.
If the answer is not in the context, say 'I don't have that information.'
Be precise and quote exact values when asked about codes or numbers.""",
knowledge=[temp_dir],
user_id="demo_user",
memory={"user_id": "demo_user"},
output="verbose",
)
@@ -50,7 +50,7 @@ Only authorized personnel with code WRENCH-992 may access this project.
instructions="You ingest and store knowledge.",
knowledge=[temp_dir],
user_id="user_alice", # Scoped to Alice
verbose=False,
output="silent",
)
# Ensure knowledge is processed
@@ -104,7 +104,7 @@ Only authorized personnel with code WRENCH-992 may access this project.
instructions="Answer questions based on the provided knowledge. If you don't have the information, say so.",
knowledge=[temp_dir],
user_id="user_alice", # Same scope as Agent A
verbose=False,
output="silent",
)
response = agent_b.chat("What is the secret access code for Project PHOENIX?")
@@ -74,7 +74,7 @@ def example_agent_locks():
agent = Agent(
name="LockTestAgent",
instructions="Test agent",
verbose=False
output="silent"
)
# Check for locks
@@ -112,7 +112,7 @@ def example_concurrent_real_api():
name="ConcurrentAgent",
instructions="Reply with one word only.",
llm="gpt-4o-mini",
verbose=False
output="silent"
)
results = []
+1 -1
View File
@@ -51,7 +51,7 @@ agent = Agent(
name="KnowledgeAssistant",
instructions="You are a helpful assistant with access to technical documentation.",
knowledge=[doc_path],
knowledge_config={
knowledge={
"vector_store": "qdrant",
"url": "http://localhost:6333"
}
+1 -1
View File
@@ -39,7 +39,7 @@ agent = Agent(
instructions="You are a helpful assistant. Keep responses brief.",
db=db_instance,
session_id="demo-session-001", # Same session_id = resume conversation
verbose=False
output="silent"
)
# Chat with persistence
@@ -8,7 +8,7 @@ Docs: https://docs.praison.ai/cli/git-integration
from praisonai.cli.features import GitIntegrationHandler
# Initialize
handler = GitIntegrationHandler(verbose=True)
handler = GitIntegrationHandler(output="verbose")
git = handler.initialize(repo_path=".")
# Check if it's a git repo
+1 -1
View File
@@ -8,7 +8,7 @@ Docs: https://docs.praison.ai/cli/repo-map
from praisonai.cli.features import RepoMapHandler
# Initialize with current directory
handler = RepoMapHandler(verbose=True)
handler = RepoMapHandler(output="verbose")
repo_map = handler.initialize(root=".")
# Get the repository map
@@ -9,7 +9,7 @@ from praisonai.cli.features import SandboxExecutorHandler
from praisonai.cli.features.sandbox_executor import SandboxMode
# Initialize with basic sandbox
handler = SandboxExecutorHandler(verbose=True)
handler = SandboxExecutorHandler(output="verbose")
sandbox = handler.initialize(mode="basic")
print(f"Sandbox mode: {handler.get_mode()}")
+1 -1
View File
@@ -26,7 +26,7 @@ pdf_agent = Agent(
backstory="You are an expert at reading, analyzing, and answering questions about PDF documents. You provide accurate, detailed answers based on the document content.",
instructions="Read the provided PDF document and answer questions about its content. Be specific and cite relevant sections when possible.",
knowledge=["document.pdf"], # Replace with your PDF file path
knowledge_config=config,
knowledge=config,
reflection=True,
@@ -24,7 +24,7 @@ knowledge_agent = Agent(
goal="Store and retrieve knowledge efficiently",
backstory="Expert in managing and utilizing stored knowledge",
knowledge=["sample.pdf"],
knowledge_config=config)
knowledge=config)
# Define a task for the agent
knowledge_task = Task(
+1 -2
View File
@@ -13,8 +13,7 @@ config = {
agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["small.pdf"],
knowledge_config=config
knowledge=["small.pdf"]
)
agent.start("What is KAG in one line?")
@@ -139,7 +139,7 @@ def main():
name="AI Research Assistant",
instructions="You are an AI research assistant. Use your knowledge base to answer questions about artificial intelligence topics.",
knowledge=sample_documents, # Will use default knowledge config
knowledge_config=rerank_config, # Use reranking config
knowledge=rerank_config, # Use reranking config
llm="gpt-4o-mini"
)
+1 -1
View File
@@ -16,7 +16,7 @@ def example_dict_output_config():
"""Example: Using dict for output configuration."""
from praisonaiagents import Agent
# Dict shorthand - equivalent to OutputConfig(verbose=True, stream=False)
# Dict shorthand - equivalent to OutputConfig(output="verbose", stream=False)
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
+7 -5
View File
@@ -104,11 +104,13 @@ def main():
agents = Agents(
agents=[researcher, retriever],
tasks=[store_task, verify_task, query_task, query_both_task], # Use same verbose level as memory
memory=True,
embedder={
"provider": "openai",
"config": {
"model": "text-embedding-3-small"
memory={
"provider": "rag",
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small"
}
}
}
)
@@ -61,7 +61,7 @@ agents_system = Agents(
agents=[knowledge_agent],
tasks=[build_task, query_task],
memory=True,
memory_config=memory_config, output="verbose"
memory=memory_config, output="verbose"
)
print("Starting graph memory demonstration...")
@@ -62,7 +62,7 @@ agents_system = Agents(
agents=[research_agent],
tasks=[build_memory_task, search_task],
memory=True,
memory_config=memory_config, output="verbose"
memory=memory_config, output="verbose"
)
print("Starting memory search optimization demonstration...")
@@ -85,13 +85,13 @@ def main():
agents = Agents(
agents=[researcher, retriever],
tasks=[store_task, retrieve_task],
memory=True,
embedder={
"provider": "gemini",
"config": {
"model": "text-embedding-004",
# Optional: specify task type for better results
# "task_type": "RETRIEVAL_DOCUMENT"
memory={
"provider": "rag",
"embedder": {
"provider": "gemini",
"config": {
"model": "text-embedding-004",
}
}
}
)
+1 -1
View File
@@ -153,7 +153,7 @@ def main():
agents=[researcher],
tasks=[build_knowledge_task, query_knowledge_task, expand_knowledge_task],
memory=True,
memory_config=memory_config
memory=memory_config
)
print("\n🚀 Starting graph memory demonstration...")
@@ -33,7 +33,7 @@ def init_agent():
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["kag-research-paper.pdf"],
knowledge_config=config,
knowledge=config,
user_id="user1",
llm="deepseek-r1"
)
@@ -31,7 +31,7 @@ agent = Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["kag-research-paper.pdf"], # Indexing
knowledge_config=config,
knowledge=config,
user_id="user1",
llm="deepseek-r1"
)
@@ -81,7 +81,7 @@ def main():
backstory="""You are a knowledge management specialist who excels at
organizing, categorizing, and retrieving information from the knowledge base.
You ensure that knowledge is properly stored, indexed, and accessible.""",
knowledge_config=mongodb_knowledge_config,
knowledge=mongodb_knowledge_config,
memory=True,
llm="gpt-4o-mini"
)
@@ -107,7 +107,7 @@ def main():
backstory="""You are a customer service representative who uses the
knowledge base and customer data to provide excellent support. You can
access customer history, product information, and troubleshooting guides.""",
knowledge_config=mongodb_knowledge_config,
knowledge=mongodb_knowledge_config,
tools=[mongodb_tools],
memory=True,
llm="gpt-4o-mini"
@@ -190,7 +190,7 @@ def main():
agents=[data_manager, knowledge_curator, business_analyst, customer_service],
tasks=business_tasks,
memory=True,
memory_config=mongodb_memory_config, output="verbose"
memory=mongodb_memory_config, output="verbose"
)
# Execute the comprehensive business pipeline
@@ -55,7 +55,7 @@ def main():
retrieve information from a comprehensive MongoDB knowledge base. You excel
at finding relevant information, synthesizing knowledge from multiple sources,
and providing accurate, context-aware responses.""",
knowledge_config=mongodb_knowledge_config,
knowledge=mongodb_knowledge_config,
knowledge=[os.path.join(os.path.dirname(__file__), "llms.md")],
memory=True,
llm="gpt-4o-mini"
@@ -94,7 +94,7 @@ def main():
agents=[research_agent],
tasks=research_tasks,
memory=True,
memory_config=mongodb_memory_config, output="verbose"
memory=mongodb_memory_config, output="verbose"
)
# Execute the research pipeline
@@ -1,155 +1,155 @@
{
"timestamp": "2025-12-31T18:45:38.187539Z",
"timestamp": "2026-01-16T05:23:04.919943Z",
"metadata": {
"python_version": "3.12.11",
"platform": "macOS-15.7.4-arm64-arm-64bit",
"praisonai_version": "2.9.2",
"timestamp": "2025-12-31T18:45:38.226558Z"
"platform": "macOS-26.3-arm64-arm-64bit",
"praisonai_version": "3.9.2",
"timestamp": "2026-01-16T05:23:04.932474Z"
},
"startup": {
"cold_ms": 80.9305000002496,
"warm_ms": 55.266541996388696
"cold_ms": 27.866583000104583,
"warm_ms": 27.75670800019725
},
"import_analysis": [
{
"module": "praisonaiagents",
"self_us": 2419,
"cumulative_us": 1645390,
"self_ms": 2.419,
"cumulative_ms": 1645.39
"self_us": 403,
"cumulative_us": 188329,
"self_ms": 0.403,
"cumulative_ms": 188.329
},
{
"module": "praisonaiagents.workflows",
"self_us": 670,
"cumulative_us": 1375919,
"self_ms": 0.67,
"cumulative_ms": 1375.919
"self_us": 106,
"cumulative_us": 112180,
"self_ms": 0.106,
"cumulative_ms": 112.18
},
{
"module": "praisonaiagents.workflows.yaml_parser",
"self_us": 775,
"cumulative_us": 1369435,
"self_ms": 0.775,
"cumulative_ms": 1369.435
"self_us": 217,
"cumulative_us": 107210,
"self_ms": 0.217,
"cumulative_ms": 107.21
},
{
"module": "praisonaiagents.agent.agent",
"self_us": 19,
"cumulative_us": 1349174,
"self_ms": 0.019,
"cumulative_ms": 1349.174
"self_us": 15,
"cumulative_us": 98811,
"self_ms": 0.015,
"cumulative_ms": 98.811
},
{
"module": "praisonaiagents.agent",
"self_us": 537,
"cumulative_us": 1349155,
"self_ms": 0.537,
"cumulative_ms": 1349.155
"self_us": 146,
"cumulative_us": 98796,
"self_ms": 0.146,
"cumulative_ms": 98.796
},
{
"module": "praisonaiagents.agent.agent",
"self_us": 1453,
"cumulative_us": 1285299,
"self_ms": 1.453,
"cumulative_ms": 1285.299
},
{
"module": "praisonaiagents.llm.openai_client",
"self_us": 7887,
"cumulative_us": 1123578,
"self_ms": 7.887,
"cumulative_ms": 1123.578
},
{
"module": "openai",
"self_us": 1083,
"cumulative_us": 1115692,
"self_ms": 1.083,
"cumulative_ms": 1115.692
},
{
"module": "openai.types",
"self_us": 2606,
"cumulative_us": 957990,
"self_ms": 2.606,
"cumulative_ms": 957.99
},
{
"module": "openai.types.batch",
"self_us": 1787,
"cumulative_us": 485667,
"self_ms": 1.787,
"cumulative_ms": 485.667
},
{
"module": "openai._models",
"self_us": 13423,
"cumulative_us": 450798,
"self_ms": 13.423,
"cumulative_ms": 450.798
},
{
"module": "openai.types.eval_create_params",
"self_us": 644,
"cumulative_us": 393275,
"self_ms": 0.644,
"cumulative_ms": 393.275
},
{
"module": "openai.types.graders.grader_inputs_param",
"self_us": 17,
"cumulative_us": 359515,
"self_ms": 0.017,
"cumulative_ms": 359.515
},
{
"module": "openai.types.graders",
"self_us": 392,
"cumulative_us": 359498,
"self_ms": 0.392,
"cumulative_ms": 359.498
},
{
"module": "openai.types.graders.multi_grader",
"self_us": 3847,
"cumulative_us": 351251,
"self_ms": 3.847,
"cumulative_ms": 351.251
},
{
"module": "openai.types.graders.label_model_grader",
"self_us": 5450,
"cumulative_us": 337291,
"self_ms": 5.45,
"cumulative_ms": 337.291
},
{
"module": "openai.types.graders.grader_inputs",
"self_us": 545,
"cumulative_us": 331841,
"self_ms": 0.545,
"cumulative_ms": 331.841
},
{
"module": "openai.types.responses.response_input_text",
"self_us": 21,
"cumulative_us": 331297,
"self_ms": 0.021,
"cumulative_ms": 331.297
},
{
"module": "openai.types.responses",
"self_us": 1301,
"cumulative_us": 331277,
"self_ms": 1.301,
"cumulative_ms": 331.277
"self_us": 1027,
"cumulative_us": 86223,
"self_ms": 1.027,
"cumulative_ms": 86.223
},
{
"module": "rich.logging",
"self_us": 1066,
"cumulative_us": 238256,
"self_ms": 1.066,
"cumulative_ms": 238.256
"self_us": 282,
"cumulative_us": 53009,
"self_ms": 0.282,
"cumulative_ms": 53.009
},
{
"module": "praisonaiagents.main",
"self_us": 18784,
"cumulative_us": 45759,
"self_ms": 18.784,
"cumulative_ms": 45.759
},
{
"module": "praisonaiagents.llm.openai_client",
"self_us": 2470,
"cumulative_us": 28251,
"self_ms": 2.47,
"cumulative_ms": 28.251
},
{
"module": "rich.traceback",
"self_us": 1478,
"cumulative_us": 18788,
"self_ms": 1.478,
"cumulative_ms": 18.788
},
{
"module": "rich.console",
"self_us": 1737,
"cumulative_us": 18587,
"self_ms": 1.737,
"cumulative_ms": 18.587
},
{
"module": "pygments.lexers",
"self_us": 242,
"cumulative_us": 15427,
"self_ms": 0.242,
"cumulative_ms": 15.427
},
{
"module": "rich.markdown",
"self_us": 409,
"cumulative_us": 13987,
"self_ms": 0.409,
"cumulative_ms": 13.987
},
{
"module": "pygments.plugin",
"self_us": 85,
"cumulative_us": 13503,
"self_ms": 0.085,
"cumulative_ms": 13.503
},
{
"module": "importlib.metadata",
"self_us": 637,
"cumulative_us": 13419,
"self_ms": 0.637,
"cumulative_ms": 13.419
},
{
"module": "rich._log_render",
"self_us": 145,
"cumulative_us": 13380,
"self_ms": 0.145,
"cumulative_ms": 13.38
},
{
"module": "markdown_it",
"self_us": 172,
"cumulative_us": 13339,
"self_ms": 0.172,
"cumulative_ms": 13.339
},
{
"module": "rich.text",
"self_us": 582,
"cumulative_us": 13236,
"self_ms": 0.582,
"cumulative_ms": 13.236
},
{
"module": "markdown_it.main",
"self_us": 298,
"cumulative_us": 13168,
"self_ms": 0.298,
"cumulative_ms": 13.168
},
{
"module": "pydantic",
"self_us": 223,
"cumulative_us": 11119,
"self_ms": 0.223,
"cumulative_ms": 11.119
}
],
"scenarios": [
@@ -162,28 +162,22 @@
"iterations": 2
},
"import_time_stats": {
"mean": 0.008833496394800022,
"min": 0.005916997906751931,
"max": 0.011749994882848114,
"stdev": 0.0041245517164382375,
"median": 0.008833496394800022
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"total_time_stats": {
"mean": 1727.4090415012324,
"min": 1367.2264170018025,
"max": 2087.5916660006624,
"stdev": 509.3751524982296,
"median": 1727.4090415012324
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"first_token_stats": null,
"raw_import_times_ms": [
0.011749994882848114,
0.005916997906751931
],
"raw_total_times_ms": [
1367.2264170018025,
2087.5916660006624
]
"raw_import_times_ms": [],
"raw_total_times_ms": []
},
{
"name": "simple_stream",
@@ -194,34 +188,22 @@
"iterations": 2
},
"import_time_stats": {
"mean": 0.008792001608526334,
"min": 0.006375004886649549,
"max": 0.01120899833040312,
"stdev": 0.0034181495442894613,
"median": 0.008792001608526334
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"total_time_stats": {
"mean": 2321.0982290001994,
"min": 1697.1089580038097,
"max": 2945.087499996589,
"stdev": 882.4540898183949,
"median": 2321.0982290001994
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"first_token_stats": {
"mean": 2312.8511880022415,
"min": 1681.2440420035273,
"max": 2944.458334000956,
"stdev": 893.2273919631452,
"median": 2312.8511880022415
},
"raw_import_times_ms": [
0.01120899833040312,
0.006375004886649549
],
"raw_total_times_ms": [
2945.087499996589,
1697.1089580038097
]
"first_token_stats": null,
"raw_import_times_ms": [],
"raw_total_times_ms": []
}
]
}
+1 -1
View File
@@ -50,5 +50,5 @@ with st.expander("🔧 Troubleshooting"):
**If you still have issues:**
- Make sure you have Node.js installed for the MCP server
- Set your OpenAI API key: `export OPENAI_API_KEY="your-key"`
- Try the debug mode by setting `verbose=True`
- Try the debug mode by setting `output="verbose"`
""")
@@ -33,7 +33,7 @@ def init_agent():
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["kag-research-paper.pdf"],
knowledge_config=config,
knowledge=config,
user_id="user1",
llm="deepseek-r1"
)
@@ -265,7 +265,7 @@ agents = Agents(
process="hierarchical",
manager_llm="gpt-4o-mini",
memory=True, # Enable shared memory for domain context
memory_config={
memory={
"provider": "rag",
"config": {
"collection_name": f"{domain.replace('.', '_')}_security_analysis"
+3 -3
View File
@@ -58,7 +58,7 @@ def auto_rag_basic():
KNOWLEDGE BASE:
{context}""",
verbose=False
output="silent"
)
# Wrap with AutoRagAgent for intelligent retrieval
@@ -105,7 +105,7 @@ def auto_rag_with_policies():
KNOWLEDGE BASE:
{context}""",
verbose=False
output="silent"
)
print("\n" + "=" * 60)
@@ -153,7 +153,7 @@ def auto_rag_config_options():
KNOWLEDGE BASE:
{context}""",
verbose=False
output="silent"
)
print("\n" + "=" * 60)
+1 -1
View File
@@ -79,7 +79,7 @@ def rag_with_query_context():
agent = Agent(
name="Research Assistant",
instructions="You answer questions based on the context provided in each query.",
verbose=False
output="silent"
)
# Simulate retrieved context (in real RAG, this comes from vector search)
+1 -1
View File
@@ -204,7 +204,7 @@ def rag_with_section_chunks():
PRODUCT DOCUMENTATION:
{context}""",
verbose=False
output="silent"
)
queries = [
+4 -4
View File
@@ -103,7 +103,7 @@ def basic_citations():
POLICY DOCUMENTS:
{context}""",
verbose=False
output="silent"
)
queries = [
@@ -146,7 +146,7 @@ def structured_citations():
POLICY DOCUMENTS:
{context}""",
verbose=False
output="silent"
)
query = "What are the requirements for remote work eligibility?"
@@ -183,7 +183,7 @@ def inline_citations():
POLICY DOCUMENTS:
{context}""",
verbose=False
output="silent"
)
query = "Summarize the key employee benefits."
@@ -267,7 +267,7 @@ def multi_source_synthesis():
POLICY DOCUMENTS:
{context}""",
verbose=False
output="silent"
)
query = "Give me a complete overview of employee benefits and policies."
+3 -3
View File
@@ -125,7 +125,7 @@ def web_augmented_rag():
COMPANY KNOWLEDGE:
{context}""",
verbose=False
output="silent"
)
# Simulate web-augmented queries
@@ -171,7 +171,7 @@ def database_augmented_rag():
COMPANY KNOWLEDGE:
{context}""",
verbose=False
output="silent"
)
queries = [
@@ -249,7 +249,7 @@ def hybrid_source_rag():
COMPANY KNOWLEDGE:
{context}""",
verbose=False
output="silent"
)
query = "Give me a complete business briefing for today"
+2 -2
View File
@@ -156,7 +156,7 @@ def hybrid_rag_example():
TROUBLESHOOTING KNOWLEDGE BASE:
{context}""",
verbose=False
output="silent"
)
# Test with different query types
@@ -204,7 +204,7 @@ def compare_retrieval_modes():
KNOWLEDGE BASE:
{context}""",
verbose=False
output="silent"
)
print(f"\n📝 Query: {query}")
+4 -4
View File
@@ -124,7 +124,7 @@ def graph_aware_rag():
KNOWLEDGE GRAPH:
{context}""",
verbose=False
output="silent"
)
# Relationship-focused queries
@@ -167,7 +167,7 @@ def multi_hop_reasoning():
KNOWLEDGE GRAPH:
{context}""",
verbose=False
output="silent"
)
# Multi-hop query requiring traversal
@@ -200,7 +200,7 @@ def entity_type_filtering():
SCIENTISTS:
{people_context}""",
verbose=False
output="silent"
)
# Filter to only concepts
@@ -213,7 +213,7 @@ def entity_type_filtering():
CONCEPTS:
{concepts_context}""",
verbose=False
output="silent"
)
query = "Tell me about the major developments in early 20th century physics."
+4 -4
View File
@@ -112,7 +112,7 @@ def multi_document_rag():
REPORTS:
{context}""",
verbose=False
output="silent"
)
# Questions that require multi-document synthesis
@@ -157,7 +157,7 @@ def cross_reference_example():
REPORTS:
{context}""",
verbose=False
output="silent"
)
query = """
@@ -185,7 +185,7 @@ def document_filtering():
POLICY REPORT:
{POLICY_REPORT['content']}""",
verbose=False
output="silent"
)
tech_agent = Agent(
@@ -194,7 +194,7 @@ def document_filtering():
TECHNOLOGY REPORT:
{TECH_REPORT['content']}""",
verbose=False
output="silent"
)
query = "What progress has been made in addressing climate change?"
+1 -1
View File
@@ -124,7 +124,7 @@ def main():
KNOWLEDGE:
{SAMPLE_KNOWLEDGE}""",
verbose=False
output="silent"
)
# Get test queries
+4 -4
View File
@@ -61,7 +61,7 @@ def main():
KNOWLEDGE BASE:
{SHARED_KNOWLEDGE}""",
verbose=False
output="silent"
)
summarizer = Agent(
@@ -75,7 +75,7 @@ def main():
KNOWLEDGE BASE:
{SHARED_KNOWLEDGE}""",
verbose=False
output="silent"
)
writer = Agent(
@@ -89,7 +89,7 @@ def main():
KNOWLEDGE BASE:
{SHARED_KNOWLEDGE}""",
verbose=False
output="silent"
)
# Define tasks
@@ -118,7 +118,7 @@ def main():
agents=[researcher, summarizer, writer],
tasks=[research_task, summary_task, report_task],
process="sequential",
verbose=False
output="silent"
)
result = agents.start()
+3 -3
View File
@@ -67,7 +67,7 @@ def main():
DOCUMENT:
{DOCUMENT_CONTENT}""",
verbose=False
output="silent"
)
query = "What are the main topics covered in this document?"
@@ -88,7 +88,7 @@ def main():
DOCUMENT:
{DOCUMENT_CONTENT}""",
verbose=False
output="silent"
)
queries = [
@@ -114,7 +114,7 @@ def main():
DOCUMENT:
{DOCUMENT_CONTENT}""",
verbose=False
output="silent"
)
analysis_query = "Compare the benefits vs challenges of remote work based on this study"
+3 -3
View File
@@ -131,7 +131,7 @@ def rag_with_reranking():
RESEARCH PAPERS:
{context}""",
verbose=False
output="silent"
)
queries = [
@@ -170,7 +170,7 @@ def compare_with_without_reranking():
RESEARCH PAPERS:
{context}""",
verbose=False
output="silent"
)
print(f"\n📝 Query: {query}")
@@ -196,7 +196,7 @@ def reranking_configuration():
agent = Agent(
name="My Agent",
knowledge=documents,
knowledge_config={
knowledge={
# Enable reranking
"rerank": True,
+3 -3
View File
@@ -134,7 +134,7 @@ def policy_based_retrieval():
IT SUPPORT DOCS:
{context}""",
verbose=False
output="silent"
)
query = "How do I set up VPN?"
@@ -217,7 +217,7 @@ def priority_based_retrieval():
HIGH PRIORITY DOCS:
{high_context}""",
verbose=False
output="silent"
)
# Full knowledge agent
@@ -227,7 +227,7 @@ def priority_based_retrieval():
IT SUPPORT DOCS:
{full_context}""",
verbose=False
output="silent"
)
query = "I'm locked out of my account!"
+5 -5
View File
@@ -110,7 +110,7 @@ def json_output_rag():
PRODUCT CATALOG:
{context}""",
verbose=False
output="silent"
)
# Request structured product info
@@ -154,7 +154,7 @@ def pydantic_guided_rag():
PRODUCT CATALOG:
{context}""",
verbose=False
output="silent"
)
query = "Compare the UltraWidget Pro and SmartHome Hub. Which should I buy?"
@@ -184,7 +184,7 @@ def table_format_rag():
PRODUCT CATALOG:
{context}""",
verbose=False
output="silent"
)
query = "Show me all products in a table with name, price, and rating."
@@ -212,7 +212,7 @@ def list_format_rag():
PRODUCT CATALOG:
{context}""",
verbose=False
output="silent"
)
queries = [
@@ -321,7 +321,7 @@ def multi_format_rag():
PRODUCT CATALOG:
{context}""",
verbose=False
output="silent"
)
response = agent.chat(query)
+1 -1
View File
@@ -6,7 +6,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "chroma",
"path": "/tmp/chroma_demo"
}
+1 -1
View File
@@ -6,7 +6,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "lancedb",
"path": "/tmp/lancedb_test"
}
+1 -1
View File
@@ -6,7 +6,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "lancedb",
"path": "/tmp/lancedb_demo"
}
+1 -1
View File
@@ -12,7 +12,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "pgvector",
"url": url
}
+1 -1
View File
@@ -12,7 +12,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "pgvector",
"url": url
}
+1 -1
View File
@@ -16,7 +16,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "pinecone",
"api_key": api_key,
"environment": "us-east-1"
+1 -1
View File
@@ -12,7 +12,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "qdrant",
"url": url
}
+1 -1
View File
@@ -17,7 +17,7 @@ agent = Agent(
name="Assistant",
instructions="You are a helpful assistant with access to documents.",
knowledge=["./docs/guide.pdf"], # Add your documents here
knowledge_config={
knowledge={
"vector_store": "weaviate",
"url": weaviate_url,
"api_key": weaviate_key
+1 -1
View File
@@ -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']}")
@@ -7,15 +7,14 @@ will be automatically displayed when tasks complete!
from praisonaiagents import Agents, Agent, Task
def main():
# Create a simple agent with metrics=True - that's it!
# Create a simple agent with output="debug" - includes metrics!
research_agent = Agent(
name="Research Agent",
role="Research Specialist",
goal="Find information about a topic",
backstory="You are an expert researcher who finds comprehensive information.",
output="verbose",
output="debug", # 🎯 SIMPLIFIED: debug preset includes metrics!
llm="gpt-4o-mini", # Use a cost-effective model for examples
metrics=True # 🎯 SIMPLIFIED: Just add this parameter!
)
# Create a simple task
@@ -143,7 +143,7 @@ def simulate_complex_workflow():
agents=[planner, executor, reviewer],
tasks=[planning_task, execution_task, review_task],
process="sequential",
verbose=False
output="silent"
)
result = workflow.start()
@@ -3,9 +3,14 @@
Agent-level guardrails example.
This example demonstrates how to use guardrails at the Agent level
which will apply to all tasks executed by that agent.
Note: Agent guardrails only support function-based validation or preset strings
(permissive, safety, strict). For LLM-based guardrails, use Task guardrails.
"""
from typing import Tuple, Any
from praisonaiagents import Agent, TaskOutput
def validate_content_length(task_output: TaskOutput) -> Tuple[bool, Any]:
"""
Validate that task output content meets minimum length requirement.
@@ -17,6 +22,8 @@ def validate_content_length(task_output: TaskOutput) -> Tuple[bool, Any]:
if len(task_output.raw) < 50:
return False, "Content too short - must be at least 50 characters"
return True, task_output
def validate_professional_tone(task_output: TaskOutput) -> Tuple[bool, Any]:
"""
Validate that the content has a professional tone.
@@ -31,14 +38,17 @@ def validate_professional_tone(task_output: TaskOutput) -> Tuple[bool, Any]:
if word in content:
return False, f"Content contains unprofessional word: '{word}'"
return True, task_output
def main():
"""Demonstrate Agent-level guardrails with function-based and LLM-based validation."""
"""Demonstrate Agent-level guardrails with function-based validation."""
print("=== Agent Guardrail Examples ===\n")
# Example 1: Function-based guardrail
# Example 1: Function-based guardrail for content length
print("1. Function-based guardrail (content length validation):")
agent1 = Agent(
name="ContentWriter"
instructions="You are a professional content writer who creates detailed responses"
name="ContentWriter",
instructions="You are a professional content writer who creates detailed responses",
guardrails=validate_content_length
)
try:
@@ -46,30 +56,37 @@ def main():
print(f"Result: {result1}\n")
except Exception as e:
print(f"Error: {e}\n")
# Example 2: LLM-based guardrail (string description)
print("2. LLM-based guardrail (professional tone validation):")
# Example 2: Function-based guardrail for professional tone
print("2. Function-based guardrail (professional tone validation):")
agent2 = Agent(
name="BusinessWriter",
instructions="You are a business communication expert"
guardrails="Ensure the content is professional, formal, and suitable for business communication. No casual language or slang."
instructions="You are a business communication expert. Write formal, professional content.",
guardrails=validate_professional_tone
)
try:
result2 = agent2.start("Write a welcome message for new employees")
print(f"Result: {result2}\n")
except Exception as e:
print(f"Error: {e}\n")
# Example 3: Multiple agents with different guardrails
print("3. Professional tone function-based guardrail:")
# Example 3: Using preset guardrails (built-in options)
print("3. Using preset guardrail ('safety'):")
agent3 = Agent(
name="ProfessionalWriter"
instructions="Write professional business content"
guardrails=validate_professional_tone
name="SafeWriter",
instructions="Write helpful content",
guardrails="safety" # Valid presets: permissive, safety, strict
)
try:
result3 = agent3.start("Write a casual greeting message")
result3 = agent3.start("Write a friendly greeting message")
print(f"Result: {result3}\n")
except Exception as e:
print(f"Error: {e}\n")
print("=== Agent Guardrails Demonstration Complete ===")
print("\nNote: For LLM-based guardrails with custom descriptions,")
print("use Task-level guardrails instead of Agent-level guardrails.")
if __name__ == "__main__":
main()
+2 -2
View File
@@ -11,7 +11,7 @@ agents = AutoAgents(
instructions="Search for information about AI Agents",
tools=[tavily],
process="sequential",
verbose=True,
output="verbose",
max_agents=3 # Maximum number of agents to create
)
@@ -53,7 +53,7 @@ agents = AutoAgents(
process="sequential",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
reflection=False,
verbose=False,
output="silent",
max_agents=3 # Maximum number of agents to create
)
+1 -2
View File
@@ -26,7 +26,6 @@ agent = DeepResearchAgent(
- Clear structure with executive summary
""",
model="deep-research-pro", # Auto-detected as Gemini
verbose=True,
)
print(f"Agent: {agent}")
@@ -39,7 +38,7 @@ result = agent.research(
# stream=True is the default, set stream=False to disable
)
# The report is already printed during streaming when verbose=True
# The report is already printed during streaming when output="verbose"
# But we can also access it programmatically:
print("\n" + "=" * 60)
print(f"Report length: {len(result.report)} characters")
+1 -2
View File
@@ -25,7 +25,6 @@ agent = DeepResearchAgent(
- Clear, structured responses
""",
model="o4-mini-deep-research", # or "o3-deep-research" for higher quality
verbose=True
)
print(f"Agent: {agent}")
@@ -38,7 +37,7 @@ result = agent.research(
# stream=True is the default, set stream=False to disable
)
# The report is already printed during streaming when verbose=True
# The report is already printed during streaming when output="verbose"
# But we can also access it programmatically:
print("\n" + "=" * 60)
print(f"Report length: {len(result.report)} characters")
@@ -15,9 +15,7 @@ agent = Agent(
instructions="You are a helpful assistant. Be concise and clear.",
llm=llm_config,
output="verbose",
reflection=True,
max_reflect=2,
min_reflect=1
reflection=True # Use simplest form - enables defaults
)
# Test with a simple prompt
+1 -1
View File
@@ -24,7 +24,7 @@ task = Task(
agents = Agents(
agents=[agent],
tasks=[task],
verbose=True
output="verbose"
)
agents.start()
+2 -5
View File
@@ -32,11 +32,8 @@ agent = Agent(
instructions="You are a helpful Assistant specialized in scientific explanations. "
"Provide clear, accurate, and engaging responses.",
llm=llm_config, # Pass the detailed configuration
output="verbose", # Enable detailed output
markdown=True, # Format responses in markdown
reflection=True, # Enable self-reflection
max_reflect=3, # Maximum reflection iterations
min_reflect=1 # Minimum reflection iterations
output="verbose", # Enable detailed output (includes markdown)
reflection=True # Enable self-reflection with defaults
)
# Test the agent
@@ -70,7 +70,7 @@ def main():
indexing, and ensure data quality and accessibility across the organization.""",
tools=[mongodb_tools],
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -82,9 +82,9 @@ def main():
backstory="""You are a knowledge management specialist who excels at
organizing, categorizing, and retrieving information from the knowledge base.
You ensure that knowledge is properly stored, indexed, and accessible.""",
knowledge_config=mongodb_knowledge_config,
knowledge=mongodb_knowledge_config,
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -98,7 +98,7 @@ def main():
and provide strategic recommendations based on data analysis.""",
tools=[mongodb_tools],
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -110,10 +110,10 @@ def main():
backstory="""You are a customer service representative who uses the
knowledge base and customer data to provide excellent support. You can
access customer history, product information, and troubleshooting guides.""",
knowledge_config=mongodb_knowledge_config,
knowledge=mongodb_knowledge_config,
tools=[mongodb_tools],
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -195,7 +195,7 @@ def main():
tasks=business_tasks,
memory=True,
memory_config=mongodb_memory_config,
verbose=True
output="verbose"
)
# Execute the comprehensive business pipeline
@@ -55,10 +55,9 @@ def main():
retrieve information from a comprehensive MongoDB knowledge base. You excel
at finding relevant information, synthesizing knowledge from multiple sources,
and providing accurate, context-aware responses.""",
knowledge_config=mongodb_knowledge_config,
knowledge=[os.path.join(os.path.dirname(__file__), "llms.md")],
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -72,7 +71,7 @@ def main():
knowledge base. You ensure information is accurate, well-structured,
and properly categorized.""",
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -128,7 +127,7 @@ def main():
agents=[research_agent, knowledge_agent],
tasks=knowledge_tasks,
memory=True,
verbose=True
output="verbose"
)
# Execute the knowledge management pipeline
@@ -49,7 +49,7 @@ def main():
You excel at maintaining context across multiple research sessions and can
retrieve relevant information from your persistent memory store.""",
memory=True,
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -94,9 +94,8 @@ def main():
research_system = Agents(
agents=[research_agent],
tasks=research_tasks,
memory=True,
memory_config=mongodb_memory_config,
verbose=True
memory=True, # Use default memory instead of mongodb config (requires proper MemoryConfig)
output="verbose"
)
# Execute the research pipeline
@@ -37,7 +37,7 @@ def main():
collections, and analyze data patterns. You excel at using MongoDB tools
for data management and retrieval.""",
tools=[mongodb_tools],
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -50,7 +50,7 @@ def main():
from MongoDB databases. You can query data, perform aggregations, and
identify patterns and trends in the stored information.""",
tools=[mongodb_tools],
verbose=True,
output="verbose",
llm="gpt-4o-mini"
)
@@ -116,7 +116,7 @@ def main():
mongodb_system = Agents(
agents=[db_agent, analysis_agent],
tasks=mongodb_tasks,
verbose=True
output="verbose"
)
# Execute the MongoDB operations pipeline
@@ -200,7 +200,7 @@ research_agent = Agent(
goal="Research and analyze performance topics quickly",
backstory="You are an expert at researching performance optimization topics.",
tools=[duckduckgo],
verbose=False # Reduce output for cleaner demo
output="silent" # Reduce output for cleaner demo
)
# Monitor agent operations
@@ -216,7 +216,7 @@ def execute_monitored_task(agent, task_description):
agents_workflow = Agents(
agents=[agent],
tasks=[task],
verbose=False
output="silent"
)
# Simulate API calls during agent execution
+3 -3
View File
@@ -4,11 +4,11 @@ from praisonaiagents import Agent
from praisonaiagents.planning import PlanningAgent, TodoList
# Step 1: Create agents
researcher = Agent(name="Researcher", role="Research Analyst", verbose=True)
writer = Agent(name="Writer", role="Content Writer", verbose=True)
researcher = Agent(name="Researcher", role="Research Analyst", output="verbose")
writer = Agent(name="Writer", role="Content Writer", output="verbose")
# Step 2: Create a plan using PlanningAgent
planner = PlanningAgent(llm="gpt-4o-mini", verbose=1)
planner = PlanningAgent(llm="gpt-4o-mini")
plan = planner.create_plan_sync(
request="Write a short article about the top 3 benefits of meditation",
agents=[researcher, writer],
@@ -12,6 +12,9 @@ class Chunking:
'semantic': ['chunk_size', 'embedding_model'],
'sdpm': ['chunk_size', 'embedding_model'],
'late': ['chunk_size', 'embedding_model'],
'code': ['chunk_size', 'tokenizer_or_token_counter'],
'neural': ['chunk_size', 'tokenizer_or_token_counter'],
'slumber': ['chunk_size', 'genie'],
}
@cached_property
@@ -24,7 +27,10 @@ class Chunking:
SemanticChunker,
SDPMChunker,
LateChunker,
RecursiveChunker
RecursiveChunker,
CodeChunker,
NeuralChunker,
SlumberChunker
)
except ImportError:
raise ImportError(
@@ -37,7 +43,10 @@ class Chunking:
'semantic': SemanticChunker,
'sdpm': SDPMChunker,
'late': LateChunker,
'recursive': RecursiveChunker
'recursive': RecursiveChunker,
'code': CodeChunker,
'neural': NeuralChunker,
'slumber': SlumberChunker
}
def __init__(
@@ -42,6 +42,8 @@ from .protocols import (
MemoryProtocol,
AsyncMemoryProtocol,
ResettableMemoryProtocol,
DeletableMemoryProtocol,
AsyncDeletableMemoryProtocol,
EntityMemoryProtocol,
)
@@ -203,5 +205,7 @@ __all__ = [
"MemoryProtocol",
"AsyncMemoryProtocol",
"ResettableMemoryProtocol",
"DeletableMemoryProtocol",
"AsyncDeletableMemoryProtocol",
"EntityMemoryProtocol",
]
@@ -806,6 +806,220 @@ class FileMemory:
self._log("Cleared all memory")
# -------------------------------------------------------------------------
# Selective Deletion
# -------------------------------------------------------------------------
def delete_short_term(self, memory_id: str) -> bool:
"""
Delete a specific short-term memory by ID.
Args:
memory_id: The unique ID of the memory to delete
Returns:
True if memory was found and deleted, False otherwise
"""
for i, item in enumerate(self._short_term):
if item.id == memory_id:
del self._short_term[i]
self._save_short_term()
self._log(f"Deleted short-term memory: {memory_id}")
return True
return False
def delete_long_term(self, memory_id: str) -> bool:
"""
Delete a specific long-term memory by ID.
Args:
memory_id: The unique ID of the memory to delete
Returns:
True if memory was found and deleted, False otherwise
"""
for i, item in enumerate(self._long_term):
if item.id == memory_id:
del self._long_term[i]
self._save_long_term()
self._log(f"Deleted long-term memory: {memory_id}")
return True
return False
def delete_entity(self, name: str) -> bool:
"""
Delete an entity by name.
Args:
name: The name of the entity to delete
Returns:
True if entity was found and deleted, False otherwise
"""
# Find entity by name using existing helper
entity = self._find_entity_by_name(name)
if entity:
# Delete by entity's ID (which is the dict key)
if entity.id in self._entities:
del self._entities[entity.id]
self._save_entities()
self._log(f"Deleted entity: {name}")
return True
# Try direct ID match as fallback (in case name IS the ID)
if name in self._entities:
del self._entities[name]
self._save_entities()
self._log(f"Deleted entity by ID: {name}")
return True
return False
def delete_episodic(self, memory_id: str, date: Optional[str] = None) -> bool:
"""
Delete a specific episodic memory by ID.
Args:
memory_id: The unique ID of the memory to delete
date: Optional date string (YYYY-MM-DD) to narrow search
Returns:
True if memory was found and deleted, False otherwise
"""
from datetime import datetime, timedelta
# Determine which files to search
if date:
dates_to_check = [date]
else:
# Check last 30 days
dates_to_check = [
(datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d")
for i in range(30)
]
for check_date in dates_to_check:
episodic_file = self.episodic_path / f"{check_date}.json"
if episodic_file.exists():
data = self._read_json(episodic_file, [])
for i, item in enumerate(data):
if item.get("id") == memory_id:
del data[i]
self._write_json(episodic_file, data)
self._log(f"Deleted episodic memory: {memory_id}")
return True
return False
def delete_memory(
self,
memory_id: str,
memory_type: Optional[str] = None
) -> bool:
"""
Delete a specific memory by ID.
This is the unified deletion method that searches across all memory types.
Use this when you have a memory ID but don't know its type.
Particularly useful for:
- Cleaning up image-based memories after processing to free context window
- Removing outdated or incorrect information
- Privacy compliance (selective erasure)
Args:
memory_id: The unique ID of the memory to delete
memory_type: Optional type hint to narrow search:
'short_term', 'long_term', 'entity', 'episodic'
If None, searches all types.
Returns:
True if memory was found and deleted, False otherwise
Example:
# Delete a specific memory after processing an image
memory.delete_memory("abc123def456")
# Delete with type hint for faster lookup
memory.delete_memory("abc123", memory_type="short_term")
"""
# If type specified, only search that type
if memory_type == "short_term":
return self.delete_short_term(memory_id)
elif memory_type == "long_term":
return self.delete_long_term(memory_id)
elif memory_type == "entity":
return self.delete_entity(memory_id)
elif memory_type == "episodic":
return self.delete_episodic(memory_id)
# Search all types
if self.delete_short_term(memory_id):
return True
if self.delete_long_term(memory_id):
return True
if self.delete_entity(memory_id):
return True
if self.delete_episodic(memory_id):
return True
return False
def delete_memories(self, memory_ids: List[str]) -> int:
"""
Delete multiple memories by their IDs.
Args:
memory_ids: List of memory IDs to delete
Returns:
Number of memories successfully deleted
"""
count = 0
for memory_id in memory_ids:
if self.delete_memory(memory_id):
count += 1
return count
def delete_memories_matching(
self,
query: str,
memory_types: Optional[List[str]] = None,
limit: int = 10
) -> int:
"""
Delete memories matching a search query.
Useful for bulk cleanup of related memories, e.g., all image-related
context after finishing an image analysis session.
Args:
query: Search query to match memories
memory_types: Optional list of types to search
limit: Maximum number of memories to delete
Returns:
Number of memories deleted
"""
# Search for matching memories
results = self.search(query, memory_types=memory_types, limit=limit)
# Extract IDs and delete
deleted = 0
for result in results:
item = result.get("item", {})
memory_id = item.get("id")
memory_type = result.get("type")
if memory_id:
if self.delete_memory(memory_id, memory_type=memory_type):
deleted += 1
if deleted:
self._log(f"Deleted {deleted} memories matching '{query}'")
return deleted
def get_stats(self) -> Dict[str, Any]:
"""Get memory statistics."""
episodic_count = sum(1 for _ in self.episodic_path.glob("*.json"))
@@ -1222,6 +1436,66 @@ Summary:"""
else:
return {"error": "Usage: /memory clear [short|all]"}
elif action == "delete":
if not args:
return {"error": "Usage: /memory delete <id> or /memory delete --query <search_query>"}
# Handle --query flag for bulk deletion
if args.startswith("--query "):
query = args[8:] # Remove "--query " prefix
if not query:
return {"error": "Usage: /memory delete --query <search_query>"}
deleted = self.delete_memories_matching(query, limit=10)
return {
"action": "delete",
"type": "query",
"query": query,
"deleted_count": deleted
}
# Single ID deletion
success = self.delete_memory(args)
return {
"action": "delete",
"type": "single",
"id": args,
"success": success
}
elif action == "list":
# List memories with IDs for easy deletion reference
memory_type = args.lower() if args else "all"
items = []
if memory_type in ("all", "short", "short_term"):
for item in self.get_short_term(limit=20):
items.append({
"type": "short_term",
"id": item.id,
"content": item.content[:100] + "..." if len(item.content) > 100 else item.content,
"importance": item.importance
})
if memory_type in ("all", "long", "long_term"):
for item in self.get_long_term(limit=20):
items.append({
"type": "long_term",
"id": item.id,
"content": item.content[:100] + "..." if len(item.content) > 100 else item.content,
"importance": item.importance
})
if memory_type in ("all", "entity", "entities"):
for name, entity in self._entities.items():
items.append({
"type": "entity",
"id": name,
"name": entity.name,
"entity_type": entity.entity_type
})
return {"action": "list", "filter": memory_type, "items": items}
elif action == "search":
if not args:
return {"error": "Usage: /memory search <query>"}
@@ -1274,8 +1548,11 @@ Summary:"""
"action": "help",
"commands": {
"/memory show": "Display memory stats and recent items",
"/memory list [short|long|entity]": "List memories with IDs for deletion",
"/memory add <content>": "Add to long-term memory",
"/memory clear [short|all]": "Clear memory",
"/memory delete <id>": "Delete a specific memory by ID",
"/memory delete --query <q>": "Delete memories matching query",
"/memory clear [short|all]": "Clear all memory (bulk)",
"/memory search <query>": "Search memories",
"/memory save <name>": "Save session",
"/memory resume <name>": "Resume session",
@@ -1117,6 +1117,213 @@ class Memory:
self.chroma_client.reset() # entire DB
self._init_chroma() # re-init fresh
# -------------------------------------------------------------------------
# Selective Deletion Methods
# -------------------------------------------------------------------------
def delete_short_term(self, memory_id: str) -> bool:
"""
Delete a specific short-term memory by ID.
Args:
memory_id: The unique ID of the memory to delete
Returns:
True if memory was found and deleted, False otherwise
"""
deleted = False
# Delete from SQLite
try:
conn = sqlite3.connect(self.short_db)
cursor = conn.execute(
"DELETE FROM short_mem WHERE id = ?", (memory_id,)
)
if cursor.rowcount > 0:
deleted = True
conn.commit()
conn.close()
except Exception as e:
self._log_verbose(f"Error deleting from SQLite short-term: {e}", logging.ERROR)
# Delete from MongoDB if enabled
if self.use_mongodb and hasattr(self, "mongo_short_term"):
try:
result = self.mongo_short_term.delete_one({"_id": memory_id})
if result.deleted_count > 0:
deleted = True
except Exception as e:
self._log_verbose(f"Error deleting from MongoDB short-term: {e}", logging.ERROR)
if deleted:
self._log_verbose(f"Deleted short-term memory: {memory_id}")
return deleted
def delete_long_term(self, memory_id: str) -> bool:
"""
Delete a specific long-term memory by ID.
Works across all backends: SQLite, ChromaDB, Mem0, and MongoDB.
Args:
memory_id: The unique ID of the memory to delete
Returns:
True if memory was found and deleted, False otherwise
"""
deleted = False
# Delete from SQLite
try:
conn = sqlite3.connect(self.long_db)
cursor = conn.execute(
"DELETE FROM long_mem WHERE id = ?", (memory_id,)
)
if cursor.rowcount > 0:
deleted = True
conn.commit()
conn.close()
except Exception as e:
self._log_verbose(f"Error deleting from SQLite long-term: {e}", logging.ERROR)
# Delete from ChromaDB if enabled
if self.use_rag and hasattr(self, "chroma_col"):
try:
# ChromaDB delete by ID
self.chroma_col.delete(ids=[memory_id])
deleted = True # ChromaDB doesn't raise on non-existent ID
except Exception as e:
self._log_verbose(f"Error deleting from ChromaDB: {e}", logging.ERROR)
# Delete from Mem0 if enabled
if self.use_mem0 and hasattr(self, "mem0_client"):
try:
# Mem0 has a delete method
self.mem0_client.delete(memory_id)
deleted = True
except Exception as e:
self._log_verbose(f"Error deleting from Mem0: {e}", logging.ERROR)
# Delete from MongoDB if enabled
if self.use_mongodb and hasattr(self, "mongo_long_term"):
try:
result = self.mongo_long_term.delete_one({"_id": memory_id})
if result.deleted_count > 0:
deleted = True
except Exception as e:
self._log_verbose(f"Error deleting from MongoDB long-term: {e}", logging.ERROR)
if deleted:
self._log_verbose(f"Deleted long-term memory: {memory_id}")
return deleted
def delete_memory(
self,
memory_id: str,
memory_type: Optional[str] = None
) -> bool:
"""
Delete a specific memory by ID.
This is the unified deletion method that searches across all memory types
and all backends (SQLite, ChromaDB, Mem0, MongoDB).
Particularly useful for:
- Cleaning up image-based memories after processing to free context window
- Removing outdated or incorrect information
- Privacy compliance (selective erasure)
Args:
memory_id: The unique ID of the memory to delete
memory_type: Optional type hint to narrow search:
'short_term', 'long_term'
If None, searches all types.
Returns:
True if memory was found and deleted, False otherwise
Example:
# Delete a specific image analysis memory
memory.delete_memory("1705123456789")
# Delete with type hint for faster lookup
memory.delete_memory("1705123456789", memory_type="short_term")
"""
# If type specified, only search that type
if memory_type == "short_term":
return self.delete_short_term(memory_id)
elif memory_type == "long_term":
return self.delete_long_term(memory_id)
# Search both types
if self.delete_short_term(memory_id):
return True
if self.delete_long_term(memory_id):
return True
return False
def delete_memories(self, memory_ids: List[str]) -> int:
"""
Delete multiple memories by their IDs.
Args:
memory_ids: List of memory IDs to delete
Returns:
Number of memories successfully deleted
"""
count = 0
for memory_id in memory_ids:
if self.delete_memory(memory_id):
count += 1
return count
def delete_memories_matching(
self,
query: str,
memory_type: Optional[str] = None,
limit: int = 10
) -> int:
"""
Delete memories matching a search query.
Useful for bulk cleanup of related memories, e.g., all image-related
context after finishing an image analysis session.
Args:
query: Search query to match memories
memory_type: Optional type ('short_term' or 'long_term')
limit: Maximum number of memories to delete
Returns:
Number of memories deleted
"""
deleted = 0
# Search short-term if applicable
if memory_type in (None, "short_term"):
results = self.search_short_term(query, limit=limit)
for result in results:
memory_id = result.get("id")
if memory_id and self.delete_short_term(memory_id):
deleted += 1
# Search long-term if applicable
if memory_type in (None, "long_term"):
results = self.search_long_term(query, limit=limit)
for result in results:
memory_id = result.get("id")
if memory_id and self.delete_long_term(memory_id):
deleted += 1
if deleted:
self._log_verbose(f"Deleted {deleted} memories matching '{query}'")
return deleted
# -------------------------------------------------------------------------
# Entity Memory Methods
# -------------------------------------------------------------------------
@@ -138,6 +138,77 @@ class ResettableMemoryProtocol(MemoryProtocol, Protocol):
...
@runtime_checkable
class DeletableMemoryProtocol(MemoryProtocol, Protocol):
"""
Protocol for memory that supports selective deletion.
Enables removing specific memories by ID without clearing all data.
Essential for:
- Privacy compliance (GDPR right to erasure)
- Context window management (removing large image-based memories)
- Correcting outdated information
Example:
```python
class MyMemory:
def delete_memory(self, memory_id: str) -> bool:
if memory_id in self._store:
del self._store[memory_id]
return True
return False
def delete_memories(self, memory_ids: List[str]) -> int:
count = 0
for mid in memory_ids:
if self.delete_memory(mid):
count += 1
return count
```
"""
def delete_memory(self, memory_id: str, memory_type: Optional[str] = None) -> bool:
"""
Delete a specific memory by ID.
Args:
memory_id: The unique identifier of the memory to delete
memory_type: Optional type hint (short_term, long_term, entity)
If None, searches all types.
Returns:
True if memory was found and deleted, False otherwise
"""
...
def delete_memories(self, memory_ids: List[str]) -> int:
"""
Delete multiple memories by their IDs.
Args:
memory_ids: List of memory IDs to delete
Returns:
Number of memories successfully deleted
"""
...
@runtime_checkable
class AsyncDeletableMemoryProtocol(Protocol):
"""
Async Protocol for memory deletion operations.
"""
async def adelete_memory(self, memory_id: str, memory_type: Optional[str] = None) -> bool:
"""Async delete a specific memory by ID."""
...
async def adelete_memories(self, memory_ids: List[str]) -> int:
"""Async delete multiple memories by their IDs."""
...
@runtime_checkable
class AsyncMemoryProtocol(Protocol):
"""
@@ -207,6 +278,9 @@ class EntityMemoryProtocol(Protocol):
__all__ = [
'MemoryProtocol',
'ResettableMemoryProtocol',
'DeletableMemoryProtocol',
'AsyncDeletableMemoryProtocol',
'AsyncMemoryProtocol',
'EntityMemoryProtocol',
]
@@ -43,6 +43,7 @@ class Task:
rerun: bool = False, # Renamed from can_rerun and logic inverted, default True for backward compatibility
retain_full_context: bool = False, # By default, only use previous task output, not all previous tasks
guardrail: Optional[Union[Callable[[TaskOutput], Tuple[bool, Any]], str]] = None,
guardrails: Optional[Union[Callable[[TaskOutput], Tuple[bool, Any]], str]] = None, # Alias for guardrail
max_retries: int = 3,
retry_count: int = 0,
agent_config: Optional[Dict[str, Any]] = None,
@@ -86,7 +87,7 @@ class Task:
self.quality_check = quality_check
self.rerun = rerun # Assigning the rerun parameter
self.retain_full_context = retain_full_context
self.guardrail = guardrail
self.guardrail = guardrail if guardrail is not None else guardrails # guardrails is alias
self.max_retries = max_retries
self.retry_count = retry_count
self._guardrail_fn = None
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.12.2"
version = "0.12.4"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
readme = "README.md"
requires-python = ">=3.10"
@@ -12,8 +12,7 @@ from praisonaiagents import QueryRewriterAgent, RewriteStrategy
def main():
# Initialize the agent
agent = QueryRewriterAgent(
model="gpt-4o-mini",
verbose=True
model="gpt-4o-mini"
)
print("=" * 60)
+1 -2
View File
@@ -4,8 +4,7 @@ agent = Agent(
instructions="You are a helpful assistant",
llm="gemini/gemini-2.0-flash",
reflection=False,
verbose=False,
stream=True
output="stream"
)
# Use return_generator=True to get the raw generator for custom streaming handling
@@ -6,12 +6,12 @@ This script directly tests the LLM class to determine where the duplicate callba
ROOT CAUSE IDENTIFIED:
In llm.py, there are two separate callback execution paths:
1. execute_sync_callback() called directly (lines 851-857, 885-891, etc.)
2. display_interaction() called when verbose=True (line 896, etc.)
2. display_interaction() called when output="verbose" (line 896, etc.)
Since display_interaction() ALSO executes sync callbacks internally (main.py lines 164-190),
this results in duplicate callback execution for the same LLM response.
The issue occurs specifically when verbose=True because both callback paths are active.
The issue occurs specifically when output="verbose" because both callback paths are active.
"""
import os
@@ -74,11 +74,11 @@ def test_llm_streaming():
register_display_callback('interaction', track_callback, is_async=False)
try:
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", verbose=False)
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", output="silent")
response = llm.get_response(
prompt="Say exactly: Hello World",
verbose=True, # This should trigger the callback
output="verbose", # This should trigger the callback
stream=True # Test with streaming
)
@@ -103,11 +103,11 @@ def test_llm_non_streaming():
register_display_callback('interaction', track_callback, is_async=False)
try:
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", verbose=False)
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", output="silent")
response = llm.get_response(
prompt="Say exactly: Hello World",
verbose=True, # This should trigger the callback
output="verbose", # This should trigger the callback
stream=False # Test without streaming
)
@@ -132,11 +132,11 @@ def test_llm_with_self_reflection():
register_display_callback('interaction', track_callback, is_async=False)
try:
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", verbose=False, reflection=True)
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", output="silent", reflection=True)
response = llm.get_response(
prompt="Say exactly: Hello World",
verbose=True, # This should trigger the callback
output="verbose", # This should trigger the callback
reflection=True,
min_reflect=1,
max_reflect=1
@@ -152,25 +152,25 @@ def test_llm_with_self_reflection():
return -1
def test_verbose_vs_non_verbose():
"""Test the key difference: verbose=True vs verbose=False to isolate the duplicate issue."""
"""Test the key difference: output="verbose" vs output="silent" to isolate the duplicate issue."""
print("🧪 TEST 4: Verbose vs Non-Verbose (Key Test for Duplicate Issue)")
print("-" * 60)
global callback_invocations
# Test with verbose=False (should trigger callback once via execute_sync_callback)
print(" 🔹 Testing with verbose=False:")
# Test with output="silent" (should trigger callback once via execute_sync_callback)
print(" 🔹 Testing with output="silent":")
callback_invocations = []
register_display_callback('interaction', track_callback, is_async=False)
try:
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", verbose=False)
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", output="silent")
response = llm.get_response(
prompt="Say exactly: Test Non-Verbose",
verbose=False # This should only trigger execute_sync_callback
output="silent" # This should only trigger execute_sync_callback
)
print(f" Response: {response}")
print(f" Callbacks with verbose=False: {len(callback_invocations)}")
print(f" Callbacks with output="silent": {len(callback_invocations)}")
non_verbose_count = len(callback_invocations)
except Exception as e:
print(f" ❌ Error: {e}")
@@ -178,19 +178,19 @@ def test_verbose_vs_non_verbose():
print()
# Test with verbose=True (should trigger callback twice: execute_sync_callback + display_interaction)
print(" 🔹 Testing with verbose=True:")
# Test with output="verbose" (should trigger callback twice: execute_sync_callback + display_interaction)
print(" 🔹 Testing with output="verbose":")
callback_invocations = []
register_display_callback('interaction', track_callback, is_async=False)
try:
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", verbose=False)
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", output="silent")
response = llm.get_response(
prompt="Say exactly: Test Verbose",
verbose=True # This should trigger BOTH execute_sync_callback AND display_interaction
output="verbose" # This should trigger BOTH execute_sync_callback AND display_interaction
)
print(f" Response: {response}")
print(f" Callbacks with verbose=True: {len(callback_invocations)}")
print(f" Callbacks with output="verbose": {len(callback_invocations)}")
verbose_count = len(callback_invocations)
# Show the call stack differences
@@ -204,12 +204,12 @@ def test_verbose_vs_non_verbose():
verbose_count = -1
print(f"\n 📊 RESULT:")
print(f" verbose=False: {non_verbose_count} callback(s)")
print(f" verbose=True: {verbose_count} callback(s)")
print(f" output="silent": {non_verbose_count} callback(s)")
print(f" output="verbose": {verbose_count} callback(s)")
if verbose_count > non_verbose_count and verbose_count > 1:
print(f" 🚨 DUPLICATE ISSUE CONFIRMED!")
print(f" verbose=True triggers {verbose_count - non_verbose_count} extra callback(s)")
print(f" output="verbose" triggers {verbose_count - non_verbose_count} extra callback(s)")
return verbose_count
@@ -225,13 +225,13 @@ def test_multiple_calls():
register_display_callback('interaction', track_callback, is_async=False)
try:
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", verbose=False)
llm = LLM(model="gemini/gemini-2.5-flash-lite-preview-06-17", output="silent")
for i in range(3):
print(f" Call {i+1}:")
response = llm.get_response(
prompt=f"Say exactly: Call {i+1}",
verbose=True
output="verbose"
)
print(f" Response: {response}")
@@ -319,10 +319,10 @@ if __name__ == "__main__":
if count > expected * 1.5: # Allow some tolerance
issues_detected.append(f"{test_name}: {count} > expected {expected}")
elif test_name == 'verbose_vs_nonverbose':
# This is the key test - verbose=True should show the duplicate issue
print(f"{test_name}: {count} invocations (this test shows verbose=True duplicates)")
# This is the key test - output="verbose" should show the duplicate issue
print(f"{test_name}: {count} invocations (this test shows output="verbose" duplicates)")
if count > 1:
issues_detected.append(f"{test_name}: verbose=True triggered {count} callbacks (duplicate issue)")
issues_detected.append(f"{test_name}: output="verbose" triggered {count} callbacks (duplicate issue)")
else:
expected = 1
print(f"{test_name}: {count} invocations (expected {expected})")
+2 -2
View File
@@ -10,7 +10,7 @@ agent = session.Agent(
name="Assistant",
instructions="You are a helpful assistant with memory.",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=False,
output="silent",
memory=True
)
@@ -33,7 +33,7 @@ anotherAgent = anotherSession.Agent(
name="Assistant",
instructions="You are a helpful assistant with memory.",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=False,
output="silent",
memory=True
)
@@ -16,7 +16,7 @@ from praisonaiagents.config import OutputConfig
agent = Agent(
instructions="You are a helpful assistant",
llm="gpt-4o-mini",
output=OutputConfig(verbose=True) # Show generation progress
output="verbose" # Show generation progress
)
# Run the agent - verbose mode shows streaming progress via Rich Live display
@@ -10,7 +10,7 @@ agent = Agent(
planning=True,
planning_tools=[search_web],
planning_reasoning=True,
verbose=False # Quiet mode
output="silent" # Quiet mode
)
result = agent.start("Research AI trends in 2025 and write a 2 sentence summary")
@@ -176,9 +176,9 @@ def test_llm_console_lazy_loading():
print("\n6. Testing LLM console lazy loading...")
# LLM without verbose (console not needed)
llm1 = LLM(model="gemini/gemini-2.0-flash-exp", verbose=False)
llm1 = LLM(model="gemini/gemini-2.0-flash-exp", output="silent")
console_created_before = hasattr(llm1, '_console') and llm1._console is not None
print(f" Console created before access (verbose=False): {console_created_before}")
print(f" Console created before access (output="silent"): {console_created_before}")
# Access console
_ = llm1.console
@@ -151,7 +151,7 @@ async def test_sequential_async_execution():
agents=[async_agent, sync_agent],
tasks=tasks,
process="sequential",
verbose=True
output="verbose"
)
# Run the workflow
@@ -48,7 +48,7 @@ def test_hierarchical_without_openai():
process="hierarchical",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
reflection=True,
verbose=True,
output="verbose",
max_agents=3 # Maximum number of agents to create
)
@@ -88,7 +88,7 @@ def test_hierarchical_with_mock_llm():
"api_key": "mock-key",
"base_url": "http://localhost:9999" # Non-existent URL
},
verbose=False,
output="silent",
max_agents=2
)
@@ -43,7 +43,7 @@ def test_callback_enhancement():
goal="Help with testing callbacks",
backstory="I am a helpful assistant for testing purposes",
llm="openai/gpt-5-nano",
verbose=True
output="verbose"
)
# Create a task with detailed information
@@ -59,7 +59,7 @@ def test_callback_enhancement():
agents = Agents(
agents=[agent],
tasks=[task],
verbose=True
output="verbose"
)
result = agents.start()
print(f"Test completed successfully! Result: {result}")
+7 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Test script to verify callbacks work with verbose=False"""
"""Test script to verify callbacks work with output="silent""""
import sys
sys.path.insert(0, 'src/praisonai-agents')
@@ -27,15 +27,15 @@ def simple_callback(message=None, response=None, **kwargs):
# Register callback
register_display_callback('interaction', simple_callback, is_async=False)
# Test with verbose=False
print("Testing with verbose=False...")
# Test with output="silent"
print("Testing with output="silent"...")
agent = Agent(
name="TestAgent",
role="Assistant",
goal="Help with tasks",
backstory="I am a helpful assistant",
llm="gpt-4o-mini", # Using a simple model for testing
verbose=False
output="silent"
)
task = Task(
@@ -47,7 +47,7 @@ task = Task(
# Clear the log file
with open('callback_test_log.txt', 'w') as f:
f.write("Starting test with verbose=False\n")
f.write("Starting test with output="silent"\n")
# Run the agent
try:
@@ -61,9 +61,9 @@ try:
# Check if callback was executed
if callback_executed:
print("✅ SUCCESS: Callback was executed with verbose=False!")
print("✅ SUCCESS: Callback was executed with output="silent"!")
else:
print("❌ FAILED: Callback was NOT executed with verbose=False")
print("❌ FAILED: Callback was NOT executed with output="silent"")
except Exception as e:
print(f"Error during test: {e}")
@@ -43,10 +43,10 @@ def test_single_display_no_tools():
MagicMock(choices=[MagicMock(delta=MagicMock(content="!"))])
]
llm = LLM(model="gpt-4o-mini", verbose=False)
llm = LLM(model="gpt-4o-mini", output="silent")
response = llm.get_response(
prompt="Test prompt",
verbose=True,
output="verbose",
stream=True
)
@@ -82,10 +82,10 @@ def test_single_display_with_reasoning():
}]
}
llm = LLM(model="o1-preview", verbose=False, reasoning_steps=True)
llm = LLM(model="o1-preview", output="silent", reasoning_steps=True)
response = llm.get_response(
prompt="What is the meaning of life?",
verbose=True,
output="verbose",
stream=False,
reasoning_steps=True
)
@@ -139,10 +139,10 @@ def test_single_display_with_self_reflection():
mock_completion.side_effect = mock_streaming
llm = LLM(model="gpt-4o-mini", verbose=False, reflection=True, min_reflect=1, max_reflect=2)
llm = LLM(model="gpt-4o-mini", output="silent", reflection=True, min_reflect=1, max_reflect=2)
response = llm.get_response(
prompt="Test prompt",
verbose=True,
output="verbose",
stream=True,
reflection=True,
min_reflect=1,
@@ -179,10 +179,10 @@ def test_async_single_display():
mock_acompletion.return_value = async_generator()
llm = LLM(model="gpt-4o-mini", verbose=False)
llm = LLM(model="gpt-4o-mini", output="silent")
response = await llm.get_response_async(
prompt="Test async",
verbose=True,
output="verbose",
stream=True
)
+1 -1
View File
@@ -15,7 +15,7 @@ def test_self_reflection_fix():
backstory="You are a helpful math assistant",
reflection=True,
llm="gpt-4o-mini", # Use a more widely available model
verbose=True,
output="verbose",
tools=[evaluate],
min_reflect=1,
max_reflect=2
@@ -25,7 +25,7 @@ test_agent = Agent(
goal="Test the fix",
tools=[simple_tool],
llm="gpt-4o-mini", # Using OpenAI for testing
verbose=False # This is key - verbose=False triggers the bug
output="silent" # This is key - output="silent" triggers the bug
)
# Simple task
@@ -44,7 +44,7 @@ async def main():
agents=[test_agent],
tasks=[test_task],
process="sequential",
verbose=False # This should not cause the error anymore
output="silent" # This should not cause the error anymore
)
try:
@@ -43,7 +43,7 @@ def test_gemini_embedding():
agents_openai = Agents(
agents=[agent],
tasks=[task],
verbose=5,
output="debug",
memory=True
)
@@ -72,7 +72,7 @@ def test_gemini_embedding():
agents_openai_explicit = Agents(
agents=[agent2],
tasks=[task2],
verbose=5,
output="debug",
memory=True,
embedder={
"provider": "openai",
@@ -108,7 +108,7 @@ def test_gemini_embedding():
agents_gemini = Agents(
agents=[agent3],
tasks=[task3],
verbose=5,
output="debug",
memory=True,
embedder={
"provider": "gemini",
+12 -12
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
Test script to reproduce and verify the fix for issue #877
where callbacks only work when verbose=True.
where callbacks only work when output="verbose".
"""
import sys
@@ -36,11 +36,11 @@ with open('callback_log.txt', 'w') as f:
register_display_callback('interaction', simple_callback, is_async=False)
print("=" * 60)
print("Testing Issue #877: Callbacks Only Work When verbose=True")
print("Testing Issue #877: Callbacks Only Work When output="verbose"")
print("=" * 60)
# Test Case 1: verbose=False
print("\nTest Case 1: verbose=False")
# Test Case 1: output="silent"
print("\nTest Case 1: output="silent"")
print("-" * 30)
callback_log.clear()
@@ -50,7 +50,7 @@ agent1 = Agent(
goal="Help with tasks",
backstory="I am a helpful assistant",
llm="gemini/gemini-2.5-flash-lite-preview-06-17", # Using the same model as in the issue
verbose=False
output="silent"
)
task1 = Task(
@@ -68,17 +68,17 @@ try:
result1 = agents1.start()
print(f"Task completed. Callbacks executed: {len(callback_log)}")
if len(callback_log) > 0:
print("✅ SUCCESS: Callbacks were executed with verbose=False!")
print("✅ SUCCESS: Callbacks were executed with output="silent"!")
print(f" First callback: message='{callback_log[0]['message'][:50]}...', response='{callback_log[0]['response'][:50]}...'")
else:
print("❌ FAILED: No callbacks were executed with verbose=False")
print("❌ FAILED: No callbacks were executed with output="silent"")
except Exception as e:
print(f"❌ ERROR during test: {e}")
import traceback
traceback.print_exc()
# Test Case 2: verbose=True (for comparison)
print("\n\nTest Case 2: verbose=True (for comparison)")
# Test Case 2: output="verbose" (for comparison)
print("\n\nTest Case 2: output="verbose" (for comparison)")
print("-" * 30)
callback_log.clear()
@@ -88,7 +88,7 @@ agent2 = Agent(
goal="Help with tasks",
backstory="I am a helpful assistant",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
output="verbose"
)
task2 = Task(
@@ -106,10 +106,10 @@ try:
result2 = agents2.start()
print(f"\nTask completed. Callbacks executed: {len(callback_log)}")
if len(callback_log) > 0:
print("✅ Callbacks were executed with verbose=True as expected")
print("✅ Callbacks were executed with output="verbose" as expected")
print(f" First callback: message='{callback_log[0]['message'][:50]}...', response='{callback_log[0]['response'][:50]}...'")
else:
print("⚠️ WARNING: No callbacks were executed even with verbose=True")
print("⚠️ WARNING: No callbacks were executed even with output="verbose"")
except Exception as e:
print(f"❌ ERROR during test: {e}")
import traceback
+5 -5
View File
@@ -23,7 +23,7 @@ article_agent = Agent(
goal="Fetch the user's favorite morning article",
tools=[fetch_favorite_article],
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
output="verbose"
)
news_agent = Agent(
@@ -32,7 +32,7 @@ news_agent = Agent(
goal="Search for trending news in Kenya",
tools=[search_trending_kenya],
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
output="verbose"
)
twitter_agent = Agent(
@@ -41,7 +41,7 @@ twitter_agent = Agent(
goal="Fetch the latest Twitter feed",
tools=[fetch_twitter_feed],
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
output="verbose"
)
aggregator = Agent(
@@ -49,7 +49,7 @@ aggregator = Agent(
role="Result Aggregator",
goal="Aggregate and summarize all results",
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
output="verbose"
)
# Tasks for each agent
@@ -94,7 +94,7 @@ async def main():
agents=[article_agent, news_agent, twitter_agent, aggregator],
tasks=[article_task, news_task, twitter_task, aggregate_task],
process="workflow",
verbose=True
output="verbose"
)
results = await workflow.astart()
+3 -3
View File
@@ -60,7 +60,7 @@ def test_router_agent():
goal="Test multi-model functionality",
models=["gpt-4o-mini"], # Using single model for testing
routing_strategy="auto",
verbose=False
output="silent"
)
print(f"Agent created: {agent.name}")
@@ -88,7 +88,7 @@ def test_integration():
goal="Test the integration",
models=["gpt-4o-mini"], # Single model for testing
routing_strategy="manual", # Use manual to avoid complexity
verbose=False
output="silent"
)
# Create a simple task
@@ -104,7 +104,7 @@ def test_integration():
agents=[agent],
tasks=[task],
process="sequential",
verbose=False
output="silent"
)
print("Created Agents with RouterAgent")
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more