mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82b83ded41 | ||
|
|
fc80db32c2 | ||
|
|
3f115ca7ba | ||
|
|
a32d45e6da | ||
|
|
8823df26ff | ||
|
|
9c2126fbd6 | ||
|
|
741b01bdf0 | ||
|
|
6ed9a69cd8 | ||
|
|
56a42e6d95 | ||
|
|
d2b4affdd1 | ||
|
|
0d5c8b478f | ||
|
|
fdfad70319 | ||
|
|
fba6b2e2a0 | ||
|
|
f9bfb85f44 | ||
|
|
aab4b5b466 | ||
|
|
7ccadde004 | ||
|
|
22a9d5ff23 | ||
|
|
98a11209ea | ||
|
|
df3c97e73a | ||
|
|
27808964be | ||
|
|
b5daf16e0d | ||
|
|
a2cac05d16 | ||
|
|
ad9828d3bb | ||
|
|
85ddf04b47 | ||
|
|
1c1a1b719b | ||
|
|
a8e0914e05 | ||
|
|
ec6d883f4f | ||
|
|
d094cbfac5 | ||
|
|
14e3724aed | ||
|
|
92436277d9 | ||
|
|
3e0fd298e2 | ||
|
|
fad0b41b52 | ||
|
|
6c3e5ee187 | ||
|
|
f842ee7048 | ||
|
|
b0ff6f3f60 | ||
|
|
798f6dad14 | ||
|
|
629bf0d669 | ||
|
|
eaca447b82 | ||
|
|
bef3693627 | ||
|
|
aa62babed6 | ||
|
|
b73f181200 | ||
|
|
66d2998047 | ||
|
|
c5b6f8b91a | ||
|
|
823e46ca84 | ||
|
|
57040333a7 | ||
|
|
8c6800f876 | ||
|
|
0d701ae43c | ||
|
|
7a8c25621c | ||
|
|
218c5443b1 | ||
|
|
f56810341c | ||
|
|
4f601a573f | ||
|
|
43c03f4813 | ||
|
|
03856b74bf | ||
|
|
eadfc0cff0 | ||
|
|
f33c44d4c5 | ||
|
|
3f7885289d | ||
|
|
833c795e9a | ||
|
|
c460c9da60 | ||
|
|
656887c700 | ||
|
|
e0089cac41 | ||
|
|
bf5e069c7b | ||
|
|
94cf264b4c | ||
|
|
ab44fd4b27 |
No files matched your search
+2
-1
@@ -65,4 +65,5 @@ outputs
|
||||
agents/praisonaiagents/build
|
||||
agents/praisonaiagents/dist
|
||||
agents/praisonaiagents/praisonaiagents.egg-info
|
||||
.codegpt
|
||||
.codegpt
|
||||
.praison
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN pip install flask praisonai==2.0.29 gunicorn markdown
|
||||
RUN pip install flask praisonai==2.0.46 gunicorn markdown
|
||||
EXPOSE 8080
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
|
||||
@@ -1,6 +1,7 @@
|
||||
framework: praisonai
|
||||
process: sequential
|
||||
topic: create movie script about cat in mars
|
||||
memory: false
|
||||
roles:
|
||||
researcher:
|
||||
backstory: Skilled in research, with a focus on gathering accurate and relevant
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
1. Make minimal code changes as possible
|
||||
2. if you are creating function, make sure to name it in more easy, user friendly , optimised manner.
|
||||
3. Never copy the exact code if presented with a code from other location or file or codebase, just make it optimised
|
||||
4. Always Follow the principle LESS IS MORE
|
||||
5. Make it minimal change as possible
|
||||
6. Firstly try not to make any modification to the existing code as possible
|
||||
7. Only modify the existing code if its highly required, without that if it cant be done, then add new code section.
|
||||
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
|
||||
+2
-1
@@ -83,4 +83,5 @@ dist
|
||||
build
|
||||
*.mp4
|
||||
*.png
|
||||
graph.py
|
||||
graph.py
|
||||
chroma_db/
|
||||
Whitespace-only changes.
@@ -0,0 +1,32 @@
|
||||
.cursorrules
|
||||
.gitignore
|
||||
.praisoncontext
|
||||
.praisonignore
|
||||
.windsurfrules
|
||||
async_example.py
|
||||
async_example_full.py
|
||||
async_example_full_multigroups.py
|
||||
auto_agents_example.py
|
||||
code_agents_example.py
|
||||
example_callback.py
|
||||
example_custom_tools.py
|
||||
example_sequential.py
|
||||
image.jpg
|
||||
langchain_example.py
|
||||
langchain_logs.py
|
||||
long_term.db
|
||||
mini_agents_example.py
|
||||
multimodal.py
|
||||
praisonaiagents/tools/*
|
||||
praisonaiagents/process/*
|
||||
pyproject.toml
|
||||
short_term.db
|
||||
structured_agents_example.py
|
||||
structured_response_example.py
|
||||
test.py
|
||||
tools_example.py
|
||||
uv.lock
|
||||
workflow_example_basic.py
|
||||
workflow_example_detailed.py
|
||||
__pycache__
|
||||
__pycache__/*
|
||||
@@ -0,0 +1,8 @@
|
||||
1. Make minimal code changes as possible
|
||||
2. if you are creating function, make sure to name it in more easy, user friendly , optimised manner.
|
||||
3. Never copy the exact code if presented with a code from other location or file or codebase, just make it optimised
|
||||
4. Always Follow the principle LESS IS MORE
|
||||
5. Make it minimal change as possible
|
||||
6. Firstly try not to make any modification to the existing code as possible
|
||||
7. Only modify the existing code if its highly required, without that if it cant be done, then add new code section.
|
||||
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
|
||||
@@ -0,0 +1,11 @@
|
||||
from praisonaiagents import AutoAgents
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agents = AutoAgents(
|
||||
instructions="Search for information about AI Agents",
|
||||
tools=[duckduckgo],
|
||||
process="sequential",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
agents.start()
|
||||
@@ -0,0 +1,114 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import time
|
||||
|
||||
def get_environment_state():
|
||||
"""Simulates getting current environment state"""
|
||||
current_time = int(time.time())
|
||||
states = ["normal", "critical", "optimal"]
|
||||
state = states[current_time % 3]
|
||||
print(f"Environment state: {state}")
|
||||
return state
|
||||
|
||||
def perform_action(state: str):
|
||||
"""Simulates performing an action based on state"""
|
||||
actions = {
|
||||
"normal": "maintain",
|
||||
"critical": "fix",
|
||||
"optimal": "enhance"
|
||||
}
|
||||
action = actions.get(state, "observe")
|
||||
print(f"Performing action: {action} for state: {state}")
|
||||
return action
|
||||
|
||||
def get_feedback():
|
||||
"""Simulates environment feedback"""
|
||||
current_time = int(time.time())
|
||||
feedback = "positive" if current_time % 2 == 0 else "negative"
|
||||
print(f"Feedback received: {feedback}")
|
||||
return feedback
|
||||
|
||||
# Create specialized agents
|
||||
llm_caller = Agent(
|
||||
name="Environment Monitor",
|
||||
role="State analyzer",
|
||||
goal="Monitor environment and analyze state",
|
||||
instructions="Check environment state and provide analysis",
|
||||
tools=[get_environment_state]
|
||||
)
|
||||
|
||||
action_agent = Agent(
|
||||
name="Action Executor",
|
||||
role="Action performer",
|
||||
goal="Execute appropriate actions based on state",
|
||||
instructions="Determine and perform actions based on environment state",
|
||||
tools=[perform_action]
|
||||
)
|
||||
|
||||
feedback_agent = Agent(
|
||||
name="Feedback Processor",
|
||||
role="Feedback analyzer",
|
||||
goal="Process environment feedback and adapt strategy",
|
||||
instructions="Analyze feedback and provide adaptation recommendations",
|
||||
tools=[get_feedback]
|
||||
)
|
||||
|
||||
# Create tasks for autonomous workflow
|
||||
monitor_task = Task(
|
||||
name="monitor_environment",
|
||||
description="Monitor and analyze environment state",
|
||||
expected_output="Current environment state analysis",
|
||||
agent=llm_caller,
|
||||
is_start=True,
|
||||
task_type="decision",
|
||||
next_tasks=["execute_action"],
|
||||
condition={
|
||||
"normal": ["execute_action"],
|
||||
"critical": ["execute_action"],
|
||||
"optimal": "exit"
|
||||
}
|
||||
)
|
||||
|
||||
action_task = Task(
|
||||
name="execute_action",
|
||||
description="Execute appropriate action based on state",
|
||||
expected_output="Action execution result",
|
||||
agent=action_agent,
|
||||
next_tasks=["process_feedback"]
|
||||
)
|
||||
|
||||
feedback_task = Task(
|
||||
name="process_feedback",
|
||||
description="Process feedback and adapt strategy",
|
||||
expected_output="Strategy adaptation based on feedback",
|
||||
agent=feedback_agent,
|
||||
next_tasks=["monitor_environment"], # Create feedback loop
|
||||
context=[monitor_task, action_task] # Access to previous states and actions
|
||||
)
|
||||
|
||||
# Create workflow manager
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[llm_caller, action_agent, feedback_agent],
|
||||
tasks=[monitor_task, action_task, feedback_task],
|
||||
process="workflow",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
def main():
|
||||
print("\nStarting Autonomous Agent Workflow...")
|
||||
print("=" * 50)
|
||||
|
||||
# Run autonomous workflow
|
||||
results = workflow.start()
|
||||
|
||||
# Print results
|
||||
print("\nAutonomous Agent Results:")
|
||||
print("=" * 50)
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
task_name = result.description
|
||||
print(f"\nTask: {task_name}")
|
||||
print(f"Result: {result.raw}")
|
||||
print("-" * 50)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,50 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import json
|
||||
from e2b_code_interpreter import Sandbox
|
||||
|
||||
def code_interpreter(code: str):
|
||||
"""
|
||||
A function to demonstrate running Python code dynamically using e2b_code_interpreter.
|
||||
"""
|
||||
print(f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}")
|
||||
exec_result = Sandbox().run_code(code)
|
||||
if exec_result.error:
|
||||
print("[Code Interpreter error]", exec_result.error)
|
||||
return {"error": str(exec_result.error)}
|
||||
else:
|
||||
results = []
|
||||
for result in exec_result.results:
|
||||
if hasattr(result, '__iter__'):
|
||||
results.extend(list(result))
|
||||
else:
|
||||
results.append(str(result))
|
||||
logs = {"stdout": list(exec_result.logs.stdout), "stderr": list(exec_result.logs.stderr)}
|
||||
return json.dumps({"results": results, "logs": logs})
|
||||
|
||||
code_agent = Agent(
|
||||
name="code_agent",
|
||||
llm="gpt-4o-mini",
|
||||
backstory="Expert in writing Python scripts",
|
||||
self_reflect=False
|
||||
)
|
||||
execution_agent = Agent(
|
||||
name="execution_agent",
|
||||
llm="gpt-4o-mini",
|
||||
backstory="Expert in executing Python scripts",
|
||||
self_reflect=False,
|
||||
tools=[code_interpreter]
|
||||
)
|
||||
|
||||
code_agent_task = Task(
|
||||
description="Write a simple Python script to print 'Hello, World!'",
|
||||
expected_output="A Python script that prints 'Hello, World!'",
|
||||
agent=code_agent
|
||||
)
|
||||
execution_agent_task = Task(
|
||||
description="Execute the Python script",
|
||||
expected_output="The output of the Python script",
|
||||
agent=execution_agent
|
||||
)
|
||||
|
||||
agents = PraisonAIAgents(agents=[code_agent, execution_agent], tasks=[code_agent_task, execution_agent_task])
|
||||
agents.start()
|
||||
@@ -0,0 +1,108 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import time
|
||||
from typing import List, Dict
|
||||
|
||||
def analyze_code_changes():
|
||||
"""Simulates code analysis"""
|
||||
issues = [
|
||||
{"type": "style", "severity": "low", "file": "main.py"},
|
||||
{"type": "security", "severity": "high", "file": "auth.py"},
|
||||
{"type": "performance", "severity": "medium", "file": "data.py"}
|
||||
]
|
||||
return issues[int(time.time()) % 3]
|
||||
|
||||
def suggest_fixes(issue: Dict):
|
||||
"""Simulates fix suggestions"""
|
||||
fixes = {
|
||||
"style": "Apply PEP 8 formatting",
|
||||
"security": "Implement input validation",
|
||||
"performance": "Use list comprehension"
|
||||
}
|
||||
return fixes.get(issue["type"], "Review manually")
|
||||
|
||||
def apply_automated_fix(fix: str):
|
||||
"""Simulates applying automated fixes"""
|
||||
success = int(time.time()) % 2 == 0
|
||||
return "fixed" if success else "manual_review"
|
||||
|
||||
# Create specialized agents
|
||||
analyzer = Agent(
|
||||
name="Code Analyzer",
|
||||
role="Code analysis",
|
||||
goal="Analyze code changes and identify issues",
|
||||
instructions="Review code changes and report issues",
|
||||
tools=[analyze_code_changes]
|
||||
)
|
||||
|
||||
fix_suggester = Agent(
|
||||
name="Fix Suggester",
|
||||
role="Solution provider",
|
||||
goal="Suggest fixes for identified issues",
|
||||
instructions="Provide appropriate fix suggestions",
|
||||
tools=[suggest_fixes]
|
||||
)
|
||||
|
||||
fix_applier = Agent(
|
||||
name="Fix Applier",
|
||||
role="Fix implementation",
|
||||
goal="Apply suggested fixes automatically when possible",
|
||||
instructions="Implement suggested fixes and report results",
|
||||
tools=[apply_automated_fix]
|
||||
)
|
||||
|
||||
# Create workflow tasks
|
||||
analysis_task = Task(
|
||||
name="analyze_code",
|
||||
description="Analyze code changes for issues",
|
||||
expected_output="Identified code issues",
|
||||
agent=analyzer,
|
||||
is_start=True,
|
||||
next_tasks=["suggest_fixes"]
|
||||
)
|
||||
|
||||
suggestion_task = Task(
|
||||
name="suggest_fixes",
|
||||
description="Suggest fixes for identified issues",
|
||||
expected_output="Fix suggestions",
|
||||
agent=fix_suggester,
|
||||
next_tasks=["apply_fixes"]
|
||||
)
|
||||
|
||||
fix_task = Task(
|
||||
name="apply_fixes",
|
||||
description="Apply suggested fixes",
|
||||
expected_output="Fix application status",
|
||||
agent=fix_applier,
|
||||
task_type="decision",
|
||||
condition={
|
||||
"fixed": "",
|
||||
"manual_review": ["suggest_fixes"] # Loop back for manual review
|
||||
}
|
||||
)
|
||||
|
||||
# Create workflow
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[analyzer, fix_suggester, fix_applier],
|
||||
tasks=[analysis_task, suggestion_task, fix_task],
|
||||
process="workflow",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
def main():
|
||||
print("\nStarting Code Review Workflow...")
|
||||
print("=" * 50)
|
||||
|
||||
# Run workflow
|
||||
results = workflow.start()
|
||||
|
||||
# Print results
|
||||
print("\nCode Review Results:")
|
||||
print("=" * 50)
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
print(f"\nTask: {task_id}")
|
||||
print(f"Result: {result.raw}")
|
||||
print("-" * 50)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,10 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import read_csv, read_excel, write_csv, write_excel, filter_data, get_summary, group_by, pivot_table
|
||||
import os
|
||||
|
||||
agent = Agent(instructions="You are a Data Analyst Agent", tools=[read_csv, read_excel, write_csv, write_excel, filter_data, get_summary, group_by, pivot_table])
|
||||
agent.start(f"""
|
||||
Read the data from the csv file {os.path.join(os.path.dirname(__file__), "tesla-stock-price.csv")}
|
||||
Analyse the data and give me the insights
|
||||
read_csv to read the file
|
||||
""")
|
||||
@@ -0,0 +1,70 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create generator and evaluator agents
|
||||
generator = Agent(
|
||||
name="Generator",
|
||||
role="Solution generator",
|
||||
goal="Generate initial solutions and incorporate feedback",
|
||||
instructions=(
|
||||
"1. Look at the context from previous tasks.\n"
|
||||
"2. If you see that you have already produced 2 points, then add another 2 new points "
|
||||
" so that the total becomes 10.\n"
|
||||
"3. Otherwise, just produce the first 2 points.\n"
|
||||
"4. Return only the final list of points, with no extra explanation."
|
||||
)
|
||||
)
|
||||
|
||||
evaluator = Agent(
|
||||
name="Evaluator",
|
||||
role="Solution evaluator",
|
||||
goal="Evaluate solutions and provide improvement feedback",
|
||||
instructions=(
|
||||
"1. Count how many lines in the response start with a number and a period (like '1. ' or '2. ').\n"
|
||||
"2. If there are 10 or more, respond with 'done'.\n"
|
||||
"3. Otherwise, respond with 'more'.\n"
|
||||
"4. Return only the single word: 'done' or 'more'."
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Create tasks for the feedback loop
|
||||
generate_task = Task(
|
||||
name="generate",
|
||||
description="Write 2 points about AI incuding if anything exiting from previous points",
|
||||
expected_output="2 points",
|
||||
agent=generator,
|
||||
is_start=True,
|
||||
task_type="decision",
|
||||
next_tasks=["evaluate"]
|
||||
)
|
||||
|
||||
evaluate_task = Task(
|
||||
name="evaluate",
|
||||
description="Check if there are 10 points about AI",
|
||||
expected_output="more or done",
|
||||
agent=evaluator,
|
||||
next_tasks=["generate"],
|
||||
context=[generate_task],
|
||||
task_type="decision",
|
||||
condition={
|
||||
"more": ["generate"], # Continue to generate
|
||||
"done": [""] # Exit when optimization complete
|
||||
}
|
||||
)
|
||||
|
||||
# Create workflow manager
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[generator, evaluator],
|
||||
tasks=[generate_task, evaluate_task],
|
||||
process="workflow",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run optimization workflow
|
||||
results = workflow.start()
|
||||
|
||||
# Print results
|
||||
print("\nEvaluator-Optimizer Results:")
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
print(f"Task {task_id}: {result.raw}")
|
||||
@@ -0,0 +1,151 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from typing import List, Dict, Union
|
||||
from duckduckgo_search import DDGS
|
||||
from langchain_community.tools import YouTubeSearchTool
|
||||
from langchain_community.utilities import WikipediaAPIWrapper
|
||||
|
||||
# 1. Tool
|
||||
def internet_search_tool(query: str) -> List[Dict]:
|
||||
"""
|
||||
Perform a search using DuckDuckGo.
|
||||
|
||||
Args:
|
||||
query (str): The search query.
|
||||
|
||||
Returns:
|
||||
list: A list of search result titles, URLs, and snippets.
|
||||
"""
|
||||
try:
|
||||
results = []
|
||||
ddgs = DDGS()
|
||||
for result in ddgs.text(keywords=query, max_results=10):
|
||||
results.append({
|
||||
"title": result.get("title", ""),
|
||||
"url": result.get("href", ""),
|
||||
"snippet": result.get("body", "")
|
||||
})
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during DuckDuckGo search: {e}")
|
||||
return []
|
||||
|
||||
def youtube_search_tool(query: str, inspect: bool = False, max_results: int = 2):
|
||||
"""
|
||||
Provide a custom wrapper around the YouTubeSearchTool.
|
||||
|
||||
Args:
|
||||
query (str): The search query for YouTube.
|
||||
inspect (bool): If True, returns tool inspection info instead of search results.
|
||||
max_results (int): Maximum number of results to return (default: 2).
|
||||
Returns:
|
||||
Union[List[str], dict]: List of YouTube video URLs or tool inspection info.
|
||||
"""
|
||||
yt = YouTubeSearchTool()
|
||||
|
||||
if inspect:
|
||||
inspection_info = {
|
||||
"type": type(yt),
|
||||
"attributes": [attr for attr in dir(yt) if not attr.startswith('_')],
|
||||
"methods": {
|
||||
"run": getattr(yt, 'run', None),
|
||||
"arun": getattr(yt, 'arun', None)
|
||||
},
|
||||
"properties": {
|
||||
"name": getattr(yt, 'name', 'youtube_search'),
|
||||
"description": getattr(yt, 'description', 'Search YouTube videos'),
|
||||
"return_direct": getattr(yt, 'return_direct', False)
|
||||
}
|
||||
}
|
||||
return inspection_info
|
||||
|
||||
# Format query with max_results
|
||||
formatted_query = f"{query}, {max_results}"
|
||||
return yt.run(formatted_query)
|
||||
|
||||
def wikipedia_search_tool(query: str, inspect: bool = False, max_chars: int = 4000, top_k: int = 3):
|
||||
"""
|
||||
Provide a custom wrapper around langchain_community's WikipediaAPIWrapper.
|
||||
|
||||
Args:
|
||||
query (str): A search query for Wikipedia.
|
||||
inspect (bool): If True, returns tool inspection info instead of search results.
|
||||
max_chars (int): Maximum characters to return (default: 4000).
|
||||
top_k (int): Number of top results to consider (default: 3).
|
||||
Returns:
|
||||
Union[str, dict]: Summary from Wikipedia or tool inspection info if inspect=True.
|
||||
"""
|
||||
w = WikipediaAPIWrapper(
|
||||
top_k_results=top_k,
|
||||
doc_content_chars_max=max_chars,
|
||||
lang='en'
|
||||
)
|
||||
|
||||
if inspect:
|
||||
inspection_info = {
|
||||
"type": type(w),
|
||||
"attributes": [attr for attr in dir(w) if not attr.startswith('_')],
|
||||
"methods": {
|
||||
"run": getattr(w, 'run', None),
|
||||
"arun": getattr(w, 'arun', None)
|
||||
},
|
||||
"properties": {
|
||||
"name": "wikipedia",
|
||||
"description": "Search and get summaries from Wikipedia",
|
||||
"top_k": w.top_k_results,
|
||||
"lang": w.lang,
|
||||
"max_chars": w.doc_content_chars_max
|
||||
}
|
||||
}
|
||||
return inspection_info
|
||||
|
||||
try:
|
||||
result = w.run(query)
|
||||
return result
|
||||
except Exception as e:
|
||||
return f"Error searching Wikipedia: {str(e)}"
|
||||
|
||||
# 2. Agent
|
||||
data_agent = Agent(
|
||||
name="DataCollector",
|
||||
role="Search Specialist",
|
||||
goal="Perform internet searches to collect relevant information.",
|
||||
backstory="Expert in finding and organizing internet data from multiple sources.",
|
||||
tools=[internet_search_tool, youtube_search_tool, wikipedia_search_tool],
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# 3. Tasks
|
||||
collect_task = Task(
|
||||
description="Perform an internet search using the query: 'AI job trends in 2024'. Return results as a list of title, URL, and snippet.",
|
||||
expected_output="List of search results with titles, URLs, and snippets.",
|
||||
agent=data_agent,
|
||||
name="collect_data",
|
||||
is_start=True,
|
||||
next_tasks=["validate_data"]
|
||||
)
|
||||
|
||||
validate_task = Task(
|
||||
description="""Validate the collected data. Check if:
|
||||
1. At least 5 results are returned.
|
||||
2. Each result contains a title and a URL.
|
||||
Return validation_result as 'valid' or 'invalid' only no other text.""",
|
||||
expected_output="Validation result indicating if data is valid or invalid.",
|
||||
agent=data_agent,
|
||||
name="validate_data",
|
||||
task_type="decision",
|
||||
condition={
|
||||
"valid": [], # End the workflow on valid data
|
||||
"invalid": ["collect_data"] # Retry data collection on invalid data
|
||||
},
|
||||
)
|
||||
|
||||
# 4. Workflow
|
||||
agents = PraisonAIAgents(
|
||||
agents=[data_agent],
|
||||
tasks=[collect_task, validate_task],
|
||||
verbose=1,
|
||||
process="workflow"
|
||||
)
|
||||
|
||||
agents.start()
|
||||
File renamed without changes.
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import get_stock_price, get_stock_info, get_historical_data
|
||||
|
||||
agent = Agent(instructions="You are a Research Agent", tools=[get_stock_price, get_stock_info, get_historical_data])
|
||||
agent.start("Understand current stock price and historical data of Apple and Google. Tell me if I can invest in them")
|
||||
@@ -0,0 +1,47 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Image Analysis Agent", tools=[duckduckgo])
|
||||
agent.start("I want to go London next week, find me a good hotel and flight")
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create Image Analysis Agent
|
||||
image_agent = Agent(
|
||||
name="ImageAnalyst",
|
||||
role="Image Analysis Specialist",
|
||||
goal="Analyze images and videos to extract meaningful information",
|
||||
backstory="""You are an expert in computer vision and image analysis.
|
||||
You excel at describing images, detecting objects, and understanding visual content.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# 1. Task with Image URL
|
||||
task1 = Task(
|
||||
name="analyze_landmark",
|
||||
description="Describe this famous landmark and its architectural features.",
|
||||
expected_output="Detailed description of the landmark's architecture and significance",
|
||||
agent=image_agent,
|
||||
images=["https://upload.wikimedia.org/wikipedia/commons/b/bf/Krakow_-_Kosciol_Mariacki.jpg"]
|
||||
)
|
||||
|
||||
# 2. Task with Local Image File
|
||||
task2 = Task(
|
||||
name="analyze_local_image",
|
||||
description="What objects can you see in this image? Describe their arrangement.",
|
||||
expected_output="Detailed description of objects and their spatial relationships",
|
||||
agent=image_agent,
|
||||
images=["image.jpg"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[image_agent],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run all tasks
|
||||
agents.start()
|
||||
@@ -0,0 +1,47 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Image Analysis Agent", tools=[duckduckgo])
|
||||
agent.start("I want to go London next week, find me a good hotel and flight")
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create Image Analysis Agent
|
||||
image_agent = Agent(
|
||||
name="ImageAnalyst",
|
||||
role="Image Analysis Specialist",
|
||||
goal="Analyze images and videos to extract meaningful information",
|
||||
backstory="""You are an expert in computer vision and image analysis.
|
||||
You excel at describing images, detecting objects, and understanding visual content.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# 1. Task with Image URL
|
||||
task1 = Task(
|
||||
name="analyze_landmark",
|
||||
description="Describe this famous landmark and its architectural features.",
|
||||
expected_output="Detailed description of the landmark's architecture and significance",
|
||||
agent=image_agent,
|
||||
images=["https://upload.wikimedia.org/wikipedia/commons/b/bf/Krakow_-_Kosciol_Mariacki.jpg"]
|
||||
)
|
||||
|
||||
# 2. Task with Local Image File
|
||||
task2 = Task(
|
||||
name="analyze_local_image",
|
||||
description="What objects can you see in this image? Describe their arrangement.",
|
||||
expected_output="Detailed description of objects and their spatial relationships",
|
||||
agent=image_agent,
|
||||
images=["image.jpg"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[image_agent],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run all tasks
|
||||
agents.start()
|
||||
@@ -0,0 +1,30 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from langchain_community.tools import YouTubeSearchTool
|
||||
from langchain_community.utilities import WikipediaAPIWrapper
|
||||
|
||||
# Create an agent with both tools
|
||||
agent = Agent(
|
||||
name="SearchAgent",
|
||||
role="Research Assistant",
|
||||
goal="Search for information from multiple sources",
|
||||
backstory="I am an AI assistant that can search YouTube and Wikipedia.",
|
||||
tools=[YouTubeSearchTool, WikipediaAPIWrapper],
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Create tasks to demonstrate both tools
|
||||
task = Task(
|
||||
name="search_task",
|
||||
description="Search for information about 'AI advancements' on both YouTube and Wikipedia",
|
||||
expected_output="Combined information from YouTube videos and Wikipedia articles",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Create and start the workflow
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
agents.start()
|
||||
@@ -0,0 +1,73 @@
|
||||
from langchain_community.tools import YouTubeSearchTool
|
||||
from langchain_community.utilities import WikipediaAPIWrapper
|
||||
|
||||
def test_tools():
|
||||
"""Test both YouTube and Wikipedia tools"""
|
||||
|
||||
print("\n=== Testing YouTube Search ===")
|
||||
# Test YouTube
|
||||
yt = YouTubeSearchTool()
|
||||
|
||||
# Test basic search
|
||||
print("\nBasic YouTube Search:")
|
||||
query = "AI advancements 2024"
|
||||
print(f"Query: {query}")
|
||||
result = yt.run(query)
|
||||
print(f"Results: {result}")
|
||||
|
||||
# Test with max results
|
||||
print("\nYouTube Search with max results:")
|
||||
query = "AI advancements 2024, 3" # Format: query, max_results
|
||||
print(f"Query: {query}")
|
||||
result = yt.run(query)
|
||||
print(f"Results: {result}")
|
||||
|
||||
print("\n=== Testing Wikipedia Search ===")
|
||||
# Test Wikipedia
|
||||
wiki = WikipediaAPIWrapper(
|
||||
top_k_results=2,
|
||||
doc_content_chars_max=500
|
||||
)
|
||||
|
||||
# Test basic search
|
||||
print("\nBasic Wikipedia Search:")
|
||||
query = "Artificial Intelligence"
|
||||
print(f"Query: {query}")
|
||||
result = wiki.run(query)
|
||||
print(f"Results: {result[:200]}..." if result else "No result")
|
||||
|
||||
# Test with different parameters
|
||||
print("\nWikipedia Search with different language:")
|
||||
wiki_fr = WikipediaAPIWrapper(
|
||||
lang='fr',
|
||||
top_k_results=1,
|
||||
doc_content_chars_max=300
|
||||
)
|
||||
result = wiki_fr.run("Intelligence Artificielle")
|
||||
print(f"French Results: {result[:200]}..." if result else "No result")
|
||||
|
||||
def print_tool_info():
|
||||
"""Print detailed information about the tools"""
|
||||
|
||||
print("\n=== Tool Information ===")
|
||||
|
||||
# YouTube Tool Info
|
||||
print("\nYouTube Tool:")
|
||||
yt = YouTubeSearchTool()
|
||||
print("Type:", type(yt))
|
||||
print("Name:", getattr(yt, 'name', None))
|
||||
print("Description:", getattr(yt, 'description', None))
|
||||
print("Arguments:", getattr(yt, 'args', None))
|
||||
|
||||
# Wikipedia Tool Info
|
||||
print("\nWikipedia Tool:")
|
||||
wiki = WikipediaAPIWrapper()
|
||||
print("Type:", type(wiki))
|
||||
print("Available Settings:")
|
||||
print("- Language:", wiki.lang)
|
||||
print("- Top K Results:", wiki.top_k_results)
|
||||
print("- Max Characters:", wiki.doc_content_chars_max)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print_tool_info()
|
||||
test_tools()
|
||||
Whitespace-only changes.
@@ -0,0 +1,4 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(instructions="You are a Markdown Agent, output in markdown format")
|
||||
agent.start("Write a blog post about AI")
|
||||
@@ -0,0 +1,183 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import logging
|
||||
import os
|
||||
|
||||
def main():
|
||||
# Initialize memory config
|
||||
memory_config = {
|
||||
"provider": "rag",
|
||||
"use_embedding": True,
|
||||
"storage": {
|
||||
"type": "sqlite",
|
||||
"path": "./.praison/memory.db"
|
||||
},
|
||||
"rag_db_path": "./.praison/chroma_db"
|
||||
}
|
||||
|
||||
# Test facts
|
||||
fact1 = "The capital city of Jujuha is Hahanu and its population is 102300"
|
||||
fact2 = "Three main ingredients in a classic proloder are eggs, sugar, and flour"
|
||||
fact3 = "The year the first Josinga was released is 2007"
|
||||
|
||||
# # Check if database exists
|
||||
# if os.path.exists("./memory.db"):
|
||||
# logger.info("Found existing memory database")
|
||||
# else:
|
||||
# logger.info("Creating new memory database")
|
||||
|
||||
# Create task config (without memory config since it's moved to PraisonAIAgents)
|
||||
task_config = {}
|
||||
|
||||
# Create agents with different roles
|
||||
researcher = Agent(
|
||||
role="Research Analyst",
|
||||
goal="Research and document key information about topics",
|
||||
backstory="Expert at analyzing and storing information in memory",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
retriever = Agent(
|
||||
role="Information Retriever",
|
||||
goal="Retrieve and verify stored information from memory",
|
||||
backstory="Specialist in searching and validating information from memory",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Task 1: Process the facts
|
||||
store_task = Task(
|
||||
description=f"""
|
||||
Process and analyze this information:
|
||||
1. {fact1}
|
||||
2. {fact2}
|
||||
3. {fact3}
|
||||
|
||||
Provide a clear summary of each fact.
|
||||
""",
|
||||
expected_output="""
|
||||
Clear statements summarizing each fact.
|
||||
Example format:
|
||||
1. [Summary of fact 1]
|
||||
2. [Summary of fact 2]
|
||||
3. [Summary of fact 3]
|
||||
""",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
# Task 2: Write essay about AI
|
||||
verify_task = Task(
|
||||
description="""
|
||||
write few points about AI
|
||||
""",
|
||||
expected_output="Points about AI",
|
||||
agent=retriever
|
||||
)
|
||||
|
||||
# Task 3: Query memory
|
||||
query_task = Task(
|
||||
description="""
|
||||
Using ONLY information found in memory:
|
||||
1. What is stored in memory about Hahanu?
|
||||
2. What ingredients for proloder are recorded in memory?
|
||||
3. What year is stored in memory for the Josinga release?
|
||||
|
||||
For each answer, cite the memory record you found.
|
||||
""",
|
||||
expected_output="Answers based solely on memory records with citations",
|
||||
agent=retriever
|
||||
)
|
||||
|
||||
# Task 4: Query both short-term and long-term memory
|
||||
query_both_task = Task(
|
||||
description="""
|
||||
Using ONLY information found in memory:
|
||||
1. What is stored in both short-term and long-term memory about Jujuha?
|
||||
2. What ingredients for proloder are recorded in both short-term and long-term memory?
|
||||
3. What year is stored in both short-term and long-term memory for the Josinga release?
|
||||
|
||||
For each answer, cite the memory record you found.
|
||||
""",
|
||||
expected_output="Answers based solely on memory records with citations",
|
||||
agent=retriever
|
||||
)
|
||||
|
||||
# Initialize PraisonAIAgents with memory configuration
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher, retriever],
|
||||
tasks=[store_task, verify_task, query_task, query_both_task],
|
||||
verbose=True, # Use same verbose level as memory
|
||||
memory=True,
|
||||
embedder={
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# agents = PraisonAIAgents(
|
||||
# agents=[researcher, retriever],
|
||||
# tasks=[store_task, verify_task, query_task, query_both_task],
|
||||
# verbose=True, # Use same verbose level as memory
|
||||
# memory=True
|
||||
# )
|
||||
|
||||
# Execute tasks
|
||||
print("\nExecuting Memory Test Tasks...")
|
||||
print("-" * 50)
|
||||
agents.start()
|
||||
|
||||
# Use shared memory for final checks
|
||||
memory = agents.shared_memory
|
||||
|
||||
# Test memory retrieval with different quality thresholds
|
||||
if memory:
|
||||
print("\nFinal Memory Check:")
|
||||
print("-" * 50)
|
||||
|
||||
queries = ["Jujuha", "proloder", "Josinga"]
|
||||
for query in queries:
|
||||
print(f"\nSearching memory for: {query}")
|
||||
|
||||
# Search in both short-term and long-term memory
|
||||
print("\nShort-term memory results:")
|
||||
stm_results = memory.search_short_term(query)
|
||||
if stm_results:
|
||||
for item in stm_results:
|
||||
print(f"Content: {item.get('content', '')[:200]}")
|
||||
if 'meta' in item:
|
||||
print(f"Metadata: {item['meta']}")
|
||||
print("-" * 20)
|
||||
else:
|
||||
print("No results found in short-term memory")
|
||||
|
||||
print("\nLong-term memory results:")
|
||||
ltm_results = memory.search_long_term(query)
|
||||
if ltm_results:
|
||||
for item in ltm_results:
|
||||
print(f"Content: {item.get('text', '')[:200]}")
|
||||
if 'metadata' in item:
|
||||
print(f"Metadata: {item['metadata']}")
|
||||
print("-" * 20)
|
||||
else:
|
||||
print("No results found in long-term memory")
|
||||
|
||||
# Also check ChromaDB if using RAG
|
||||
if memory.use_rag and hasattr(memory, "chroma_col"):
|
||||
print("\nChromaDB results:")
|
||||
try:
|
||||
all_items = memory.chroma_col.get()
|
||||
print(f"Found {len(all_items['ids'])} items in ChromaDB")
|
||||
for i in range(len(all_items['ids'])):
|
||||
print(f"ID: {all_items['ids'][i]}")
|
||||
print(f"Content: {all_items['documents'][i][:200]}")
|
||||
print(f"Metadata: {all_items['metadatas'][i]}")
|
||||
print("-" * 20)
|
||||
except Exception as e:
|
||||
print(f"Error querying ChromaDB: {e}")
|
||||
|
||||
print("-" * 30)
|
||||
else:
|
||||
print("\nNo memory available for final checks")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,63 @@
|
||||
from praisonaiagents.agents.agents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Test facts
|
||||
fact1 = "The capital city of Jujuha is Hahanu and its population is 102300"
|
||||
fact2 = "Three main ingredients in a classic proloder are eggs, sugar, and flour"
|
||||
fact3 = "The year the first Josinga was released is 2007"
|
||||
|
||||
fact_agent = Agent(
|
||||
name="Fact Agent",
|
||||
instructions="You are a fact agent, you store and retrieve facts in memory",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
research_agent = Agent(
|
||||
name="Research Agent",
|
||||
instructions="You are a research analyst, you research and document key points about topics",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
blog_agent = Agent(
|
||||
name="Blog Agent",
|
||||
instructions="You are a blog writer, you write a blog post about the research",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
fact_task = Task(
|
||||
description="Store the following facts in memory: " + fact1 + ", " + fact2 + ", " + fact3,
|
||||
agent=fact_agent
|
||||
)
|
||||
|
||||
research_task = Task(
|
||||
description="Research and document 2 key points about AI",
|
||||
agent=research_agent
|
||||
)
|
||||
|
||||
research_task2 = Task(
|
||||
description="Research and document 2 key points about AI in healthcare",
|
||||
agent=research_agent
|
||||
)
|
||||
|
||||
research_task3 = Task(
|
||||
description="Research and document 2 key points about AI in education",
|
||||
agent=research_agent
|
||||
)
|
||||
|
||||
research_task4 = Task(
|
||||
description="Research and document 2 key points about AI in finance",
|
||||
agent=research_agent
|
||||
)
|
||||
|
||||
blog_task = Task(
|
||||
description="Write a blog post about Jujuha",
|
||||
agent=blog_agent
|
||||
)
|
||||
|
||||
agents = PraisonAIAgents(
|
||||
agents=[fact_agent, research_agent, blog_agent],
|
||||
tasks=[fact_task, research_task, research_task2, research_task3, research_task4, blog_task],
|
||||
memory=True
|
||||
)
|
||||
|
||||
agents.start()
|
||||
@@ -51,9 +51,4 @@ agents = PraisonAIAgents(
|
||||
)
|
||||
|
||||
# Run all tasks
|
||||
result = agents.start()
|
||||
|
||||
# Print results
|
||||
for task_id, task_result in result["task_results"].items():
|
||||
print(f"\nTask {task_id} Result:")
|
||||
print(task_result.raw)
|
||||
agents.start()
|
||||
@@ -0,0 +1,110 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import time
|
||||
|
||||
def get_time_check():
|
||||
current_time = int(time.time())
|
||||
if current_time % 3 == 0:
|
||||
result = 1
|
||||
elif current_time % 3 == 1:
|
||||
result = 2
|
||||
else:
|
||||
result = 3
|
||||
print(f"Time: {current_time}, Result: {result}")
|
||||
return result
|
||||
|
||||
# Create orchestrator and worker agents
|
||||
router = Agent(
|
||||
name="Router",
|
||||
role="Task router",
|
||||
goal="Distribute tasks to based on response from get_time_check",
|
||||
tools=[get_time_check]
|
||||
)
|
||||
|
||||
worker1 = Agent(
|
||||
name="Worker 1",
|
||||
role="Specialized worker",
|
||||
goal="Handle specific subtask type 1",
|
||||
)
|
||||
|
||||
worker2 = Agent(
|
||||
name="Worker 2",
|
||||
role="Specialized worker",
|
||||
goal="Handle specific subtask type 2",
|
||||
)
|
||||
|
||||
worker3 = Agent(
|
||||
name="Worker 3",
|
||||
role="Specialized worker",
|
||||
goal="Handle specific subtask type 3",
|
||||
)
|
||||
|
||||
synthesizer = Agent(
|
||||
name="Synthesizer",
|
||||
role="Result synthesizer",
|
||||
goal="Combine and synthesize worker outputs",
|
||||
)
|
||||
|
||||
# Create orchestrated workflow tasks
|
||||
router_task = Task(
|
||||
name="route_task",
|
||||
description="Analyze input from get_time_check and route to appropriate workers",
|
||||
expected_output="Task routing decision, 1 , 2 or 3",
|
||||
agent=router,
|
||||
is_start=True,
|
||||
task_type="decision",
|
||||
next_tasks=["worker1_task", "worker2_task", "worker3_task"],
|
||||
condition={
|
||||
"1": ["worker1_task"],
|
||||
"2": ["worker2_task"],
|
||||
"3": ["worker3_task"]
|
||||
}
|
||||
)
|
||||
|
||||
worker1_task = Task(
|
||||
name="worker1_task",
|
||||
description="Process type 1 operation",
|
||||
expected_output="Worker 1 result",
|
||||
agent=worker1,
|
||||
next_tasks=["synthesize_task"]
|
||||
)
|
||||
|
||||
worker2_task = Task(
|
||||
name="worker2_task",
|
||||
description="Process type 2 operation",
|
||||
expected_output="Worker 2 result",
|
||||
agent=worker2,
|
||||
next_tasks=["synthesize_task"]
|
||||
)
|
||||
|
||||
worker3_task = Task(
|
||||
name="worker3_task",
|
||||
description="Process type 3 operation",
|
||||
expected_output="Worker 3 result",
|
||||
agent=worker3,
|
||||
next_tasks=["synthesize_task"]
|
||||
)
|
||||
|
||||
synthesize_task = Task(
|
||||
name="synthesize_task",
|
||||
description="Synthesize worker results into final output",
|
||||
expected_output="Final synthesized result",
|
||||
agent=synthesizer,
|
||||
context=[worker1_task, worker2_task, worker3_task]
|
||||
)
|
||||
|
||||
# Create workflow manager
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[router, worker1, worker2, worker3, synthesizer],
|
||||
tasks=[router_task, worker1_task, worker2_task, worker3_task, synthesize_task],
|
||||
process="workflow",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run orchestrated workflow
|
||||
results = workflow.start()
|
||||
|
||||
# Print results
|
||||
print("\nOrchestrator-Workers Results:")
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
print(f"Task {task_id}: {result.raw}")
|
||||
@@ -0,0 +1,95 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
|
||||
def process_time():
|
||||
"""Simulate processing"""
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"Processing at: {current_time}")
|
||||
return f"Processed at {current_time}"
|
||||
|
||||
# Create parallel processing agents
|
||||
agent1 = Agent(
|
||||
name="Processor 1",
|
||||
role="Time collector",
|
||||
goal="Get the time and return it",
|
||||
tools=[process_time]
|
||||
)
|
||||
|
||||
agent2 = Agent(
|
||||
name="Processor 2",
|
||||
role="Time collector",
|
||||
goal="Get the time and return it",
|
||||
tools=[process_time]
|
||||
)
|
||||
|
||||
agent3 = Agent(
|
||||
name="Processor 3",
|
||||
role="Time collector",
|
||||
goal="Get the time and return it",
|
||||
tools=[process_time]
|
||||
)
|
||||
|
||||
aggregator = Agent(
|
||||
name="Aggregator",
|
||||
role="Result aggregator",
|
||||
goal="Collect all the processed time from all tasks"
|
||||
)
|
||||
|
||||
# Create parallel tasks with memory disabled
|
||||
task1 = Task(
|
||||
name="process_1",
|
||||
description="Use process_time tool to get the time",
|
||||
expected_output="processed time",
|
||||
agent=agent1,
|
||||
is_start=True,
|
||||
async_execution=True
|
||||
)
|
||||
|
||||
task2 = Task(
|
||||
name="process_2",
|
||||
description="Use process_time tool to get the time",
|
||||
expected_output="processed time",
|
||||
agent=agent2,
|
||||
is_start=True,
|
||||
async_execution=True
|
||||
)
|
||||
|
||||
task3 = Task(
|
||||
name="process_3",
|
||||
description="Use process_time tool to get the time",
|
||||
expected_output="processed time",
|
||||
agent=agent3,
|
||||
is_start=True,
|
||||
async_execution=True
|
||||
)
|
||||
|
||||
aggregate_task = Task(
|
||||
name="aggregate",
|
||||
description="Collect all the processed time from all tasks",
|
||||
expected_output="Output all the processed time from all tasks and just the time",
|
||||
agent=aggregator,
|
||||
context=[task1, task2, task3]
|
||||
)
|
||||
|
||||
async def main():
|
||||
|
||||
# Create workflow manager
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[agent1, agent2, agent3, aggregator],
|
||||
tasks=[task1, task2, task3, aggregate_task],
|
||||
process="workflow"
|
||||
)
|
||||
|
||||
# Run parallel workflow
|
||||
results = await workflow.astart()
|
||||
|
||||
# Print results
|
||||
print("\nParallel Processing Results:")
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
print(f"Task {task_id}: {result.raw}")
|
||||
|
||||
# Run the async main function
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,41 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create PDF Analysis Agent
|
||||
pdf_agent = Agent(
|
||||
name="PDFAnalyst",
|
||||
role="PDF Document Specialist",
|
||||
goal="Analyze PDF documents to extract meaningful information",
|
||||
backstory="""You are an expert in PDF document analysis and text extraction.
|
||||
You excel at understanding document structure, extracting content, and analyzing textual information.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# 1. Task with PDF URL
|
||||
task1 = Task(
|
||||
name="analyze_pdf_url",
|
||||
description="Extract and analyze content from this PDF document.",
|
||||
expected_output="Detailed analysis of the PDF content and structure",
|
||||
agent=pdf_agent,
|
||||
input=["https://example.com/document.pdf"]
|
||||
)
|
||||
|
||||
# 2. Task with Local PDF File
|
||||
task2 = Task(
|
||||
name="analyze_local_pdf",
|
||||
description="What information can you extract from this PDF? Analyze its content.",
|
||||
expected_output="Detailed analysis of the PDF content and structure",
|
||||
agent=pdf_agent,
|
||||
input=["document.pdf"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[pdf_agent],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run all tasks
|
||||
agents.start()
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Planning Agent", tools=[duckduckgo])
|
||||
agent.start("I want to go London next week, find me a good hotel and flight")
|
||||
@@ -6,6 +6,8 @@ from .agent.agent import Agent
|
||||
from .agents.agents import PraisonAIAgents
|
||||
from .task.task import Task
|
||||
from .tools.tools import Tools
|
||||
from .agents.autoagents import AutoAgents
|
||||
from .memory.memory import Memory
|
||||
from .main import (
|
||||
TaskOutput,
|
||||
ReflectionOutput,
|
||||
@@ -33,6 +35,8 @@ __all__ = [
|
||||
'Task',
|
||||
'TaskOutput',
|
||||
'ReflectionOutput',
|
||||
'AutoAgents',
|
||||
'Memory',
|
||||
'display_interaction',
|
||||
'display_self_reflection',
|
||||
'display_instruction',
|
||||
|
||||
@@ -16,8 +16,10 @@ from ..main import (
|
||||
display_self_reflection,
|
||||
ReflectionOutput,
|
||||
client,
|
||||
error_logs
|
||||
error_logs,
|
||||
adisplay_instruction
|
||||
)
|
||||
import inspect
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..task.task import Task
|
||||
@@ -67,9 +69,24 @@ class Agent:
|
||||
return None
|
||||
|
||||
import inspect
|
||||
# Langchain tools
|
||||
if inspect.isclass(func) and hasattr(func, 'run'):
|
||||
original_func = func
|
||||
func = func.run
|
||||
function_name = original_func.__name__
|
||||
|
||||
sig = inspect.signature(func)
|
||||
logging.debug(f"Function signature: {sig}")
|
||||
|
||||
# Skip self, *args, **kwargs, so they don't get passed in arguments
|
||||
parameters_list = []
|
||||
for name, param in sig.parameters.items():
|
||||
if name == "self":
|
||||
continue
|
||||
if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
|
||||
continue
|
||||
parameters_list.append((name, param))
|
||||
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
@@ -95,7 +112,7 @@ class Agent:
|
||||
|
||||
logging.debug(f"Parameter descriptions: {param_descriptions}")
|
||||
|
||||
for name, param in sig.parameters.items():
|
||||
for name, param in parameters_list:
|
||||
param_type = "string" # Default type
|
||||
if param.annotation != inspect.Parameter.empty:
|
||||
if param.annotation == int:
|
||||
@@ -140,13 +157,13 @@ class Agent:
|
||||
goal: Optional[str] = None,
|
||||
backstory: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
llm: Optional[Union[str, Any]] = "gpt-4o",
|
||||
llm: Optional[Union[str, Any]] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
function_calling_llm: Optional[Any] = None,
|
||||
max_iter: int = 20,
|
||||
max_rpm: Optional[int] = None,
|
||||
max_execution_time: Optional[int] = None,
|
||||
memory: bool = True,
|
||||
memory: Optional[Any] = None,
|
||||
verbose: bool = True,
|
||||
allow_delegation: bool = False,
|
||||
step_callback: Optional[Any] = None,
|
||||
@@ -162,7 +179,7 @@ class Agent:
|
||||
knowledge_sources: Optional[List[Any]] = None,
|
||||
use_system_prompt: Optional[bool] = True,
|
||||
markdown: bool = True,
|
||||
self_reflect: Optional[bool] = None,
|
||||
self_reflect: bool = False,
|
||||
max_reflect: int = 3,
|
||||
min_reflect: int = 1,
|
||||
reflect_llm: Optional[str] = None
|
||||
@@ -189,7 +206,8 @@ class Agent:
|
||||
self.self_reflect = True if self_reflect is None else self_reflect
|
||||
|
||||
self.instructions = instructions
|
||||
self.llm = llm
|
||||
# Check for model name in environment variable if not provided
|
||||
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
|
||||
self.tools = tools if tools else [] # Store original tools
|
||||
self.function_calling_llm = function_calling_llm
|
||||
self.max_iter = max_iter
|
||||
@@ -214,7 +232,8 @@ class Agent:
|
||||
self.markdown = markdown
|
||||
self.max_reflect = max_reflect
|
||||
self.min_reflect = min_reflect
|
||||
self.reflect_llm = reflect_llm
|
||||
# Use the same model selection logic for reflect_llm
|
||||
self.reflect_llm = reflect_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
|
||||
self.console = Console() # Create a single console instance for the agent
|
||||
|
||||
# Initialize system prompt
|
||||
@@ -247,25 +266,31 @@ Your Goal: {self.goal}
|
||||
# Try to find the function in the agent's tools list first
|
||||
func = None
|
||||
for tool in self.tools:
|
||||
if callable(tool) and getattr(tool, '__name__', '') == function_name:
|
||||
if (callable(tool) and getattr(tool, '__name__', '') == function_name) or \
|
||||
(inspect.isclass(tool) and tool.__name__ == function_name):
|
||||
func = tool
|
||||
break
|
||||
|
||||
logging.debug(f"Looking for {function_name} in agent tools: {func is not None}")
|
||||
|
||||
# If not found in tools, try globals and main
|
||||
if not func:
|
||||
if func is None:
|
||||
# If not found in tools, try globals and main
|
||||
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 func and callable(func):
|
||||
if func:
|
||||
try:
|
||||
return func(**arguments)
|
||||
# If it's a class with run method, instantiate and call run
|
||||
if inspect.isclass(func) and hasattr(func, 'run'):
|
||||
instance = func()
|
||||
# Extract only the parameters that run() expects
|
||||
run_params = {k: v for k, v in arguments.items()
|
||||
if k in inspect.signature(instance.run).parameters
|
||||
and k != 'self'}
|
||||
return instance.run(**run_params)
|
||||
# Otherwise treat as regular function
|
||||
elif callable(func):
|
||||
return func(**arguments)
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
logging.error(f"Error executing tool {function_name}: {error_msg}")
|
||||
@@ -357,7 +382,7 @@ Your Goal: {self.goal}
|
||||
display_generating("", start_time),
|
||||
console=self.console,
|
||||
refresh_per_second=4,
|
||||
transient=False, # Changed to False to preserve output
|
||||
transient=True, # Changed to False to preserve output
|
||||
vertical_overflow="ellipsis",
|
||||
auto_refresh=True
|
||||
) as live:
|
||||
@@ -392,7 +417,7 @@ Your Goal: {self.goal}
|
||||
display_error(f"Error in chat completion: {e}")
|
||||
return None
|
||||
|
||||
def chat(self, prompt, temperature=0.2, tools=None, output_json=None):
|
||||
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
|
||||
if self.use_system_prompt:
|
||||
system_prompt = f"""{self.backstory}\n
|
||||
Your Role: {self.role}\n
|
||||
@@ -400,6 +425,8 @@ Your Goal: {self.goal}
|
||||
"""
|
||||
if output_json:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
|
||||
elif output_pydantic:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
|
||||
else:
|
||||
system_prompt = None
|
||||
|
||||
@@ -408,9 +435,9 @@ Your Goal: {self.goal}
|
||||
messages.append({"role": "system", "content": system_prompt})
|
||||
messages.extend(self.chat_history)
|
||||
|
||||
# Modify prompt if output_json is specified
|
||||
# Modify prompt if output_json or output_pydantic is specified
|
||||
original_prompt = prompt
|
||||
if output_json:
|
||||
if output_json or output_pydantic:
|
||||
if isinstance(prompt, str):
|
||||
prompt += "\nReturn ONLY a valid JSON object. No other text or explanation."
|
||||
elif isinstance(prompt, list):
|
||||
@@ -440,7 +467,15 @@ Your Goal: {self.goal}
|
||||
display_text = next((item["text"] for item in prompt if item["type"] == "text"), "")
|
||||
|
||||
if display_text and str(display_text).strip():
|
||||
display_instruction(f"Agent {self.name} is processing prompt: {display_text}", console=self.console)
|
||||
# Pass agent information to display_instruction
|
||||
agent_tools = [t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools]
|
||||
display_instruction(
|
||||
f"Agent {self.name} is processing prompt: {display_text}",
|
||||
console=self.console,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=agent_tools
|
||||
)
|
||||
|
||||
response = self._chat_completion(messages, temperature=temperature, tools=tools if tools else None)
|
||||
if not response:
|
||||
@@ -485,29 +520,21 @@ Your Goal: {self.goal}
|
||||
return None
|
||||
response_text = response.choices[0].message.content.strip()
|
||||
|
||||
# Handle output_json if specified
|
||||
if output_json:
|
||||
try:
|
||||
# Clean the response text to get only JSON
|
||||
cleaned_json = self.clean_json_output(response_text)
|
||||
# Parse into Pydantic model
|
||||
parsed_model = output_json.model_validate_json(cleaned_json)
|
||||
# Add to chat history and return
|
||||
self.chat_history.append({"role": "user", "content": original_prompt})
|
||||
self.chat_history.append({"role": "assistant", "content": response_text})
|
||||
if self.verbose:
|
||||
display_interaction(original_prompt, response_text, markdown=self.markdown,
|
||||
generation_time=time.time() - start_time, console=self.console)
|
||||
return parsed_model
|
||||
except Exception as e:
|
||||
display_error(f"Failed to parse response as {output_json.__name__}: {e}")
|
||||
return None
|
||||
# Handle output_json or output_pydantic if specified
|
||||
if output_json or output_pydantic:
|
||||
# Add to chat history and return raw response
|
||||
self.chat_history.append({"role": "user", "content": original_prompt})
|
||||
self.chat_history.append({"role": "assistant", "content": response_text})
|
||||
if self.verbose:
|
||||
display_interaction(original_prompt, response_text, markdown=self.markdown,
|
||||
generation_time=time.time() - start_time, console=self.console)
|
||||
return response_text
|
||||
|
||||
if not self.self_reflect:
|
||||
self.chat_history.append({"role": "user", "content": original_prompt})
|
||||
self.chat_history.append({"role": "assistant", "content": response_text})
|
||||
if self.verbose:
|
||||
logging.info(f"Agent {self.name} final response: {response_text}")
|
||||
logging.debug(f"Agent {self.name} final response: {response_text}")
|
||||
display_interaction(original_prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
|
||||
return response_text
|
||||
|
||||
@@ -583,19 +610,21 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
cleaned = cleaned[:-3].strip()
|
||||
return cleaned
|
||||
|
||||
async def achat(self, prompt, temperature=0.2, tools=None, output_json=None):
|
||||
async def achat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
|
||||
"""Async version of chat method"""
|
||||
try:
|
||||
# Build system prompt
|
||||
system_prompt = self.system_prompt
|
||||
if output_json:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
|
||||
elif output_pydantic:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
|
||||
|
||||
# Build messages
|
||||
if isinstance(prompt, str):
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt + ("\nReturn ONLY a valid JSON object. No other text or explanation." if output_json else "")}
|
||||
{"role": "user", "content": prompt + ("\nReturn ONLY a valid JSON object. No other text or explanation." if (output_json or output_pydantic) else "")}
|
||||
]
|
||||
else:
|
||||
# For multimodal prompts
|
||||
@@ -603,13 +632,29 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
if output_json:
|
||||
if output_json or output_pydantic:
|
||||
# Add JSON instruction to text content
|
||||
for item in messages[-1]["content"]:
|
||||
if item["type"] == "text":
|
||||
item["text"] += "\nReturn ONLY a valid JSON object. No other text or explanation."
|
||||
break
|
||||
|
||||
# Display instruction with agent info if verbose
|
||||
if self.verbose:
|
||||
display_text = prompt
|
||||
if isinstance(prompt, list):
|
||||
display_text = next((item["text"] for item in prompt if item["type"] == "text"), "")
|
||||
|
||||
if display_text and str(display_text).strip():
|
||||
agent_tools = [t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools]
|
||||
await adisplay_instruction(
|
||||
f"Agent {self.name} is processing prompt: {display_text}",
|
||||
console=self.console,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=agent_tools
|
||||
)
|
||||
|
||||
# Format tools if provided
|
||||
formatted_tools = []
|
||||
if tools:
|
||||
@@ -637,22 +682,15 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
tools=formatted_tools
|
||||
)
|
||||
return await self._achat_completion(response, tools)
|
||||
elif output_json:
|
||||
elif output_json or output_pydantic:
|
||||
response = await async_client.chat.completions.create(
|
||||
model=self.llm,
|
||||
messages=messages,
|
||||
temperature=temperature,
|
||||
response_format={"type": "json_object"}
|
||||
)
|
||||
result = response.choices[0].message.content
|
||||
# Clean and parse the JSON response
|
||||
cleaned_json = self.clean_json_output(result)
|
||||
try:
|
||||
parsed = json.loads(cleaned_json)
|
||||
return output_json(**parsed)
|
||||
except Exception as e:
|
||||
display_error(f"Error parsing JSON response: {e}")
|
||||
return None
|
||||
# Return the raw response
|
||||
return response.choices[0].message.content
|
||||
else:
|
||||
response = await async_client.chat.completions.create(
|
||||
model=self.llm,
|
||||
@@ -720,4 +758,12 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
return None
|
||||
except Exception as e:
|
||||
display_error(f"Error in _achat_completion: {e}")
|
||||
return None
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
"""Alias for start() method"""
|
||||
return self.start()
|
||||
|
||||
def start(self, prompt: str, **kwargs):
|
||||
"""Start the agent with a prompt. This is a convenience method that wraps chat()."""
|
||||
return self.chat(prompt, **kwargs)
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Agents module for managing multiple AI agents"""
|
||||
from .agents import PraisonAIAgents
|
||||
from .autoagents import AutoAgents
|
||||
|
||||
__all__ = ['PraisonAIAgents']
|
||||
__all__ = ['PraisonAIAgents', 'AutoAgents']
|
||||
@@ -13,6 +13,9 @@ from ..task.task import Task
|
||||
from ..process.process import Process, LoopItems
|
||||
import asyncio
|
||||
|
||||
# Set up logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def encode_file_to_base64(file_path: str) -> str:
|
||||
"""Base64-encode a file."""
|
||||
import base64
|
||||
@@ -41,7 +44,7 @@ def process_video(video_path: str, seconds_per_frame=2):
|
||||
return base64_frames
|
||||
|
||||
class PraisonAIAgents:
|
||||
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
|
||||
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None, memory=False, memory_config=None, embedder=None):
|
||||
if not agents:
|
||||
raise ValueError("At least one agent must be provided")
|
||||
|
||||
@@ -54,9 +57,24 @@ class PraisonAIAgents:
|
||||
self.verbose = verbose
|
||||
self.max_retries = max_retries
|
||||
self.process = process
|
||||
if not manager_llm:
|
||||
logging.debug("No manager_llm provided. Using OPENAI_MODEL_NAME environment variable or defaulting to 'gpt-4o'")
|
||||
self.manager_llm = manager_llm if manager_llm else os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
|
||||
|
||||
# Check for manager_llm in environment variable if not provided
|
||||
self.manager_llm = manager_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
|
||||
|
||||
# Set logger level based on verbose
|
||||
if verbose >= 5:
|
||||
logger.setLevel(logging.INFO)
|
||||
else:
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
# Also set third-party loggers to WARNING
|
||||
logging.getLogger('chromadb').setLevel(logging.WARNING)
|
||||
logging.getLogger('openai').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpx').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpcore').setLevel(logging.WARNING)
|
||||
|
||||
if self.verbose:
|
||||
logger.info(f"Using model {self.manager_llm} for manager")
|
||||
|
||||
# If no tasks provided, generate them from agents
|
||||
if tasks is None:
|
||||
@@ -64,12 +82,12 @@ class PraisonAIAgents:
|
||||
for agent in self.agents:
|
||||
task = agent.generate_task()
|
||||
tasks.append(task)
|
||||
logging.info(f"Auto-generated {len(tasks)} tasks from agents")
|
||||
logger.info(f"Auto-generated {len(tasks)} tasks from agents")
|
||||
else:
|
||||
# Validate tasks for backward compatibility
|
||||
if not tasks:
|
||||
raise ValueError("If tasks are provided, at least one task must be present")
|
||||
logging.info(f"Using {len(tasks)} provided tasks")
|
||||
logger.info(f"Using {len(tasks)} provided tasks")
|
||||
|
||||
# Add tasks and set their status
|
||||
for task in tasks:
|
||||
@@ -85,9 +103,65 @@ class PraisonAIAgents:
|
||||
if tasks[i + 1].context is None:
|
||||
tasks[i + 1].context = []
|
||||
tasks[i + 1].context.append(tasks[i])
|
||||
logging.info("Set up sequential flow with automatic context passing")
|
||||
logger.info("Set up sequential flow with automatic context passing")
|
||||
|
||||
self._state = {} # Add state storage at PraisonAIAgents level
|
||||
|
||||
# Initialize memory system
|
||||
self.shared_memory = None
|
||||
if memory:
|
||||
try:
|
||||
from ..memory.memory import Memory
|
||||
|
||||
# Get memory config from parameter or first task
|
||||
mem_cfg = memory_config
|
||||
if not mem_cfg:
|
||||
mem_cfg = next((t.config.get('memory_config') for t in tasks if hasattr(t, 'config') and t.config), None)
|
||||
|
||||
# Set default memory config if none provided
|
||||
if not mem_cfg:
|
||||
mem_cfg = {
|
||||
"provider": "rag",
|
||||
"use_embedding": True,
|
||||
"storage": {
|
||||
"type": "sqlite",
|
||||
"path": "./.praison/memory.db"
|
||||
},
|
||||
"rag_db_path": "./.praison/chroma_db"
|
||||
}
|
||||
|
||||
# Add embedder config if provided
|
||||
if embedder:
|
||||
if isinstance(embedder, dict):
|
||||
mem_cfg = mem_cfg or {}
|
||||
mem_cfg["embedder"] = embedder
|
||||
else:
|
||||
# Handle direct embedder function
|
||||
mem_cfg = mem_cfg or {}
|
||||
mem_cfg["embedder_function"] = embedder
|
||||
|
||||
if mem_cfg:
|
||||
# Pass verbose level to Memory
|
||||
self.shared_memory = Memory(config=mem_cfg, verbose=verbose)
|
||||
if verbose >= 5:
|
||||
logger.info("Initialized shared memory for PraisonAIAgents")
|
||||
|
||||
# Distribute memory to tasks
|
||||
for task in tasks:
|
||||
if not task.memory:
|
||||
task.memory = self.shared_memory
|
||||
if verbose >= 5:
|
||||
logger.info(f"Assigned shared memory to task {task.id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize shared memory: {e}")
|
||||
|
||||
# Update tasks with shared memory
|
||||
if self.shared_memory:
|
||||
for task in tasks:
|
||||
if not task.memory:
|
||||
task.memory = self.shared_memory
|
||||
logger.info(f"Assigned shared memory to task {task.id}")
|
||||
|
||||
def add_task(self, task):
|
||||
task_id = self.task_id_counter
|
||||
@@ -137,10 +211,15 @@ class PraisonAIAgents:
|
||||
|
||||
executor_agent = task.agent
|
||||
|
||||
# Ensure tools are available from both task and agent
|
||||
tools = task.tools or []
|
||||
if executor_agent and executor_agent.tools:
|
||||
tools.extend(executor_agent.tools)
|
||||
|
||||
task_prompt = f"""
|
||||
You need to do the following task: {task.description}.
|
||||
Expected Output: {task.expected_output}.
|
||||
"""
|
||||
"""
|
||||
if task.context:
|
||||
context_results = ""
|
||||
for context_task in task.context:
|
||||
@@ -155,8 +234,8 @@ Here are the results of previous tasks that might be useful:\n
|
||||
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
|
||||
|
||||
if self.verbose >= 2:
|
||||
logging.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
|
||||
logging.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
|
||||
logger.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
|
||||
logger.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
|
||||
|
||||
if task.images:
|
||||
def _get_multimodal_message(text_prompt, images):
|
||||
@@ -193,10 +272,17 @@ Here are the results of previous tasks that might be useful:\n
|
||||
|
||||
agent_output = await executor_agent.achat(
|
||||
_get_multimodal_message(task_prompt, task.images),
|
||||
tools=task.tools
|
||||
tools=tools,
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic
|
||||
)
|
||||
else:
|
||||
agent_output = await executor_agent.achat(task_prompt, tools=task.tools)
|
||||
agent_output = await executor_agent.achat(
|
||||
task_prompt,
|
||||
tools=tools,
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic
|
||||
)
|
||||
|
||||
if agent_output:
|
||||
task_output = TaskOutput(
|
||||
@@ -214,8 +300,8 @@ Here are the results of previous tasks that might be useful:\n
|
||||
task_output.json_dict = parsed
|
||||
task_output.output_format = "JSON"
|
||||
except:
|
||||
logging.warning(f"Warning: Could not parse output of task {task_id} as JSON")
|
||||
logging.debug(f"Output that failed JSON parsing: {agent_output}")
|
||||
logger.warning(f"Warning: Could not parse output of task {task_id} as JSON")
|
||||
logger.debug(f"Output that failed JSON parsing: {agent_output}")
|
||||
|
||||
if task.output_pydantic:
|
||||
cleaned = self.clean_json_output(agent_output)
|
||||
@@ -225,8 +311,8 @@ Here are the results of previous tasks that might be useful:\n
|
||||
task_output.pydantic = pyd_obj
|
||||
task_output.output_format = "Pydantic"
|
||||
except:
|
||||
logging.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
|
||||
logging.debug(f"Output that failed Pydantic parsing: {agent_output}")
|
||||
logger.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
|
||||
logger.debug(f"Output that failed Pydantic parsing: {agent_output}")
|
||||
|
||||
task.result = task_output
|
||||
return task_output
|
||||
@@ -241,37 +327,53 @@ Here are the results of previous tasks that might be useful:\n
|
||||
return
|
||||
task = self.tasks[task_id]
|
||||
if task.status == "completed":
|
||||
logging.info(f"Task with ID {task_id} is already completed")
|
||||
logger.info(f"Task with ID {task_id} is already completed")
|
||||
return
|
||||
|
||||
retries = 0
|
||||
while task.status != "completed" and retries < self.max_retries:
|
||||
logging.debug(f"Attempt {retries+1} for task {task_id}")
|
||||
logger.debug(f"Attempt {retries+1} for task {task_id}")
|
||||
if task.status in ["not started", "in progress"]:
|
||||
task_output = await self.aexecute_task(task_id)
|
||||
if task_output and self.completion_checker(task, task_output.raw):
|
||||
task.status = "completed"
|
||||
if task.callback:
|
||||
# Run execute_callback for memory operations
|
||||
try:
|
||||
await task.execute_callback(task_output)
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing memory callback for task {task_id}: {e}")
|
||||
logger.exception(e)
|
||||
|
||||
# Run task callback if exists
|
||||
if task.callback:
|
||||
try:
|
||||
if asyncio.iscoroutinefunction(task.callback):
|
||||
await task.callback(task_output)
|
||||
else:
|
||||
task.callback(task_output)
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing task callback for task {task_id}: {e}")
|
||||
logger.exception(e)
|
||||
|
||||
self.save_output_to_file(task, task_output)
|
||||
if self.verbose >= 1:
|
||||
logging.info(f"Task {task_id} completed successfully.")
|
||||
logger.info(f"Task {task_id} completed successfully.")
|
||||
else:
|
||||
task.status = "in progress"
|
||||
if self.verbose >= 1:
|
||||
logging.info(f"Task {task_id} not completed, retrying")
|
||||
logger.info(f"Task {task_id} not completed, retrying")
|
||||
await asyncio.sleep(1)
|
||||
retries += 1
|
||||
else:
|
||||
if task.status == "failed":
|
||||
logging.info("Task is failed, resetting to in-progress for another try...")
|
||||
logger.info("Task is failed, resetting to in-progress for another try...")
|
||||
task.status = "in progress"
|
||||
else:
|
||||
logging.info("Invalid Task status")
|
||||
logger.info("Invalid Task status")
|
||||
break
|
||||
|
||||
if retries == self.max_retries and task.status != "completed":
|
||||
logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
|
||||
logger.info(f"Task {task_id} failed after {self.max_retries} retries.")
|
||||
|
||||
async def arun_all_tasks(self):
|
||||
"""Async version of run_all_tasks method"""
|
||||
@@ -283,11 +385,25 @@ Here are the results of previous tasks that might be useful:\n
|
||||
)
|
||||
|
||||
if self.process == "workflow":
|
||||
# Collect all tasks that should run in parallel
|
||||
parallel_tasks = []
|
||||
async for task_id in process.aworkflow():
|
||||
if self.tasks[task_id].async_execution:
|
||||
await self.arun_task(task_id)
|
||||
else:
|
||||
self.run_task(task_id)
|
||||
if self.tasks[task_id].async_execution and self.tasks[task_id].is_start:
|
||||
parallel_tasks.append(task_id)
|
||||
elif parallel_tasks:
|
||||
# Execute collected parallel tasks
|
||||
await asyncio.gather(*[self.arun_task(t) for t in parallel_tasks])
|
||||
parallel_tasks = []
|
||||
# Run the current non-parallel task
|
||||
if self.tasks[task_id].async_execution:
|
||||
await self.arun_task(task_id)
|
||||
else:
|
||||
self.run_task(task_id)
|
||||
|
||||
# Execute any remaining parallel tasks
|
||||
if parallel_tasks:
|
||||
await asyncio.gather(*[self.arun_task(t) for t in parallel_tasks])
|
||||
|
||||
elif self.process == "sequential":
|
||||
async for task_id in process.asequential():
|
||||
if self.tasks[task_id].async_execution:
|
||||
@@ -319,7 +435,7 @@ Here are the results of previous tasks that might be useful:\n
|
||||
with open(task.output_file, "w") as f:
|
||||
f.write(str(task_output))
|
||||
if self.verbose >= 1:
|
||||
logging.info(f"Task output saved to {task.output_file}")
|
||||
logger.info(f"Task output saved to {task.output_file}")
|
||||
except Exception as e:
|
||||
display_error(f"Error saving task output to file: {e}")
|
||||
|
||||
@@ -330,6 +446,15 @@ Here are the results of previous tasks that might be useful:\n
|
||||
return
|
||||
task = self.tasks[task_id]
|
||||
|
||||
logger.info(f"Starting execution of task {task_id}")
|
||||
logger.info(f"Task config: {task.config}")
|
||||
|
||||
# Initialize memory before task execution
|
||||
if not task.memory:
|
||||
task.memory = task.initialize_memory()
|
||||
|
||||
logger.info(f"Task memory status: {'Initialized' if task.memory else 'Not initialized'}")
|
||||
|
||||
# Only import multimodal dependencies if task has images
|
||||
if task.images and task.status == "not started":
|
||||
try:
|
||||
@@ -362,11 +487,20 @@ Expected Output: {task.expected_output}.
|
||||
Here are the results of previous tasks that might be useful:\n
|
||||
{context_results}
|
||||
"""
|
||||
# Add memory context if available
|
||||
if task.memory:
|
||||
try:
|
||||
memory_context = task.memory.build_context_for_task(task.description)
|
||||
if memory_context:
|
||||
task_prompt += f"\n\nRelevant memory context:\n{memory_context}"
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting memory context: {e}")
|
||||
|
||||
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
|
||||
|
||||
if self.verbose >= 2:
|
||||
logging.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
|
||||
logging.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
|
||||
logger.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
|
||||
logger.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
|
||||
|
||||
if task.images:
|
||||
def _get_multimodal_message(text_prompt, images):
|
||||
@@ -403,12 +537,30 @@ Here are the results of previous tasks that might be useful:\n
|
||||
|
||||
agent_output = executor_agent.chat(
|
||||
_get_multimodal_message(task_prompt, task.images),
|
||||
tools=task.tools
|
||||
tools=task.tools,
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic
|
||||
)
|
||||
else:
|
||||
agent_output = executor_agent.chat(task_prompt, tools=task.tools)
|
||||
agent_output = executor_agent.chat(
|
||||
task_prompt,
|
||||
tools=task.tools,
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic
|
||||
)
|
||||
|
||||
if agent_output:
|
||||
# Store the response in memory
|
||||
if task.memory:
|
||||
try:
|
||||
task.store_in_memory(
|
||||
content=agent_output,
|
||||
agent_name=executor_agent.name,
|
||||
task_id=task_id
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store agent output in memory: {e}")
|
||||
|
||||
task_output = TaskOutput(
|
||||
description=task.description,
|
||||
summary=task.description[:10],
|
||||
@@ -424,8 +576,8 @@ Here are the results of previous tasks that might be useful:\n
|
||||
task_output.json_dict = parsed
|
||||
task_output.output_format = "JSON"
|
||||
except:
|
||||
logging.warning(f"Warning: Could not parse output of task {task_id} as JSON")
|
||||
logging.debug(f"Output that failed JSON parsing: {agent_output}")
|
||||
logger.warning(f"Warning: Could not parse output of task {task_id} as JSON")
|
||||
logger.debug(f"Output that failed JSON parsing: {agent_output}")
|
||||
|
||||
if task.output_pydantic:
|
||||
cleaned = self.clean_json_output(agent_output)
|
||||
@@ -435,8 +587,8 @@ Here are the results of previous tasks that might be useful:\n
|
||||
task_output.pydantic = pyd_obj
|
||||
task_output.output_format = "Pydantic"
|
||||
except:
|
||||
logging.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
|
||||
logging.debug(f"Output that failed Pydantic parsing: {agent_output}")
|
||||
logger.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
|
||||
logger.debug(f"Output that failed Pydantic parsing: {agent_output}")
|
||||
|
||||
task.result = task_output
|
||||
return task_output
|
||||
@@ -451,37 +603,63 @@ Here are the results of previous tasks that might be useful:\n
|
||||
return
|
||||
task = self.tasks[task_id]
|
||||
if task.status == "completed":
|
||||
logging.info(f"Task with ID {task_id} is already completed")
|
||||
logger.info(f"Task with ID {task_id} is already completed")
|
||||
return
|
||||
|
||||
retries = 0
|
||||
while task.status != "completed" and retries < self.max_retries:
|
||||
logging.debug(f"Attempt {retries+1} for task {task_id}")
|
||||
logger.debug(f"Attempt {retries+1} for task {task_id}")
|
||||
if task.status in ["not started", "in progress"]:
|
||||
task_output = self.execute_task(task_id)
|
||||
if task_output and self.completion_checker(task, task_output.raw):
|
||||
task.status = "completed"
|
||||
# Run execute_callback for memory operations
|
||||
try:
|
||||
if asyncio.get_event_loop().is_running():
|
||||
asyncio.create_task(task.execute_callback(task_output))
|
||||
else:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(task.execute_callback(task_output))
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing memory callback for task {task_id}: {e}")
|
||||
logger.exception(e)
|
||||
|
||||
# Run task callback if exists
|
||||
if task.callback:
|
||||
task.callback(task_output)
|
||||
try:
|
||||
if asyncio.iscoroutinefunction(task.callback):
|
||||
if asyncio.get_event_loop().is_running():
|
||||
asyncio.create_task(task.callback(task_output))
|
||||
else:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(task.callback(task_output))
|
||||
else:
|
||||
task.callback(task_output)
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing task callback for task {task_id}: {e}")
|
||||
logger.exception(e)
|
||||
|
||||
self.save_output_to_file(task, task_output)
|
||||
if self.verbose >= 1:
|
||||
logging.info(f"Task {task_id} completed successfully.")
|
||||
logger.info(f"Task {task_id} completed successfully.")
|
||||
else:
|
||||
task.status = "in progress"
|
||||
if self.verbose >= 1:
|
||||
logging.info(f"Task {task_id} not completed, retrying")
|
||||
logger.info(f"Task {task_id} not completed, retrying")
|
||||
time.sleep(1)
|
||||
retries += 1
|
||||
else:
|
||||
if task.status == "failed":
|
||||
logging.info("Task is failed, resetting to in-progress for another try...")
|
||||
logger.info("Task is failed, resetting to in-progress for another try...")
|
||||
task.status = "in progress"
|
||||
else:
|
||||
logging.info("Invalid Task status")
|
||||
logger.info("Invalid Task status")
|
||||
break
|
||||
|
||||
if retries == self.max_retries and task.status != "completed":
|
||||
logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
|
||||
logger.info(f"Task {task_id} failed after {self.max_retries} retries.")
|
||||
|
||||
def run_all_tasks(self):
|
||||
"""Synchronous version of run_all_tasks method"""
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
"""
|
||||
AutoAgents - A class for automatically creating and managing AI agents and tasks.
|
||||
|
||||
This class provides a simplified interface for creating and running AI agents with tasks.
|
||||
It automatically handles agent creation, task setup, and execution flow.
|
||||
"""
|
||||
|
||||
from .agents import PraisonAIAgents
|
||||
from ..agent.agent import Agent
|
||||
from ..task.task import Task
|
||||
from typing import List, Any, Optional, Dict, Union
|
||||
import logging
|
||||
import os
|
||||
from pydantic import BaseModel, Field
|
||||
from ..main import display_instruction, display_tool_call, display_interaction, client
|
||||
|
||||
# Define Pydantic models for structured output
|
||||
class TaskConfig(BaseModel):
|
||||
name: str
|
||||
description: str
|
||||
expected_output: str
|
||||
tools: List[str]
|
||||
|
||||
class AgentConfig(BaseModel):
|
||||
name: str
|
||||
role: str
|
||||
goal: str
|
||||
backstory: str
|
||||
tools: List[str]
|
||||
tasks: List[TaskConfig]
|
||||
|
||||
class AutoAgentsConfig(BaseModel):
|
||||
main_instruction: str
|
||||
process_type: str
|
||||
agents: List[AgentConfig]
|
||||
|
||||
class AutoAgents(PraisonAIAgents):
|
||||
def __init__(
|
||||
self,
|
||||
instructions: str,
|
||||
tools: Optional[List[Any]] = None,
|
||||
verbose: bool = False,
|
||||
process: str = "sequential",
|
||||
manager_llm: Optional[str] = None,
|
||||
max_retries: int = 5,
|
||||
completion_checker: Optional[Any] = None,
|
||||
allow_code_execution: bool = False,
|
||||
memory: bool = True,
|
||||
markdown: bool = True,
|
||||
self_reflect: bool = False,
|
||||
max_reflect: int = 3,
|
||||
min_reflect: int = 1,
|
||||
llm: Optional[str] = None,
|
||||
function_calling_llm: Optional[str] = None,
|
||||
respect_context_window: bool = True,
|
||||
code_execution_mode: str = "safe",
|
||||
embedder_config: Optional[Dict[str, Any]] = None,
|
||||
knowledge_sources: Optional[List[Any]] = None,
|
||||
use_system_prompt: bool = True,
|
||||
cache: bool = True,
|
||||
allow_delegation: bool = False,
|
||||
step_callback: Optional[Any] = None,
|
||||
system_template: Optional[str] = None,
|
||||
prompt_template: Optional[str] = None,
|
||||
response_template: Optional[str] = None,
|
||||
max_rpm: Optional[int] = None,
|
||||
max_execution_time: Optional[int] = None,
|
||||
max_iter: int = 20,
|
||||
reflect_llm: Optional[str] = None,
|
||||
base_url: Optional[str] = None,
|
||||
api_key: Optional[str] = None,
|
||||
max_agents: int = 3 # New parameter for maximum number of agents
|
||||
):
|
||||
"""Initialize AutoAgents with configuration for automatic agent and task creation."""
|
||||
if max_agents < 1:
|
||||
raise ValueError("max_agents must be at least 1")
|
||||
if max_agents > 10:
|
||||
raise ValueError("max_agents cannot exceed 10")
|
||||
|
||||
# Store all configuration parameters first
|
||||
self.instructions = instructions
|
||||
self.tools = tools or []
|
||||
self.verbose = verbose # Store verbose parameter
|
||||
self.max_agents = max_agents # Store max_agents parameter
|
||||
self.allow_code_execution = allow_code_execution
|
||||
self.memory = memory
|
||||
self.markdown = markdown
|
||||
self.self_reflect = self_reflect
|
||||
self.max_reflect = max_reflect
|
||||
self.min_reflect = min_reflect
|
||||
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
|
||||
self.function_calling_llm = function_calling_llm
|
||||
self.respect_context_window = respect_context_window
|
||||
self.code_execution_mode = code_execution_mode
|
||||
self.embedder_config = embedder_config
|
||||
self.knowledge_sources = knowledge_sources
|
||||
self.use_system_prompt = use_system_prompt
|
||||
self.cache = cache
|
||||
self.allow_delegation = allow_delegation
|
||||
self.step_callback = step_callback
|
||||
self.system_template = system_template
|
||||
self.prompt_template = prompt_template
|
||||
self.response_template = response_template
|
||||
self.max_rpm = max_rpm
|
||||
self.max_execution_time = max_execution_time
|
||||
self.max_iter = max_iter
|
||||
self.reflect_llm = reflect_llm
|
||||
|
||||
# Display initial instruction
|
||||
if self.verbose:
|
||||
display_instruction(f"🎯 Main Task: {self.instructions}")
|
||||
display_instruction(f"📊 Maximum Agents: {self.max_agents}")
|
||||
if self.tools:
|
||||
tool_names = [t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools]
|
||||
display_tool_call(f"🛠️ Available Tools: {', '.join(tool_names)}")
|
||||
|
||||
# Generate agents and tasks configuration
|
||||
config = self._generate_config()
|
||||
agents, tasks = self._create_agents_and_tasks(config)
|
||||
|
||||
# Display agents and their tasks
|
||||
if self.verbose:
|
||||
self._display_agents_and_tasks(agents, tasks)
|
||||
|
||||
# Initialize parent class with generated agents and tasks
|
||||
super().__init__(
|
||||
agents=agents,
|
||||
tasks=tasks,
|
||||
verbose=verbose,
|
||||
completion_checker=completion_checker,
|
||||
max_retries=max_retries,
|
||||
process=process,
|
||||
manager_llm=manager_llm
|
||||
)
|
||||
|
||||
def _display_agents_and_tasks(self, agents: List[Agent], tasks: List[Task]):
|
||||
"""Display the created agents and their assigned tasks"""
|
||||
display_instruction("\n🤖 Generated Agents and Tasks:")
|
||||
|
||||
# Create a mapping of agents to their tasks
|
||||
agent_tasks = {}
|
||||
for task in tasks:
|
||||
if task.agent not in agent_tasks:
|
||||
agent_tasks[task.agent] = []
|
||||
agent_tasks[task.agent].append(task)
|
||||
|
||||
# Display each agent and their tasks
|
||||
for agent in agents:
|
||||
agent_tools = [t.__name__ if hasattr(t, '__name__') else str(t) for t in agent.tools]
|
||||
display_interaction(
|
||||
f"\n👤 Agent: {agent.name}",
|
||||
f"""Role: {agent.role}
|
||||
Goal: {agent.goal}
|
||||
Tools: {', '.join(agent_tools)}"""
|
||||
)
|
||||
|
||||
# Display tasks for this agent
|
||||
if agent in agent_tasks:
|
||||
for i, task in enumerate(agent_tasks[agent], 1):
|
||||
task_tools = [t.__name__ if hasattr(t, '__name__') else str(t) for t in task.tools]
|
||||
display_instruction(
|
||||
f""" 📋 Task {i}: {task.name}
|
||||
Description: {task.description}
|
||||
Expected Output: {task.expected_output}
|
||||
Tools: {', '.join(task_tools)}"""
|
||||
)
|
||||
|
||||
def _get_available_tools(self) -> List[str]:
|
||||
"""Get list of available tools"""
|
||||
if not self.tools:
|
||||
return []
|
||||
return [t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools]
|
||||
|
||||
def _get_tool_by_name(self, tool_name: str) -> Optional[Any]:
|
||||
"""Get tool object by its name"""
|
||||
for tool in self.tools:
|
||||
if (hasattr(tool, '__name__') and tool.__name__ == tool_name) or str(tool) == tool_name:
|
||||
return tool
|
||||
return None
|
||||
|
||||
def _assign_tools_to_agent(self, agent_config: AgentConfig) -> List[Any]:
|
||||
"""
|
||||
Assign appropriate tools to an agent based on its role and tasks.
|
||||
|
||||
Args:
|
||||
agent_config: The agent configuration containing role and required tools
|
||||
|
||||
Returns:
|
||||
List of tool objects assigned to this agent
|
||||
"""
|
||||
assigned_tools = []
|
||||
tool_names = set(agent_config.tools)
|
||||
|
||||
# Also look at task requirements
|
||||
for task in agent_config.tasks:
|
||||
tool_names.update(task.tools)
|
||||
|
||||
# Assign tools that match the requirements
|
||||
for tool_name in tool_names:
|
||||
tool = self._get_tool_by_name(tool_name)
|
||||
if tool:
|
||||
assigned_tools.append(tool)
|
||||
|
||||
# If no specific tools matched but we have tools available,
|
||||
# assign all tools to ensure functionality
|
||||
if not assigned_tools and self.tools:
|
||||
assigned_tools = self.tools
|
||||
|
||||
return assigned_tools
|
||||
|
||||
def _generate_config(self) -> AutoAgentsConfig:
|
||||
"""Generate the configuration for agents and tasks"""
|
||||
prompt = f"""
|
||||
Generate a configuration for AI agents to accomplish this task: "{self.instructions}"
|
||||
|
||||
The configuration should include:
|
||||
1. A main instruction that clearly states the overall goal
|
||||
2. A process type (sequential, workflow, or hierarchical)
|
||||
3. A list of maximum {self.max_agents} agents (no more, no less), each with:
|
||||
- Name, role, goal, and backstory
|
||||
- List of required tools from: {self._get_available_tools()}
|
||||
- Only add tools that are needed for the agent to perform its task
|
||||
- Only one task per agent. Add more than one task if absolutely necessary.
|
||||
- List of specific tasks they need to perform
|
||||
- Whether they should self-reflect or allow delegation
|
||||
|
||||
Requirements:
|
||||
1. Each agent should have clear, focused responsibilities
|
||||
2. Tasks should be broken down into manageable steps
|
||||
3. Tool selection should be appropriate for each task
|
||||
4. The process type should match the task requirements
|
||||
5. Generate maximum {self.max_agents} agents to handle this task efficiently
|
||||
|
||||
Return the configuration in a structured JSON format matching the AutoAgentsConfig schema.
|
||||
"""
|
||||
|
||||
try:
|
||||
response = client.beta.chat.completions.parse(
|
||||
model=self.llm,
|
||||
response_format=AutoAgentsConfig,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant designed to generate AI agent configurations."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
|
||||
# Ensure we have exactly max_agents number of agents
|
||||
if len(response.choices[0].message.parsed.agents) > self.max_agents:
|
||||
response.choices[0].message.parsed.agents = response.choices[0].message.parsed.agents[:self.max_agents]
|
||||
elif len(response.choices[0].message.parsed.agents) < self.max_agents:
|
||||
logging.warning(f"Generated {len(response.choices[0].message.parsed.agents)} agents, expected {self.max_agents}")
|
||||
|
||||
return response.choices[0].message.parsed
|
||||
except Exception as e:
|
||||
logging.error(f"Error generating configuration: {e}")
|
||||
raise
|
||||
|
||||
def _create_agents_and_tasks(self, config: AutoAgentsConfig) -> tuple[List[Agent], List[Task]]:
|
||||
"""Create agents and tasks from configuration"""
|
||||
agents = []
|
||||
tasks = []
|
||||
|
||||
for agent_config in config.agents:
|
||||
# Get appropriate tools for this agent
|
||||
agent_tools = self._assign_tools_to_agent(agent_config)
|
||||
|
||||
# Create the agent with all parameters
|
||||
agent = Agent(
|
||||
name=agent_config.name,
|
||||
role=agent_config.role,
|
||||
goal=agent_config.goal,
|
||||
backstory=agent_config.backstory,
|
||||
tools=agent_tools, # Use assigned tools
|
||||
verbose=self.verbose >= 1,
|
||||
allow_code_execution=self.allow_code_execution,
|
||||
memory=self.memory,
|
||||
markdown=self.markdown,
|
||||
self_reflect=self.self_reflect,
|
||||
max_reflect=self.max_reflect,
|
||||
min_reflect=self.min_reflect,
|
||||
llm=self.llm,
|
||||
function_calling_llm=self.function_calling_llm,
|
||||
respect_context_window=self.respect_context_window,
|
||||
code_execution_mode=self.code_execution_mode,
|
||||
embedder_config=self.embedder_config,
|
||||
knowledge_sources=self.knowledge_sources,
|
||||
use_system_prompt=self.use_system_prompt,
|
||||
cache=self.cache,
|
||||
allow_delegation=self.allow_delegation,
|
||||
step_callback=self.step_callback,
|
||||
system_template=self.system_template,
|
||||
prompt_template=self.prompt_template,
|
||||
response_template=self.response_template,
|
||||
max_rpm=self.max_rpm,
|
||||
max_execution_time=self.max_execution_time,
|
||||
max_iter=self.max_iter,
|
||||
reflect_llm=self.reflect_llm
|
||||
)
|
||||
agents.append(agent)
|
||||
|
||||
# Create tasks for this agent
|
||||
for task_config in agent_config.tasks:
|
||||
# Get task-specific tools
|
||||
task_tools = [self._get_tool_by_name(t) for t in task_config.tools]
|
||||
task_tools = [t for t in task_tools if t] # Remove None values
|
||||
|
||||
# If no specific tools matched, use agent's tools
|
||||
if not task_tools:
|
||||
task_tools = agent_tools
|
||||
|
||||
task = Task(
|
||||
name=task_config.name,
|
||||
description=task_config.description,
|
||||
expected_output=task_config.expected_output,
|
||||
agent=agent,
|
||||
tools=task_tools # Use task-specific tools
|
||||
)
|
||||
tasks.append(task)
|
||||
|
||||
return agents, tasks
|
||||
|
||||
async def astart(self):
|
||||
"""
|
||||
Async version of start() method.
|
||||
Creates tasks based on the instructions, then starts execution.
|
||||
Returns the task status and results dictionary.
|
||||
"""
|
||||
return await super().astart()
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Creates tasks based on the instructions, then starts execution.
|
||||
Returns the task status and results dictionary.
|
||||
"""
|
||||
return super().start()
|
||||
@@ -116,10 +116,10 @@ def display_interaction(message, response, markdown=True, generation_time=None,
|
||||
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
|
||||
|
||||
if markdown:
|
||||
console.print(Panel.fit(Markdown(message), title="Message", border_style="cyan"))
|
||||
console.print(Panel.fit(Markdown(message), title="Task", border_style="cyan"))
|
||||
console.print(Panel.fit(Markdown(response), title="Response", border_style="cyan"))
|
||||
else:
|
||||
console.print(Panel.fit(Text(message, style="bold green"), title="Message", border_style="cyan"))
|
||||
console.print(Panel.fit(Text(message, style="bold green"), title="Task", border_style="cyan"))
|
||||
console.print(Panel.fit(Text(response, style="bold blue"), title="Response", border_style="cyan"))
|
||||
|
||||
def display_self_reflection(message: str, console=None):
|
||||
@@ -135,7 +135,7 @@ def display_self_reflection(message: str, console=None):
|
||||
|
||||
console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
|
||||
|
||||
def display_instruction(message: str, console=None):
|
||||
def display_instruction(message: str, console=None, agent_name: str = None, agent_role: str = None, agent_tools: List[str] = None):
|
||||
if not message or not message.strip():
|
||||
return
|
||||
if console is None:
|
||||
@@ -146,7 +146,19 @@ def display_instruction(message: str, console=None):
|
||||
if 'instruction' in sync_display_callbacks:
|
||||
sync_display_callbacks['instruction'](message=message)
|
||||
|
||||
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
|
||||
# Display agent info if available
|
||||
if agent_name:
|
||||
agent_info = f"[bold #FF9B9B]👤 Agent:[/] [#FFE5E5]{agent_name}[/]"
|
||||
if agent_role:
|
||||
agent_info += f"\n[bold #B4B4B3]Role:[/] [#FFE5E5]{agent_role}[/]"
|
||||
if agent_tools:
|
||||
tools_str = ", ".join(f"[italic #B4D4FF]{tool}[/]" for tool in agent_tools)
|
||||
agent_info += f"\n[bold #86A789]Tools:[/] {tools_str}"
|
||||
console.print(Panel(agent_info, border_style="#D2E3C8", title="[bold]Agent Info[/]", title_align="left", padding=(1, 2)))
|
||||
|
||||
# Only print if log level is DEBUG
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
|
||||
|
||||
def display_tool_call(message: str, console=None):
|
||||
if not message or not message.strip():
|
||||
@@ -222,10 +234,10 @@ async def adisplay_interaction(message, response, markdown=True, generation_time
|
||||
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
|
||||
|
||||
if markdown:
|
||||
console.print(Panel.fit(Markdown(message), title="Message", border_style="cyan"))
|
||||
console.print(Panel.fit(Markdown(message), title="Task", border_style="cyan"))
|
||||
console.print(Panel.fit(Markdown(response), title="Response", border_style="cyan"))
|
||||
else:
|
||||
console.print(Panel.fit(Text(message, style="bold green"), title="Message", border_style="cyan"))
|
||||
console.print(Panel.fit(Text(message, style="bold green"), title="Task", border_style="cyan"))
|
||||
console.print(Panel.fit(Text(response, style="bold blue"), title="Response", border_style="cyan"))
|
||||
|
||||
async def adisplay_self_reflection(message: str, console=None):
|
||||
@@ -241,7 +253,7 @@ async def adisplay_self_reflection(message: str, console=None):
|
||||
|
||||
console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
|
||||
|
||||
async def adisplay_instruction(message: str, console=None):
|
||||
async def adisplay_instruction(message: str, console=None, agent_name: str = None, agent_role: str = None, agent_tools: List[str] = None):
|
||||
"""Async version of display_instruction."""
|
||||
if not message or not message.strip():
|
||||
return
|
||||
@@ -252,7 +264,19 @@ async def adisplay_instruction(message: str, console=None):
|
||||
if 'instruction' in async_display_callbacks:
|
||||
await async_display_callbacks['instruction'](message=message)
|
||||
|
||||
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
|
||||
# Display agent info if available
|
||||
if agent_name:
|
||||
agent_info = f"[bold #FF9B9B]👤 Agent:[/] [#FFE5E5]{agent_name}[/]"
|
||||
if agent_role:
|
||||
agent_info += f"\n[bold #B4B4B3]Role:[/] [#FFE5E5]{agent_role}[/]"
|
||||
if agent_tools:
|
||||
tools_str = ", ".join(f"[italic #B4D4FF]{tool}[/]" for tool in agent_tools)
|
||||
agent_info += f"\n[bold #86A789]Tools:[/] {tools_str}"
|
||||
console.print(Panel(agent_info, border_style="#D2E3C8", title="[bold]Agent Info[/]", title_align="left", padding=(1, 2)))
|
||||
|
||||
# Only print if log level is DEBUG
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
|
||||
|
||||
async def adisplay_tool_call(message: str, console=None):
|
||||
"""Async version of display_tool_call."""
|
||||
|
||||
@@ -0,0 +1,931 @@
|
||||
import os
|
||||
import sqlite3
|
||||
import json
|
||||
import time
|
||||
import shutil
|
||||
from typing import Any, Dict, List, Optional, Union, Literal
|
||||
import logging
|
||||
|
||||
# Set up logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import chromadb
|
||||
from chromadb.config import Settings as ChromaSettings
|
||||
CHROMADB_AVAILABLE = True
|
||||
except ImportError:
|
||||
CHROMADB_AVAILABLE = False
|
||||
logger.warning("To use memory features, please run: pip install \"praisonaiagents[memory]\"")
|
||||
|
||||
try:
|
||||
import mem0
|
||||
MEM0_AVAILABLE = True
|
||||
except ImportError:
|
||||
MEM0_AVAILABLE = False
|
||||
|
||||
try:
|
||||
import openai
|
||||
OPENAI_AVAILABLE = True
|
||||
except ImportError:
|
||||
OPENAI_AVAILABLE = False
|
||||
|
||||
|
||||
|
||||
|
||||
class Memory:
|
||||
"""
|
||||
A single-file memory manager covering:
|
||||
- Short-term memory (STM) for ephemeral context
|
||||
- Long-term memory (LTM) for persistent knowledge
|
||||
- Entity memory (structured data about named entities)
|
||||
- User memory (preferences/history for each user)
|
||||
- Quality score logic for deciding which data to store in LTM
|
||||
- Context building from multiple memory sources
|
||||
|
||||
Config example:
|
||||
{
|
||||
"provider": "rag" or "mem0" or "none",
|
||||
"use_embedding": True,
|
||||
"short_db": "short_term.db",
|
||||
"long_db": "long_term.db",
|
||||
"rag_db_path": "rag_db", # optional path for local embedding store
|
||||
"config": {
|
||||
"api_key": "...", # if mem0 usage
|
||||
"org_id": "...",
|
||||
"project_id": "...",
|
||||
...
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any], verbose: int = 0):
|
||||
self.cfg = config or {}
|
||||
self.verbose = verbose
|
||||
|
||||
# Set logger level based on verbose
|
||||
if verbose >= 5:
|
||||
logger.setLevel(logging.INFO)
|
||||
else:
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
# Also set ChromaDB and OpenAI client loggers to WARNING
|
||||
logging.getLogger('chromadb').setLevel(logging.WARNING)
|
||||
logging.getLogger('openai').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpx').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpcore').setLevel(logging.WARNING)
|
||||
logging.getLogger('chromadb.segment.impl.vector.local_persistent_hnsw').setLevel(logging.ERROR)
|
||||
|
||||
self.provider = self.cfg.get("provider", "rag")
|
||||
self.use_mem0 = (self.provider.lower() == "mem0") and MEM0_AVAILABLE
|
||||
self.use_rag = (self.provider.lower() == "rag") and CHROMADB_AVAILABLE and self.cfg.get("use_embedding", False)
|
||||
|
||||
# Create .praison directory if it doesn't exist
|
||||
os.makedirs(".praison", exist_ok=True)
|
||||
|
||||
# Short-term DB
|
||||
self.short_db = self.cfg.get("short_db", ".praison/short_term.db")
|
||||
self._init_stm()
|
||||
|
||||
# Long-term DB
|
||||
self.long_db = self.cfg.get("long_db", ".praison/long_term.db")
|
||||
self._init_ltm()
|
||||
|
||||
# Conditionally init Mem0 or local RAG
|
||||
if self.use_mem0:
|
||||
self._init_mem0()
|
||||
elif self.use_rag:
|
||||
self._init_chroma()
|
||||
|
||||
def _log_verbose(self, msg: str, level: int = logging.INFO):
|
||||
"""Only log if verbose >= 5"""
|
||||
if self.verbose >= 5:
|
||||
logger.log(level, msg)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Initialization
|
||||
# -------------------------------------------------------------------------
|
||||
def _init_stm(self):
|
||||
"""Creates or verifies short-term memory table."""
|
||||
os.makedirs(os.path.dirname(self.short_db) or ".", exist_ok=True)
|
||||
conn = sqlite3.connect(self.short_db)
|
||||
c = conn.cursor()
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS short_mem (
|
||||
id TEXT PRIMARY KEY,
|
||||
content TEXT,
|
||||
meta TEXT,
|
||||
created_at REAL
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def _init_ltm(self):
|
||||
"""Creates or verifies long-term memory table."""
|
||||
os.makedirs(os.path.dirname(self.long_db) or ".", exist_ok=True)
|
||||
conn = sqlite3.connect(self.long_db)
|
||||
c = conn.cursor()
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS long_mem (
|
||||
id TEXT PRIMARY KEY,
|
||||
content TEXT,
|
||||
meta TEXT,
|
||||
created_at REAL
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def _init_mem0(self):
|
||||
"""Initialize Mem0 client for agent or user memory."""
|
||||
from mem0 import MemoryClient
|
||||
mem_cfg = self.cfg.get("config", {})
|
||||
api_key = mem_cfg.get("api_key", os.getenv("MEM0_API_KEY"))
|
||||
org_id = mem_cfg.get("org_id")
|
||||
proj_id = mem_cfg.get("project_id")
|
||||
if org_id and proj_id:
|
||||
self.mem0_client = MemoryClient(api_key=api_key, org_id=org_id, project_id=proj_id)
|
||||
else:
|
||||
self.mem0_client = MemoryClient(api_key=api_key)
|
||||
|
||||
def _init_chroma(self):
|
||||
"""Initialize a local Chroma client for embedding-based search."""
|
||||
try:
|
||||
# Create directory if it doesn't exist
|
||||
rag_path = self.cfg.get("rag_db_path", "chroma_db")
|
||||
os.makedirs(rag_path, exist_ok=True)
|
||||
|
||||
# Initialize ChromaDB with persistent storage
|
||||
self.chroma_client = chromadb.PersistentClient(
|
||||
path=rag_path,
|
||||
settings=ChromaSettings(
|
||||
anonymized_telemetry=False,
|
||||
allow_reset=True
|
||||
)
|
||||
)
|
||||
|
||||
collection_name = "memory_store"
|
||||
try:
|
||||
self.chroma_col = self.chroma_client.get_collection(name=collection_name)
|
||||
self._log_verbose("Using existing ChromaDB collection")
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Collection '{collection_name}' not found. Creating new collection. Error: {e}")
|
||||
self.chroma_col = self.chroma_client.create_collection(
|
||||
name=collection_name,
|
||||
metadata={"hnsw:space": "cosine"}
|
||||
)
|
||||
self._log_verbose("Created new ChromaDB collection")
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Failed to initialize ChromaDB: {e}", logging.ERROR)
|
||||
self.use_rag = False
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Basic Quality Score Computation
|
||||
# -------------------------------------------------------------------------
|
||||
def compute_quality_score(
|
||||
self,
|
||||
completeness: float,
|
||||
relevance: float,
|
||||
clarity: float,
|
||||
accuracy: float,
|
||||
weights: Dict[str, float] = None
|
||||
) -> float:
|
||||
"""
|
||||
Combine multiple sub-metrics into one final score, as an example.
|
||||
|
||||
Args:
|
||||
completeness (float): 0-1
|
||||
relevance (float): 0-1
|
||||
clarity (float): 0-1
|
||||
accuracy (float): 0-1
|
||||
weights (Dict[str, float]): optional weighting like {"completeness": 0.25, "relevance": 0.3, ...}
|
||||
|
||||
Returns:
|
||||
float: Weighted average 0-1
|
||||
"""
|
||||
if not weights:
|
||||
weights = {
|
||||
"completeness": 0.25,
|
||||
"relevance": 0.25,
|
||||
"clarity": 0.25,
|
||||
"accuracy": 0.25
|
||||
}
|
||||
total = (completeness * weights["completeness"]
|
||||
+ relevance * weights["relevance"]
|
||||
+ clarity * weights["clarity"]
|
||||
+ accuracy * weights["accuracy"]
|
||||
)
|
||||
return round(total, 3) # e.g. round to 3 decimal places
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Short-Term Methods
|
||||
# -------------------------------------------------------------------------
|
||||
def store_short_term(
|
||||
self,
|
||||
text: str,
|
||||
metadata: Dict[str, Any] = None,
|
||||
completeness: float = None,
|
||||
relevance: float = None,
|
||||
clarity: float = None,
|
||||
accuracy: float = None,
|
||||
weights: Dict[str, float] = None,
|
||||
evaluator_quality: float = None
|
||||
):
|
||||
"""Store in short-term memory with optional quality metrics"""
|
||||
logger.info(f"Storing in short-term memory: {text[:100]}...")
|
||||
logger.info(f"Metadata: {metadata}")
|
||||
|
||||
metadata = self._process_quality_metrics(
|
||||
metadata, completeness, relevance, clarity,
|
||||
accuracy, weights, evaluator_quality
|
||||
)
|
||||
logger.info(f"Processed metadata: {metadata}")
|
||||
|
||||
# Existing store logic
|
||||
try:
|
||||
conn = sqlite3.connect(self.short_db)
|
||||
ident = str(time.time_ns())
|
||||
conn.execute(
|
||||
"INSERT INTO short_mem (id, content, meta, created_at) VALUES (?,?,?,?)",
|
||||
(ident, text, json.dumps(metadata), time.time())
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
logger.info(f"Successfully stored in short-term memory with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in short-term memory: {e}")
|
||||
raise
|
||||
|
||||
def search_short_term(
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 5,
|
||||
min_quality: float = 0.0,
|
||||
relevance_cutoff: float = 0.0
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search short-term memory with optional quality filter"""
|
||||
self._log_verbose(f"Searching short memory for: {query}")
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
results = self.mem0_client.search(query=query, limit=limit)
|
||||
filtered = [r for r in results if r.get("score", 1.0) >= relevance_cutoff]
|
||||
return filtered
|
||||
|
||||
elif self.use_rag and hasattr(self, "chroma_col"):
|
||||
try:
|
||||
from openai import OpenAI
|
||||
client = OpenAI()
|
||||
|
||||
response = client.embeddings.create(
|
||||
input=query,
|
||||
model="text-embedding-3-small"
|
||||
)
|
||||
query_embedding = response.data[0].embedding
|
||||
|
||||
resp = self.chroma_col.query(
|
||||
query_embeddings=[query_embedding],
|
||||
n_results=limit
|
||||
)
|
||||
|
||||
results = []
|
||||
if resp["ids"]:
|
||||
for i in range(len(resp["ids"][0])):
|
||||
metadata = resp["metadatas"][0][i] if "metadatas" in resp else {}
|
||||
quality = metadata.get("quality", 0.0)
|
||||
score = 1.0 - (resp["distances"][0][i] if "distances" in resp else 0.0)
|
||||
if quality >= min_quality and score >= relevance_cutoff:
|
||||
results.append({
|
||||
"id": resp["ids"][0][i],
|
||||
"text": resp["documents"][0][i],
|
||||
"metadata": metadata,
|
||||
"score": score
|
||||
})
|
||||
return results
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error searching ChromaDB: {e}", logging.ERROR)
|
||||
return []
|
||||
|
||||
else:
|
||||
# Local fallback
|
||||
conn = sqlite3.connect(self.short_db)
|
||||
c = conn.cursor()
|
||||
rows = c.execute(
|
||||
"SELECT id, content, meta FROM short_mem WHERE content LIKE ? LIMIT ?",
|
||||
(f"%{query}%", limit)
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
results = []
|
||||
for row in rows:
|
||||
meta = json.loads(row[2] or "{}")
|
||||
quality = meta.get("quality", 0.0)
|
||||
if quality >= min_quality:
|
||||
results.append({
|
||||
"id": row[0],
|
||||
"text": row[1],
|
||||
"metadata": meta
|
||||
})
|
||||
return results
|
||||
|
||||
def reset_short_term(self):
|
||||
"""Completely clears short-term memory."""
|
||||
conn = sqlite3.connect(self.short_db)
|
||||
conn.execute("DELETE FROM short_mem")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Long-Term Methods
|
||||
# -------------------------------------------------------------------------
|
||||
def _sanitize_metadata(self, metadata: Dict) -> Dict:
|
||||
"""Sanitize metadata for ChromaDB - convert to acceptable types"""
|
||||
sanitized = {}
|
||||
for k, v in metadata.items():
|
||||
if v is None:
|
||||
continue
|
||||
if isinstance(v, (str, int, float, bool)):
|
||||
sanitized[k] = v
|
||||
elif isinstance(v, dict):
|
||||
# Convert dict to string representation
|
||||
sanitized[k] = str(v)
|
||||
else:
|
||||
# Convert other types to string
|
||||
sanitized[k] = str(v)
|
||||
return sanitized
|
||||
|
||||
def store_long_term(
|
||||
self,
|
||||
text: str,
|
||||
metadata: Dict[str, Any] = None,
|
||||
completeness: float = None,
|
||||
relevance: float = None,
|
||||
clarity: float = None,
|
||||
accuracy: float = None,
|
||||
weights: Dict[str, float] = None,
|
||||
evaluator_quality: float = None
|
||||
):
|
||||
"""Store in long-term memory with optional quality metrics"""
|
||||
logger.info(f"Storing in long-term memory: {text[:100]}...")
|
||||
logger.info(f"Initial metadata: {metadata}")
|
||||
|
||||
# Process metadata
|
||||
metadata = metadata or {}
|
||||
metadata = self._process_quality_metrics(
|
||||
metadata, completeness, relevance, clarity,
|
||||
accuracy, weights, evaluator_quality
|
||||
)
|
||||
logger.info(f"Processed metadata: {metadata}")
|
||||
|
||||
# Generate unique ID
|
||||
ident = str(time.time_ns())
|
||||
created = time.time()
|
||||
|
||||
# Store in SQLite
|
||||
try:
|
||||
conn = sqlite3.connect(self.long_db)
|
||||
conn.execute(
|
||||
"INSERT INTO long_mem (id, content, meta, created_at) VALUES (?,?,?,?)",
|
||||
(ident, text, json.dumps(metadata), created)
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
logger.info(f"Successfully stored in SQLite with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing in SQLite: {e}")
|
||||
return
|
||||
|
||||
# Store in vector database if enabled
|
||||
if self.use_rag and hasattr(self, "chroma_col"):
|
||||
try:
|
||||
from openai import OpenAI
|
||||
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # Ensure API key is correctly set
|
||||
|
||||
logger.info("Getting embeddings from OpenAI...")
|
||||
logger.debug(f"Embedding input text: {text}") # Log the input text
|
||||
|
||||
response = client.embeddings.create(
|
||||
input=text,
|
||||
model="text-embedding-3-small"
|
||||
)
|
||||
embedding = response.data[0].embedding
|
||||
logger.info("Successfully got embeddings")
|
||||
logger.debug(f"Received embedding of length: {len(embedding)}") # Log embedding details
|
||||
|
||||
# Sanitize metadata for ChromaDB
|
||||
sanitized_metadata = self._sanitize_metadata(metadata)
|
||||
|
||||
# Store in ChromaDB with embedding
|
||||
self.chroma_col.add(
|
||||
documents=[text],
|
||||
metadatas=[sanitized_metadata],
|
||||
ids=[ident],
|
||||
embeddings=[embedding]
|
||||
)
|
||||
logger.info(f"Successfully stored in ChromaDB with ID: {ident}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing in ChromaDB: {e}")
|
||||
|
||||
elif self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
try:
|
||||
self.mem0_client.add(text, metadata=metadata)
|
||||
logger.info("Successfully stored in Mem0")
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing in Mem0: {e}")
|
||||
|
||||
|
||||
def search_long_term(
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 5,
|
||||
relevance_cutoff: float = 0.0,
|
||||
min_quality: float = 0.0
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search long-term memory with optional quality filter"""
|
||||
self._log_verbose(f"Searching long memory for: {query}")
|
||||
self._log_verbose(f"Min quality: {min_quality}")
|
||||
|
||||
found = []
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
results = self.mem0_client.search(query=query, limit=limit)
|
||||
# Filter by quality
|
||||
filtered = [r for r in results if r.get("metadata", {}).get("quality", 0.0) >= min_quality]
|
||||
logger.info(f"Found {len(filtered)} results in Mem0")
|
||||
return filtered
|
||||
|
||||
elif self.use_rag and hasattr(self, "chroma_col"):
|
||||
try:
|
||||
from openai import OpenAI
|
||||
client = OpenAI()
|
||||
|
||||
# Get query embedding
|
||||
response = client.embeddings.create(
|
||||
input=query,
|
||||
model="text-embedding-3-small" # Using consistent model
|
||||
)
|
||||
query_embedding = response.data[0].embedding
|
||||
|
||||
# Search ChromaDB with embedding
|
||||
resp = self.chroma_col.query(
|
||||
query_embeddings=[query_embedding],
|
||||
n_results=limit,
|
||||
include=["documents", "metadatas", "distances"]
|
||||
)
|
||||
|
||||
results = []
|
||||
if resp["ids"]:
|
||||
for i in range(len(resp["ids"][0])):
|
||||
metadata = resp["metadatas"][0][i] if "metadatas" in resp else {}
|
||||
text = resp["documents"][0][i]
|
||||
# Add memory record citation
|
||||
text = f"{text} (Memory record: {text})"
|
||||
found.append({
|
||||
"id": resp["ids"][0][i],
|
||||
"text": text,
|
||||
"metadata": metadata,
|
||||
"score": 1.0 - (resp["distances"][0][i] if "distances" in resp else 0.0)
|
||||
})
|
||||
logger.info(f"Found {len(found)} results in ChromaDB")
|
||||
|
||||
except Exception as e:
|
||||
self._log_verbose(f"Error searching ChromaDB: {e}", logging.ERROR)
|
||||
|
||||
# Always try SQLite as fallback or additional source
|
||||
conn = sqlite3.connect(self.long_db)
|
||||
c = conn.cursor()
|
||||
rows = c.execute(
|
||||
"SELECT id, content, meta, created_at FROM long_mem WHERE content LIKE ? LIMIT ?",
|
||||
(f"%{query}%", limit)
|
||||
).fetchall()
|
||||
conn.close()
|
||||
|
||||
for row in rows:
|
||||
meta = json.loads(row[2] or "{}")
|
||||
text = row[1]
|
||||
# Add memory record citation if not already present
|
||||
if "(Memory record:" not in text:
|
||||
text = f"{text} (Memory record: {text})"
|
||||
# Only add if not already found by ChromaDB/Mem0
|
||||
if not any(f["id"] == row[0] for f in found):
|
||||
found.append({
|
||||
"id": row[0],
|
||||
"text": text,
|
||||
"metadata": meta,
|
||||
"created_at": row[3]
|
||||
})
|
||||
logger.info(f"Found {len(found)} total results after SQLite")
|
||||
|
||||
results = found
|
||||
|
||||
# Filter by quality if needed
|
||||
if min_quality > 0:
|
||||
self._log_verbose(f"Found {len(results)} initial results")
|
||||
results = [
|
||||
r for r in results
|
||||
if r.get("metadata", {}).get("quality", 0.0) >= min_quality
|
||||
]
|
||||
self._log_verbose(f"After quality filter: {len(results)} results")
|
||||
|
||||
# Apply relevance cutoff if specified
|
||||
if relevance_cutoff > 0:
|
||||
results = [r for r in results if r.get("score", 1.0) >= relevance_cutoff]
|
||||
logger.info(f"After relevance filter: {len(results)} results")
|
||||
|
||||
return results[:limit]
|
||||
|
||||
def reset_long_term(self):
|
||||
"""Clear local LTM DB, plus Chroma or mem0 if in use."""
|
||||
conn = sqlite3.connect(self.long_db)
|
||||
conn.execute("DELETE FROM long_mem")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
# Mem0 has no universal reset API. Could implement partial or no-op.
|
||||
pass
|
||||
if self.use_rag and hasattr(self, "chroma_client"):
|
||||
self.chroma_client.reset() # entire DB
|
||||
self._init_chroma() # re-init fresh
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Entity Memory Methods
|
||||
# -------------------------------------------------------------------------
|
||||
def store_entity(self, name: str, type_: str, desc: str, relations: str):
|
||||
"""
|
||||
Save entity info in LTM (or mem0/rag).
|
||||
We'll label the metadata type = entity for easy filtering.
|
||||
"""
|
||||
data = f"Entity {name}({type_}): {desc} | relationships: {relations}"
|
||||
self.store_long_term(data, metadata={"category": "entity"})
|
||||
|
||||
def search_entity(self, query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Filter to items that have metadata 'category=entity'.
|
||||
"""
|
||||
all_hits = self.search_long_term(query, limit=20) # gather more
|
||||
ents = []
|
||||
for h in all_hits:
|
||||
meta = h.get("metadata") or {}
|
||||
if meta.get("category") == "entity":
|
||||
ents.append(h)
|
||||
return ents[:limit]
|
||||
|
||||
def reset_entity_only(self):
|
||||
"""
|
||||
If you only want to drop entity items from LTM, you'd do a custom
|
||||
delete from local DB where meta LIKE '%category=entity%'.
|
||||
For brevity, we do a full LTM reset here.
|
||||
"""
|
||||
self.reset_long_term()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# User Memory Methods
|
||||
# -------------------------------------------------------------------------
|
||||
def store_user_memory(self, user_id: str, text: str, extra: Dict[str, Any] = None):
|
||||
"""
|
||||
If mem0 is used, do user-based addition. Otherwise store in LTM with user in metadata.
|
||||
"""
|
||||
meta = {"user_id": user_id}
|
||||
if extra:
|
||||
meta.update(extra)
|
||||
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
self.mem0_client.add(text, user_id=user_id, metadata=meta)
|
||||
else:
|
||||
self.store_long_term(text, metadata=meta)
|
||||
|
||||
def search_user_memory(self, user_id: str, query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
If mem0 is used, pass user_id in. Otherwise fallback to local filter on user in metadata.
|
||||
"""
|
||||
if self.use_mem0 and hasattr(self, "mem0_client"):
|
||||
return self.mem0_client.search(query=query, limit=limit, user_id=user_id)
|
||||
else:
|
||||
hits = self.search_long_term(query, limit=20)
|
||||
filtered = []
|
||||
for h in hits:
|
||||
meta = h.get("metadata", {})
|
||||
if meta.get("user_id") == user_id:
|
||||
filtered.append(h)
|
||||
return filtered[:limit]
|
||||
|
||||
def reset_user_memory(self):
|
||||
"""
|
||||
Clear all user-based info. For simplicity, we do a full LTM reset.
|
||||
Real usage might filter only metadata "user_id".
|
||||
"""
|
||||
self.reset_long_term()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Putting it all Together: Task Finalization
|
||||
# -------------------------------------------------------------------------
|
||||
def finalize_task_output(
|
||||
self,
|
||||
content: str,
|
||||
agent_name: str,
|
||||
quality_score: float,
|
||||
threshold: float = 0.7,
|
||||
metrics: Dict[str, Any] = None,
|
||||
task_id: str = None
|
||||
):
|
||||
"""Store task output in memory with appropriate metadata"""
|
||||
logger.info(f"Finalizing task output: {content[:100]}...")
|
||||
logger.info(f"Agent: {agent_name}, Quality: {quality_score}, Threshold: {threshold}")
|
||||
|
||||
metadata = {
|
||||
"task_id": task_id,
|
||||
"agent": agent_name,
|
||||
"quality": quality_score,
|
||||
"metrics": metrics,
|
||||
"task_type": "output",
|
||||
"stored_at": time.time()
|
||||
}
|
||||
logger.info(f"Prepared metadata: {metadata}")
|
||||
|
||||
# Always store in short-term memory
|
||||
try:
|
||||
logger.info("Storing in short-term memory...")
|
||||
self.store_short_term(
|
||||
text=content,
|
||||
metadata=metadata
|
||||
)
|
||||
logger.info("Successfully stored in short-term memory")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in short-term memory: {e}")
|
||||
|
||||
# Store in long-term memory if quality meets threshold
|
||||
if quality_score >= threshold:
|
||||
try:
|
||||
logger.info(f"Quality score {quality_score} >= {threshold}, storing in long-term memory...")
|
||||
self.store_long_term(
|
||||
text=content,
|
||||
metadata=metadata
|
||||
)
|
||||
logger.info("Successfully stored in long-term memory")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in long-term memory: {e}")
|
||||
else:
|
||||
logger.info(f"Quality score {quality_score} < {threshold}, skipping long-term storage")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Building Context (Short, Long, Entities, User)
|
||||
# -------------------------------------------------------------------------
|
||||
def build_context_for_task(
|
||||
self,
|
||||
task_descr: str,
|
||||
user_id: Optional[str] = None,
|
||||
additional: str = "",
|
||||
max_items: int = 3
|
||||
) -> str:
|
||||
"""
|
||||
Merges relevant short-term, long-term, entity, user memories
|
||||
into a single text block with deduplication and clean formatting.
|
||||
"""
|
||||
q = (task_descr + " " + additional).strip()
|
||||
lines = []
|
||||
seen_contents = set() # Track unique contents
|
||||
|
||||
def normalize_content(content: str) -> str:
|
||||
"""Normalize content for deduplication"""
|
||||
# Extract just the main content without citations for comparison
|
||||
normalized = content.split("(Memory record:")[0].strip()
|
||||
# Keep more characters to reduce false duplicates
|
||||
normalized = ''.join(c.lower() for c in normalized if not c.isspace())
|
||||
return normalized
|
||||
|
||||
def format_content(content: str, max_len: int = 150) -> str:
|
||||
"""Format content with clean truncation at word boundaries"""
|
||||
if not content:
|
||||
return ""
|
||||
|
||||
# Clean up content by removing extra whitespace and newlines
|
||||
content = ' '.join(content.split())
|
||||
|
||||
# If content contains a memory citation, preserve it
|
||||
if "(Memory record:" in content:
|
||||
return content # Keep original citation format
|
||||
|
||||
# Regular content truncation
|
||||
if len(content) <= max_len:
|
||||
return content
|
||||
|
||||
truncate_at = content.rfind(' ', 0, max_len - 3)
|
||||
if truncate_at == -1:
|
||||
truncate_at = max_len - 3
|
||||
return content[:truncate_at] + "..."
|
||||
|
||||
def add_section(title: str, hits: List[Any]) -> None:
|
||||
"""Add a section of memory hits with deduplication"""
|
||||
if not hits:
|
||||
return
|
||||
|
||||
formatted_hits = []
|
||||
for h in hits:
|
||||
content = h.get('text', '') if isinstance(h, dict) else str(h)
|
||||
if not content:
|
||||
continue
|
||||
|
||||
# Keep original format if it has a citation
|
||||
if "(Memory record:" in content:
|
||||
formatted = content
|
||||
else:
|
||||
formatted = format_content(content)
|
||||
|
||||
# Only add if we haven't seen this normalized content before
|
||||
normalized = normalize_content(formatted)
|
||||
if normalized not in seen_contents:
|
||||
seen_contents.add(normalized)
|
||||
formatted_hits.append(formatted)
|
||||
|
||||
if formatted_hits:
|
||||
# Add section header
|
||||
if lines:
|
||||
lines.append("") # Space before new section
|
||||
lines.append(title)
|
||||
lines.append("=" * len(title)) # Underline the title
|
||||
lines.append("") # Space after title
|
||||
|
||||
# Add formatted content with bullet points
|
||||
for content in formatted_hits:
|
||||
lines.append(f" • {content}")
|
||||
|
||||
# Add each section
|
||||
# First get all results
|
||||
short_term = self.search_short_term(q, limit=max_items)
|
||||
long_term = self.search_long_term(q, limit=max_items)
|
||||
entities = self.search_entity(q, limit=max_items)
|
||||
user_mem = self.search_user_memory(user_id, q, limit=max_items) if user_id else []
|
||||
|
||||
# Add sections in order of priority
|
||||
add_section("Short-term Memory Context", short_term)
|
||||
add_section("Long-term Memory Context", long_term)
|
||||
add_section("Entity Context", entities)
|
||||
if user_id:
|
||||
add_section("User Context", user_mem)
|
||||
|
||||
return "\n".join(lines) if lines else ""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Master Reset (Everything)
|
||||
# -------------------------------------------------------------------------
|
||||
def reset_all(self):
|
||||
"""
|
||||
Fully wipes short-term, long-term, and any memory in mem0 or rag.
|
||||
"""
|
||||
self.reset_short_term()
|
||||
self.reset_long_term()
|
||||
# Entities & user memory are stored in LTM or mem0, so no separate step needed.
|
||||
|
||||
def _process_quality_metrics(
|
||||
self,
|
||||
metadata: Dict[str, Any],
|
||||
completeness: float = None,
|
||||
relevance: float = None,
|
||||
clarity: float = None,
|
||||
accuracy: float = None,
|
||||
weights: Dict[str, float] = None,
|
||||
evaluator_quality: float = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Process and store quality metrics in metadata"""
|
||||
metadata = metadata or {}
|
||||
|
||||
# Handle sub-metrics if provided
|
||||
if None not in [completeness, relevance, clarity, accuracy]:
|
||||
metadata.update({
|
||||
"completeness": completeness,
|
||||
"relevance": relevance,
|
||||
"clarity": clarity,
|
||||
"accuracy": accuracy,
|
||||
"quality": self.compute_quality_score(
|
||||
completeness, relevance, clarity, accuracy, weights
|
||||
)
|
||||
})
|
||||
# Handle external evaluator quality if provided
|
||||
elif evaluator_quality is not None:
|
||||
metadata["quality"] = evaluator_quality
|
||||
|
||||
return metadata
|
||||
|
||||
def calculate_quality_metrics(
|
||||
self,
|
||||
output: str,
|
||||
expected_output: str,
|
||||
llm: Optional[str] = None,
|
||||
custom_prompt: Optional[str] = None
|
||||
) -> Dict[str, float]:
|
||||
"""Calculate quality metrics using LLM"""
|
||||
logger.info("Calculating quality metrics for output")
|
||||
logger.info(f"Output: {output[:100]}...")
|
||||
logger.info(f"Expected: {expected_output[:100]}...")
|
||||
|
||||
# Default evaluation prompt
|
||||
default_prompt = f"""
|
||||
Evaluate the following output against expected output.
|
||||
Score each metric from 0.0 to 1.0:
|
||||
- Completeness: Does it address all requirements?
|
||||
- Relevance: Does it match expected output?
|
||||
- Clarity: Is it clear and well-structured?
|
||||
- Accuracy: Is it factually correct?
|
||||
|
||||
Expected: {expected_output}
|
||||
Actual: {output}
|
||||
|
||||
Return ONLY a JSON with these keys: completeness, relevance, clarity, accuracy
|
||||
Example: {{"completeness": 0.95, "relevance": 0.8, "clarity": 0.9, "accuracy": 0.85}}
|
||||
"""
|
||||
|
||||
try:
|
||||
# Use OpenAI client from main.py
|
||||
from ..main import client
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=llm or "gpt-4o",
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": custom_prompt or default_prompt
|
||||
}],
|
||||
response_format={"type": "json_object"},
|
||||
temperature=0.3
|
||||
)
|
||||
|
||||
metrics = json.loads(response.choices[0].message.content)
|
||||
|
||||
# Validate metrics
|
||||
required = ["completeness", "relevance", "clarity", "accuracy"]
|
||||
if not all(k in metrics for k in required):
|
||||
raise ValueError("Missing required metrics in LLM response")
|
||||
|
||||
logger.info(f"Calculated metrics: {metrics}")
|
||||
return metrics
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating metrics: {e}")
|
||||
return {
|
||||
"completeness": 0.0,
|
||||
"relevance": 0.0,
|
||||
"clarity": 0.0,
|
||||
"accuracy": 0.0
|
||||
}
|
||||
|
||||
def store_quality(
|
||||
self,
|
||||
text: str,
|
||||
quality_score: float,
|
||||
task_id: Optional[str] = None,
|
||||
iteration: Optional[int] = None,
|
||||
metrics: Optional[Dict[str, float]] = None,
|
||||
memory_type: Literal["short", "long"] = "long"
|
||||
) -> None:
|
||||
"""Store quality metrics in memory"""
|
||||
logger.info(f"Attempting to store in {memory_type} memory: {text[:100]}...")
|
||||
|
||||
metadata = {
|
||||
"quality": quality_score,
|
||||
"task_id": task_id,
|
||||
"iteration": iteration
|
||||
}
|
||||
|
||||
if metrics:
|
||||
metadata.update({
|
||||
k: v for k, v in metrics.items() # Remove metric_ prefix
|
||||
})
|
||||
|
||||
logger.info(f"With metadata: {metadata}")
|
||||
|
||||
try:
|
||||
if memory_type == "short":
|
||||
self.store_short_term(text, metadata=metadata)
|
||||
logger.info("Successfully stored in short-term memory")
|
||||
else:
|
||||
self.store_long_term(text, metadata=metadata)
|
||||
logger.info("Successfully stored in long-term memory")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store in memory: {e}")
|
||||
|
||||
def search_with_quality(
|
||||
self,
|
||||
query: str,
|
||||
min_quality: float = 0.0,
|
||||
memory_type: Literal["short", "long"] = "long",
|
||||
limit: int = 5
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Search with quality filter"""
|
||||
logger.info(f"Searching {memory_type} memory for: {query}")
|
||||
logger.info(f"Min quality: {min_quality}")
|
||||
|
||||
search_func = (
|
||||
self.search_short_term if memory_type == "short"
|
||||
else self.search_long_term
|
||||
)
|
||||
|
||||
results = search_func(query, limit=limit)
|
||||
logger.info(f"Found {len(results)} initial results")
|
||||
|
||||
filtered = [
|
||||
r for r in results
|
||||
if r.get("metadata", {}).get("quality", 0.0) >= min_quality
|
||||
]
|
||||
logger.info(f"After quality filter: {len(filtered)} results")
|
||||
|
||||
return filtered
|
||||
@@ -10,14 +10,16 @@ class LoopItems(BaseModel):
|
||||
items: List[Any]
|
||||
|
||||
class Process:
|
||||
def __init__(self, tasks: Dict[str, Task], agents: List[Agent], manager_llm: Optional[str] = None, verbose: bool = False):
|
||||
def __init__(self, tasks: Dict[str, Task], agents: List[Agent], manager_llm: Optional[str] = None, verbose: bool = False, max_iter: int = 10):
|
||||
self.tasks = tasks
|
||||
self.agents = agents
|
||||
self.manager_llm = manager_llm
|
||||
self.verbose = verbose
|
||||
self.max_iter = max_iter
|
||||
|
||||
async def aworkflow(self) -> AsyncGenerator[str, None]:
|
||||
"""Async version of workflow method"""
|
||||
current_iter = 0 # Track how many times we've looped
|
||||
# Build workflow relationships first
|
||||
for task in self.tasks.values():
|
||||
if task.next_tasks:
|
||||
@@ -41,7 +43,12 @@ class Process:
|
||||
visited_tasks = set()
|
||||
loop_data = {} # Store loop-specific data
|
||||
|
||||
while current_task and current_task.id not in visited_tasks:
|
||||
while current_task:
|
||||
current_iter += 1
|
||||
if current_iter > self.max_iter:
|
||||
logging.info(f"Max iteration limit {self.max_iter} reached, ending workflow.")
|
||||
break
|
||||
|
||||
task_id = current_task.id
|
||||
logging.info(f"Executing workflow task: {current_task.name if current_task.name else task_id}")
|
||||
|
||||
@@ -111,6 +118,11 @@ Return a JSON object with an 'items' array containing the items to process.
|
||||
yield task_id
|
||||
visited_tasks.add(task_id)
|
||||
|
||||
# Reset completed task to "not started" so it can run again
|
||||
if self.tasks[task_id].status == "completed":
|
||||
logging.debug(f"Task {task_id} was completed, resetting to 'not started' for next iteration.")
|
||||
self.tasks[task_id].status = "not started"
|
||||
|
||||
# Handle loop progression
|
||||
if current_task.task_type == "loop":
|
||||
loop_key = f"loop_{current_task.name}"
|
||||
@@ -130,22 +142,28 @@ Return a JSON object with an 'items' array containing the items to process.
|
||||
|
||||
# Determine next task based on result
|
||||
next_task = None
|
||||
if current_task.result:
|
||||
if current_task and current_task.result:
|
||||
if current_task.task_type in ["decision", "loop"]:
|
||||
result = current_task.result.raw.lower()
|
||||
# Check conditions
|
||||
for condition, tasks in current_task.condition.items():
|
||||
if condition.lower() in result and tasks:
|
||||
next_task_name = tasks[0]
|
||||
if condition.lower() in result:
|
||||
# Handle both list and direct string values
|
||||
task_value = tasks[0] if isinstance(tasks, list) else tasks
|
||||
if not task_value or task_value == "exit": # If empty or explicit exit
|
||||
logging.info("Workflow exit condition met, ending workflow")
|
||||
current_task = None
|
||||
break
|
||||
next_task_name = task_value
|
||||
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
|
||||
# For loops, allow revisiting the same task
|
||||
if next_task and next_task.id == current_task.id:
|
||||
visited_tasks.discard(current_task.id)
|
||||
break
|
||||
|
||||
if not next_task and current_task.next_tasks:
|
||||
next_task_name = current_task.next_tasks[0]
|
||||
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
|
||||
if not next_task and current_task and current_task.next_tasks:
|
||||
next_task_name = current_task.next_tasks[0]
|
||||
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
|
||||
|
||||
current_task = next_task
|
||||
if not current_task:
|
||||
@@ -285,6 +303,7 @@ Provide a JSON with the structure:
|
||||
|
||||
def workflow(self):
|
||||
"""Synchronous version of workflow method"""
|
||||
current_iter = 0 # Track how many times we've looped
|
||||
# Build workflow relationships first
|
||||
for task in self.tasks.values():
|
||||
if task.next_tasks:
|
||||
@@ -308,7 +327,12 @@ Provide a JSON with the structure:
|
||||
visited_tasks = set()
|
||||
loop_data = {} # Store loop-specific data
|
||||
|
||||
while current_task and current_task.id not in visited_tasks:
|
||||
while current_task:
|
||||
current_iter += 1
|
||||
if current_iter > self.max_iter:
|
||||
logging.info(f"Max iteration limit {self.max_iter} reached, ending workflow.")
|
||||
break
|
||||
|
||||
task_id = current_task.id
|
||||
logging.info(f"Executing workflow task: {current_task.name if current_task.name else task_id}")
|
||||
|
||||
@@ -372,6 +396,11 @@ Return a JSON object with an 'items' array containing the items to process.
|
||||
yield task_id
|
||||
visited_tasks.add(task_id)
|
||||
|
||||
# Reset completed task to "not started" so it can run again
|
||||
if self.tasks[task_id].status == "completed":
|
||||
logging.debug(f"Task {task_id} was completed, resetting to 'not started' for next iteration.")
|
||||
self.tasks[task_id].status = "not started"
|
||||
|
||||
# Handle loop progression
|
||||
if current_task.task_type == "loop":
|
||||
loop_key = f"loop_{current_task.name}"
|
||||
@@ -391,22 +420,28 @@ Return a JSON object with an 'items' array containing the items to process.
|
||||
|
||||
# Determine next task based on result
|
||||
next_task = None
|
||||
if current_task.result:
|
||||
if current_task and current_task.result:
|
||||
if current_task.task_type in ["decision", "loop"]:
|
||||
result = current_task.result.raw.lower()
|
||||
# Check conditions
|
||||
for condition, tasks in current_task.condition.items():
|
||||
if condition.lower() in result and tasks:
|
||||
next_task_name = tasks[0]
|
||||
if condition.lower() in result:
|
||||
# Handle both list and direct string values
|
||||
task_value = tasks[0] if isinstance(tasks, list) else tasks
|
||||
if not task_value or task_value == "exit": # If empty or explicit exit
|
||||
logging.info("Workflow exit condition met, ending workflow")
|
||||
current_task = None
|
||||
break
|
||||
next_task_name = task_value
|
||||
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
|
||||
# For loops, allow revisiting the same task
|
||||
if next_task and next_task.id == current_task.id:
|
||||
visited_tasks.discard(current_task.id)
|
||||
break
|
||||
|
||||
if not next_task and current_task.next_tasks:
|
||||
next_task_name = current_task.next_tasks[0]
|
||||
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
|
||||
if not next_task and current_task and current_task.next_tasks:
|
||||
next_task_name = current_task.next_tasks[0]
|
||||
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
|
||||
|
||||
current_task = next_task
|
||||
if not current_task:
|
||||
|
||||
@@ -5,12 +5,17 @@ from pydantic import BaseModel
|
||||
from ..main import TaskOutput
|
||||
from ..agent.agent import Agent
|
||||
import uuid
|
||||
import os
|
||||
import time
|
||||
|
||||
# Set up logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Task:
|
||||
def __init__(
|
||||
self,
|
||||
description: str,
|
||||
expected_output: str,
|
||||
expected_output: Optional[str] = None,
|
||||
agent: Optional[Agent] = None,
|
||||
name: Optional[str] = None,
|
||||
tools: Optional[List[Any]] = None,
|
||||
@@ -30,12 +35,14 @@ class Task:
|
||||
task_type: str = "task",
|
||||
condition: Optional[Dict[str, List[str]]] = None,
|
||||
is_start: bool = False,
|
||||
loop_state: Optional[Dict[str, Union[str, int]]] = None
|
||||
loop_state: Optional[Dict[str, Union[str, int]]] = None,
|
||||
memory=None,
|
||||
quality_check=True
|
||||
):
|
||||
self.id = str(uuid.uuid4()) if id is None else str(id)
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.expected_output = expected_output
|
||||
self.expected_output = expected_output if expected_output is not None else "Complete the task successfully"
|
||||
self.agent = agent
|
||||
self.tools = tools if tools else []
|
||||
self.context = context if context else []
|
||||
@@ -54,6 +61,21 @@ class Task:
|
||||
self.condition = condition if condition else {}
|
||||
self.is_start = is_start
|
||||
self.loop_state = loop_state if loop_state else {}
|
||||
self.memory = memory
|
||||
self.quality_check = quality_check
|
||||
|
||||
# Set logger level based on config verbose level
|
||||
verbose = self.config.get("verbose", 0)
|
||||
if verbose >= 5:
|
||||
logger.setLevel(logging.INFO)
|
||||
else:
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
# Also set third-party loggers to WARNING
|
||||
logging.getLogger('chromadb').setLevel(logging.WARNING)
|
||||
logging.getLogger('openai').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpx').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpcore').setLevel(logging.WARNING)
|
||||
|
||||
if self.output_json and self.output_pydantic:
|
||||
raise ValueError("Only one output type can be defined")
|
||||
@@ -64,11 +86,137 @@ class Task:
|
||||
def __str__(self):
|
||||
return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"
|
||||
|
||||
def initialize_memory(self):
|
||||
"""Initialize memory if config exists but memory doesn't"""
|
||||
if not self.memory and self.config.get('memory_config'):
|
||||
try:
|
||||
from ..memory.memory import Memory
|
||||
logger.info(f"Task {self.id}: Initializing memory from config: {self.config['memory_config']}")
|
||||
self.memory = Memory(config=self.config['memory_config'])
|
||||
logger.info(f"Task {self.id}: Memory initialized successfully")
|
||||
|
||||
# Verify database was created
|
||||
if os.path.exists(self.config['memory_config']['storage']['path']):
|
||||
logger.info(f"Task {self.id}: Memory database exists after initialization")
|
||||
else:
|
||||
logger.error(f"Task {self.id}: Failed to create memory database!")
|
||||
return self.memory
|
||||
except Exception as e:
|
||||
logger.error(f"Task {self.id}: Failed to initialize memory: {e}")
|
||||
logger.exception(e)
|
||||
return None
|
||||
|
||||
def store_in_memory(self, content: str, agent_name: str = None, task_id: str = None):
|
||||
"""Store content in memory with metadata"""
|
||||
if self.memory:
|
||||
try:
|
||||
logger.info(f"Task {self.id}: Storing content in memory...")
|
||||
self.memory.store_long_term(
|
||||
text=content,
|
||||
metadata={
|
||||
"agent_name": agent_name or "Agent",
|
||||
"task_id": task_id or self.id,
|
||||
"timestamp": time.time()
|
||||
}
|
||||
)
|
||||
logger.info(f"Task {self.id}: Content stored in memory")
|
||||
except Exception as e:
|
||||
logger.error(f"Task {self.id}: Failed to store content in memory: {e}")
|
||||
logger.exception(e)
|
||||
|
||||
async def execute_callback(self, task_output: TaskOutput) -> None:
|
||||
"""Execute the callback function, handling both sync and async callbacks"""
|
||||
"""Execute callback and store quality metrics if enabled"""
|
||||
logger.info(f"Task {self.id}: execute_callback called")
|
||||
logger.info(f"Quality check enabled: {self.quality_check}")
|
||||
|
||||
# Initialize memory if not already initialized
|
||||
if not self.memory:
|
||||
self.memory = self.initialize_memory()
|
||||
|
||||
logger.info(f"Memory object exists: {self.memory is not None}")
|
||||
if self.memory:
|
||||
logger.info(f"Memory config: {self.memory.cfg}")
|
||||
# Store task output in memory
|
||||
try:
|
||||
logger.info(f"Task {self.id}: Storing task output in memory...")
|
||||
self.store_in_memory(
|
||||
content=task_output.raw,
|
||||
agent_name=self.agent.name if self.agent else "Agent",
|
||||
task_id=self.id
|
||||
)
|
||||
logger.info(f"Task {self.id}: Task output stored in memory")
|
||||
except Exception as e:
|
||||
logger.error(f"Task {self.id}: Failed to store task output in memory: {e}")
|
||||
logger.exception(e)
|
||||
|
||||
logger.info(f"Task output: {task_output.raw[:100]}...")
|
||||
|
||||
if self.quality_check and self.memory:
|
||||
try:
|
||||
logger.info(f"Task {self.id}: Starting memory operations")
|
||||
logger.info(f"Task {self.id}: Calculating quality metrics for output: {task_output.raw[:100]}...")
|
||||
|
||||
# Get quality metrics from LLM
|
||||
metrics = self.memory.calculate_quality_metrics(
|
||||
task_output.raw,
|
||||
self.expected_output
|
||||
)
|
||||
logger.info(f"Task {self.id}: Quality metrics calculated: {metrics}")
|
||||
|
||||
quality_score = metrics.get("accuracy", 0.0)
|
||||
logger.info(f"Task {self.id}: Quality score: {quality_score}")
|
||||
|
||||
# Store in both short and long-term memory with higher threshold
|
||||
logger.info(f"Task {self.id}: Finalizing task output in memory...")
|
||||
self.memory.finalize_task_output(
|
||||
content=task_output.raw,
|
||||
agent_name=self.agent.name if self.agent else "Agent",
|
||||
quality_score=quality_score,
|
||||
threshold=0.7, # Only high quality outputs in long-term memory
|
||||
metrics=metrics,
|
||||
task_id=self.id
|
||||
)
|
||||
logger.info(f"Task {self.id}: Finalized task output in memory")
|
||||
|
||||
# Store quality metrics separately
|
||||
logger.info(f"Task {self.id}: Storing quality metrics...")
|
||||
self.memory.store_quality(
|
||||
text=task_output.raw,
|
||||
quality_score=quality_score,
|
||||
task_id=self.id,
|
||||
metrics=metrics
|
||||
)
|
||||
|
||||
# Store in both short and long-term memory with higher threshold
|
||||
self.memory.finalize_task_output(
|
||||
content=task_output.raw,
|
||||
agent_name=self.agent.name if self.agent else "Agent",
|
||||
quality_score=quality_score,
|
||||
threshold=0.7 # Only high quality outputs in long-term memory
|
||||
)
|
||||
|
||||
# Build context for next tasks
|
||||
if self.next_tasks:
|
||||
logger.info(f"Task {self.id}: Building context for next tasks...")
|
||||
context = self.memory.build_context_for_task(
|
||||
task_descr=task_output.raw,
|
||||
max_items=5
|
||||
)
|
||||
logger.info(f"Task {self.id}: Built context for next tasks: {len(context)} items")
|
||||
|
||||
logger.info(f"Task {self.id}: Memory operations complete")
|
||||
except Exception as e:
|
||||
logger.error(f"Task {self.id}: Failed to process memory operations: {e}")
|
||||
logger.exception(e) # Print full stack trace
|
||||
# Continue execution even if memory operations fail
|
||||
|
||||
# Execute original callback
|
||||
if self.callback:
|
||||
if asyncio.iscoroutinefunction(self.callback):
|
||||
await self.callback(task_output)
|
||||
else:
|
||||
loop = asyncio.get_event_loop()
|
||||
await loop.run_in_executor(None, self.callback, task_output)
|
||||
try:
|
||||
if asyncio.iscoroutinefunction(self.callback):
|
||||
await self.callback(task_output)
|
||||
else:
|
||||
self.callback(task_output)
|
||||
except Exception as e:
|
||||
logger.error(f"Task {self.id}: Failed to execute callback: {e}")
|
||||
logger.exception(e)
|
||||
@@ -0,0 +1,149 @@
|
||||
# PraisonAI Tools Guide
|
||||
|
||||
Welcome to the PraisonAI Tools directory! This guide will help you understand how our tools work and how to create new ones, whether you're a beginner or an experienced programmer.
|
||||
|
||||
## What is a Tool?
|
||||
|
||||
A tool is a piece of code that helps our AI agents perform specific tasks. Think of tools as special abilities that we give to our agents. For example:
|
||||
- An internet search tool lets agents search the web
|
||||
- A stock market tool lets agents check stock prices
|
||||
- A weather tool lets agents check the weather
|
||||
|
||||
## Creating New Tools: The Two Approaches
|
||||
|
||||
### 1. Function-Based Approach (Simple Tools)
|
||||
|
||||
Best for simple tools that do one specific thing. Like a calculator that just adds numbers.
|
||||
|
||||
**When to use:**
|
||||
- Tool does one simple task
|
||||
- Doesn't need to remember information between uses
|
||||
- Doesn't need to share information with other tools
|
||||
- Quick, one-time operations
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
def internet_search(query: str):
|
||||
# Search the internet and return results
|
||||
return search_results
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
from praisonaiagents.tools import internet_search
|
||||
|
||||
results = internet_search("AI news")
|
||||
```
|
||||
|
||||
### 2. Class-Based Approach (Complex Tools)
|
||||
|
||||
Best for tools that do multiple related things or need to remember information. Like a smart calculator that remembers your previous calculations and can do many different math operations.
|
||||
|
||||
**When to use:**
|
||||
- Tool has multiple related functions
|
||||
- Needs to remember or share information
|
||||
- Needs to manage resources efficiently
|
||||
- Has complex setup requirements
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
class StockTools:
|
||||
def get_stock_price(self, symbol):
|
||||
# Get current stock price
|
||||
return price
|
||||
|
||||
def get_stock_info(self, symbol):
|
||||
# Get detailed stock information
|
||||
return info
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
from praisonaiagents.tools import get_stock_price, get_stock_info
|
||||
|
||||
price = get_stock_price("AAPL")
|
||||
info = get_stock_info("AAPL")
|
||||
```
|
||||
|
||||
## How to Choose Your Approach
|
||||
|
||||
Ask yourself these questions:
|
||||
|
||||
1. **Is your tool doing one simple thing?**
|
||||
- Yes → Use Function-Based Approach
|
||||
- No → Consider Class-Based Approach
|
||||
|
||||
2. **Does your tool need to remember information?**
|
||||
- Yes → Use Class-Based Approach
|
||||
- No → Use Function-Based Approach
|
||||
|
||||
3. **Are your tool's operations related to each other?**
|
||||
- Yes → Use Class-Based Approach
|
||||
- No → Use Function-Based Approach
|
||||
|
||||
4. **Does your tool need to manage resources efficiently?**
|
||||
- Yes → Use Class-Based Approach
|
||||
- No → Use Function-Based Approach
|
||||
|
||||
## Real-World Examples
|
||||
|
||||
### Internet Search Tool (Function-Based)
|
||||
- Does one thing: searches the internet
|
||||
- Doesn't need to remember previous searches
|
||||
- Each search is independent
|
||||
- Simple input/output operation
|
||||
|
||||
### Stock Market Tool (Class-Based)
|
||||
- Does multiple things: check prices, get company info, get historical data
|
||||
- Remembers stock information to avoid repeated downloads
|
||||
- Operations are related (all about stocks)
|
||||
- Manages connections efficiently
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Choose Your Approach** based on the guidelines above
|
||||
|
||||
2. **Create Your Tool File**:
|
||||
- Name it descriptively (e.g., `weather_tools.py`)
|
||||
- Place it in the `praisonaiagents/tools` directory
|
||||
|
||||
3. **Write Your Tool**:
|
||||
- Add clear documentation
|
||||
- Include type hints for better understanding
|
||||
- Handle errors gracefully
|
||||
|
||||
4. **Test Your Tool**:
|
||||
- Make sure it works as expected
|
||||
- Test error cases
|
||||
- Check performance
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Documentation**:
|
||||
- Explain what your tool does
|
||||
- Provide examples
|
||||
- List any requirements
|
||||
|
||||
2. **Error Handling**:
|
||||
- Always handle possible errors
|
||||
- Return helpful error messages
|
||||
- Don't let your tool crash
|
||||
|
||||
3. **Performance**:
|
||||
- Keep it efficient
|
||||
- Don't waste resources
|
||||
- Cache when helpful
|
||||
|
||||
4. **User-Friendly**:
|
||||
- Make it easy to use
|
||||
- Use clear function/method names
|
||||
- Keep it simple
|
||||
|
||||
## Need Help?
|
||||
|
||||
- Check existing tools for examples
|
||||
- Ask in our community
|
||||
- Read the documentation
|
||||
- Don't hesitate to ask questions!
|
||||
|
||||
Remember: The goal is to make tools that are easy to use and maintain. Choose the approach that makes the most sense for your specific tool's needs.
|
||||
@@ -1,4 +1,178 @@
|
||||
"""Tools package for PraisonAI Agents"""
|
||||
from .tools import Tools
|
||||
from importlib import import_module
|
||||
from typing import Any
|
||||
|
||||
__all__ = ['Tools']
|
||||
# Map of function names to their module and class (if any)
|
||||
TOOL_MAPPINGS = {
|
||||
# Direct functions
|
||||
'internet_search': ('.duckduckgo_tools', None),
|
||||
'duckduckgo': ('.duckduckgo_tools', None),
|
||||
|
||||
# arXiv Tools
|
||||
'search_arxiv': ('.arxiv_tools', None),
|
||||
'get_arxiv_paper': ('.arxiv_tools', None),
|
||||
'get_papers_by_author': ('.arxiv_tools', None),
|
||||
'get_papers_by_category': ('.arxiv_tools', None),
|
||||
'arxiv_tools': ('.arxiv_tools', None),
|
||||
|
||||
# Wikipedia Tools
|
||||
'wiki_search': ('.wikipedia_tools', None),
|
||||
'wiki_summary': ('.wikipedia_tools', None),
|
||||
'wiki_page': ('.wikipedia_tools', None),
|
||||
'wiki_random': ('.wikipedia_tools', None),
|
||||
'wiki_language': ('.wikipedia_tools', None),
|
||||
'wikipedia_tools': ('.wikipedia_tools', None),
|
||||
|
||||
# Newspaper Tools
|
||||
'get_article': ('.newspaper_tools', None),
|
||||
'get_news_sources': ('.newspaper_tools', None),
|
||||
'get_articles_from_source': ('.newspaper_tools', None),
|
||||
'get_trending_topics': ('.newspaper_tools', None),
|
||||
'newspaper_tools': ('.newspaper_tools', None),
|
||||
|
||||
# Spider Tools
|
||||
'scrape_page': ('.spider_tools', None),
|
||||
'extract_links': ('.spider_tools', None),
|
||||
'crawl': ('.spider_tools', None),
|
||||
'extract_text': ('.spider_tools', None),
|
||||
'spider_tools': ('.spider_tools', None),
|
||||
|
||||
# DuckDB Tools
|
||||
'query': ('.duckdb_tools', None),
|
||||
'create_table': ('.duckdb_tools', None),
|
||||
'load_data': ('.duckdb_tools', None),
|
||||
'export_data': ('.duckdb_tools', None),
|
||||
'get_table_info': ('.duckdb_tools', None),
|
||||
'analyze_data': ('.duckdb_tools', None),
|
||||
'duckdb_tools': ('.duckdb_tools', None),
|
||||
|
||||
# Shell Tools
|
||||
'execute_command': ('.shell_tools', None),
|
||||
'list_processes': ('.shell_tools', None),
|
||||
'kill_process': ('.shell_tools', None),
|
||||
'get_system_info': ('.shell_tools', None),
|
||||
'shell_tools': ('.shell_tools', None),
|
||||
|
||||
# Calculator Tools
|
||||
'evaluate': ('.calculator_tools', None),
|
||||
'solve_equation': ('.calculator_tools', None),
|
||||
'convert_units': ('.calculator_tools', None),
|
||||
'calculate_statistics': ('.calculator_tools', None),
|
||||
'calculate_financial': ('.calculator_tools', None),
|
||||
'calculator_tools': ('.calculator_tools', None),
|
||||
|
||||
# Class methods from YFinance
|
||||
'get_stock_price': ('.yfinance_tools', 'YFinanceTools'),
|
||||
'get_stock_info': ('.yfinance_tools', 'YFinanceTools'),
|
||||
'get_historical_data': ('.yfinance_tools', 'YFinanceTools'),
|
||||
'yfinance': ('.yfinance_tools', 'YFinanceTools'),
|
||||
|
||||
# File Tools
|
||||
'read_file': ('.file_tools', 'FileTools'),
|
||||
'write_file': ('.file_tools', 'FileTools'),
|
||||
'list_files': ('.file_tools', 'FileTools'),
|
||||
'get_file_info': ('.file_tools', 'FileTools'),
|
||||
'copy_file': ('.file_tools', 'FileTools'),
|
||||
'move_file': ('.file_tools', 'FileTools'),
|
||||
'delete_file': ('.file_tools', 'FileTools'),
|
||||
'file_tools': ('.file_tools', 'FileTools'),
|
||||
|
||||
# CSV Tools
|
||||
'read_csv': ('.csv_tools', 'CSVTools'),
|
||||
'write_csv': ('.csv_tools', 'CSVTools'),
|
||||
'merge_csv': ('.csv_tools', 'CSVTools'),
|
||||
'analyze_csv': ('.csv_tools', 'CSVTools'),
|
||||
'split_csv': ('.csv_tools', 'CSVTools'),
|
||||
'csv_tools': ('.csv_tools', 'CSVTools'),
|
||||
|
||||
# JSON Tools
|
||||
'read_json': ('.json_tools', 'JSONTools'),
|
||||
'write_json': ('.json_tools', 'JSONTools'),
|
||||
'merge_json': ('.json_tools', 'JSONTools'),
|
||||
'validate_json': ('.json_tools', 'JSONTools'),
|
||||
'analyze_json': ('.json_tools', 'JSONTools'),
|
||||
'transform_json': ('.json_tools', 'JSONTools'),
|
||||
'json_tools': ('.json_tools', 'JSONTools'),
|
||||
|
||||
# Excel Tools
|
||||
'read_excel': ('.excel_tools', 'ExcelTools'),
|
||||
'write_excel': ('.excel_tools', 'ExcelTools'),
|
||||
'merge_excel': ('.excel_tools', 'ExcelTools'),
|
||||
'create_chart': ('.excel_tools', 'ExcelTools'),
|
||||
'add_chart_to_sheet': ('.excel_tools', 'ExcelTools'),
|
||||
'excel_tools': ('.excel_tools', 'ExcelTools'),
|
||||
|
||||
# XML Tools
|
||||
'read_xml': ('.xml_tools', 'XMLTools'),
|
||||
'write_xml': ('.xml_tools', 'XMLTools'),
|
||||
'transform_xml': ('.xml_tools', 'XMLTools'),
|
||||
'validate_xml': ('.xml_tools', 'XMLTools'),
|
||||
'xml_to_dict': ('.xml_tools', 'XMLTools'),
|
||||
'dict_to_xml': ('.xml_tools', 'XMLTools'),
|
||||
'xpath_query': ('.xml_tools', 'XMLTools'),
|
||||
'xml_tools': ('.xml_tools', 'XMLTools'),
|
||||
|
||||
# YAML Tools
|
||||
'read_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'write_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'merge_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'validate_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'analyze_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'transform_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'yaml_tools': ('.yaml_tools', 'YAMLTools'),
|
||||
|
||||
# Python Tools
|
||||
'execute_code': ('.python_tools', 'PythonTools'),
|
||||
'analyze_code': ('.python_tools', 'PythonTools'),
|
||||
'format_code': ('.python_tools', 'PythonTools'),
|
||||
'lint_code': ('.python_tools', 'PythonTools'),
|
||||
'disassemble_code': ('.python_tools', 'PythonTools'),
|
||||
'python_tools': ('.python_tools', 'PythonTools'),
|
||||
|
||||
# Pandas Tools
|
||||
'filter_data': ('.pandas_tools', 'PandasTools'),
|
||||
'get_summary': ('.pandas_tools', 'PandasTools'),
|
||||
'group_by': ('.pandas_tools', 'PandasTools'),
|
||||
'pivot_table': ('.pandas_tools', 'PandasTools'),
|
||||
'pandas_tools': ('.pandas_tools', 'PandasTools'),
|
||||
}
|
||||
|
||||
_instances = {} # Cache for class instances
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
"""Smart lazy loading of tools with class method support."""
|
||||
if name not in TOOL_MAPPINGS:
|
||||
raise AttributeError(f"module '{__package__}' has no attribute '{name}'")
|
||||
|
||||
module_path, class_name = TOOL_MAPPINGS[name]
|
||||
|
||||
if class_name is None:
|
||||
# Direct function import
|
||||
module = import_module(module_path, __package__)
|
||||
if name in [
|
||||
'duckduckgo', 'internet_search',
|
||||
'search_arxiv', 'get_arxiv_paper', 'get_papers_by_author', 'get_papers_by_category',
|
||||
'wiki_search', 'wiki_summary', 'wiki_page', 'wiki_random', 'wiki_language',
|
||||
'get_article', 'get_news_sources', 'get_articles_from_source', 'get_trending_topics',
|
||||
'scrape_page', 'extract_links', 'crawl', 'extract_text',
|
||||
'query', 'create_table', 'load_data', 'export_data', 'get_table_info', 'analyze_data',
|
||||
'execute_command', 'list_processes', 'kill_process', 'get_system_info',
|
||||
'evaluate', 'solve_equation', 'convert_units', 'calculate_statistics', 'calculate_financial'
|
||||
]:
|
||||
return getattr(module, name)
|
||||
if name in ['file_tools', 'pandas_tools', 'wikipedia_tools',
|
||||
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools']:
|
||||
return module # Returns the callable module
|
||||
return getattr(module, name)
|
||||
else:
|
||||
# Class method import
|
||||
if class_name not in _instances:
|
||||
module = import_module(module_path, __package__)
|
||||
class_ = getattr(module, class_name)
|
||||
_instances[class_name] = class_()
|
||||
|
||||
# Get the method and bind it to the instance
|
||||
method = getattr(_instances[class_name], name)
|
||||
return method
|
||||
|
||||
__all__ = list(TOOL_MAPPINGS.keys())
|
||||
@@ -0,0 +1,292 @@
|
||||
"""Tools for searching and retrieving papers from arXiv.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import arxiv_tools
|
||||
papers = arxiv_tools.search("quantum computing")
|
||||
paper = arxiv_tools.get_paper("2401.00123")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import search_arxiv, get_arxiv_paper
|
||||
papers = search_arxiv("quantum computing")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any
|
||||
from importlib import util
|
||||
import json
|
||||
|
||||
# Map sort criteria to arxiv.SortCriterion
|
||||
SORT_CRITERIA = {
|
||||
"relevance": "Relevance",
|
||||
"lastupdateddate": "LastUpdatedDate",
|
||||
"submitteddate": "SubmittedDate"
|
||||
}
|
||||
|
||||
# Map sort order to arxiv.SortOrder
|
||||
SORT_ORDER = {
|
||||
"ascending": "Ascending",
|
||||
"descending": "Descending"
|
||||
}
|
||||
|
||||
class ArxivTools:
|
||||
"""Tools for searching and retrieving papers from arXiv."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize ArxivTools and check for arxiv package."""
|
||||
self._check_arxiv()
|
||||
|
||||
def _check_arxiv(self):
|
||||
"""Check if arxiv package is installed."""
|
||||
if util.find_spec("arxiv") is None:
|
||||
raise ImportError("arxiv package is not available. Please install it using: pip install arxiv")
|
||||
global arxiv
|
||||
import arxiv
|
||||
|
||||
def search(
|
||||
self,
|
||||
query: str,
|
||||
max_results: int = 10,
|
||||
sort_by: str = "relevance",
|
||||
sort_order: str = "descending",
|
||||
include_fields: Optional[List[str]] = None
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""
|
||||
Search arXiv for papers matching the query.
|
||||
|
||||
Args:
|
||||
query: Search query (e.g., "quantum computing", "author:Einstein")
|
||||
max_results: Maximum number of results to return
|
||||
sort_by: Sort results by ("relevance", "lastUpdatedDate", "submittedDate")
|
||||
sort_order: Sort order ("ascending" or "descending")
|
||||
include_fields: List of fields to include in results. If None, includes all.
|
||||
Available fields: ["title", "authors", "summary", "comment", "journal_ref",
|
||||
"doi", "primary_category", "categories", "links"]
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: List of papers or error dict
|
||||
"""
|
||||
try:
|
||||
import arxiv
|
||||
|
||||
# Configure search client
|
||||
client = arxiv.Client()
|
||||
|
||||
# Map sort criteria
|
||||
sort_by_enum = getattr(arxiv.SortCriterion, SORT_CRITERIA[sort_by.lower()])
|
||||
sort_order_enum = getattr(arxiv.SortOrder, SORT_ORDER[sort_order.lower()])
|
||||
|
||||
# Build search query
|
||||
search = arxiv.Search(
|
||||
query=query,
|
||||
max_results=max_results,
|
||||
sort_by=sort_by_enum,
|
||||
sort_order=sort_order_enum
|
||||
)
|
||||
|
||||
# Execute search
|
||||
results = []
|
||||
for result in client.results(search):
|
||||
# Convert to dict with selected fields
|
||||
paper = self._result_to_dict(result, include_fields)
|
||||
results.append(paper)
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
error_msg = f"Error searching arXiv: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_paper(
|
||||
self,
|
||||
paper_id: str,
|
||||
include_fields: Optional[List[str]] = None
|
||||
) -> Union[Dict[str, Any], Dict[str, str]]:
|
||||
"""
|
||||
Get details of a specific paper by its arXiv ID.
|
||||
|
||||
Args:
|
||||
paper_id: arXiv paper ID (e.g., "2401.00123")
|
||||
include_fields: List of fields to include in results. If None, includes all.
|
||||
Available fields: ["title", "authors", "summary", "comment", "journal_ref",
|
||||
"doi", "primary_category", "categories", "links"]
|
||||
|
||||
Returns:
|
||||
Dict: Paper details or error dict
|
||||
"""
|
||||
try:
|
||||
import arxiv
|
||||
|
||||
# Configure client
|
||||
client = arxiv.Client()
|
||||
|
||||
# Get paper by ID
|
||||
search = arxiv.Search(id_list=[paper_id])
|
||||
results = list(client.results(search))
|
||||
|
||||
if not results:
|
||||
return {"error": f"Paper with ID {paper_id} not found"}
|
||||
|
||||
# Convert to dict with selected fields
|
||||
paper = self._result_to_dict(results[0], include_fields)
|
||||
return paper
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting paper {paper_id}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_papers_by_author(
|
||||
self,
|
||||
author: str,
|
||||
max_results: int = 10,
|
||||
sort_by: str = "submittedDate",
|
||||
sort_order: str = "descending",
|
||||
include_fields: Optional[List[str]] = None
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""
|
||||
Get papers by a specific author.
|
||||
|
||||
Args:
|
||||
author: Author name (e.g., "Einstein")
|
||||
max_results: Maximum number of results to return
|
||||
sort_by: Sort results by ("relevance", "lastUpdatedDate", "submittedDate")
|
||||
sort_order: Sort order ("ascending" or "descending")
|
||||
include_fields: List of fields to include in results
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: List of papers or error dict
|
||||
"""
|
||||
query = f'au:"{author}"'
|
||||
return self.search(query, max_results, sort_by, sort_order, include_fields)
|
||||
|
||||
def get_papers_by_category(
|
||||
self,
|
||||
category: str,
|
||||
max_results: int = 10,
|
||||
sort_by: str = "submittedDate",
|
||||
sort_order: str = "descending",
|
||||
include_fields: Optional[List[str]] = None
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""
|
||||
Get papers from a specific category.
|
||||
|
||||
Args:
|
||||
category: arXiv category (e.g., "cs.AI", "physics.gen-ph")
|
||||
max_results: Maximum number of results to return
|
||||
sort_by: Sort results by ("relevance", "lastUpdatedDate", "submittedDate")
|
||||
sort_order: Sort order ("ascending" or "descending")
|
||||
include_fields: List of fields to include in results
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: List of papers or error dict
|
||||
"""
|
||||
query = f'cat:{category}'
|
||||
return self.search(query, max_results, sort_by, sort_order, include_fields)
|
||||
|
||||
def _result_to_dict(
|
||||
self,
|
||||
result: Any,
|
||||
include_fields: Optional[List[str]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Convert arxiv.Result to dictionary with selected fields."""
|
||||
# Default fields to include
|
||||
if include_fields is None:
|
||||
include_fields = [
|
||||
"title", "authors", "summary", "comment", "journal_ref",
|
||||
"doi", "primary_category", "categories", "links"
|
||||
]
|
||||
|
||||
# Build paper dict with selected fields
|
||||
paper = {}
|
||||
|
||||
# Always include these basic fields
|
||||
paper["arxiv_id"] = result.entry_id.split("/")[-1]
|
||||
paper["updated"] = result.updated.isoformat() if result.updated else None
|
||||
paper["published"] = result.published.isoformat() if result.published else None
|
||||
|
||||
# Add requested fields
|
||||
if "title" in include_fields:
|
||||
paper["title"] = result.title
|
||||
if "authors" in include_fields:
|
||||
paper["authors"] = [str(author) for author in result.authors]
|
||||
if "summary" in include_fields:
|
||||
paper["summary"] = result.summary
|
||||
if "comment" in include_fields:
|
||||
paper["comment"] = result.comment
|
||||
if "journal_ref" in include_fields:
|
||||
paper["journal_ref"] = result.journal_ref
|
||||
if "doi" in include_fields:
|
||||
paper["doi"] = result.doi
|
||||
if "primary_category" in include_fields:
|
||||
paper["primary_category"] = result.primary_category
|
||||
if "categories" in include_fields:
|
||||
paper["categories"] = result.categories
|
||||
if "links" in include_fields:
|
||||
paper["pdf_url"] = result.pdf_url
|
||||
paper["abstract_url"] = f"https://arxiv.org/abs/{paper['arxiv_id']}"
|
||||
|
||||
return paper
|
||||
|
||||
# Create instance for direct function access
|
||||
_arxiv_tools = ArxivTools()
|
||||
search_arxiv = _arxiv_tools.search
|
||||
get_arxiv_paper = _arxiv_tools.get_paper
|
||||
get_papers_by_author = _arxiv_tools.get_papers_by_author
|
||||
get_papers_by_category = _arxiv_tools.get_papers_by_category
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("ArxivTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# 1. Search for papers
|
||||
print("1. Searching for Papers")
|
||||
print("------------------------------")
|
||||
query = "quantum computing"
|
||||
papers = search_arxiv(query, max_results=3)
|
||||
print(f"Papers about {query}:")
|
||||
if isinstance(papers, list):
|
||||
print(json.dumps(papers, indent=2))
|
||||
else:
|
||||
print(papers) # Show error
|
||||
print()
|
||||
|
||||
# 2. Get specific paper
|
||||
print("2. Getting Specific Paper")
|
||||
print("------------------------------")
|
||||
if isinstance(papers, list) and papers:
|
||||
paper_id = papers[0]["arxiv_id"]
|
||||
paper = get_arxiv_paper(paper_id)
|
||||
print(f"Paper {paper_id}:")
|
||||
if "error" not in paper:
|
||||
print(json.dumps(paper, indent=2))
|
||||
else:
|
||||
print(paper) # Show error
|
||||
print()
|
||||
|
||||
# 3. Get papers by author
|
||||
print("3. Getting Papers by Author")
|
||||
print("------------------------------")
|
||||
author = "Yoshua Bengio"
|
||||
author_papers = get_papers_by_author(author, max_results=3)
|
||||
print(f"Papers by {author}:")
|
||||
if isinstance(author_papers, list):
|
||||
print(json.dumps(author_papers, indent=2))
|
||||
else:
|
||||
print(author_papers) # Show error
|
||||
print()
|
||||
|
||||
# 4. Get papers by category
|
||||
print("4. Getting Papers by Category")
|
||||
print("------------------------------")
|
||||
category = "cs.AI"
|
||||
category_papers = get_papers_by_category(category, max_results=3)
|
||||
print(f"Papers in category {category}:")
|
||||
if isinstance(category_papers, list):
|
||||
print(json.dumps(category_papers, indent=2))
|
||||
else:
|
||||
print(category_papers) # Show error
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,278 @@
|
||||
"""Tools for performing calculations.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import calculator_tools
|
||||
result = calculator_tools.evaluate("2 + 2 * 3")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import evaluate, solve_equation, convert_units
|
||||
result = evaluate("2 + 2 * 3")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Optional, Any
|
||||
from importlib import util
|
||||
import math
|
||||
|
||||
class CalculatorTools:
|
||||
"""Tools for performing calculations."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize CalculatorTools."""
|
||||
self._check_dependencies()
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if required packages are installed."""
|
||||
missing = []
|
||||
for package in ['sympy', 'pint']:
|
||||
if util.find_spec(package) is None:
|
||||
missing.append(package)
|
||||
|
||||
if missing:
|
||||
raise ImportError(
|
||||
f"Required packages not available. Please install: {', '.join(missing)}\n"
|
||||
f"Run: pip install {' '.join(missing)}"
|
||||
)
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
expression: str,
|
||||
variables: Optional[Dict[str, float]] = None,
|
||||
precision: int = 10
|
||||
) -> Dict[str, Any]:
|
||||
"""Evaluate a mathematical expression."""
|
||||
try:
|
||||
if util.find_spec('sympy') is None:
|
||||
error_msg = "sympy package is not available. Please install it using: pip install sympy"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Import sympy only when needed
|
||||
import sympy
|
||||
|
||||
# Replace common mathematical functions
|
||||
expression = expression.lower()
|
||||
replacements = {
|
||||
'sin(': 'math.sin(',
|
||||
'cos(': 'math.cos(',
|
||||
'tan(': 'math.tan(',
|
||||
'sqrt(': 'math.sqrt(',
|
||||
'log(': 'math.log(',
|
||||
'exp(': 'math.exp(',
|
||||
'pi': str(math.pi),
|
||||
'e': str(math.e)
|
||||
}
|
||||
for old, new in replacements.items():
|
||||
expression = expression.replace(old, new)
|
||||
|
||||
# Create safe namespace
|
||||
safe_dict = {
|
||||
'math': math,
|
||||
'__builtins__': None
|
||||
}
|
||||
if variables:
|
||||
safe_dict.update(variables)
|
||||
|
||||
# Evaluate expression
|
||||
result = eval(expression, safe_dict)
|
||||
|
||||
# Round to specified precision
|
||||
if isinstance(result, (int, float)):
|
||||
return {"result": round(float(result), precision)}
|
||||
return {"error": "Invalid expression"}
|
||||
except Exception as e:
|
||||
error_msg = f"Error evaluating expression: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def solve_equation(
|
||||
self,
|
||||
equation: str,
|
||||
variable: str = 'x'
|
||||
) -> Dict[str, Any]:
|
||||
"""Solve a mathematical equation."""
|
||||
try:
|
||||
if util.find_spec('sympy') is None:
|
||||
error_msg = "sympy package is not available. Please install it using: pip install sympy"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Import sympy only when needed
|
||||
import sympy
|
||||
|
||||
# Parse equation
|
||||
if '=' in equation:
|
||||
left, right = equation.split('=')
|
||||
equation = f"({left}) - ({right})"
|
||||
|
||||
# Convert to SymPy expression
|
||||
x = sympy.Symbol(variable)
|
||||
expr = sympy.sympify(equation)
|
||||
|
||||
# Solve equation
|
||||
solutions = sympy.solve(expr, x)
|
||||
|
||||
# Convert complex solutions to real if possible
|
||||
real_solutions = []
|
||||
for sol in solutions:
|
||||
if sol.is_real:
|
||||
real_solutions.append(float(sol))
|
||||
|
||||
return {"solutions": real_solutions} if real_solutions else {"error": "No real solutions found"}
|
||||
except Exception as e:
|
||||
error_msg = f"Error solving equation: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def convert_units(
|
||||
self,
|
||||
value: float,
|
||||
from_unit: str,
|
||||
to_unit: str
|
||||
) -> Dict[str, Any]:
|
||||
"""Convert between different units."""
|
||||
try:
|
||||
if util.find_spec('pint') is None:
|
||||
error_msg = "pint package is not available. Please install it using: pip install pint"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Import pint only when needed
|
||||
import pint
|
||||
|
||||
# Create quantity with source unit
|
||||
ureg = pint.UnitRegistry()
|
||||
quantity = value * ureg(from_unit)
|
||||
|
||||
# Convert to target unit
|
||||
result = quantity.to(to_unit)
|
||||
|
||||
return {"result": float(result.magnitude)}
|
||||
except Exception as e:
|
||||
error_msg = f"Error converting units: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def calculate_statistics(
|
||||
self,
|
||||
numbers: List[float]
|
||||
) -> Dict[str, Any]:
|
||||
"""Calculate basic statistics for a list of numbers."""
|
||||
try:
|
||||
if not numbers:
|
||||
return {"error": "No numbers provided"}
|
||||
|
||||
n = len(numbers)
|
||||
mean = sum(numbers) / n
|
||||
|
||||
# Calculate variance and std dev
|
||||
squared_diff_sum = sum((x - mean) ** 2 for x in numbers)
|
||||
variance = squared_diff_sum / (n - 1) if n > 1 else 0
|
||||
std_dev = math.sqrt(variance)
|
||||
|
||||
# Calculate median
|
||||
sorted_nums = sorted(numbers)
|
||||
if n % 2 == 0:
|
||||
median = (sorted_nums[n//2 - 1] + sorted_nums[n//2]) / 2
|
||||
else:
|
||||
median = sorted_nums[n//2]
|
||||
|
||||
return {
|
||||
"mean": mean,
|
||||
"median": median,
|
||||
"std_dev": std_dev,
|
||||
"variance": variance,
|
||||
"min": min(numbers),
|
||||
"max": max(numbers),
|
||||
"range": max(numbers) - min(numbers),
|
||||
"count": n
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error calculating statistics: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def calculate_financial(
|
||||
self,
|
||||
principal: float,
|
||||
rate: float,
|
||||
time: float,
|
||||
frequency: str = 'yearly'
|
||||
) -> Dict[str, Any]:
|
||||
"""Calculate financial metrics like compound interest."""
|
||||
try:
|
||||
# Convert rate to decimal
|
||||
rate = rate / 100
|
||||
|
||||
# Get compounding frequency
|
||||
freq_map = {
|
||||
'yearly': 1,
|
||||
'semi-annual': 2,
|
||||
'quarterly': 4,
|
||||
'monthly': 12,
|
||||
'daily': 365
|
||||
}
|
||||
n = freq_map.get(frequency.lower(), 1)
|
||||
|
||||
# Calculate compound interest
|
||||
amount = principal * (1 + rate/n)**(n*time)
|
||||
interest = amount - principal
|
||||
|
||||
# Calculate simple interest
|
||||
simple_interest = principal * rate * time
|
||||
|
||||
return {
|
||||
"final_amount": amount,
|
||||
"compound_interest": interest,
|
||||
"simple_interest": simple_interest,
|
||||
"difference": interest - simple_interest
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error calculating financial metrics: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Create instance for direct function access
|
||||
_calculator_tools = CalculatorTools()
|
||||
evaluate = _calculator_tools.evaluate
|
||||
solve_equation = _calculator_tools.solve_equation
|
||||
convert_units = _calculator_tools.convert_units
|
||||
calculate_statistics = _calculator_tools.calculate_statistics
|
||||
calculate_financial = _calculator_tools.calculate_financial
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("CalculatorTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
print("1. Basic Arithmetic")
|
||||
print("------------------------------")
|
||||
print("2 + 2 =", evaluate("2 + 2")["result"])
|
||||
print("5 * 6 =", evaluate("5 * 6")["result"])
|
||||
print("10 / 3 =", evaluate("10 / 3")["result"])
|
||||
print()
|
||||
|
||||
print("2. Complex Expressions")
|
||||
print("------------------------------")
|
||||
print("(2 + 3) * 4 =", evaluate("(2 + 3) * 4")["result"])
|
||||
print("2^3 + 4 =", evaluate("2^3 + 4")["result"])
|
||||
print("sqrt(16) + 2 =", evaluate("sqrt(16) + 2")["result"])
|
||||
print()
|
||||
|
||||
print("3. Solving Equations")
|
||||
print("------------------------------")
|
||||
result = solve_equation("x^2 - 5*x + 6 = 0")
|
||||
print("x^2 - 5*x + 6 = 0")
|
||||
print("Solutions:", result["solutions"])
|
||||
print()
|
||||
|
||||
print("4. Unit Conversions")
|
||||
print("------------------------------")
|
||||
print("100 meters to kilometers:", convert_units(100, "meters", "kilometers")["result"])
|
||||
print("1 hour to minutes:", convert_units(1, "hours", "minutes")["result"])
|
||||
print("32 fahrenheit to celsius:", convert_units(32, "fahrenheit", "celsius")["result"])
|
||||
print()
|
||||
|
||||
print("==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,266 @@
|
||||
"""Tools for working with CSV files.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import csv_tools
|
||||
df = csv_tools.read_csv("data.csv")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_csv, write_csv, merge_csv
|
||||
df = read_csv("data.csv")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any, TYPE_CHECKING
|
||||
from importlib import util
|
||||
import json
|
||||
import csv
|
||||
from pathlib import Path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import pandas as pd
|
||||
|
||||
class CSVTools:
|
||||
"""Tools for working with CSV files."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize CSVTools."""
|
||||
pass
|
||||
|
||||
def _get_pandas(self) -> Optional['pd']:
|
||||
"""Get pandas module, installing if needed"""
|
||||
if util.find_spec('pandas') is None:
|
||||
error_msg = "pandas package is not available. Please install it using: pip install pandas"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import pandas as pd
|
||||
return pd
|
||||
|
||||
def read_csv(
|
||||
self,
|
||||
filepath: str,
|
||||
encoding: str = 'utf-8',
|
||||
delimiter: str = ',',
|
||||
header: Union[int, List[int], None] = 0,
|
||||
usecols: Optional[List[str]] = None,
|
||||
dtype: Optional[Dict[str, str]] = None,
|
||||
parse_dates: Optional[List[str]] = None,
|
||||
na_values: Optional[List[str]] = None,
|
||||
nrows: Optional[int] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Read a CSV file with advanced options.
|
||||
|
||||
Args:
|
||||
filepath: Path to CSV file
|
||||
encoding: File encoding
|
||||
delimiter: Column delimiter
|
||||
header: Row number(s) to use as column names
|
||||
usecols: Columns to read
|
||||
dtype: Dict of column dtypes
|
||||
parse_dates: List of columns to parse as dates
|
||||
na_values: Additional strings to recognize as NA/NaN
|
||||
nrows: Number of rows to read
|
||||
|
||||
Returns:
|
||||
List of row dicts
|
||||
"""
|
||||
try:
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return {"error": "pandas package not available"}
|
||||
|
||||
df = pd.read_csv(
|
||||
filepath,
|
||||
encoding=encoding,
|
||||
delimiter=delimiter,
|
||||
header=header,
|
||||
usecols=usecols,
|
||||
dtype=dtype,
|
||||
parse_dates=parse_dates,
|
||||
na_values=na_values,
|
||||
nrows=nrows
|
||||
)
|
||||
return df.to_dict('records')
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading CSV file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def write_csv(
|
||||
self,
|
||||
filepath: str,
|
||||
data: List[Dict[str, Any]],
|
||||
encoding: str = 'utf-8',
|
||||
delimiter: str = ',',
|
||||
index: bool = False,
|
||||
header: bool = True,
|
||||
float_format: Optional[str] = None,
|
||||
date_format: Optional[str] = None,
|
||||
mode: str = 'w'
|
||||
) -> bool:
|
||||
"""Write data to a CSV file.
|
||||
|
||||
Args:
|
||||
filepath: Path to CSV file
|
||||
data: List of row dicts to write
|
||||
encoding: File encoding
|
||||
delimiter: Column delimiter
|
||||
index: Whether to write row indices
|
||||
header: Whether to write column headers
|
||||
float_format: Format string for float values
|
||||
date_format: Format string for date values
|
||||
mode: Write mode ('w' for write, 'a' for append)
|
||||
|
||||
Returns:
|
||||
bool: Success status
|
||||
"""
|
||||
try:
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return False
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
df.to_csv(
|
||||
filepath,
|
||||
encoding=encoding,
|
||||
sep=delimiter,
|
||||
index=index,
|
||||
header=header,
|
||||
float_format=float_format,
|
||||
date_format=date_format,
|
||||
mode=mode
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing CSV file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def merge_csv(
|
||||
self,
|
||||
files: List[str],
|
||||
output_file: str,
|
||||
how: str = 'inner',
|
||||
on: Optional[Union[str, List[str]]] = None,
|
||||
suffixes: Optional[tuple] = None
|
||||
) -> bool:
|
||||
"""Merge multiple CSV files.
|
||||
|
||||
Args:
|
||||
files: List of CSV files to merge
|
||||
output_file: Output file path
|
||||
how: Merge method ('inner', 'outer', 'left', 'right')
|
||||
on: Column(s) to merge on
|
||||
suffixes: Suffixes for overlapping columns
|
||||
|
||||
Returns:
|
||||
bool: Success status
|
||||
"""
|
||||
try:
|
||||
if len(files) < 2:
|
||||
error_msg = "At least two files are required for merging"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return False
|
||||
|
||||
# Read first file
|
||||
result = pd.read_csv(files[0])
|
||||
|
||||
# Merge with remaining files
|
||||
for file in files[1:]:
|
||||
df = pd.read_csv(file)
|
||||
result = pd.merge(
|
||||
result,
|
||||
df,
|
||||
how=how,
|
||||
on=on,
|
||||
suffixes=suffixes or ('_1', '_2')
|
||||
)
|
||||
|
||||
# Write merged result
|
||||
result.to_csv(output_file, index=False)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error merging CSV files: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Create instance for direct function access
|
||||
_csv_tools = CSVTools()
|
||||
read_csv = _csv_tools.read_csv
|
||||
write_csv = _csv_tools.write_csv
|
||||
merge_csv = _csv_tools.merge_csv
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("CSVTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Create a temporary file for testing
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp:
|
||||
temp_file = temp.name
|
||||
|
||||
print("1. Writing CSV File")
|
||||
print("------------------------------")
|
||||
data = [
|
||||
{"name": "Alice", "age": 25, "city": "New York"},
|
||||
{"name": "Bob", "age": 30, "city": "San Francisco"},
|
||||
{"name": "Charlie", "age": 35, "city": "Chicago"}
|
||||
]
|
||||
result = write_csv(temp_file, data)
|
||||
print(f"Data written successfully: {result}")
|
||||
print()
|
||||
|
||||
print("2. Reading CSV File")
|
||||
print("------------------------------")
|
||||
read_data = read_csv(temp_file)
|
||||
print("Contents of CSV file:")
|
||||
for row in read_data:
|
||||
print(row)
|
||||
print()
|
||||
|
||||
print("3. Merging CSV Files")
|
||||
print("------------------------------")
|
||||
# Create a second file for merging
|
||||
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp2:
|
||||
temp_file2 = temp2.name
|
||||
data2 = [
|
||||
{"name": "Alice", "salary": 75000},
|
||||
{"name": "Bob", "salary": 85000},
|
||||
{"name": "Charlie", "salary": 95000}
|
||||
]
|
||||
write_csv(temp_file2, data2)
|
||||
|
||||
# Merge files
|
||||
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp3:
|
||||
temp_file3 = temp3.name
|
||||
result = merge_csv(
|
||||
[temp_file, temp_file2],
|
||||
temp_file3,
|
||||
how='inner',
|
||||
on='name'
|
||||
)
|
||||
print(f"Files merged successfully: {result}")
|
||||
if result:
|
||||
merged_data = read_csv(temp_file3)
|
||||
print("\nMerged contents:")
|
||||
for row in merged_data:
|
||||
print(row)
|
||||
print()
|
||||
|
||||
# Clean up temporary files
|
||||
os.unlink(temp_file)
|
||||
os.unlink(temp_file2)
|
||||
os.unlink(temp_file3)
|
||||
|
||||
print("==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================\n")
|
||||
@@ -0,0 +1,268 @@
|
||||
"""Tools for working with DuckDB databases.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import duckdb_tools
|
||||
df = duckdb_tools.query("SELECT * FROM my_table")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import query_db, create_table, load_data
|
||||
df = query_db("SELECT * FROM my_table")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Any, Optional, Union, TYPE_CHECKING
|
||||
from importlib import util
|
||||
import json
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import duckdb
|
||||
import pandas as pd
|
||||
|
||||
class DuckDBTools:
|
||||
"""Tools for working with DuckDB databases."""
|
||||
|
||||
def __init__(self, database: str = ':memory:'):
|
||||
"""Initialize DuckDBTools.
|
||||
|
||||
Args:
|
||||
database: Path to database file or ':memory:' for in-memory database
|
||||
"""
|
||||
self.database = database
|
||||
self._conn = None
|
||||
|
||||
def _get_duckdb(self) -> Optional['duckdb']:
|
||||
"""Get duckdb module, installing if needed"""
|
||||
if util.find_spec('duckdb') is None:
|
||||
error_msg = "duckdb package is not available. Please install it using: pip install duckdb"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import duckdb
|
||||
return duckdb
|
||||
|
||||
def _get_pandas(self) -> Optional['pd']:
|
||||
"""Get pandas module, installing if needed"""
|
||||
if util.find_spec('pandas') is None:
|
||||
error_msg = "pandas package is not available. Please install it using: pip install pandas"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import pandas as pd
|
||||
return pd
|
||||
|
||||
def _get_connection(self) -> Optional['duckdb.DuckDBPyConnection']:
|
||||
"""Get or create database connection"""
|
||||
if self._conn is None:
|
||||
duckdb = self._get_duckdb()
|
||||
if duckdb is None:
|
||||
return None
|
||||
try:
|
||||
self._conn = duckdb.connect(self.database)
|
||||
except Exception as e:
|
||||
error_msg = f"Error connecting to database {self.database}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
return self._conn
|
||||
|
||||
def execute_query(
|
||||
self,
|
||||
query: str,
|
||||
params: Optional[Union[tuple, dict]] = None,
|
||||
return_df: bool = True
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""Execute a SQL query.
|
||||
|
||||
Args:
|
||||
query: SQL query to execute
|
||||
params: Query parameters
|
||||
return_df: If True, return results as DataFrame records
|
||||
|
||||
Returns:
|
||||
Query results as list of dicts, or error dict
|
||||
"""
|
||||
try:
|
||||
conn = self._get_connection()
|
||||
if conn is None:
|
||||
return {"error": "Could not connect to database"}
|
||||
|
||||
if params:
|
||||
result = conn.execute(query, params)
|
||||
else:
|
||||
result = conn.execute(query)
|
||||
|
||||
if return_df:
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return {"error": "pandas package not available"}
|
||||
df = result.df()
|
||||
return df.to_dict('records')
|
||||
else:
|
||||
return [dict(row) for row in result.fetchall()]
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error executing query: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def load_csv(
|
||||
self,
|
||||
table_name: str,
|
||||
filepath: str,
|
||||
schema: Optional[Dict[str, str]] = None,
|
||||
if_exists: str = 'replace'
|
||||
) -> bool:
|
||||
"""Load a CSV file into a table.
|
||||
|
||||
Args:
|
||||
table_name: Name of table to create
|
||||
filepath: Path to CSV file
|
||||
schema: Optional column definitions
|
||||
if_exists: What to do if table exists ('fail', 'replace', 'append')
|
||||
|
||||
Returns:
|
||||
bool: Success status
|
||||
"""
|
||||
try:
|
||||
conn = self._get_connection()
|
||||
if conn is None:
|
||||
return False
|
||||
|
||||
# Check if table exists
|
||||
exists = conn.execute(f"""
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name='{table_name}'
|
||||
""").fetchone() is not None
|
||||
|
||||
if exists:
|
||||
if if_exists == 'fail':
|
||||
raise ValueError(f"Table {table_name} already exists")
|
||||
elif if_exists == 'replace':
|
||||
conn.execute(f"DROP TABLE IF EXISTS {table_name}")
|
||||
elif if_exists != 'append':
|
||||
raise ValueError("if_exists must be 'fail', 'replace', or 'append'")
|
||||
|
||||
# Create table if needed
|
||||
if not exists or if_exists == 'replace':
|
||||
if schema:
|
||||
# Create table with schema
|
||||
columns = ', '.join(f"{k} {v}" for k, v in schema.items())
|
||||
conn.execute(f"CREATE TABLE {table_name} ({columns})")
|
||||
else:
|
||||
# Infer schema from CSV
|
||||
conn.execute(f"""
|
||||
CREATE TABLE {table_name} AS
|
||||
SELECT * FROM read_csv_auto('{filepath}')
|
||||
WHERE 1=0
|
||||
""")
|
||||
|
||||
# Load data
|
||||
conn.execute(f"""
|
||||
INSERT INTO {table_name}
|
||||
SELECT * FROM read_csv_auto('{filepath}')
|
||||
""")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error loading CSV file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def export_csv(
|
||||
self,
|
||||
query: str,
|
||||
filepath: str,
|
||||
params: Optional[Union[tuple, dict]] = None
|
||||
) -> bool:
|
||||
"""Export query results to CSV.
|
||||
|
||||
Args:
|
||||
query: SQL query to execute
|
||||
filepath: Output file path
|
||||
params: Optional query parameters
|
||||
|
||||
Returns:
|
||||
bool: Success status
|
||||
"""
|
||||
try:
|
||||
# Execute query and get results as DataFrame
|
||||
results = self.execute_query(query, params)
|
||||
if isinstance(results, dict) and 'error' in results:
|
||||
return False
|
||||
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return False
|
||||
|
||||
# Convert to DataFrame and save
|
||||
df = pd.DataFrame(results)
|
||||
df.to_csv(filepath, index=False)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error exporting to CSV file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
"""Close database connection."""
|
||||
if self._conn:
|
||||
self._conn.close()
|
||||
self._conn = None
|
||||
|
||||
# Create instance for direct function access
|
||||
_duckdb_tools = DuckDBTools()
|
||||
execute_query = _duckdb_tools.execute_query
|
||||
load_csv = _duckdb_tools.load_csv
|
||||
export_csv = _duckdb_tools.export_csv
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("DuckDBTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Create a temporary file for testing
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp:
|
||||
temp_file = temp.name
|
||||
|
||||
# Create sample data
|
||||
with open(temp_file, 'w') as f:
|
||||
f.write("name,age,city\n")
|
||||
f.write("Alice,25,New York\n")
|
||||
f.write("Bob,30,San Francisco\n")
|
||||
f.write("Charlie,35,Chicago\n")
|
||||
|
||||
print("1. Loading CSV File")
|
||||
print("------------------------------")
|
||||
result = load_csv('users', temp_file)
|
||||
print(f"CSV loaded successfully: {result}")
|
||||
print()
|
||||
|
||||
print("2. Executing Query")
|
||||
print("------------------------------")
|
||||
query = "SELECT * FROM users WHERE age > 25"
|
||||
results = execute_query(query)
|
||||
print("Query results:")
|
||||
for row in results:
|
||||
print(row)
|
||||
print()
|
||||
|
||||
print("3. Exporting Query Results")
|
||||
print("------------------------------")
|
||||
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp2:
|
||||
temp_file2 = temp2.name
|
||||
result = export_csv(query, temp_file2)
|
||||
print(f"Results exported successfully: {result}")
|
||||
if result:
|
||||
print("\nExported file contents:")
|
||||
with open(temp_file2) as f:
|
||||
print(f.read())
|
||||
|
||||
# Clean up temporary files
|
||||
os.unlink(temp_file)
|
||||
os.unlink(temp_file2)
|
||||
|
||||
print("==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================\n")
|
||||
@@ -0,0 +1,52 @@
|
||||
"""DuckDuckGo search functionality.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import internet_search
|
||||
results = internet_search("AI news")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
results = duckduckgo("AI news")
|
||||
"""
|
||||
|
||||
from typing import List, Dict
|
||||
import logging
|
||||
from importlib import util
|
||||
|
||||
def internet_search(query: str) -> List[Dict]:
|
||||
"""Perform an internet search using DuckDuckGo."""
|
||||
# Check if duckduckgo_search is installed
|
||||
if util.find_spec("duckduckgo_search") is None:
|
||||
error_msg = "DuckDuckGo search is not available. Please install duckduckgo_search package using: pip install duckduckgo_search"
|
||||
logging.error(error_msg)
|
||||
return [{"error": error_msg}]
|
||||
|
||||
try:
|
||||
# Import only when needed
|
||||
from duckduckgo_search import DDGS
|
||||
results = []
|
||||
ddgs = DDGS()
|
||||
for result in ddgs.text(keywords=query, max_results=5):
|
||||
results.append({
|
||||
"title": result.get("title", ""),
|
||||
"url": result.get("href", ""),
|
||||
"snippet": result.get("body", "")
|
||||
})
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error during DuckDuckGo search: {e}"
|
||||
logging.error(error_msg)
|
||||
return [{"error": error_msg}]
|
||||
|
||||
def duckduckgo(query: str) -> List[Dict]:
|
||||
"""Alias for internet_search function."""
|
||||
return internet_search(query)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
results = internet_search("Python programming")
|
||||
for result in results:
|
||||
print(f"\nTitle: {result.get('title')}")
|
||||
print(f"URL: {result.get('url')}")
|
||||
print(f"Snippet: {result.get('snippet')}")
|
||||
@@ -0,0 +1,310 @@
|
||||
"""Tools for working with Excel files.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import excel_tools
|
||||
df = excel_tools.read_excel("data.xlsx")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_excel, write_excel, merge_excel
|
||||
df = read_excel("data.xlsx")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any, TYPE_CHECKING, Tuple
|
||||
from importlib import util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import pandas as pd
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
|
||||
from openpyxl.chart.label import DataLabelList
|
||||
|
||||
class ExcelTools:
|
||||
"""Tools for working with Excel files."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize ExcelTools."""
|
||||
pass
|
||||
|
||||
def _get_pandas(self) -> Optional['pd']:
|
||||
"""Get pandas module, installing if needed"""
|
||||
if util.find_spec('pandas') is None:
|
||||
error_msg = "pandas package is not available. Please install it using: pip install pandas"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
if util.find_spec('openpyxl') is None:
|
||||
error_msg = "openpyxl package is not available. Please install it using: pip install openpyxl"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import pandas as pd
|
||||
return pd
|
||||
|
||||
def read_excel(
|
||||
self,
|
||||
filepath: str,
|
||||
sheet_name: Optional[Union[str, int, List[Union[str, int]]]] = 0,
|
||||
header: Optional[int] = 0,
|
||||
usecols: Optional[List[str]] = None,
|
||||
skiprows: Optional[Union[int, List[int]]] = None,
|
||||
na_values: Optional[List[str]] = None,
|
||||
dtype: Optional[Dict[str, str]] = None
|
||||
) -> Union[Dict[str, List[Dict[str, Any]]], List[Dict[str, Any]]]:
|
||||
"""Read an Excel file with advanced options.
|
||||
|
||||
Args:
|
||||
filepath: Path to Excel file
|
||||
sheet_name: Sheet name(s) or index(es)
|
||||
header: Row number(s) to use as column names
|
||||
usecols: Columns to read
|
||||
skiprows: Line numbers to skip
|
||||
na_values: Additional strings to recognize as NA/NaN
|
||||
dtype: Dict of column dtypes
|
||||
|
||||
Returns:
|
||||
Dict of sheet names to data if multiple sheets, else list of row dicts
|
||||
"""
|
||||
try:
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return {"error": "Required packages not available"}
|
||||
|
||||
# Read Excel file
|
||||
df = pd.read_excel(
|
||||
filepath,
|
||||
sheet_name=sheet_name,
|
||||
header=header,
|
||||
usecols=usecols,
|
||||
skiprows=skiprows,
|
||||
na_values=na_values,
|
||||
dtype=dtype,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
# Convert to dict format
|
||||
if isinstance(df, dict):
|
||||
return {
|
||||
name: df[name].to_dict('records')
|
||||
for name in df.keys()
|
||||
}
|
||||
else:
|
||||
return df.to_dict('records')
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading Excel file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def write_excel(
|
||||
self,
|
||||
filepath: str,
|
||||
data: Union[Dict[str, List[Dict[str, Any]]], List[Dict[str, Any]]],
|
||||
sheet_name: Optional[str] = None,
|
||||
index: bool = False,
|
||||
header: bool = True,
|
||||
mode: str = 'w'
|
||||
) -> bool:
|
||||
"""Write data to an Excel file.
|
||||
|
||||
Args:
|
||||
filepath: Path to Excel file
|
||||
data: Data to write (dict of sheet names to data or list of row dicts)
|
||||
sheet_name: Sheet name if data is a list
|
||||
index: Whether to write row indices
|
||||
header: Whether to write column headers
|
||||
mode: Write mode ('w' for write, 'a' for append)
|
||||
|
||||
Returns:
|
||||
bool: Success status
|
||||
"""
|
||||
try:
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return False
|
||||
|
||||
# Convert data to DataFrame(s)
|
||||
if isinstance(data, dict):
|
||||
if mode == 'a' and os.path.exists(filepath):
|
||||
book = pd.ExcelFile(filepath)
|
||||
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
|
||||
# Copy existing sheets
|
||||
for sheet in book.sheet_names:
|
||||
pd.read_excel(filepath, sheet_name=sheet).to_excel(
|
||||
writer, sheet_name=sheet, index=index, header=header
|
||||
)
|
||||
# Add new sheets
|
||||
for name, sheet_data in data.items():
|
||||
df = pd.DataFrame(sheet_data)
|
||||
df.to_excel(
|
||||
writer, sheet_name=name, index=index, header=header
|
||||
)
|
||||
else:
|
||||
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
|
||||
for name, sheet_data in data.items():
|
||||
df = pd.DataFrame(sheet_data)
|
||||
df.to_excel(
|
||||
writer, sheet_name=name, index=index, header=header
|
||||
)
|
||||
else:
|
||||
if mode == 'a' and os.path.exists(filepath):
|
||||
book = pd.ExcelFile(filepath)
|
||||
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
|
||||
# Copy existing sheets
|
||||
for sheet in book.sheet_names:
|
||||
pd.read_excel(filepath, sheet_name=sheet).to_excel(
|
||||
writer, sheet_name=sheet, index=index, header=header
|
||||
)
|
||||
# Add new sheet
|
||||
df = pd.DataFrame(data)
|
||||
df.to_excel(
|
||||
writer,
|
||||
sheet_name=sheet_name or 'Sheet1',
|
||||
index=index,
|
||||
header=header
|
||||
)
|
||||
else:
|
||||
df = pd.DataFrame(data)
|
||||
df.to_excel(
|
||||
filepath,
|
||||
sheet_name=sheet_name or 'Sheet1',
|
||||
index=index,
|
||||
header=header,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing Excel file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def merge_excel(
|
||||
self,
|
||||
files: List[str],
|
||||
output_file: str,
|
||||
how: str = 'inner',
|
||||
on: Optional[Union[str, List[str]]] = None,
|
||||
suffixes: Optional[Tuple[str, str]] = None
|
||||
) -> bool:
|
||||
"""Merge multiple Excel files.
|
||||
|
||||
Args:
|
||||
files: List of Excel files to merge
|
||||
output_file: Output file path
|
||||
how: Merge method ('inner', 'outer', 'left', 'right')
|
||||
on: Column(s) to merge on
|
||||
suffixes: Suffixes for overlapping columns
|
||||
|
||||
Returns:
|
||||
bool: Success status
|
||||
"""
|
||||
try:
|
||||
if len(files) < 2:
|
||||
error_msg = "At least two files are required for merging"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Read first file
|
||||
result = self.read_excel(files[0])
|
||||
|
||||
# Merge with remaining files
|
||||
for file in files[1:]:
|
||||
df = self.read_excel(file)
|
||||
if isinstance(df, dict):
|
||||
df = next(iter(df.values()))
|
||||
|
||||
pd = self._get_pandas()
|
||||
if pd is None:
|
||||
return False
|
||||
|
||||
result = pd.merge(
|
||||
pd.DataFrame(result),
|
||||
pd.DataFrame(df),
|
||||
how=how,
|
||||
on=on,
|
||||
suffixes=suffixes or ('_1', '_2')
|
||||
).to_dict('records')
|
||||
|
||||
# Write merged result
|
||||
return self.write_excel(output_file, result)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error merging Excel files: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Create instance for direct function access
|
||||
_excel_tools = ExcelTools()
|
||||
read_excel = _excel_tools.read_excel
|
||||
write_excel = _excel_tools.write_excel
|
||||
merge_excel = _excel_tools.merge_excel
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("ExcelTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Create a temporary file for testing
|
||||
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp:
|
||||
temp_file = temp.name
|
||||
|
||||
print("1. Writing Excel File")
|
||||
print("------------------------------")
|
||||
data = [
|
||||
{"name": "Alice", "age": 25, "city": "New York"},
|
||||
{"name": "Bob", "age": 30, "city": "San Francisco"},
|
||||
{"name": "Charlie", "age": 35, "city": "Chicago"}
|
||||
]
|
||||
result = write_excel(temp_file, data, "People")
|
||||
print(f"Data written successfully: {result}")
|
||||
print()
|
||||
|
||||
print("2. Reading Excel File")
|
||||
print("------------------------------")
|
||||
read_data = read_excel(temp_file)
|
||||
print("Contents of Excel file:")
|
||||
for row in read_data:
|
||||
print(row)
|
||||
print()
|
||||
|
||||
print("3. Merging Excel Files")
|
||||
print("------------------------------")
|
||||
# Create a second file for merging
|
||||
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp2:
|
||||
temp_file2 = temp2.name
|
||||
data2 = [
|
||||
{"name": "Alice", "salary": 75000},
|
||||
{"name": "Bob", "salary": 85000},
|
||||
{"name": "Charlie", "salary": 95000}
|
||||
]
|
||||
write_excel(temp_file2, data2, "Salaries")
|
||||
|
||||
# Merge files
|
||||
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp3:
|
||||
temp_file3 = temp3.name
|
||||
result = merge_excel(
|
||||
[temp_file, temp_file2],
|
||||
temp_file3,
|
||||
how='inner',
|
||||
on='name'
|
||||
)
|
||||
print(f"Files merged successfully: {result}")
|
||||
if result:
|
||||
merged_data = read_excel(temp_file3)
|
||||
print("\nMerged contents:")
|
||||
for row in merged_data:
|
||||
print(row)
|
||||
print()
|
||||
|
||||
# Clean up temporary files
|
||||
os.unlink(temp_file)
|
||||
os.unlink(temp_file2)
|
||||
os.unlink(temp_file3)
|
||||
|
||||
print("==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================\n")
|
||||
@@ -0,0 +1,274 @@
|
||||
"""File handling tools for basic file operations.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import file_tools
|
||||
content = file_tools.read_file("example.txt")
|
||||
file_tools.write_file("output.txt", "Hello World")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_file, write_file, list_files
|
||||
content = read_file("example.txt")
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from typing import List, Dict, Union, Optional
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import logging
|
||||
|
||||
class FileTools:
|
||||
"""Tools for file operations including read, write, list, and information."""
|
||||
|
||||
@staticmethod
|
||||
def read_file(filepath: str, encoding: str = 'utf-8') -> str:
|
||||
"""
|
||||
Read content from a file.
|
||||
|
||||
Args:
|
||||
filepath: Path to the file
|
||||
encoding: File encoding (default: utf-8)
|
||||
|
||||
Returns:
|
||||
str: Content of the file
|
||||
"""
|
||||
try:
|
||||
with open(filepath, 'r', encoding=encoding) as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return error_msg
|
||||
|
||||
@staticmethod
|
||||
def write_file(filepath: str, content: str, encoding: str = 'utf-8') -> bool:
|
||||
"""
|
||||
Write content to a file.
|
||||
|
||||
Args:
|
||||
filepath: Path to the file
|
||||
content: Content to write
|
||||
encoding: File encoding (default: utf-8)
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Create directory if it doesn't exist
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
with open(filepath, 'w', encoding=encoding) as f:
|
||||
f.write(content)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing to file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def list_files(directory: str, pattern: Optional[str] = None) -> List[Dict[str, Union[str, int]]]:
|
||||
"""
|
||||
List files in a directory with optional pattern matching.
|
||||
|
||||
Args:
|
||||
directory: Directory path
|
||||
pattern: Optional glob pattern (e.g., "*.txt")
|
||||
|
||||
Returns:
|
||||
List[Dict]: List of file information dictionaries
|
||||
"""
|
||||
try:
|
||||
path = Path(directory)
|
||||
if pattern:
|
||||
files = path.glob(pattern)
|
||||
else:
|
||||
files = path.iterdir()
|
||||
|
||||
result = []
|
||||
for file in files:
|
||||
if file.is_file():
|
||||
stat = file.stat()
|
||||
result.append({
|
||||
'name': file.name,
|
||||
'path': str(file),
|
||||
'size': stat.st_size,
|
||||
'modified': stat.st_mtime,
|
||||
'created': stat.st_ctime
|
||||
})
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error listing files in {directory}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return [{'error': error_msg}]
|
||||
|
||||
@staticmethod
|
||||
def get_file_info(filepath: str) -> Dict[str, Union[str, int]]:
|
||||
"""
|
||||
Get detailed information about a file.
|
||||
|
||||
Args:
|
||||
filepath: Path to the file
|
||||
|
||||
Returns:
|
||||
Dict: File information including size, dates, etc.
|
||||
"""
|
||||
try:
|
||||
path = Path(filepath)
|
||||
if not path.exists():
|
||||
return {'error': f'File not found: {filepath}'}
|
||||
|
||||
stat = path.stat()
|
||||
return {
|
||||
'name': path.name,
|
||||
'path': str(path),
|
||||
'size': stat.st_size,
|
||||
'modified': stat.st_mtime,
|
||||
'created': stat.st_ctime,
|
||||
'is_file': path.is_file(),
|
||||
'is_dir': path.is_dir(),
|
||||
'extension': path.suffix,
|
||||
'parent': str(path.parent)
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting file info for {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {'error': error_msg}
|
||||
|
||||
@staticmethod
|
||||
def copy_file(src: str, dst: str) -> bool:
|
||||
"""
|
||||
Copy a file from source to destination.
|
||||
|
||||
Args:
|
||||
src: Source file path
|
||||
dst: Destination file path
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Create destination directory if it doesn't exist
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error copying file from {src} to {dst}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def move_file(src: str, dst: str) -> bool:
|
||||
"""
|
||||
Move a file from source to destination.
|
||||
|
||||
Args:
|
||||
src: Source file path
|
||||
dst: Destination file path
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Create destination directory if it doesn't exist
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
shutil.move(src, dst)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error moving file from {src} to {dst}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def delete_file(filepath: str) -> bool:
|
||||
"""
|
||||
Delete a file.
|
||||
|
||||
Args:
|
||||
filepath: Path to the file
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
os.remove(filepath)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error deleting file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Create instance for direct function access
|
||||
_file_tools = FileTools()
|
||||
read_file = _file_tools.read_file
|
||||
write_file = _file_tools.write_file
|
||||
list_files = _file_tools.list_files
|
||||
get_file_info = _file_tools.get_file_info
|
||||
copy_file = _file_tools.copy_file
|
||||
move_file = _file_tools.move_file
|
||||
delete_file = _file_tools.delete_file
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("FileTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Create a test directory
|
||||
test_dir = os.path.join(os.getcwd(), "test_files")
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
|
||||
# Create test files
|
||||
test_file = os.path.join(test_dir, "test_file.txt")
|
||||
test_content = "Hello, this is a test file!"
|
||||
|
||||
print("1. Writing to file")
|
||||
print("------------------------------")
|
||||
success = write_file(test_file, test_content)
|
||||
print(f"Write successful: {success}\n")
|
||||
|
||||
print("2. Reading from file")
|
||||
print("------------------------------")
|
||||
content = read_file(test_file)
|
||||
print(f"Content: {content}\n")
|
||||
|
||||
print("3. File Information")
|
||||
print("------------------------------")
|
||||
info = get_file_info(test_file)
|
||||
print(json.dumps(info, indent=2))
|
||||
print()
|
||||
|
||||
print("4. Listing Files")
|
||||
print("------------------------------")
|
||||
files = list_files(test_dir, "*.txt")
|
||||
for file in files:
|
||||
print(f"Found: {file['name']} ({file['size']} bytes)")
|
||||
print()
|
||||
|
||||
print("5. Copying File")
|
||||
print("------------------------------")
|
||||
copy_file_path = os.path.join(test_dir, "test_file_copy.txt")
|
||||
copy_success = copy_file(test_file, copy_file_path)
|
||||
print(f"Copy successful: {copy_success}\n")
|
||||
|
||||
print("6. Moving File")
|
||||
print("------------------------------")
|
||||
move_file_path = os.path.join(test_dir, "test_file_moved.txt")
|
||||
move_success = move_file(copy_file_path, move_file_path)
|
||||
print(f"Move successful: {move_success}\n")
|
||||
|
||||
print("7. Deleting Files")
|
||||
print("------------------------------")
|
||||
delete_success = delete_file(test_file)
|
||||
print(f"Delete original successful: {delete_success}")
|
||||
delete_success = delete_file(move_file_path)
|
||||
print(f"Delete moved file successful: {delete_success}\n")
|
||||
|
||||
# Clean up test directory
|
||||
try:
|
||||
shutil.rmtree(test_dir)
|
||||
print("Test directory cleaned up successfully")
|
||||
except Exception as e:
|
||||
print(f"Error cleaning up test directory: {str(e)}")
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,515 @@
|
||||
"""Tools for working with JSON files.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import json_tools
|
||||
data = json_tools.read_json("data.json")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_json, write_json, merge_json
|
||||
data = read_json("data.json")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any, Tuple
|
||||
from importlib import util
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
class JSONTools:
|
||||
"""Tools for working with JSON files."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize JSONTools."""
|
||||
pass
|
||||
|
||||
def read_json(
|
||||
self,
|
||||
filepath: str,
|
||||
encoding: str = 'utf-8',
|
||||
validate_schema: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Read a JSON file with optional schema validation.
|
||||
|
||||
Args:
|
||||
filepath: Path to JSON file
|
||||
encoding: File encoding
|
||||
validate_schema: Optional JSON schema for validation
|
||||
|
||||
Returns:
|
||||
Dict with JSON data
|
||||
"""
|
||||
try:
|
||||
# Read JSON file
|
||||
with open(filepath, 'r', encoding=encoding) as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Validate against schema if provided
|
||||
if validate_schema:
|
||||
if util.find_spec('jsonschema') is None:
|
||||
error_msg = "jsonschema package is not available. Please install it using: pip install jsonschema"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
import jsonschema
|
||||
try:
|
||||
jsonschema.validate(instance=data, schema=validate_schema)
|
||||
except jsonschema.exceptions.ValidationError as e:
|
||||
error_msg = f"JSON validation failed: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading JSON file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def write_json(
|
||||
self,
|
||||
data: Union[Dict[str, Any], List[Any]],
|
||||
filepath: str,
|
||||
encoding: str = 'utf-8',
|
||||
indent: int = 2,
|
||||
sort_keys: bool = False,
|
||||
ensure_ascii: bool = False
|
||||
) -> bool:
|
||||
"""Write data to a JSON file.
|
||||
|
||||
Args:
|
||||
data: Data to write
|
||||
filepath: Output file path
|
||||
encoding: File encoding
|
||||
indent: Number of spaces for indentation
|
||||
sort_keys: Sort dictionary keys
|
||||
ensure_ascii: Escape non-ASCII characters
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
with open(filepath, 'w', encoding=encoding) as f:
|
||||
json.dump(
|
||||
data,
|
||||
f,
|
||||
indent=indent,
|
||||
sort_keys=sort_keys,
|
||||
ensure_ascii=ensure_ascii
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing JSON file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def merge_json(
|
||||
self,
|
||||
files: List[str],
|
||||
output_file: str,
|
||||
merge_arrays: bool = True,
|
||||
overwrite_duplicates: bool = True
|
||||
) -> bool:
|
||||
"""Merge multiple JSON files.
|
||||
|
||||
Args:
|
||||
files: List of JSON files to merge
|
||||
output_file: Output file path
|
||||
merge_arrays: Merge arrays instead of overwriting
|
||||
overwrite_duplicates: Overwrite duplicate keys
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
if len(files) < 2:
|
||||
raise ValueError("At least two files are required for merging")
|
||||
|
||||
# Read first file
|
||||
result = self.read_json(files[0])
|
||||
|
||||
# Merge with remaining files
|
||||
for file in files[1:]:
|
||||
data = self.read_json(file)
|
||||
result = self._deep_merge(
|
||||
result,
|
||||
data,
|
||||
merge_arrays=merge_arrays,
|
||||
overwrite_duplicates=overwrite_duplicates
|
||||
)
|
||||
|
||||
# Write merged result
|
||||
return self.write_json(result, output_file)
|
||||
except Exception as e:
|
||||
error_msg = f"Error merging JSON files: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def _deep_merge(
|
||||
self,
|
||||
dict1: Dict[str, Any],
|
||||
dict2: Dict[str, Any],
|
||||
merge_arrays: bool = True,
|
||||
overwrite_duplicates: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""Deep merge two dictionaries.
|
||||
|
||||
Args:
|
||||
dict1: First dictionary
|
||||
dict2: Second dictionary
|
||||
merge_arrays: Merge arrays instead of overwriting
|
||||
overwrite_duplicates: Overwrite duplicate keys
|
||||
|
||||
Returns:
|
||||
Merged dictionary
|
||||
"""
|
||||
result = dict1.copy()
|
||||
|
||||
for key, value in dict2.items():
|
||||
if key in result:
|
||||
if isinstance(result[key], dict) and isinstance(value, dict):
|
||||
result[key] = self._deep_merge(
|
||||
result[key],
|
||||
value,
|
||||
merge_arrays=merge_arrays,
|
||||
overwrite_duplicates=overwrite_duplicates
|
||||
)
|
||||
elif isinstance(result[key], list) and isinstance(value, list):
|
||||
if merge_arrays:
|
||||
result[key].extend(value)
|
||||
elif overwrite_duplicates:
|
||||
result[key] = value
|
||||
elif overwrite_duplicates:
|
||||
result[key] = value
|
||||
else:
|
||||
result[key] = value
|
||||
|
||||
return result
|
||||
|
||||
def validate_json(
|
||||
self,
|
||||
data: Union[Dict[str, Any], str],
|
||||
schema: Dict[str, Any]
|
||||
) -> Tuple[bool, Optional[str]]:
|
||||
"""Validate JSON data against a schema.
|
||||
|
||||
Args:
|
||||
data: JSON data or filepath
|
||||
schema: JSON schema for validation
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, error_message)
|
||||
"""
|
||||
try:
|
||||
if util.find_spec('jsonschema') is None:
|
||||
error_msg = "jsonschema package is not available. Please install it using: pip install jsonschema"
|
||||
logging.error(error_msg)
|
||||
return False, error_msg
|
||||
import jsonschema
|
||||
|
||||
# Load data if filepath provided
|
||||
if isinstance(data, str):
|
||||
with open(data, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
jsonschema.validate(instance=data, schema=schema)
|
||||
return True, None
|
||||
|
||||
except jsonschema.exceptions.ValidationError as e:
|
||||
error_msg = f"JSON validation failed: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False, error_msg
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error validating JSON: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False, error_msg
|
||||
|
||||
def analyze_json(
|
||||
self,
|
||||
data: Union[Dict[str, Any], str],
|
||||
max_depth: int = 10
|
||||
) -> Dict[str, Any]:
|
||||
"""Analyze JSON data structure.
|
||||
|
||||
Args:
|
||||
data: JSON data or filepath
|
||||
max_depth: Maximum depth to analyze
|
||||
|
||||
Returns:
|
||||
Dict with analysis results
|
||||
"""
|
||||
try:
|
||||
# Load data if filepath provided
|
||||
if isinstance(data, str):
|
||||
data = self.read_json(data)
|
||||
|
||||
def analyze_value(value: Any, depth: int = 0) -> Dict[str, Any]:
|
||||
if depth >= max_depth:
|
||||
return {'type': 'max_depth_reached'}
|
||||
|
||||
result = {'type': type(value).__name__}
|
||||
|
||||
if isinstance(value, dict):
|
||||
result['size'] = len(value)
|
||||
result['keys'] = list(value.keys())
|
||||
if depth < max_depth - 1:
|
||||
result['children'] = {
|
||||
k: analyze_value(v, depth + 1)
|
||||
for k, v in value.items()
|
||||
}
|
||||
|
||||
elif isinstance(value, list):
|
||||
result['length'] = len(value)
|
||||
if value:
|
||||
result['element_types'] = list(set(
|
||||
type(x).__name__ for x in value
|
||||
))
|
||||
if depth < max_depth - 1:
|
||||
result['sample_elements'] = [
|
||||
analyze_value(x, depth + 1)
|
||||
for x in value[:5]
|
||||
]
|
||||
|
||||
elif isinstance(value, (int, float)):
|
||||
result.update({
|
||||
'value': value,
|
||||
'is_integer': isinstance(value, int)
|
||||
})
|
||||
|
||||
elif isinstance(value, str):
|
||||
result.update({
|
||||
'length': len(value),
|
||||
'sample': value[:100] if len(value) > 100 else value
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
return {
|
||||
'analysis_time': datetime.now().isoformat(),
|
||||
'structure': analyze_value(data)
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error analyzing JSON: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {}
|
||||
|
||||
def transform_json(
|
||||
self,
|
||||
data: Union[Dict[str, Any], str],
|
||||
transformations: List[Dict[str, Any]]
|
||||
) -> Dict[str, Any]:
|
||||
"""Transform JSON data using a list of operations.
|
||||
|
||||
Args:
|
||||
data: JSON data or filepath
|
||||
transformations: List of transformation operations
|
||||
|
||||
Returns:
|
||||
Transformed JSON data
|
||||
"""
|
||||
try:
|
||||
# Load data if filepath provided
|
||||
if isinstance(data, str):
|
||||
data = self.read_json(data)
|
||||
|
||||
result = data.copy()
|
||||
|
||||
for transform in transformations:
|
||||
op = transform.get('operation')
|
||||
path = transform.get('path', '').split('.')
|
||||
value = transform.get('value')
|
||||
|
||||
if op == 'set':
|
||||
self._set_value(result, path, value)
|
||||
elif op == 'delete':
|
||||
self._delete_value(result, path)
|
||||
elif op == 'rename':
|
||||
old_path = path
|
||||
new_path = value.split('.')
|
||||
self._rename_key(result, old_path, new_path)
|
||||
elif op == 'move':
|
||||
old_path = path
|
||||
new_path = value.split('.')
|
||||
self._move_value(result, old_path, new_path)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error transforming JSON: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return data
|
||||
|
||||
def _set_value(self, data: Dict[str, Any], path: List[str], value: Any):
|
||||
"""Set a value at the specified path."""
|
||||
current = data
|
||||
for key in path[:-1]:
|
||||
if key not in current:
|
||||
current[key] = {}
|
||||
current = current[key]
|
||||
current[path[-1]] = value
|
||||
|
||||
def _delete_value(self, data: Dict[str, Any], path: List[str]):
|
||||
"""Delete a value at the specified path."""
|
||||
current = data
|
||||
for key in path[:-1]:
|
||||
if key not in current:
|
||||
return
|
||||
current = current[key]
|
||||
if path[-1] in current:
|
||||
del current[path[-1]]
|
||||
|
||||
def _rename_key(
|
||||
self,
|
||||
data: Dict[str, Any],
|
||||
old_path: List[str],
|
||||
new_path: List[str]
|
||||
):
|
||||
"""Rename a key at the specified path."""
|
||||
value = self._get_value(data, old_path)
|
||||
if value is not None:
|
||||
self._delete_value(data, old_path)
|
||||
self._set_value(data, new_path, value)
|
||||
|
||||
def _move_value(
|
||||
self,
|
||||
data: Dict[str, Any],
|
||||
old_path: List[str],
|
||||
new_path: List[str]
|
||||
):
|
||||
"""Move a value from one path to another."""
|
||||
self._rename_key(data, old_path, new_path)
|
||||
|
||||
def _get_value(
|
||||
self,
|
||||
data: Dict[str, Any],
|
||||
path: List[str]
|
||||
) -> Optional[Any]:
|
||||
"""Get a value at the specified path."""
|
||||
current = data
|
||||
for key in path:
|
||||
if key not in current:
|
||||
return None
|
||||
current = current[key]
|
||||
return current
|
||||
|
||||
# Create instance for direct function access
|
||||
_json_tools = JSONTools()
|
||||
read_json = _json_tools.read_json
|
||||
write_json = _json_tools.write_json
|
||||
merge_json = _json_tools.merge_json
|
||||
validate_json = _json_tools.validate_json
|
||||
analyze_json = _json_tools.analyze_json
|
||||
transform_json = _json_tools.transform_json
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("JSONTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Sample data
|
||||
data1 = {
|
||||
'id': 1,
|
||||
'name': 'Alice',
|
||||
'scores': [95, 87, 92],
|
||||
'details': {
|
||||
'age': 25,
|
||||
'city': 'New York'
|
||||
}
|
||||
}
|
||||
|
||||
data2 = {
|
||||
'id': 2,
|
||||
'name': 'Bob',
|
||||
'scores': [88, 90, 85],
|
||||
'details': {
|
||||
'age': 30,
|
||||
'country': 'USA'
|
||||
}
|
||||
}
|
||||
|
||||
# 1. Write JSON files
|
||||
print("1. Writing JSON Files")
|
||||
print("------------------------------")
|
||||
success = write_json(data1, 'test1.json')
|
||||
print(f"First file written: {success}")
|
||||
success = write_json(data2, 'test2.json')
|
||||
print(f"Second file written: {success}")
|
||||
print()
|
||||
|
||||
# 2. Read JSON file
|
||||
print("2. Reading JSON File")
|
||||
print("------------------------------")
|
||||
data = read_json('test1.json')
|
||||
print("Contents of test1.json:")
|
||||
print(json.dumps(data, indent=2))
|
||||
print()
|
||||
|
||||
# 3. Merge JSON files
|
||||
print("3. Merging JSON Files")
|
||||
print("------------------------------")
|
||||
success = merge_json(['test1.json', 'test2.json'], 'merged.json')
|
||||
print(f"Files merged: {success}")
|
||||
if success:
|
||||
print("Merged contents:")
|
||||
print(json.dumps(read_json('merged.json'), indent=2))
|
||||
print()
|
||||
|
||||
# 4. Validate JSON
|
||||
print("4. Validating JSON")
|
||||
print("------------------------------")
|
||||
schema = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'integer'},
|
||||
'name': {'type': 'string'},
|
||||
'scores': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'number'}
|
||||
},
|
||||
'details': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'age': {'type': 'integer'},
|
||||
'city': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'required': ['id', 'name']
|
||||
}
|
||||
|
||||
is_valid, error = validate_json(data1, schema)
|
||||
print(f"Validation result: {is_valid}")
|
||||
if error:
|
||||
print(f"Validation error: {error}")
|
||||
print()
|
||||
|
||||
# 5. Analyze JSON
|
||||
print("5. Analyzing JSON")
|
||||
print("------------------------------")
|
||||
analysis = analyze_json(data1)
|
||||
print("Analysis results:")
|
||||
print(json.dumps(analysis, indent=2))
|
||||
print()
|
||||
|
||||
# 6. Transform JSON
|
||||
print("6. Transforming JSON")
|
||||
print("------------------------------")
|
||||
transformations = [
|
||||
{
|
||||
'operation': 'set',
|
||||
'path': 'details.status',
|
||||
'value': 'active'
|
||||
},
|
||||
{
|
||||
'operation': 'rename',
|
||||
'path': 'details.city',
|
||||
'value': 'details.location'
|
||||
}
|
||||
]
|
||||
|
||||
transformed = transform_json(data1, transformations)
|
||||
print("Transformed data:")
|
||||
print(json.dumps(transformed, indent=2))
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,354 @@
|
||||
"""Newspaper tools for scraping and parsing news articles.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import newspaper_tools
|
||||
article = newspaper_tools.get_article("https://example.com/article")
|
||||
sources = newspaper_tools.get_news_sources("technology")
|
||||
articles = newspaper_tools.get_articles_from_source("https://techcrunch.com")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import get_article, get_news_sources
|
||||
article = get_article("https://example.com/article")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any
|
||||
from importlib import util
|
||||
import json
|
||||
from urllib.parse import urlparse
|
||||
|
||||
# Predefined list of popular news sources
|
||||
POPULAR_NEWS_SOURCES = {
|
||||
'technology': [
|
||||
'https://techcrunch.com',
|
||||
'https://www.theverge.com',
|
||||
'https://www.wired.com',
|
||||
'https://www.engadget.com',
|
||||
'https://arstechnica.com'
|
||||
],
|
||||
'business': [
|
||||
'https://www.bloomberg.com',
|
||||
'https://www.reuters.com',
|
||||
'https://www.wsj.com',
|
||||
'https://www.ft.com',
|
||||
'https://www.cnbc.com'
|
||||
],
|
||||
'general': [
|
||||
'https://www.nytimes.com',
|
||||
'https://www.theguardian.com',
|
||||
'https://www.washingtonpost.com',
|
||||
'https://www.bbc.com',
|
||||
'https://www.cnn.com'
|
||||
],
|
||||
'sports': [
|
||||
'https://www.espn.com',
|
||||
'https://sports.yahoo.com',
|
||||
'https://www.cbssports.com',
|
||||
'https://www.skysports.com',
|
||||
'https://www.bleacherreport.com'
|
||||
],
|
||||
'entertainment': [
|
||||
'https://variety.com',
|
||||
'https://www.hollywoodreporter.com',
|
||||
'https://www.ew.com',
|
||||
'https://www.deadline.com',
|
||||
'https://www.imdb.com/news'
|
||||
],
|
||||
'science': [
|
||||
'https://www.scientificamerican.com',
|
||||
'https://www.sciencedaily.com',
|
||||
'https://www.newscientist.com',
|
||||
'https://www.sciencemag.org',
|
||||
'https://www.nature.com/news'
|
||||
]
|
||||
}
|
||||
|
||||
class NewspaperTools:
|
||||
"""Tools for scraping and parsing news articles."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize NewspaperTools and check for newspaper package."""
|
||||
self._check_newspaper()
|
||||
|
||||
def _check_newspaper(self):
|
||||
"""Check if newspaper package is installed."""
|
||||
if util.find_spec("newspaper") is None:
|
||||
raise ImportError("newspaper3k package is not available. Please install it using: pip install newspaper3k")
|
||||
global newspaper
|
||||
import newspaper
|
||||
|
||||
def get_article(
|
||||
self,
|
||||
url: str,
|
||||
language: str = 'en'
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Extract and parse a news article from a URL.
|
||||
|
||||
Args:
|
||||
url: URL of the article
|
||||
language: Language code (e.g., 'en' for English)
|
||||
|
||||
Returns:
|
||||
Dict: Article information including title, text, authors, etc.
|
||||
"""
|
||||
try:
|
||||
from newspaper import Article, Config
|
||||
|
||||
# Configure article download
|
||||
config = Config()
|
||||
config.browser_user_agent = 'Mozilla/5.0'
|
||||
config.language = language
|
||||
|
||||
# Download and parse article
|
||||
article = Article(url, config=config)
|
||||
article.download()
|
||||
article.parse()
|
||||
|
||||
# Try to extract additional information
|
||||
try:
|
||||
article.nlp()
|
||||
except Exception as e:
|
||||
logging.warning(f"NLP processing failed: {str(e)}")
|
||||
|
||||
# Build response
|
||||
response = {
|
||||
"url": url,
|
||||
"title": article.title,
|
||||
"text": article.text,
|
||||
"authors": article.authors,
|
||||
"publish_date": article.publish_date.isoformat() if article.publish_date else None,
|
||||
"top_image": article.top_image,
|
||||
"images": list(article.images),
|
||||
"movies": list(article.movies),
|
||||
"source_domain": urlparse(url).netloc,
|
||||
}
|
||||
|
||||
# Add NLP results if available
|
||||
if hasattr(article, 'keywords') and article.keywords:
|
||||
response["keywords"] = article.keywords
|
||||
if hasattr(article, 'summary') and article.summary:
|
||||
response["summary"] = article.summary
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
error_msg = f"Error extracting article from {url}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_news_sources(
|
||||
self,
|
||||
category: Optional[str] = None,
|
||||
language: str = 'en',
|
||||
country: Optional[str] = None
|
||||
) -> Union[List[Dict[str, str]], Dict[str, str]]:
|
||||
"""
|
||||
Get a list of news sources, optionally filtered by category.
|
||||
|
||||
Args:
|
||||
category: Category to filter by (e.g., 'technology', 'sports')
|
||||
language: Language code
|
||||
country: Country code
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: List of news sources or error dict
|
||||
"""
|
||||
try:
|
||||
sources = []
|
||||
|
||||
# Get sources for the specified category or all categories
|
||||
if category:
|
||||
category = category.lower()
|
||||
if category in POPULAR_NEWS_SOURCES:
|
||||
urls = POPULAR_NEWS_SOURCES[category]
|
||||
else:
|
||||
urls = []
|
||||
for cat_urls in POPULAR_NEWS_SOURCES.values():
|
||||
urls.extend(cat_urls)
|
||||
else:
|
||||
urls = []
|
||||
for cat_urls in POPULAR_NEWS_SOURCES.values():
|
||||
urls.extend(cat_urls)
|
||||
|
||||
# Create source objects
|
||||
for url in urls:
|
||||
domain = urlparse(url).netloc
|
||||
source = {
|
||||
"url": url,
|
||||
"domain": domain,
|
||||
"name": domain.replace("www.", "").split(".")[0].title(),
|
||||
"category": category if category else "general"
|
||||
}
|
||||
sources.append(source)
|
||||
|
||||
return sources
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting news sources: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_articles_from_source(
|
||||
self,
|
||||
source_url: str,
|
||||
limit: int = 10,
|
||||
language: str = 'en'
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""
|
||||
Get recent articles from a news source.
|
||||
|
||||
Args:
|
||||
source_url: URL of the news source
|
||||
limit: Maximum number of articles to return
|
||||
language: Language code
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: List of articles or error dict
|
||||
"""
|
||||
try:
|
||||
from newspaper import Source, Config
|
||||
|
||||
# Configure source scraping
|
||||
config = Config()
|
||||
config.browser_user_agent = 'Mozilla/5.0'
|
||||
config.language = language
|
||||
config.fetch_images = False # Speed up processing
|
||||
|
||||
# Build news source
|
||||
source = Source(source_url, config=config)
|
||||
source.build()
|
||||
|
||||
# Get articles
|
||||
articles = []
|
||||
for article_url in source.article_urls()[:limit]:
|
||||
try:
|
||||
article = self.get_article(article_url, language)
|
||||
if "error" not in article:
|
||||
articles.append(article)
|
||||
except Exception as e:
|
||||
logging.warning(f"Error processing article {article_url}: {str(e)}")
|
||||
continue
|
||||
|
||||
if len(articles) >= limit:
|
||||
break
|
||||
|
||||
return articles
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting articles from {source_url}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_trending_topics(
|
||||
self,
|
||||
sources: Optional[List[str]] = None,
|
||||
limit: int = 10,
|
||||
language: str = 'en'
|
||||
) -> Union[List[str], Dict[str, str]]:
|
||||
"""
|
||||
Get trending topics across news sources.
|
||||
|
||||
Args:
|
||||
sources: List of source URLs to analyze
|
||||
limit: Maximum number of trending topics to return
|
||||
language: Language code
|
||||
|
||||
Returns:
|
||||
List[str] or Dict: List of trending topics or error dict
|
||||
"""
|
||||
try:
|
||||
from collections import Counter
|
||||
|
||||
# Use default sources if none provided
|
||||
if not sources:
|
||||
sources_data = self.get_news_sources(language=language)
|
||||
if isinstance(sources_data, dict) and "error" in sources_data:
|
||||
return sources_data
|
||||
sources = [s["url"] for s in sources_data[:5]] # Use top 5 sources
|
||||
|
||||
# Collect keywords from articles
|
||||
all_keywords = []
|
||||
for source_url in sources:
|
||||
try:
|
||||
articles = self.get_articles_from_source(source_url, limit=5, language=language)
|
||||
if isinstance(articles, list):
|
||||
for article in articles:
|
||||
if "keywords" in article:
|
||||
all_keywords.extend(article["keywords"])
|
||||
except Exception as e:
|
||||
logging.warning(f"Error processing source {source_url}: {str(e)}")
|
||||
continue
|
||||
|
||||
# Get most common keywords
|
||||
trending = Counter(all_keywords).most_common(limit)
|
||||
return [topic for topic, count in trending]
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting trending topics: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Create instance for direct function access
|
||||
_newspaper_tools = NewspaperTools()
|
||||
get_article = _newspaper_tools.get_article
|
||||
get_news_sources = _newspaper_tools.get_news_sources
|
||||
get_articles_from_source = _newspaper_tools.get_articles_from_source
|
||||
get_trending_topics = _newspaper_tools.get_trending_topics
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("NewspaperTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# 1. Get news sources
|
||||
print("1. Getting News Sources")
|
||||
print("------------------------------")
|
||||
tech_sources = get_news_sources("technology")
|
||||
print("Technology news sources:")
|
||||
if isinstance(tech_sources, list):
|
||||
print(json.dumps(tech_sources[:3], indent=2)) # Show first 3 sources
|
||||
else:
|
||||
print(tech_sources) # Show error
|
||||
print()
|
||||
|
||||
if isinstance(tech_sources, list) and tech_sources:
|
||||
source_url = tech_sources[0]["url"]
|
||||
|
||||
# 2. Get articles from a source
|
||||
print("2. Getting Articles from Source")
|
||||
print("------------------------------")
|
||||
articles = get_articles_from_source(source_url, limit=2)
|
||||
print(f"Articles from {source_url}:")
|
||||
if isinstance(articles, list):
|
||||
for article in articles:
|
||||
print(f"- {article['title']}")
|
||||
if "summary" in article:
|
||||
print(f" Summary: {article['summary'][:200]}...")
|
||||
else:
|
||||
print(articles) # Show error
|
||||
print()
|
||||
|
||||
# 3. Get a single article
|
||||
print("3. Getting Single Article")
|
||||
print("------------------------------")
|
||||
if isinstance(articles, list) and articles:
|
||||
article_url = articles[0]["url"]
|
||||
article = get_article(article_url)
|
||||
if "error" not in article:
|
||||
print(f"Article: {article['title']}")
|
||||
if "summary" in article:
|
||||
print(f"Summary: {article['summary'][:200]}...")
|
||||
print(f"Authors: {', '.join(article['authors'])}")
|
||||
print(f"Date: {article['publish_date']}")
|
||||
else:
|
||||
print(article) # Show error
|
||||
print()
|
||||
|
||||
# 4. Get trending topics
|
||||
print("4. Getting Trending Topics")
|
||||
print("------------------------------")
|
||||
topics = get_trending_topics([source_url], limit=5)
|
||||
print("Trending topics:")
|
||||
print(json.dumps(topics, indent=2))
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,329 @@
|
||||
"""Pandas tools for data manipulation and analysis.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import pandas_tools
|
||||
df = pandas_tools.read_csv("data.csv")
|
||||
df = pandas_tools.filter_data(df, "column > 5")
|
||||
summary = pandas_tools.get_summary(df)
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_csv, filter_data, get_summary
|
||||
df = read_csv("data.csv")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any
|
||||
from importlib import util
|
||||
import json
|
||||
import os
|
||||
|
||||
# Import pandas for type hints, but don't use it until we check it's installed
|
||||
if util.find_spec("pandas") is not None:
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
else:
|
||||
# Create a placeholder for type hints
|
||||
class pd:
|
||||
DataFrame = None
|
||||
|
||||
def _convert_to_serializable(obj: Any) -> Any:
|
||||
"""Convert numpy/pandas types to JSON serializable Python types."""
|
||||
if isinstance(obj, (np.integer, np.floating)):
|
||||
return obj.item()
|
||||
elif isinstance(obj, np.ndarray):
|
||||
return obj.tolist()
|
||||
elif isinstance(obj, pd.Series):
|
||||
return obj.to_list()
|
||||
elif isinstance(obj, pd.DataFrame):
|
||||
return obj.to_dict(orient='records')
|
||||
return obj
|
||||
|
||||
class PandasTools:
|
||||
"""Tools for data manipulation and analysis using pandas."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize PandasTools and check for pandas installation."""
|
||||
self._check_pandas()
|
||||
|
||||
def _check_pandas(self):
|
||||
"""Check if pandas is installed."""
|
||||
if util.find_spec("pandas") is None:
|
||||
raise ImportError("pandas is not available. Please install it using: pip install pandas")
|
||||
global pd, np
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
def read_csv(self, filepath: str, **kwargs) -> Union[pd.DataFrame, Dict[str, str]]:
|
||||
"""
|
||||
Read a CSV file into a pandas DataFrame.
|
||||
|
||||
Args:
|
||||
filepath: Path to the CSV file
|
||||
**kwargs: Additional arguments to pass to pd.read_csv()
|
||||
|
||||
Returns:
|
||||
pd.DataFrame or Dict: DataFrame if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
return pd.read_csv(filepath, **kwargs)
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading CSV file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def read_excel(self, filepath: str, **kwargs) -> Union[pd.DataFrame, Dict[str, str]]:
|
||||
"""
|
||||
Read an Excel file into a pandas DataFrame.
|
||||
|
||||
Args:
|
||||
filepath: Path to the Excel file
|
||||
**kwargs: Additional arguments to pass to pd.read_excel()
|
||||
|
||||
Returns:
|
||||
pd.DataFrame or Dict: DataFrame if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
return pd.read_excel(filepath, **kwargs)
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading Excel file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def write_csv(self, df: pd.DataFrame, filepath: str, **kwargs) -> bool:
|
||||
"""
|
||||
Write DataFrame to a CSV file.
|
||||
|
||||
Args:
|
||||
df: DataFrame to write
|
||||
filepath: Output file path
|
||||
**kwargs: Additional arguments to pass to df.to_csv()
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
df.to_csv(filepath, **kwargs)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing CSV file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def write_excel(self, df: pd.DataFrame, filepath: str, **kwargs) -> bool:
|
||||
"""
|
||||
Write DataFrame to an Excel file.
|
||||
|
||||
Args:
|
||||
df: DataFrame to write
|
||||
filepath: Output file path
|
||||
**kwargs: Additional arguments to pass to df.to_excel()
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
df.to_excel(filepath, **kwargs)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing Excel file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def filter_data(self, df: pd.DataFrame, query: str) -> Union[pd.DataFrame, Dict[str, str]]:
|
||||
"""
|
||||
Filter DataFrame using a query string.
|
||||
|
||||
Args:
|
||||
df: Input DataFrame
|
||||
query: Query string (e.g., "column > 5 and other_column == 'value'")
|
||||
|
||||
Returns:
|
||||
pd.DataFrame or Dict: Filtered DataFrame if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
return df.query(query)
|
||||
except Exception as e:
|
||||
error_msg = f"Error filtering data with query '{query}': {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_summary(self, df: pd.DataFrame) -> Dict[str, Any]:
|
||||
"""
|
||||
Get a summary of the DataFrame including basic statistics and info.
|
||||
|
||||
Args:
|
||||
df: Input DataFrame
|
||||
|
||||
Returns:
|
||||
Dict: Summary statistics and information
|
||||
"""
|
||||
try:
|
||||
if not isinstance(df, pd.DataFrame):
|
||||
raise TypeError(f"Expected pandas DataFrame, got {type(df).__name__}")
|
||||
|
||||
numeric_summary = df.describe().to_dict()
|
||||
# Convert numpy types to native Python types
|
||||
for col in numeric_summary:
|
||||
numeric_summary[col] = {k: _convert_to_serializable(v)
|
||||
for k, v in numeric_summary[col].items()}
|
||||
|
||||
summary = {
|
||||
"shape": list(df.shape),
|
||||
"columns": list(df.columns),
|
||||
"dtypes": df.dtypes.astype(str).to_dict(),
|
||||
"null_counts": df.isnull().sum().to_dict(),
|
||||
"numeric_summary": numeric_summary,
|
||||
"memory_usage": int(df.memory_usage(deep=True).sum()),
|
||||
}
|
||||
return summary
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting data summary: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def group_by(
|
||||
self,
|
||||
df: pd.DataFrame,
|
||||
columns: Union[str, List[str]],
|
||||
agg_dict: Dict[str, Union[str, List[str]]]
|
||||
) -> Union[pd.DataFrame, Dict[str, str]]:
|
||||
"""
|
||||
Group DataFrame by columns and apply aggregation functions.
|
||||
|
||||
Args:
|
||||
df: Input DataFrame
|
||||
columns: Column(s) to group by
|
||||
agg_dict: Dictionary of column:function pairs for aggregation
|
||||
|
||||
Returns:
|
||||
pd.DataFrame or Dict: Grouped DataFrame if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
return df.groupby(columns).agg(agg_dict).reset_index()
|
||||
except Exception as e:
|
||||
error_msg = f"Error grouping data: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def pivot_table(
|
||||
self,
|
||||
df: pd.DataFrame,
|
||||
index: Union[str, List[str]],
|
||||
columns: Optional[Union[str, List[str]]] = None,
|
||||
values: Optional[Union[str, List[str]]] = None,
|
||||
aggfunc: str = "mean"
|
||||
) -> Union[pd.DataFrame, Dict[str, str]]:
|
||||
"""
|
||||
Create a pivot table from DataFrame.
|
||||
|
||||
Args:
|
||||
df: Input DataFrame
|
||||
index: Column(s) to use as index
|
||||
columns: Column(s) to use as columns
|
||||
values: Column(s) to aggregate
|
||||
aggfunc: Aggregation function to use
|
||||
|
||||
Returns:
|
||||
pd.DataFrame or Dict: Pivot table if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
return pd.pivot_table(
|
||||
df,
|
||||
index=index,
|
||||
columns=columns,
|
||||
values=values,
|
||||
aggfunc=aggfunc
|
||||
).reset_index()
|
||||
except Exception as e:
|
||||
error_msg = f"Error creating pivot table: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Create instance for direct function access
|
||||
_pandas_tools = PandasTools()
|
||||
read_csv = _pandas_tools.read_csv
|
||||
read_excel = _pandas_tools.read_excel
|
||||
write_csv = _pandas_tools.write_csv
|
||||
write_excel = _pandas_tools.write_excel
|
||||
filter_data = _pandas_tools.filter_data
|
||||
get_summary = _pandas_tools.get_summary
|
||||
group_by = _pandas_tools.group_by
|
||||
pivot_table = _pandas_tools.pivot_table
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("PandasTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Create a test directory
|
||||
test_dir = os.path.join(os.getcwd(), "test_files")
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
|
||||
# Create a sample DataFrame
|
||||
df = pd.DataFrame({
|
||||
'name': ['John', 'Jane', 'Bob', 'Alice', 'Charlie'],
|
||||
'age': [25, 30, 35, 28, 32],
|
||||
'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
|
||||
'salary': [50000, 60000, 75000, 65000, 55000]
|
||||
})
|
||||
|
||||
# 1. Write to CSV
|
||||
print("1. Writing to CSV")
|
||||
print("------------------------------")
|
||||
csv_file = os.path.join(test_dir, "sample.csv")
|
||||
success = write_csv(df, csv_file, index=False)
|
||||
print(f"Write successful: {success}\n")
|
||||
|
||||
# 2. Read from CSV
|
||||
print("2. Reading from CSV")
|
||||
print("------------------------------")
|
||||
df_read = read_csv(csv_file)
|
||||
print("First few rows:")
|
||||
print(df_read.head())
|
||||
print()
|
||||
|
||||
# 3. Filter Data
|
||||
print("3. Filtering Data")
|
||||
print("------------------------------")
|
||||
filtered_df = filter_data(df, "age > 30 and salary > 60000")
|
||||
print("People over 30 with salary > 60000:")
|
||||
print(filtered_df)
|
||||
print()
|
||||
|
||||
# 4. Get Summary
|
||||
print("4. Data Summary")
|
||||
print("------------------------------")
|
||||
summary = get_summary(df)
|
||||
print(json.dumps(summary, indent=2))
|
||||
print()
|
||||
|
||||
# 5. Group By
|
||||
print("5. Group By")
|
||||
print("------------------------------")
|
||||
grouped = group_by(df, "city", {"salary": ["mean", "count"], "age": "mean"})
|
||||
print("Statistics by city:")
|
||||
print(grouped)
|
||||
print()
|
||||
|
||||
# 6. Pivot Table
|
||||
print("6. Pivot Table")
|
||||
print("------------------------------")
|
||||
pivoted = pivot_table(df, index="city", values=["salary", "age"])
|
||||
print("Pivot table by city:")
|
||||
print(pivoted)
|
||||
print()
|
||||
|
||||
# Clean up test directory
|
||||
try:
|
||||
import shutil
|
||||
shutil.rmtree(test_dir)
|
||||
print("Test directory cleaned up successfully")
|
||||
except Exception as e:
|
||||
print(f"Error cleaning up test directory: {str(e)}")
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,423 @@
|
||||
"""Tools for Python code execution and analysis.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import python_tools
|
||||
result = python_tools.execute_code("print('Hello, World!')")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import execute_code, analyze_code, format_code
|
||||
result = execute_code("print('Hello, World!')")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Optional, Any
|
||||
from importlib import util
|
||||
import io
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
import traceback
|
||||
|
||||
class PythonTools:
|
||||
"""Tools for Python code execution and analysis."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize PythonTools."""
|
||||
self._check_dependencies()
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if required packages are installed."""
|
||||
missing = []
|
||||
for package in ['black', 'pylint', 'autopep8']:
|
||||
if util.find_spec(package) is None:
|
||||
missing.append(package)
|
||||
|
||||
if missing:
|
||||
raise ImportError(
|
||||
f"Required packages not available. Please install: {', '.join(missing)}\n"
|
||||
f"Run: pip install {' '.join(missing)}"
|
||||
)
|
||||
|
||||
def execute_code(
|
||||
self,
|
||||
code: str,
|
||||
globals_dict: Optional[Dict[str, Any]] = None,
|
||||
locals_dict: Optional[Dict[str, Any]] = None,
|
||||
timeout: int = 30,
|
||||
max_output_size: int = 10000
|
||||
) -> Dict[str, Any]:
|
||||
"""Execute Python code safely."""
|
||||
try:
|
||||
# Set up execution environment
|
||||
if globals_dict is None:
|
||||
globals_dict = {'__builtins__': __builtins__}
|
||||
if locals_dict is None:
|
||||
locals_dict = {}
|
||||
|
||||
# Capture output
|
||||
stdout_buffer = io.StringIO()
|
||||
stderr_buffer = io.StringIO()
|
||||
|
||||
try:
|
||||
# Compile code
|
||||
compiled_code = compile(code, '<string>', 'exec')
|
||||
|
||||
# Execute with output capture
|
||||
with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer):
|
||||
exec(compiled_code, globals_dict, locals_dict)
|
||||
|
||||
# Get last expression value if any
|
||||
import ast
|
||||
tree = ast.parse(code)
|
||||
if tree.body and isinstance(tree.body[-1], ast.Expr):
|
||||
result = eval(
|
||||
compile(ast.Expression(tree.body[-1].value), '<string>', 'eval'),
|
||||
globals_dict,
|
||||
locals_dict
|
||||
)
|
||||
else:
|
||||
result = None
|
||||
|
||||
# Get output
|
||||
stdout = stdout_buffer.getvalue()
|
||||
stderr = stderr_buffer.getvalue()
|
||||
|
||||
# Truncate output if too large
|
||||
if len(stdout) > max_output_size:
|
||||
stdout = stdout[:max_output_size] + "...[truncated]"
|
||||
if len(stderr) > max_output_size:
|
||||
stderr = stderr[:max_output_size] + "...[truncated]"
|
||||
|
||||
return {
|
||||
'result': result,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
'success': True
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error executing code: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {
|
||||
'result': None,
|
||||
'stdout': stdout_buffer.getvalue(),
|
||||
'stderr': error_msg,
|
||||
'success': False
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error executing code: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {
|
||||
'result': None,
|
||||
'stdout': '',
|
||||
'stderr': error_msg,
|
||||
'success': False
|
||||
}
|
||||
|
||||
def analyze_code(
|
||||
self,
|
||||
code: str
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Analyze Python code structure and quality.
|
||||
|
||||
Args:
|
||||
code: Python code to analyze
|
||||
|
||||
Returns:
|
||||
Dictionary with analysis results
|
||||
"""
|
||||
try:
|
||||
# Import ast only when needed
|
||||
import ast
|
||||
|
||||
# Parse code
|
||||
tree = ast.parse(code)
|
||||
|
||||
# Analyze structure
|
||||
analysis = {
|
||||
'imports': [],
|
||||
'functions': [],
|
||||
'classes': [],
|
||||
'variables': [],
|
||||
'complexity': {
|
||||
'lines': len(code.splitlines()),
|
||||
'functions': 0,
|
||||
'classes': 0,
|
||||
'branches': 0
|
||||
}
|
||||
}
|
||||
|
||||
# Analyze nodes
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
for name in node.names:
|
||||
analysis['imports'].append(name.name)
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
module = node.module or ''
|
||||
for name in node.names:
|
||||
analysis['imports'].append(f"{module}.{name.name}")
|
||||
elif isinstance(node, ast.FunctionDef):
|
||||
analysis['functions'].append({
|
||||
'name': node.name,
|
||||
'args': [
|
||||
arg.arg for arg in node.args.args
|
||||
],
|
||||
'decorators': [
|
||||
ast.unparse(d) for d in node.decorator_list
|
||||
]
|
||||
})
|
||||
analysis['complexity']['functions'] += 1
|
||||
elif isinstance(node, ast.ClassDef):
|
||||
analysis['classes'].append({
|
||||
'name': node.name,
|
||||
'bases': [
|
||||
ast.unparse(base) for base in node.bases
|
||||
],
|
||||
'decorators': [
|
||||
ast.unparse(d) for d in node.decorator_list
|
||||
]
|
||||
})
|
||||
analysis['complexity']['classes'] += 1
|
||||
elif isinstance(node, ast.Assign):
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
analysis['variables'].append(target.id)
|
||||
elif isinstance(node, (ast.If, ast.While, ast.For)):
|
||||
analysis['complexity']['branches'] += 1
|
||||
|
||||
return analysis
|
||||
except Exception as e:
|
||||
error_msg = f"Error analyzing code: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def format_code(
|
||||
self,
|
||||
code: str,
|
||||
style: str = 'black',
|
||||
line_length: int = 88
|
||||
) -> Optional[str]:
|
||||
"""Format Python code according to style guide.
|
||||
|
||||
Args:
|
||||
code: Python code to format
|
||||
style: Formatting style ('black' or 'pep8')
|
||||
line_length: Maximum line length
|
||||
|
||||
Returns:
|
||||
Formatted code
|
||||
"""
|
||||
try:
|
||||
if util.find_spec(style) is None:
|
||||
error_msg = f"{style} package is not available. Please install it using: pip install {style}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
if style == 'black':
|
||||
import black
|
||||
return black.format_str(
|
||||
code,
|
||||
mode=black.FileMode(
|
||||
line_length=line_length
|
||||
)
|
||||
)
|
||||
else: # pep8
|
||||
import autopep8
|
||||
return autopep8.fix_code(
|
||||
code,
|
||||
options={
|
||||
'max_line_length': line_length
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
error_msg = f"Error formatting code: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def lint_code(
|
||||
self,
|
||||
code: str
|
||||
) -> Optional[Dict[str, List[Dict[str, Any]]]]:
|
||||
"""Lint Python code for potential issues.
|
||||
|
||||
Args:
|
||||
code: Python code to lint
|
||||
|
||||
Returns:
|
||||
Dictionary with linting results
|
||||
"""
|
||||
try:
|
||||
if util.find_spec('pylint') is None:
|
||||
error_msg = "pylint package is not available. Please install it using: pip install pylint"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
# Import pylint only when needed
|
||||
from pylint.reporters import JSONReporter
|
||||
from pylint.lint.run import Run
|
||||
|
||||
# Create temporary file for pylint
|
||||
import tempfile
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode='w',
|
||||
suffix='.py',
|
||||
delete=False
|
||||
) as f:
|
||||
f.write(code)
|
||||
temp_path = f.name
|
||||
|
||||
# Run pylint
|
||||
reporter = JSONReporter()
|
||||
Run(
|
||||
[temp_path],
|
||||
reporter=reporter,
|
||||
exit=False
|
||||
)
|
||||
|
||||
# Process results
|
||||
results = {
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'conventions': []
|
||||
}
|
||||
|
||||
for msg in reporter.messages:
|
||||
item = {
|
||||
'type': msg.category,
|
||||
'module': msg.module,
|
||||
'obj': msg.obj,
|
||||
'line': msg.line,
|
||||
'column': msg.column,
|
||||
'path': msg.path,
|
||||
'symbol': msg.symbol,
|
||||
'message': msg.msg,
|
||||
'message-id': msg.msg_id
|
||||
}
|
||||
|
||||
if msg.category in ['error', 'fatal']:
|
||||
results['errors'].append(item)
|
||||
elif msg.category == 'warning':
|
||||
results['warnings'].append(item)
|
||||
else:
|
||||
results['conventions'].append(item)
|
||||
|
||||
# Clean up
|
||||
import os
|
||||
os.unlink(temp_path)
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
error_msg = f"Error linting code: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'conventions': []
|
||||
}
|
||||
|
||||
def disassemble_code(
|
||||
self,
|
||||
code: str
|
||||
) -> Optional[str]:
|
||||
"""Disassemble Python code to bytecode.
|
||||
|
||||
Args:
|
||||
code: Python code to disassemble
|
||||
|
||||
Returns:
|
||||
Disassembled bytecode as string
|
||||
"""
|
||||
try:
|
||||
# Import dis only when needed
|
||||
import dis
|
||||
|
||||
# Compile code
|
||||
compiled_code = compile(code, '<string>', 'exec')
|
||||
|
||||
# Capture disassembly
|
||||
output = io.StringIO()
|
||||
with redirect_stdout(output):
|
||||
dis.dis(compiled_code)
|
||||
|
||||
return output.getvalue()
|
||||
except Exception as e:
|
||||
error_msg = f"Error disassembling code: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
# Create instance for direct function access
|
||||
_python_tools = PythonTools()
|
||||
execute_code = _python_tools.execute_code
|
||||
analyze_code = _python_tools.analyze_code
|
||||
format_code = _python_tools.format_code
|
||||
lint_code = _python_tools.lint_code
|
||||
disassemble_code = _python_tools.disassemble_code
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("PythonTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
print("1. Execute Python Code")
|
||||
print("------------------------------")
|
||||
code = """
|
||||
def greet(name):
|
||||
return f"Hello, {name}!"
|
||||
|
||||
result = greet("World")
|
||||
print(result)
|
||||
"""
|
||||
print("Code to execute:")
|
||||
print(code)
|
||||
print("\nOutput:")
|
||||
result = execute_code(code)
|
||||
print(result["stdout"])
|
||||
print()
|
||||
|
||||
print("2. Format Python Code")
|
||||
print("------------------------------")
|
||||
code = """
|
||||
def messy_function(x,y, z):
|
||||
if x>0:
|
||||
return y+z
|
||||
else:
|
||||
return y-z
|
||||
"""
|
||||
print("Before formatting:")
|
||||
print(code)
|
||||
print("\nAfter formatting:")
|
||||
result = format_code(code)
|
||||
print(result)
|
||||
print()
|
||||
|
||||
print("3. Lint Python Code")
|
||||
print("------------------------------")
|
||||
code = """
|
||||
def bad_function():
|
||||
unused_var = 42
|
||||
return 'result'
|
||||
"""
|
||||
print("Code to lint:")
|
||||
print(code)
|
||||
print("\nLinting results:")
|
||||
result = lint_code(code)
|
||||
print(result)
|
||||
print()
|
||||
|
||||
print("4. Execute Code with Variables")
|
||||
print("------------------------------")
|
||||
code = """
|
||||
x = 10
|
||||
y = 20
|
||||
result = x + y
|
||||
print(f"The sum of {x} and {y} is {result}")
|
||||
"""
|
||||
print("Code to execute:")
|
||||
print(code)
|
||||
print("\nOutput:")
|
||||
result = execute_code(code)
|
||||
print(result["stdout"])
|
||||
print()
|
||||
|
||||
print("==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,278 @@
|
||||
"""Tools for executing shell commands safely.
|
||||
|
||||
This module provides a safe interface for executing shell commands with:
|
||||
- Timeout control
|
||||
- Output capture
|
||||
- Error handling
|
||||
- Resource limits
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import shlex
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
class ShellTools:
|
||||
"""Tools for executing shell commands safely."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize ShellTools."""
|
||||
self._check_dependencies()
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if required packages are installed."""
|
||||
try:
|
||||
import psutil
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Required package not available. Please install: psutil\n"
|
||||
"Run: pip install psutil"
|
||||
)
|
||||
|
||||
def execute_command(
|
||||
self,
|
||||
command: str,
|
||||
cwd: Optional[str] = None,
|
||||
timeout: int = 30,
|
||||
shell: bool = False,
|
||||
env: Optional[Dict[str, str]] = None,
|
||||
max_output_size: int = 10000
|
||||
) -> Dict[str, Union[str, int, bool]]:
|
||||
"""Execute a shell command safely.
|
||||
|
||||
Args:
|
||||
command: Command to execute
|
||||
cwd: Working directory
|
||||
timeout: Maximum execution time in seconds
|
||||
shell: Whether to run command in shell
|
||||
env: Environment variables
|
||||
max_output_size: Maximum output size in bytes
|
||||
|
||||
Returns:
|
||||
Dictionary with execution results
|
||||
"""
|
||||
try:
|
||||
# Split command if not using shell
|
||||
if not shell:
|
||||
command = shlex.split(command)
|
||||
|
||||
# Set up process environment
|
||||
process_env = os.environ.copy()
|
||||
if env:
|
||||
process_env.update(env)
|
||||
|
||||
# Start process
|
||||
start_time = time.time()
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cwd,
|
||||
shell=shell,
|
||||
env=process_env,
|
||||
text=True
|
||||
)
|
||||
|
||||
try:
|
||||
# Wait for process with timeout
|
||||
stdout, stderr = process.communicate(timeout=timeout)
|
||||
|
||||
# Truncate output if too large
|
||||
if len(stdout) > max_output_size:
|
||||
stdout = stdout[:max_output_size] + "...[truncated]"
|
||||
if len(stderr) > max_output_size:
|
||||
stderr = stderr[:max_output_size] + "...[truncated]"
|
||||
|
||||
return {
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
'exit_code': process.returncode,
|
||||
'success': process.returncode == 0,
|
||||
'execution_time': time.time() - start_time
|
||||
}
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
# Kill process on timeout
|
||||
parent = psutil.Process(process.pid)
|
||||
children = parent.children(recursive=True)
|
||||
for child in children:
|
||||
child.kill()
|
||||
parent.kill()
|
||||
|
||||
return {
|
||||
'stdout': '',
|
||||
'stderr': f'Command timed out after {timeout} seconds',
|
||||
'exit_code': -1,
|
||||
'success': False,
|
||||
'execution_time': timeout
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error executing command: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {
|
||||
'stdout': '',
|
||||
'stderr': error_msg,
|
||||
'exit_code': -1,
|
||||
'success': False,
|
||||
'execution_time': 0
|
||||
}
|
||||
|
||||
def list_processes(self) -> List[Dict[str, Union[int, str, float]]]:
|
||||
"""List running processes with their details.
|
||||
|
||||
Returns:
|
||||
List of process information dictionaries
|
||||
"""
|
||||
try:
|
||||
processes = []
|
||||
for proc in psutil.process_iter(['pid', 'name', 'username', 'memory_percent', 'cpu_percent']):
|
||||
try:
|
||||
pinfo = proc.info
|
||||
processes.append({
|
||||
'pid': pinfo['pid'],
|
||||
'name': pinfo['name'],
|
||||
'username': pinfo['username'],
|
||||
'memory_percent': round(pinfo['memory_percent'], 2),
|
||||
'cpu_percent': round(pinfo['cpu_percent'], 2)
|
||||
})
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
pass
|
||||
return processes
|
||||
except Exception as e:
|
||||
error_msg = f"Error listing processes: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return []
|
||||
|
||||
def kill_process(
|
||||
self,
|
||||
pid: int,
|
||||
force: bool = False
|
||||
) -> Dict[str, Union[bool, str]]:
|
||||
"""Kill a process by its PID.
|
||||
|
||||
Args:
|
||||
pid: Process ID to kill
|
||||
force: Whether to force kill (-9)
|
||||
|
||||
Returns:
|
||||
Dictionary with operation results
|
||||
"""
|
||||
try:
|
||||
process = psutil.Process(pid)
|
||||
if force:
|
||||
process.kill() # SIGKILL
|
||||
else:
|
||||
process.terminate() # SIGTERM
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'Process {pid} killed successfully'
|
||||
}
|
||||
except psutil.NoSuchProcess:
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'No process found with PID {pid}'
|
||||
}
|
||||
except psutil.AccessDenied:
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'Access denied to kill process {pid}'
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error killing process: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {
|
||||
'success': False,
|
||||
'message': error_msg
|
||||
}
|
||||
|
||||
def get_system_info(self) -> Dict[str, Union[float, int, str, Dict]]:
|
||||
"""Get system information.
|
||||
|
||||
Returns:
|
||||
Dictionary with system information
|
||||
"""
|
||||
try:
|
||||
cpu_percent = psutil.cpu_percent(interval=1)
|
||||
memory = psutil.virtual_memory()
|
||||
disk = psutil.disk_usage('/')
|
||||
|
||||
return {
|
||||
'cpu': {
|
||||
'percent': cpu_percent,
|
||||
'cores': psutil.cpu_count(),
|
||||
'physical_cores': psutil.cpu_count(logical=False)
|
||||
},
|
||||
'memory': {
|
||||
'total': memory.total,
|
||||
'available': memory.available,
|
||||
'percent': memory.percent,
|
||||
'used': memory.used,
|
||||
'free': memory.free
|
||||
},
|
||||
'disk': {
|
||||
'total': disk.total,
|
||||
'used': disk.used,
|
||||
'free': disk.free,
|
||||
'percent': disk.percent
|
||||
},
|
||||
'boot_time': psutil.boot_time(),
|
||||
'platform': os.uname().sysname
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting system info: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {}
|
||||
|
||||
# Create instance for direct function access
|
||||
_shell_tools = ShellTools()
|
||||
execute_command = _shell_tools.execute_command
|
||||
list_processes = _shell_tools.list_processes
|
||||
kill_process = _shell_tools.kill_process
|
||||
get_system_info = _shell_tools.get_system_info
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("ShellTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# 1. Execute command
|
||||
print("1. Command Execution")
|
||||
print("------------------------------")
|
||||
result = execute_command("ls -la")
|
||||
print(f"Success: {result['success']}")
|
||||
print(f"Output:\n{result['stdout']}")
|
||||
if result['stderr']:
|
||||
print(f"Errors:\n{result['stderr']}")
|
||||
print(f"Execution time: {result['execution_time']:.2f}s")
|
||||
print()
|
||||
|
||||
# 2. System Information
|
||||
print("2. System Information")
|
||||
print("------------------------------")
|
||||
info = get_system_info()
|
||||
print(f"CPU Usage: {info['cpu']['percent']}%")
|
||||
print(f"Memory Usage: {info['memory']['percent']}%")
|
||||
print(f"Disk Usage: {info['disk']['percent']}%")
|
||||
print(f"Platform: {info['platform']}")
|
||||
print()
|
||||
|
||||
# 3. Process List
|
||||
print("3. Process List (top 5 by CPU)")
|
||||
print("------------------------------")
|
||||
processes = sorted(
|
||||
list_processes(),
|
||||
key=lambda x: x['cpu_percent'],
|
||||
reverse=True
|
||||
)[:5]
|
||||
for proc in processes:
|
||||
print(f"PID: {proc['pid']}, Name: {proc['name']}, CPU: {proc['cpu_percent']}%")
|
||||
print()
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,431 @@
|
||||
"""Tools for web scraping and crawling.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import spider_tools
|
||||
content = spider_tools.scrape_page("https://example.com")
|
||||
links = spider_tools.extract_links("https://example.com")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import scrape_page, extract_links
|
||||
content = scrape_page("https://example.com")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any
|
||||
from importlib import util
|
||||
import json
|
||||
from urllib.parse import urljoin, urlparse
|
||||
import re
|
||||
import os
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
class SpiderTools:
|
||||
"""Tools for web scraping and crawling."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize SpiderTools and check for required packages."""
|
||||
self._session = None
|
||||
|
||||
def _get_session(self):
|
||||
"""Get or create requests session with common headers."""
|
||||
if util.find_spec('requests') is None:
|
||||
error_msg = "requests package is not available. Please install it using: pip install requests"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
if self._session is None:
|
||||
import requests
|
||||
self._session = requests.Session()
|
||||
self._session.headers.update({
|
||||
'User-Agent': 'Mozilla/5.0 (compatible; PraisonAI/1.0; +http://praisonai.com/bot)',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Accept-Language': 'en-US,en;q=0.5',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Connection': 'keep-alive',
|
||||
})
|
||||
return self._session
|
||||
|
||||
def scrape_page(
|
||||
self,
|
||||
url: str,
|
||||
selector: Optional[str] = None,
|
||||
extract_images: bool = False,
|
||||
extract_links: bool = False,
|
||||
timeout: int = 30,
|
||||
verify_ssl: bool = True
|
||||
) -> Union[Dict[str, Any], Dict[str, str]]:
|
||||
"""
|
||||
Scrape content from a webpage.
|
||||
|
||||
Args:
|
||||
url: URL to scrape
|
||||
selector: Optional CSS selector to extract specific content
|
||||
extract_images: Whether to extract image URLs
|
||||
extract_links: Whether to extract links
|
||||
timeout: Request timeout in seconds
|
||||
verify_ssl: Whether to verify SSL certificates
|
||||
|
||||
Returns:
|
||||
Dict: Scraped content or error dict
|
||||
"""
|
||||
try:
|
||||
session = self._get_session()
|
||||
if session is None:
|
||||
return {"error": "requests package not available"}
|
||||
|
||||
# Import BeautifulSoup only when needed
|
||||
if util.find_spec('bs4') is None:
|
||||
error_msg = "bs4 package is not available. Please install it using: pip install beautifulsoup4"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
# Make request
|
||||
response = session.get(
|
||||
url,
|
||||
timeout=timeout,
|
||||
verify=verify_ssl
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# Parse HTML
|
||||
soup = BeautifulSoup(response.text, 'lxml')
|
||||
|
||||
# Remove unwanted elements
|
||||
for element in soup(['script', 'style']):
|
||||
element.decompose()
|
||||
|
||||
# Initialize result
|
||||
result = {
|
||||
'url': url,
|
||||
'status_code': response.status_code,
|
||||
'encoding': response.encoding,
|
||||
'headers': dict(response.headers),
|
||||
}
|
||||
|
||||
# Extract content based on selector
|
||||
if selector:
|
||||
elements = soup.select(selector)
|
||||
result['content'] = [elem.get_text(strip=True) for elem in elements]
|
||||
result['html'] = [str(elem) for elem in elements]
|
||||
else:
|
||||
result['title'] = soup.title.string if soup.title else None
|
||||
result['content'] = soup.get_text(separator=' ', strip=True)
|
||||
result['html'] = str(soup)
|
||||
|
||||
# Extract metadata
|
||||
meta_tags = {}
|
||||
for meta in soup.find_all('meta'):
|
||||
name = meta.get('name') or meta.get('property')
|
||||
if name:
|
||||
meta_tags[name] = meta.get('content')
|
||||
result['meta_tags'] = meta_tags
|
||||
|
||||
# Extract images if requested
|
||||
if extract_images:
|
||||
images = []
|
||||
for img in soup.find_all('img'):
|
||||
src = img.get('src')
|
||||
if src:
|
||||
images.append({
|
||||
'src': urljoin(url, src),
|
||||
'alt': img.get('alt', ''),
|
||||
'title': img.get('title', '')
|
||||
})
|
||||
result['images'] = images
|
||||
|
||||
# Extract links if requested
|
||||
if extract_links:
|
||||
links = []
|
||||
for link in soup.find_all('a'):
|
||||
href = link.get('href')
|
||||
if href:
|
||||
links.append({
|
||||
'url': urljoin(url, href),
|
||||
'text': link.get_text(strip=True),
|
||||
'title': link.get('title', '')
|
||||
})
|
||||
result['links'] = links
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error scraping {url}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def extract_links(
|
||||
self,
|
||||
url: str,
|
||||
same_domain: bool = True,
|
||||
exclude_patterns: Optional[List[str]] = None,
|
||||
timeout: int = 30,
|
||||
verify_ssl: bool = True
|
||||
) -> Union[List[Dict[str, str]], Dict[str, str]]:
|
||||
"""
|
||||
Extract all links from a webpage.
|
||||
|
||||
Args:
|
||||
url: URL to extract links from
|
||||
same_domain: Only return links from the same domain
|
||||
exclude_patterns: List of regex patterns to exclude
|
||||
timeout: Request timeout in seconds
|
||||
verify_ssl: Whether to verify SSL certificates
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: List of links or error dict
|
||||
"""
|
||||
try:
|
||||
# Compile exclude patterns
|
||||
if exclude_patterns:
|
||||
exclude_patterns = [re.compile(p) for p in exclude_patterns]
|
||||
|
||||
# Get base domain
|
||||
base_domain = urlparse(url).netloc
|
||||
|
||||
# Scrape page
|
||||
result = self.scrape_page(
|
||||
url,
|
||||
extract_links=True,
|
||||
timeout=timeout,
|
||||
verify_ssl=verify_ssl
|
||||
)
|
||||
|
||||
if "error" in result:
|
||||
return result
|
||||
|
||||
# Filter and clean links
|
||||
links = []
|
||||
seen_urls = set()
|
||||
|
||||
for link in result.get('links', []):
|
||||
link_url = link['url']
|
||||
|
||||
# Skip if already seen
|
||||
if link_url in seen_urls:
|
||||
continue
|
||||
|
||||
# Parse URL
|
||||
parsed = urlparse(link_url)
|
||||
|
||||
# Skip if not same domain and same_domain is True
|
||||
if same_domain and parsed.netloc != base_domain:
|
||||
continue
|
||||
|
||||
# Skip if matches exclude patterns
|
||||
if exclude_patterns and any(p.search(link_url) for p in exclude_patterns):
|
||||
continue
|
||||
|
||||
# Add to results
|
||||
links.append(link)
|
||||
seen_urls.add(link_url)
|
||||
|
||||
return links
|
||||
except Exception as e:
|
||||
error_msg = f"Error extracting links from {url}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def crawl(
|
||||
self,
|
||||
start_url: str,
|
||||
max_pages: int = 10,
|
||||
same_domain: bool = True,
|
||||
exclude_patterns: Optional[List[str]] = None,
|
||||
delay: float = 1.0,
|
||||
timeout: int = 30,
|
||||
verify_ssl: bool = True,
|
||||
output_dir: Optional[str] = None
|
||||
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
|
||||
"""
|
||||
Crawl multiple pages starting from a URL.
|
||||
|
||||
Args:
|
||||
start_url: Starting URL
|
||||
max_pages: Maximum number of pages to crawl
|
||||
same_domain: Only crawl pages from the same domain
|
||||
exclude_patterns: List of regex patterns to exclude
|
||||
delay: Delay between requests in seconds
|
||||
timeout: Request timeout in seconds
|
||||
verify_ssl: Whether to verify SSL certificates
|
||||
output_dir: Directory to save crawled pages
|
||||
|
||||
Returns:
|
||||
List[Dict] or Dict: Crawled pages or error dict
|
||||
"""
|
||||
try:
|
||||
# Create output directory if needed
|
||||
if output_dir:
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Initialize crawl state
|
||||
to_visit = {start_url}
|
||||
visited = set()
|
||||
results = []
|
||||
|
||||
while to_visit and len(visited) < max_pages:
|
||||
# Get next URL
|
||||
url = to_visit.pop()
|
||||
|
||||
# Skip if already visited
|
||||
if url in visited:
|
||||
continue
|
||||
|
||||
# Add to visited
|
||||
visited.add(url)
|
||||
|
||||
# Delay if not first request
|
||||
if len(visited) > 1:
|
||||
time.sleep(delay)
|
||||
|
||||
# Scrape page
|
||||
result = self.scrape_page(
|
||||
url,
|
||||
extract_links=True,
|
||||
timeout=timeout,
|
||||
verify_ssl=verify_ssl
|
||||
)
|
||||
|
||||
if "error" in result:
|
||||
logging.warning(f"Error crawling {url}: {result['error']}")
|
||||
continue
|
||||
|
||||
# Save result
|
||||
results.append(result)
|
||||
|
||||
# Save to file if requested
|
||||
if output_dir:
|
||||
filename = hashlib.md5(url.encode()).hexdigest() + '.json'
|
||||
filepath = os.path.join(output_dir, filename)
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
json.dump(result, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Add new links to visit
|
||||
for link in result.get('links', []):
|
||||
link_url = link['url']
|
||||
parsed = urlparse(link_url)
|
||||
|
||||
# Skip if not same domain and same_domain is True
|
||||
if same_domain and parsed.netloc != urlparse(start_url).netloc:
|
||||
continue
|
||||
|
||||
# Skip if matches exclude patterns
|
||||
if exclude_patterns and any(
|
||||
re.compile(p).search(link_url) for p in exclude_patterns
|
||||
):
|
||||
continue
|
||||
|
||||
# Add to visit if not visited
|
||||
if link_url not in visited:
|
||||
to_visit.add(link_url)
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
error_msg = f"Error crawling from {start_url}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def extract_text(
|
||||
self,
|
||||
url: str,
|
||||
selector: Optional[str] = None,
|
||||
timeout: int = 30,
|
||||
verify_ssl: bool = True
|
||||
) -> Union[str, Dict[str, str]]:
|
||||
"""
|
||||
Extract clean text content from a webpage.
|
||||
|
||||
Args:
|
||||
url: URL to extract text from
|
||||
selector: Optional CSS selector to extract specific content
|
||||
timeout: Request timeout in seconds
|
||||
verify_ssl: Whether to verify SSL certificates
|
||||
|
||||
Returns:
|
||||
str or Dict: Extracted text or error dict
|
||||
"""
|
||||
try:
|
||||
result = self.scrape_page(
|
||||
url,
|
||||
selector=selector,
|
||||
timeout=timeout,
|
||||
verify_ssl=verify_ssl
|
||||
)
|
||||
|
||||
if "error" in result:
|
||||
return result
|
||||
|
||||
if selector:
|
||||
return '\n'.join(result['content'])
|
||||
return result['content']
|
||||
except Exception as e:
|
||||
error_msg = f"Error extracting text from {url}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
# Create instance for direct function access
|
||||
_spider_tools = SpiderTools()
|
||||
scrape_page = _spider_tools.scrape_page
|
||||
extract_links = _spider_tools.extract_links
|
||||
crawl = _spider_tools.crawl
|
||||
extract_text = _spider_tools.extract_text
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("SpiderTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# 1. Scrape a webpage
|
||||
print("1. Scraping Webpage")
|
||||
print("------------------------------")
|
||||
url = "https://example.com"
|
||||
result = scrape_page(url, extract_images=True, extract_links=True)
|
||||
print(f"Content from {url}:")
|
||||
if "error" not in result:
|
||||
print(f"Title: {result['title']}")
|
||||
print(f"Content length: {len(result['content'])} characters")
|
||||
print(f"Number of images: {len(result.get('images', []))}")
|
||||
print(f"Number of links: {len(result.get('links', []))}")
|
||||
else:
|
||||
print(result) # Show error
|
||||
print()
|
||||
|
||||
# 2. Extract links
|
||||
print("2. Extracting Links")
|
||||
print("------------------------------")
|
||||
links = extract_links(url)
|
||||
print(f"Links from {url}:")
|
||||
if isinstance(links, list):
|
||||
for link in links:
|
||||
print(f"- {link['url']} ({link['text']})")
|
||||
else:
|
||||
print(links) # Show error
|
||||
print()
|
||||
|
||||
# 3. Extract text
|
||||
print("3. Extracting Text")
|
||||
print("------------------------------")
|
||||
text = extract_text(url)
|
||||
print(f"Text from {url}:")
|
||||
if isinstance(text, str):
|
||||
print(text[:500] + "..." if len(text) > 500 else text)
|
||||
else:
|
||||
print(text) # Show error
|
||||
print()
|
||||
|
||||
# 4. Crawl multiple pages
|
||||
print("4. Crawling Multiple Pages")
|
||||
print("------------------------------")
|
||||
results = crawl(url, max_pages=2, delay=1.0)
|
||||
print(f"Crawl results from {url}:")
|
||||
if isinstance(results, list):
|
||||
print(f"Crawled {len(results)} pages")
|
||||
for result in results:
|
||||
print(f"- {result['url']}: {result['title']}")
|
||||
else:
|
||||
print(results) # Show error
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test runner for all tools."""
|
||||
|
||||
import os
|
||||
import glob
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def main():
|
||||
"""Run all tool files."""
|
||||
logger.info("Starting tests...")
|
||||
|
||||
# Get all *_tools.py files
|
||||
tools_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
tool_files = glob.glob(os.path.join(tools_dir, "*_tools.py"))
|
||||
|
||||
# Run each tool file
|
||||
for tool_file in sorted(tool_files):
|
||||
module_name = os.path.basename(tool_file)
|
||||
logger.info(f"\nRunning {module_name}...")
|
||||
|
||||
try:
|
||||
# Run the tool file directly
|
||||
result = subprocess.run(
|
||||
["python3", tool_file],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=tools_dir
|
||||
)
|
||||
|
||||
# Log output
|
||||
if result.stdout:
|
||||
logger.info(f"Output:\n{result.stdout}")
|
||||
if result.stderr:
|
||||
logger.error(f"Errors:\n{result.stderr}")
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info(f" {module_name} completed successfully")
|
||||
else:
|
||||
logger.error(f" {module_name} failed with return code {result.returncode}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error running {module_name}: {str(e)}")
|
||||
continue
|
||||
|
||||
logger.info("\nAll tests completed!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,40 +1,9 @@
|
||||
"""Tools module for PraisonAI Agents"""
|
||||
from typing import List, Dict
|
||||
import logging
|
||||
import importlib
|
||||
from .duckduckgo_tools import internet_search
|
||||
|
||||
class Tools:
|
||||
@staticmethod
|
||||
def internet_search(query: str) -> List[Dict]:
|
||||
"""
|
||||
Perform a search using DuckDuckGo.
|
||||
"""Tools class for backward compatibility"""
|
||||
internet_search = staticmethod(internet_search)
|
||||
|
||||
Args:
|
||||
query (str): The search query.
|
||||
|
||||
Returns:
|
||||
list: A list of search result titles, URLs, and snippets.
|
||||
"""
|
||||
# Check if duckduckgo_search is installed
|
||||
if importlib.util.find_spec("duckduckgo_search") is None:
|
||||
error_msg = "DuckDuckGo search is not available. Please install duckduckgo_search package using: pip install duckduckgo_search"
|
||||
logging.error(error_msg)
|
||||
return [{"error": error_msg}]
|
||||
|
||||
try:
|
||||
# Import only when needed
|
||||
from duckduckgo_search import DDGS
|
||||
results = []
|
||||
ddgs = DDGS()
|
||||
for result in ddgs.text(keywords=query, max_results=5):
|
||||
results.append({
|
||||
"title": result.get("title", ""),
|
||||
"url": result.get("href", ""),
|
||||
"snippet": result.get("body", "")
|
||||
})
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error during DuckDuckGo search: {e}"
|
||||
logging.error(error_msg)
|
||||
return [{"error": error_msg}]
|
||||
# Re-export the function
|
||||
__all__ = ['Tools', 'internet_search']
|
||||
@@ -0,0 +1,272 @@
|
||||
"""Wikipedia tools for accessing and searching Wikipedia content.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import wikipedia_tools
|
||||
summary = wikipedia_tools.wiki_summary("Python programming language")
|
||||
page = wikipedia_tools.wiki_page("Python programming language")
|
||||
results = wikipedia_tools.wiki_search("Python programming")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page
|
||||
summary = wiki_summary("Python programming language")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Any
|
||||
from importlib import util
|
||||
import json
|
||||
|
||||
class WikipediaTools:
|
||||
"""Tools for accessing and searching Wikipedia content."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize WikipediaTools and check for wikipedia package."""
|
||||
self._check_wikipedia()
|
||||
|
||||
def _check_wikipedia(self):
|
||||
"""Check if wikipedia package is installed."""
|
||||
if util.find_spec("wikipedia") is None:
|
||||
raise ImportError("wikipedia package is not available. Please install it using: pip install wikipedia")
|
||||
global wikipedia
|
||||
import wikipedia
|
||||
# Set default language to English
|
||||
wikipedia.set_lang("en")
|
||||
|
||||
def wiki_search(
|
||||
self,
|
||||
query: str,
|
||||
results: int = 10,
|
||||
suggestion: bool = True
|
||||
) -> Union[List[str], Dict[str, str]]:
|
||||
"""
|
||||
Search Wikipedia for a query.
|
||||
|
||||
Args:
|
||||
query: Search query
|
||||
results: Maximum number of results to return
|
||||
suggestion: Whether to suggest similar queries
|
||||
|
||||
Returns:
|
||||
List[str] or Dict: List of search results or error dict
|
||||
"""
|
||||
try:
|
||||
search_results = wikipedia.search(query, results=results, suggestion=suggestion)
|
||||
if isinstance(search_results, tuple):
|
||||
# If suggestion is True, returns (results, suggestion)
|
||||
return {
|
||||
"results": search_results[0],
|
||||
"suggestion": search_results[1] if search_results[1] else None
|
||||
}
|
||||
return {"results": search_results, "suggestion": None}
|
||||
except Exception as e:
|
||||
error_msg = f"Error searching Wikipedia for '{query}': {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def wiki_summary(
|
||||
self,
|
||||
title: str,
|
||||
sentences: int = 5,
|
||||
auto_suggest: bool = True
|
||||
) -> Union[str, Dict[str, str]]:
|
||||
"""
|
||||
Get a summary of a Wikipedia page.
|
||||
|
||||
Args:
|
||||
title: Title of the Wikipedia page
|
||||
sentences: Number of sentences to return
|
||||
auto_suggest: Whether to auto-suggest similar titles
|
||||
|
||||
Returns:
|
||||
str or Dict: Page summary if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
return wikipedia.summary(title, sentences=sentences, auto_suggest=auto_suggest)
|
||||
except wikipedia.exceptions.DisambiguationError as e:
|
||||
return {
|
||||
"error": "Disambiguation page",
|
||||
"options": e.options[:10] # Limit to first 10 options
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting summary for '{title}': {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def wiki_page(
|
||||
self,
|
||||
title: str,
|
||||
auto_suggest: bool = True
|
||||
) -> Union[Dict[str, Any], Dict[str, str]]:
|
||||
"""
|
||||
Get detailed information about a Wikipedia page.
|
||||
|
||||
Args:
|
||||
title: Title of the Wikipedia page
|
||||
auto_suggest: Whether to auto-suggest similar titles
|
||||
|
||||
Returns:
|
||||
Dict: Page information if successful, error dict if failed
|
||||
"""
|
||||
try:
|
||||
page = wikipedia.page(title, auto_suggest=auto_suggest)
|
||||
return {
|
||||
"title": page.title,
|
||||
"url": page.url,
|
||||
"content": page.content,
|
||||
"summary": page.summary,
|
||||
"references": page.references,
|
||||
"categories": page.categories,
|
||||
"links": page.links,
|
||||
"images": page.images,
|
||||
"sections": self.get_sections(page)
|
||||
}
|
||||
except wikipedia.exceptions.DisambiguationError as e:
|
||||
return {
|
||||
"error": "Disambiguation page",
|
||||
"options": e.options[:10] # Limit to first 10 options
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting page for '{title}': {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_sections(self, page: Any) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract sections and their content from a Wikipedia page.
|
||||
|
||||
Args:
|
||||
page: Wikipedia page object
|
||||
|
||||
Returns:
|
||||
List[Dict]: List of sections with their titles and content
|
||||
"""
|
||||
sections = []
|
||||
content = page.content
|
||||
section_titles = [s.strip() for s in content.split("==")[1::2]]
|
||||
section_contents = [s.strip() for s in content.split("==")[2::2]]
|
||||
|
||||
# Add introduction section
|
||||
intro = content.split("==")[0].strip()
|
||||
if intro:
|
||||
sections.append({
|
||||
"title": "Introduction",
|
||||
"level": 0,
|
||||
"content": intro
|
||||
})
|
||||
|
||||
# Process remaining sections
|
||||
for title, content in zip(section_titles, section_contents):
|
||||
# Count leading '=' to determine section level
|
||||
level = title.count("=") // 2
|
||||
clean_title = title.strip("= ")
|
||||
|
||||
sections.append({
|
||||
"title": clean_title,
|
||||
"level": level,
|
||||
"content": content.strip()
|
||||
})
|
||||
|
||||
return sections
|
||||
|
||||
def wiki_random(self, pages: int = 1) -> Union[List[str], Dict[str, str]]:
|
||||
"""
|
||||
Get random Wikipedia page titles.
|
||||
|
||||
Args:
|
||||
pages: Number of random pages to return
|
||||
|
||||
Returns:
|
||||
List[str] or Dict: List of random page titles or error dict
|
||||
"""
|
||||
try:
|
||||
return wikipedia.random(pages)
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting random pages: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def wiki_language(self, language: str) -> bool:
|
||||
"""
|
||||
Set the language for Wikipedia searches.
|
||||
|
||||
Args:
|
||||
language: Language code (e.g., 'en' for English, 'es' for Spanish)
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False if failed
|
||||
"""
|
||||
try:
|
||||
wikipedia.set_lang(language)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error setting language to '{language}': {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Create instance for direct function access
|
||||
_wikipedia_tools = WikipediaTools()
|
||||
wiki_search = _wikipedia_tools.wiki_search
|
||||
wiki_summary = _wikipedia_tools.wiki_summary
|
||||
wiki_page = _wikipedia_tools.wiki_page
|
||||
wiki_random = _wikipedia_tools.wiki_random
|
||||
wiki_language = _wikipedia_tools.wiki_language
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("WikipediaTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# 1. Search for a topic
|
||||
print("1. Searching Wikipedia")
|
||||
print("------------------------------")
|
||||
search_results = wiki_search("Python programming language")
|
||||
print("Search results:")
|
||||
print(json.dumps(search_results, indent=2))
|
||||
print()
|
||||
|
||||
# 2. Get a page summary
|
||||
print("2. Getting Page Summary")
|
||||
print("------------------------------")
|
||||
summary = wiki_summary("Python programming language", sentences=3)
|
||||
print("Summary:")
|
||||
print(summary)
|
||||
print()
|
||||
|
||||
# 3. Get detailed page information
|
||||
print("3. Getting Full Page Information")
|
||||
print("------------------------------")
|
||||
page = wiki_page("Python programming language")
|
||||
if isinstance(page, dict) and "error" not in page:
|
||||
print("Page sections:")
|
||||
for section in page["sections"]:
|
||||
print(f"- {section['title']} (Level {section['level']})")
|
||||
print(f"\nNumber of references: {len(page['references'])}")
|
||||
print(f"Number of categories: {len(page['categories'])}")
|
||||
print(f"Number of links: {len(page['links'])}")
|
||||
print(f"Number of images: {len(page['images'])}")
|
||||
else:
|
||||
print("Error getting page:", page.get("error"))
|
||||
print()
|
||||
|
||||
# 4. Get random pages
|
||||
print("4. Getting Random Pages")
|
||||
print("------------------------------")
|
||||
random_pages = wiki_random(3)
|
||||
print("Random page titles:")
|
||||
print(json.dumps(random_pages, indent=2))
|
||||
print()
|
||||
|
||||
# 5. Try different language
|
||||
print("5. Changing Language")
|
||||
print("------------------------------")
|
||||
success = wiki_language("es")
|
||||
if success:
|
||||
summary = wiki_summary("Python (lenguaje de programación)", sentences=1)
|
||||
print("Spanish summary:")
|
||||
print(summary)
|
||||
print()
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,498 @@
|
||||
"""Tools for working with XML files.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import xml_tools
|
||||
tree = xml_tools.read_xml("data.xml")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_xml, write_xml, transform_xml
|
||||
tree = read_xml("data.xml")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any, Tuple
|
||||
from importlib import util
|
||||
import xml.etree.ElementTree as ET
|
||||
import xml.dom.minidom as minidom
|
||||
from io import StringIO
|
||||
import json
|
||||
|
||||
class XMLTools:
|
||||
"""Tools for working with XML files."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize XMLTools."""
|
||||
pass
|
||||
|
||||
def read_xml(
|
||||
self,
|
||||
filepath: str,
|
||||
encoding: str = 'utf-8',
|
||||
validate_schema: Optional[str] = None,
|
||||
parser: str = 'lxml'
|
||||
) -> ET.Element:
|
||||
"""Read an XML file with optional schema validation.
|
||||
|
||||
Args:
|
||||
filepath: Path to XML file
|
||||
encoding: File encoding
|
||||
validate_schema: Optional path to XSD schema file
|
||||
parser: XML parser to use ('lxml' or 'etree')
|
||||
|
||||
Returns:
|
||||
ElementTree root element
|
||||
"""
|
||||
try:
|
||||
if parser == 'lxml':
|
||||
if util.find_spec('lxml') is None:
|
||||
error_msg = "lxml package is not available. Please install it using: pip install lxml"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import lxml.etree as lxml_etree
|
||||
tree = lxml_etree.parse(filepath)
|
||||
root = tree.getroot()
|
||||
else:
|
||||
tree = ET.parse(filepath)
|
||||
root = tree.getroot()
|
||||
|
||||
if validate_schema:
|
||||
if util.find_spec('xmlschema') is None:
|
||||
error_msg = "xmlschema package is not available. Please install it using: pip install xmlschema"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import xmlschema
|
||||
schema = xmlschema.XMLSchema(validate_schema)
|
||||
if not schema.is_valid(filepath):
|
||||
error_msg = f"XML file does not validate against schema: {schema.validate(filepath)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
return root
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading XML file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def write_xml(
|
||||
self,
|
||||
root: ET.Element,
|
||||
filepath: str,
|
||||
encoding: str = 'utf-8',
|
||||
pretty: bool = True,
|
||||
xml_declaration: bool = True
|
||||
) -> bool:
|
||||
"""Write XML Element tree to file.
|
||||
|
||||
Args:
|
||||
root: XML Element tree root
|
||||
filepath: Output file path
|
||||
encoding: File encoding
|
||||
pretty: Format output with proper indentation
|
||||
xml_declaration: Include XML declaration
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Convert to string
|
||||
if pretty:
|
||||
xml_str = minidom.parseString(
|
||||
ET.tostring(root, encoding='unicode')
|
||||
).toprettyxml(indent=' ')
|
||||
else:
|
||||
xml_str = ET.tostring(
|
||||
root,
|
||||
encoding='unicode'
|
||||
)
|
||||
|
||||
# Add declaration if requested
|
||||
if xml_declaration:
|
||||
if not xml_str.startswith('<?xml'):
|
||||
xml_str = (
|
||||
f'<?xml version="1.0" encoding="{encoding}"?>\n'
|
||||
+ xml_str
|
||||
)
|
||||
|
||||
# Write to file
|
||||
with open(filepath, 'w', encoding=encoding) as f:
|
||||
f.write(xml_str)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing XML file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def transform_xml(
|
||||
self,
|
||||
xml_file: str,
|
||||
xslt_file: str,
|
||||
output_file: str,
|
||||
params: Optional[Dict[str, str]] = None
|
||||
) -> bool:
|
||||
"""Transform XML using XSLT stylesheet.
|
||||
|
||||
Args:
|
||||
xml_file: Input XML file
|
||||
xslt_file: XSLT stylesheet file
|
||||
output_file: Output file path
|
||||
params: Optional parameters for transformation
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Parse XML and XSLT
|
||||
if util.find_spec('lxml') is None:
|
||||
error_msg = "lxml package is not available. Please install it using: pip install lxml"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
import lxml.etree as lxml_etree
|
||||
xml_doc = lxml_etree.parse(xml_file)
|
||||
xslt_doc = lxml_etree.parse(xslt_file)
|
||||
transform = lxml_etree.XSLT(xslt_doc)
|
||||
|
||||
# Apply transformation
|
||||
if params:
|
||||
result = transform(xml_doc, **params)
|
||||
else:
|
||||
result = transform(xml_doc)
|
||||
|
||||
# Write result
|
||||
result.write(
|
||||
output_file,
|
||||
pretty_print=True,
|
||||
xml_declaration=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error transforming XML: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def validate_xml(
|
||||
self,
|
||||
xml_file: str,
|
||||
schema_file: str
|
||||
) -> Tuple[bool, Optional[str]]:
|
||||
"""Validate XML against XSD schema.
|
||||
|
||||
Args:
|
||||
xml_file: XML file to validate
|
||||
schema_file: XSD schema file
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, error_message)
|
||||
"""
|
||||
try:
|
||||
if util.find_spec('xmlschema') is None:
|
||||
error_msg = "xmlschema package is not available. Please install it using: pip install xmlschema"
|
||||
logging.error(error_msg)
|
||||
return False, error_msg
|
||||
import xmlschema
|
||||
schema = xmlschema.XMLSchema(schema_file)
|
||||
schema.validate(xml_file)
|
||||
return True, None
|
||||
except xmlschema.validators.exceptions.XMLSchemaValidationError as e:
|
||||
return False, str(e)
|
||||
except Exception as e:
|
||||
error_msg = f"Error validating XML: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False, error_msg
|
||||
|
||||
def xml_to_dict(
|
||||
self,
|
||||
root: Union[str, ET.Element],
|
||||
preserve_attrs: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""Convert XML to dictionary.
|
||||
|
||||
Args:
|
||||
root: XML string or Element tree root
|
||||
preserve_attrs: Keep XML attributes in result
|
||||
|
||||
Returns:
|
||||
Dict representation of XML
|
||||
"""
|
||||
try:
|
||||
# Parse XML if string
|
||||
if isinstance(root, str):
|
||||
if root.startswith('<'):
|
||||
root = ET.fromstring(root)
|
||||
else:
|
||||
root = ET.parse(root).getroot()
|
||||
|
||||
result = {}
|
||||
|
||||
# Add attributes if present and requested
|
||||
if preserve_attrs and root.attrib:
|
||||
result['@attributes'] = dict(root.attrib)
|
||||
|
||||
# Add children
|
||||
children = list(root)
|
||||
if not children:
|
||||
text = root.text
|
||||
if text is not None and text.strip():
|
||||
result = text.strip()
|
||||
else:
|
||||
for child in children:
|
||||
child_data = self.xml_to_dict(child, preserve_attrs)
|
||||
if child.tag in result:
|
||||
if not isinstance(result[child.tag], list):
|
||||
result[child.tag] = [result[child.tag]]
|
||||
result[child.tag].append(child_data)
|
||||
else:
|
||||
result[child.tag] = child_data
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error converting XML to dict: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {}
|
||||
|
||||
def dict_to_xml(
|
||||
self,
|
||||
data: Dict[str, Any],
|
||||
root_tag: str = 'root'
|
||||
) -> Optional[ET.Element]:
|
||||
"""Convert dictionary to XML.
|
||||
|
||||
Args:
|
||||
data: Dictionary to convert
|
||||
root_tag: Tag for root element
|
||||
|
||||
Returns:
|
||||
XML Element tree root
|
||||
"""
|
||||
try:
|
||||
def _create_element(
|
||||
parent: ET.Element,
|
||||
key: str,
|
||||
value: Any
|
||||
):
|
||||
"""Create XML element from key-value pair."""
|
||||
if key == '@attributes':
|
||||
for attr_key, attr_val in value.items():
|
||||
parent.set(attr_key, str(attr_val))
|
||||
elif isinstance(value, dict):
|
||||
child = ET.SubElement(parent, key)
|
||||
for k, v in value.items():
|
||||
_create_element(child, k, v)
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
child = ET.SubElement(parent, key)
|
||||
if isinstance(item, dict):
|
||||
for k, v in item.items():
|
||||
_create_element(child, k, v)
|
||||
else:
|
||||
child.text = str(item)
|
||||
else:
|
||||
child = ET.SubElement(parent, key)
|
||||
child.text = str(value)
|
||||
|
||||
root = ET.Element(root_tag)
|
||||
for key, value in data.items():
|
||||
_create_element(root, key, value)
|
||||
|
||||
return root
|
||||
except Exception as e:
|
||||
error_msg = f"Error converting dict to XML: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def xpath_query(
|
||||
self,
|
||||
root: Union[str, ET.Element],
|
||||
query: str,
|
||||
namespaces: Optional[Dict[str, str]] = None
|
||||
) -> List[ET.Element]:
|
||||
"""Execute XPath query on XML.
|
||||
|
||||
Args:
|
||||
root: XML string or Element tree root
|
||||
query: XPath query string
|
||||
namespaces: Optional namespace mappings
|
||||
|
||||
Returns:
|
||||
List of matching elements
|
||||
"""
|
||||
try:
|
||||
# Parse XML if string
|
||||
if isinstance(root, str):
|
||||
if root.startswith('<'):
|
||||
if util.find_spec('lxml') is None:
|
||||
error_msg = "lxml package is not available. Please install it using: pip install lxml"
|
||||
logging.error(error_msg)
|
||||
return []
|
||||
import lxml.etree as lxml_etree
|
||||
tree = lxml_etree.fromstring(root)
|
||||
else:
|
||||
tree = ET.parse(root)
|
||||
else:
|
||||
if util.find_spec('lxml') is None:
|
||||
error_msg = "lxml package is not available. Please install it using: pip install lxml"
|
||||
logging.error(error_msg)
|
||||
return []
|
||||
import lxml.etree as lxml_etree
|
||||
tree = lxml_etree.fromstring(
|
||||
ET.tostring(root, encoding='unicode')
|
||||
)
|
||||
|
||||
# Execute query
|
||||
results = tree.xpath(
|
||||
query,
|
||||
namespaces=namespaces or {}
|
||||
)
|
||||
|
||||
# Convert results to standard ElementTree elements
|
||||
return [
|
||||
ET.fromstring(lxml_etree.tostring(elem, encoding='unicode'))
|
||||
for elem in results
|
||||
]
|
||||
except Exception as e:
|
||||
error_msg = f"Error executing XPath query: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return []
|
||||
|
||||
# Create instance for direct function access
|
||||
_xml_tools = XMLTools()
|
||||
read_xml = _xml_tools.read_xml
|
||||
write_xml = _xml_tools.write_xml
|
||||
transform_xml = _xml_tools.transform_xml
|
||||
validate_xml = _xml_tools.validate_xml
|
||||
xml_to_dict = _xml_tools.xml_to_dict
|
||||
dict_to_xml = _xml_tools.dict_to_xml
|
||||
xpath_query = _xml_tools.xpath_query
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n==================================================")
|
||||
print("XMLTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Create temporary files
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
temp_file = tempfile.mktemp(suffix='.xml')
|
||||
try:
|
||||
print("1. Creating XML Document")
|
||||
print("------------------------------")
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bookstore>
|
||||
<book category="fiction">
|
||||
<title>The Great Gatsby</title>
|
||||
<author>F. Scott Fitzgerald</author>
|
||||
<year>1925</year>
|
||||
<price>10.99</price>
|
||||
</book>
|
||||
<book category="non-fiction">
|
||||
<title>A Brief History of Time</title>
|
||||
<author>Stephen Hawking</author>
|
||||
<year>1988</year>
|
||||
<price>15.99</price>
|
||||
</book>
|
||||
</bookstore>"""
|
||||
|
||||
with open(temp_file, 'w') as f:
|
||||
f.write(xml_content)
|
||||
print("Sample XML file created")
|
||||
print()
|
||||
|
||||
print("2. Parsing XML")
|
||||
print("------------------------------")
|
||||
result = read_xml(temp_file)
|
||||
print("XML structure:")
|
||||
print(minidom.parseString(ET.tostring(result, encoding='unicode')).toprettyxml(indent=' '))
|
||||
print()
|
||||
|
||||
print("3. Querying XML")
|
||||
print("------------------------------")
|
||||
xpath = "//book[@category='fiction']/title/text()"
|
||||
result = xpath_query(result, xpath)
|
||||
print(f"Fiction book titles (XPath: {xpath}):")
|
||||
for title in result:
|
||||
print(title.text)
|
||||
print()
|
||||
|
||||
print("4. Validating XML")
|
||||
print("------------------------------")
|
||||
schema_file = tempfile.mktemp(suffix='.xsd')
|
||||
schema_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="bookstore">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="book" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="title" type="xs:string"/>
|
||||
<xs:element name="author" type="xs:string"/>
|
||||
<xs:element name="year" type="xs:integer"/>
|
||||
<xs:element name="price" type="xs:decimal"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="category" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>"""
|
||||
with open(schema_file, 'w') as f:
|
||||
f.write(schema_content)
|
||||
result, error = validate_xml(temp_file, schema_file)
|
||||
print(f"XML validation result: {result}")
|
||||
if error:
|
||||
print(f"Error: {error}")
|
||||
print()
|
||||
|
||||
print("5. Transforming XML")
|
||||
print("------------------------------")
|
||||
xslt_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:template match="/">
|
||||
<html>
|
||||
<body>
|
||||
<h2>Bookstore Inventory</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
<xsl:for-each select="bookstore/book">
|
||||
<tr>
|
||||
<td><xsl:value-of select="title"/></td>
|
||||
<td><xsl:value-of select="author"/></td>
|
||||
<td><xsl:value-of select="price"/></td>
|
||||
</tr>
|
||||
</xsl:for-each>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>"""
|
||||
|
||||
xslt_file = tempfile.mktemp(suffix='.xslt')
|
||||
with open(xslt_file, 'w') as f:
|
||||
f.write(xslt_content)
|
||||
|
||||
output_file = tempfile.mktemp(suffix='.html')
|
||||
result = transform_xml(temp_file, xslt_file, output_file)
|
||||
print(f"XML transformation result: {result}")
|
||||
if result and os.path.exists(output_file):
|
||||
print("\nTransformed HTML content:")
|
||||
with open(output_file, 'r') as f:
|
||||
print(f.read())
|
||||
|
||||
finally:
|
||||
# Clean up temporary files
|
||||
for file in [temp_file, schema_file, xslt_file, output_file]:
|
||||
if os.path.exists(file):
|
||||
os.unlink(file)
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,417 @@
|
||||
"""Tools for working with YAML files.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import yaml_tools
|
||||
data = yaml_tools.read_yaml("config.yaml")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import read_yaml, write_yaml, merge_yaml
|
||||
data = read_yaml("config.yaml")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Union, Optional, Any
|
||||
from importlib import util
|
||||
import os
|
||||
from copy import deepcopy
|
||||
|
||||
class YAMLTools:
|
||||
"""Tools for working with YAML files."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize YAMLTools."""
|
||||
self._check_dependencies()
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if required packages are installed."""
|
||||
missing = []
|
||||
for package, module in [('pyyaml', 'yaml'), ('jsonschema', 'jsonschema')]:
|
||||
if util.find_spec(module) is None:
|
||||
missing.append(package)
|
||||
|
||||
if missing:
|
||||
raise ImportError(
|
||||
f"Required packages not available. Please install: {', '.join(missing)}\n"
|
||||
f"Run: pip install {' '.join(missing)}"
|
||||
)
|
||||
|
||||
def read_yaml(
|
||||
self,
|
||||
filepath: str,
|
||||
safe_load: bool = True,
|
||||
encoding: str = 'utf-8'
|
||||
) -> Optional[Union[Dict[str, Any], List[Any]]]:
|
||||
"""Read a YAML file."""
|
||||
try:
|
||||
if util.find_spec('yaml') is None:
|
||||
error_msg = "pyyaml package is not available. Please install it using: pip install pyyaml"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
# Import yaml only when needed
|
||||
import yaml
|
||||
|
||||
with open(filepath, 'r', encoding=encoding) as f:
|
||||
if safe_load:
|
||||
return yaml.safe_load(f)
|
||||
return yaml.load(f, Loader=yaml.FullLoader)
|
||||
except Exception as e:
|
||||
error_msg = f"Error reading YAML file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def write_yaml(
|
||||
self,
|
||||
data: Union[Dict[str, Any], List[Any]],
|
||||
filepath: str,
|
||||
default_flow_style: bool = False,
|
||||
encoding: str = 'utf-8',
|
||||
sort_keys: bool = False,
|
||||
allow_unicode: bool = True
|
||||
) -> bool:
|
||||
"""Write data to YAML file."""
|
||||
try:
|
||||
if util.find_spec('yaml') is None:
|
||||
error_msg = "pyyaml package is not available. Please install it using: pip install pyyaml"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Import yaml only when needed
|
||||
import yaml
|
||||
|
||||
with open(filepath, 'w', encoding=encoding) as f:
|
||||
yaml.dump(
|
||||
data,
|
||||
f,
|
||||
default_flow_style=default_flow_style,
|
||||
sort_keys=sort_keys,
|
||||
allow_unicode=allow_unicode
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
error_msg = f"Error writing YAML file {filepath}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def merge_yaml(
|
||||
self,
|
||||
files: List[str],
|
||||
output_file: Optional[str] = None,
|
||||
strategy: str = 'deep'
|
||||
) -> Union[Dict[str, Any], List[Any], None]:
|
||||
"""Merge multiple YAML files."""
|
||||
try:
|
||||
if util.find_spec('yaml') is None:
|
||||
error_msg = "pyyaml package is not available. Please install it using: pip install pyyaml"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def deep_merge(source: dict, destination: dict) -> dict:
|
||||
"""Deep merge two dictionaries."""
|
||||
for key, value in source.items():
|
||||
if key in destination:
|
||||
if isinstance(value, dict) and isinstance(destination[key], dict):
|
||||
destination[key] = deep_merge(value, destination[key])
|
||||
elif isinstance(value, list) and isinstance(destination[key], list):
|
||||
destination[key].extend(value)
|
||||
else:
|
||||
destination[key] = value
|
||||
else:
|
||||
destination[key] = value
|
||||
return destination
|
||||
|
||||
result = {}
|
||||
for file in files:
|
||||
data = self.read_yaml(file)
|
||||
if data is None:
|
||||
continue
|
||||
|
||||
if strategy == 'deep':
|
||||
if isinstance(data, dict):
|
||||
result = deep_merge(data, result)
|
||||
elif isinstance(data, list):
|
||||
if not result:
|
||||
result = []
|
||||
result.extend(data)
|
||||
else: # shallow
|
||||
if isinstance(data, dict):
|
||||
result.update(data)
|
||||
elif isinstance(data, list):
|
||||
if not result:
|
||||
result = []
|
||||
result.extend(data)
|
||||
|
||||
if output_file:
|
||||
self.write_yaml(result, output_file)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error merging YAML files: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def validate_yaml(
|
||||
self,
|
||||
data: Union[Dict[str, Any], List[Any], str],
|
||||
schema: Dict[str, Any]
|
||||
) -> bool:
|
||||
"""Validate YAML data against a schema."""
|
||||
try:
|
||||
if util.find_spec('jsonschema') is None:
|
||||
error_msg = "jsonschema package is not available. Please install it using: pip install jsonschema"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
# Import jsonschema only when needed
|
||||
from jsonschema import validate, ValidationError
|
||||
|
||||
# Load data if file path
|
||||
if isinstance(data, str):
|
||||
data = self.read_yaml(data)
|
||||
if data is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
validate(instance=data, schema=schema)
|
||||
return True
|
||||
except ValidationError as e:
|
||||
logging.error(f"YAML validation error: {str(e)}")
|
||||
return False
|
||||
except Exception as e:
|
||||
error_msg = f"Error validating YAML: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return False
|
||||
|
||||
def analyze_yaml(
|
||||
self,
|
||||
data: Union[Dict[str, Any], List[Any], str]
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Analyze YAML data structure."""
|
||||
try:
|
||||
# Load data if file path
|
||||
if isinstance(data, str):
|
||||
data = self.read_yaml(data)
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
def analyze_value(value: Any) -> Dict[str, Any]:
|
||||
"""Analyze a single value."""
|
||||
result = {
|
||||
'type': type(value).__name__,
|
||||
'size': len(value) if hasattr(value, '__len__') else 1
|
||||
}
|
||||
|
||||
if isinstance(value, dict):
|
||||
result['keys'] = list(value.keys())
|
||||
result['nested_types'] = {
|
||||
k: type(v).__name__
|
||||
for k, v in value.items()
|
||||
}
|
||||
elif isinstance(value, list):
|
||||
result['element_types'] = list(set(
|
||||
type(x).__name__ for x in value
|
||||
))
|
||||
|
||||
return result
|
||||
|
||||
result = {
|
||||
'structure': analyze_value(data),
|
||||
'stats': {
|
||||
'total_keys': sum(1 for _ in self._walk_dict(data))
|
||||
if isinstance(data, dict) else len(data)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error analyzing YAML: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
def _walk_dict(self, d: Dict[str, Any]) -> Any:
|
||||
"""Walk through nested dictionary."""
|
||||
for k, v in d.items():
|
||||
yield k
|
||||
if isinstance(v, dict):
|
||||
yield from self._walk_dict(v)
|
||||
|
||||
def transform_yaml(
|
||||
self,
|
||||
data: Union[Dict[str, Any], List[Any], str],
|
||||
operations: List[Dict[str, Any]]
|
||||
) -> Optional[Union[Dict[str, Any], List[Any]]]:
|
||||
"""Transform YAML data using specified operations."""
|
||||
try:
|
||||
# Load data if file path
|
||||
if isinstance(data, str):
|
||||
data = self.read_yaml(data)
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
result = deepcopy(data)
|
||||
|
||||
for op in operations:
|
||||
op_type = op.get('type')
|
||||
path = op.get('path', '')
|
||||
value = op.get('value')
|
||||
|
||||
if not op_type:
|
||||
continue
|
||||
|
||||
# Split path into parts
|
||||
parts = [p for p in path.split('/') if p]
|
||||
|
||||
# Get reference to target location
|
||||
target = result
|
||||
parent = None
|
||||
last_key = None
|
||||
|
||||
for part in parts[:-1]:
|
||||
if isinstance(target, dict):
|
||||
if part not in target:
|
||||
target[part] = {}
|
||||
parent = target
|
||||
target = target[part]
|
||||
last_key = part
|
||||
elif isinstance(target, list):
|
||||
idx = int(part)
|
||||
while len(target) <= idx:
|
||||
target.append({})
|
||||
parent = target
|
||||
target = target[idx]
|
||||
last_key = idx
|
||||
|
||||
if parts:
|
||||
last_part = parts[-1]
|
||||
if isinstance(target, dict):
|
||||
if op_type == 'set':
|
||||
target[last_part] = value
|
||||
elif op_type == 'delete':
|
||||
target.pop(last_part, None)
|
||||
elif op_type == 'append' and isinstance(target.get(last_part), list):
|
||||
target[last_part].append(value)
|
||||
elif isinstance(target, list):
|
||||
idx = int(last_part)
|
||||
if op_type == 'set':
|
||||
while len(target) <= idx:
|
||||
target.append(None)
|
||||
target[idx] = value
|
||||
elif op_type == 'delete' and idx < len(target):
|
||||
del target[idx]
|
||||
elif op_type == 'append':
|
||||
target.append(value)
|
||||
else:
|
||||
if op_type == 'set':
|
||||
result = value
|
||||
elif op_type == 'append' and isinstance(result, list):
|
||||
result.append(value)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
error_msg = f"Error transforming YAML: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
|
||||
# Create instance for direct function access
|
||||
_yaml_tools = YAMLTools()
|
||||
read_yaml = _yaml_tools.read_yaml
|
||||
write_yaml = _yaml_tools.write_yaml
|
||||
merge_yaml = _yaml_tools.merge_yaml
|
||||
validate_yaml = _yaml_tools.validate_yaml
|
||||
analyze_yaml = _yaml_tools.analyze_yaml
|
||||
transform_yaml = _yaml_tools.transform_yaml
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("YAMLTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
# Sample data
|
||||
data1 = {
|
||||
'server': {
|
||||
'host': 'localhost',
|
||||
'port': 8080,
|
||||
'debug': True
|
||||
},
|
||||
'database': {
|
||||
'url': 'postgresql://localhost:5432/db',
|
||||
'pool_size': 5
|
||||
}
|
||||
}
|
||||
|
||||
data2 = {
|
||||
'server': {
|
||||
'workers': 4,
|
||||
'timeout': 30
|
||||
},
|
||||
'logging': {
|
||||
'level': 'INFO',
|
||||
'file': 'app.log'
|
||||
}
|
||||
}
|
||||
|
||||
# 1. Write YAML files
|
||||
print("1. Writing YAML Files")
|
||||
print("------------------------------")
|
||||
success1 = write_yaml(data1, 'config1.yaml')
|
||||
success2 = write_yaml(data2, 'config2.yaml')
|
||||
print(f"First file written: {success1}")
|
||||
print(f"Second file written: {success2}")
|
||||
print()
|
||||
|
||||
# 2. Read YAML file
|
||||
print("2. Reading YAML File")
|
||||
print("------------------------------")
|
||||
config = read_yaml('config1.yaml')
|
||||
print("YAML content:")
|
||||
print(config)
|
||||
print()
|
||||
|
||||
# 3. Merge YAML files
|
||||
print("3. Merging YAML Files")
|
||||
print("------------------------------")
|
||||
merged = merge_yaml(['config1.yaml', 'config2.yaml'])
|
||||
print("Merged content:")
|
||||
print(merged)
|
||||
print()
|
||||
|
||||
# 4. Analyze YAML
|
||||
print("4. Analyzing YAML")
|
||||
print("------------------------------")
|
||||
analysis = analyze_yaml(merged)
|
||||
print("Analysis results:")
|
||||
print(analysis)
|
||||
print()
|
||||
|
||||
# 5. Transform YAML
|
||||
print("5. Transforming YAML")
|
||||
print("------------------------------")
|
||||
operations = [
|
||||
{
|
||||
'type': 'set',
|
||||
'path': 'server/host',
|
||||
'value': '0.0.0.0'
|
||||
},
|
||||
{
|
||||
'type': 'delete',
|
||||
'path': 'server/debug'
|
||||
},
|
||||
{
|
||||
'type': 'set',
|
||||
'path': 'logging/handlers',
|
||||
'value': ['console', 'file']
|
||||
}
|
||||
]
|
||||
transformed = transform_yaml(merged, operations)
|
||||
print("Transformed content:")
|
||||
print(transformed)
|
||||
|
||||
print("\n==================================================")
|
||||
print("Demonstration Complete")
|
||||
print("==================================================")
|
||||
|
||||
# Cleanup
|
||||
for file in ['config1.yaml', 'config2.yaml']:
|
||||
if os.path.exists(file):
|
||||
os.remove(file)
|
||||
@@ -0,0 +1,221 @@
|
||||
"""YFinance tools for stock market data.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import get_stock_price, get_stock_info
|
||||
price = get_stock_price("AAPL")
|
||||
info = get_stock_info("AAPL")
|
||||
|
||||
or
|
||||
from praisonaiagents.tools import yfinance
|
||||
price = yfinance.get_stock_price("AAPL")
|
||||
info = yfinance.get_stock_info("AAPL")
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Optional, Any
|
||||
import logging
|
||||
from importlib import util
|
||||
from datetime import datetime
|
||||
|
||||
class YFinanceTools:
|
||||
"""A comprehensive tool for financial data analysis using yfinance"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize YFinanceTools"""
|
||||
self._tickers = {}
|
||||
|
||||
def _get_yfinance(self):
|
||||
"""Get yfinance module, installing if needed"""
|
||||
if util.find_spec('yfinance') is None:
|
||||
error_msg = "yfinance package is not available. Please install it using: pip install yfinance"
|
||||
logging.error(error_msg)
|
||||
return None
|
||||
import yfinance as yf
|
||||
return yf
|
||||
|
||||
def _get_ticker(self, symbol: str):
|
||||
"""Get or create ticker instance"""
|
||||
if symbol not in self._tickers:
|
||||
yf = self._get_yfinance()
|
||||
if yf is None:
|
||||
return None
|
||||
self._tickers[symbol] = yf.Ticker(symbol)
|
||||
return self._tickers[symbol]
|
||||
|
||||
def get_stock_price(self, symbol: str) -> Dict[str, float]:
|
||||
"""
|
||||
Get current stock price and related price metrics
|
||||
|
||||
Args:
|
||||
symbol (str): Stock ticker symbol
|
||||
|
||||
Returns:
|
||||
Dict[str, float]: Current price information
|
||||
"""
|
||||
try:
|
||||
ticker = self._get_ticker(symbol)
|
||||
if ticker is None:
|
||||
return {"error": "yfinance package not available"}
|
||||
|
||||
info = ticker.info
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'price': info.get('regularMarketPrice', 0.0),
|
||||
'open': info.get('regularMarketOpen', 0.0),
|
||||
'high': info.get('regularMarketDayHigh', 0.0),
|
||||
'low': info.get('regularMarketDayLow', 0.0),
|
||||
'volume': info.get('regularMarketVolume', 0),
|
||||
'previous_close': info.get('regularMarketPreviousClose', 0.0),
|
||||
'change': info.get('regularMarketChange', 0.0),
|
||||
'change_percent': info.get('regularMarketChangePercent', 0.0)
|
||||
}
|
||||
except Exception as e:
|
||||
error_msg = f"Error getting stock price for {symbol}: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_stock_info(self, symbol: str) -> Dict:
|
||||
"""
|
||||
Get detailed information about a stock
|
||||
|
||||
Args:
|
||||
symbol (str): Stock ticker symbol
|
||||
|
||||
Returns:
|
||||
Dict: Stock information including company details and key metrics
|
||||
"""
|
||||
try:
|
||||
ticker = self._get_ticker(symbol)
|
||||
if ticker is None:
|
||||
return {"error": "yfinance package not available"}
|
||||
|
||||
info = ticker.info
|
||||
|
||||
relevant_info = {
|
||||
"longName": info.get("longName"),
|
||||
"symbol": info.get("symbol"),
|
||||
"sector": info.get("sector"),
|
||||
"industry": info.get("industry"),
|
||||
"country": info.get("country"),
|
||||
"marketCap": info.get("marketCap"),
|
||||
"currentPrice": info.get("currentPrice"),
|
||||
"currency": info.get("currency"),
|
||||
"exchange": info.get("exchange"),
|
||||
"fiftyTwoWeekHigh": info.get("fiftyTwoWeekHigh"),
|
||||
"fiftyTwoWeekLow": info.get("fiftyTwoWeekLow"),
|
||||
"trailingPE": info.get("trailingPE"),
|
||||
"forwardPE": info.get("forwardPE"),
|
||||
"dividendYield": info.get("dividendYield"),
|
||||
"beta": info.get("beta"),
|
||||
"volume": info.get("volume"),
|
||||
"averageVolume": info.get("averageVolume"),
|
||||
"sharesOutstanding": info.get("sharesOutstanding"),
|
||||
"website": info.get("website"),
|
||||
"longBusinessSummary": info.get("longBusinessSummary")
|
||||
}
|
||||
return {k: v for k, v in relevant_info.items() if v is not None}
|
||||
except Exception as e:
|
||||
error_msg = f"Error fetching stock info: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_historical_data(
|
||||
self,
|
||||
symbol: str,
|
||||
period: str = "1y",
|
||||
interval: str = "1d",
|
||||
start: Optional[datetime] = None,
|
||||
end: Optional[datetime] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get historical price data for a stock
|
||||
|
||||
Args:
|
||||
symbol (str): Stock ticker symbol
|
||||
period (str): Data period e.g. 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max
|
||||
interval (str): Data interval e.g. 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo
|
||||
start (datetime, optional): Start date for data
|
||||
end (datetime, optional): End date for data
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: List of historical price data points
|
||||
"""
|
||||
try:
|
||||
ticker = self._get_ticker(symbol)
|
||||
if ticker is None:
|
||||
return [{"error": "yfinance package not available"}]
|
||||
|
||||
hist = ticker.history(period=period, interval=interval, start=start, end=end)
|
||||
data = hist.reset_index().to_dict('records')
|
||||
|
||||
# Convert timestamps to ISO format strings
|
||||
for record in data:
|
||||
if 'Date' in record and hasattr(record['Date'], 'isoformat'):
|
||||
record['Date'] = record['Date'].isoformat()
|
||||
if 'Datetime' in record and hasattr(record['Datetime'], 'isoformat'):
|
||||
record['Datetime'] = record['Datetime'].isoformat()
|
||||
return data
|
||||
except Exception as e:
|
||||
error_msg = f"Error fetching historical data: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
return [{"error": error_msg}]
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
print("\n==================================================")
|
||||
print("YFinanceTools Demonstration")
|
||||
print("==================================================\n")
|
||||
|
||||
try:
|
||||
yf_tools = YFinanceTools()
|
||||
|
||||
# 1. Basic Stock Information
|
||||
print("1. Basic Stock Information for AAPL")
|
||||
print("------------------------------")
|
||||
info = yf_tools.get_stock_info("AAPL")
|
||||
if "error" not in info:
|
||||
print(f"Company: {info.get('longName')}")
|
||||
print(f"Sector: {info.get('sector')}")
|
||||
print(f"Industry: {info.get('industry')}")
|
||||
print(f"Country: {info.get('country')}\n")
|
||||
else:
|
||||
print(f"Error: {info['error']}\n")
|
||||
|
||||
# 2. Current Price Data
|
||||
print("2. Current Price Data")
|
||||
print("------------------------------")
|
||||
price_data = yf_tools.get_stock_price("AAPL")
|
||||
if "error" not in price_data:
|
||||
print(f"Current Price: ${price_data.get('price', 'N/A')}")
|
||||
print(f"Previous Close: ${price_data.get('previous_close', 'N/A')}")
|
||||
print(f"Day Range: ${price_data.get('low', 'N/A')} - ${price_data.get('high', 'N/A')}\n")
|
||||
else:
|
||||
print(f"Error: {price_data['error']}\n")
|
||||
|
||||
# 3. Historical Data
|
||||
print("3. Historical Data (Last 5 Days)")
|
||||
print("------------------------------")
|
||||
hist_data = yf_tools.get_historical_data("AAPL", period="5d")
|
||||
if not any("error" in d for d in hist_data):
|
||||
for day in hist_data:
|
||||
print(f"Date: {day.get('Date')}, Close: ${day.get('Close', 'N/A'):.2f}")
|
||||
print()
|
||||
else:
|
||||
print(f"{hist_data[0]['error']}\n")
|
||||
|
||||
# 4. Multi-Stock Example
|
||||
print("5. Multi-Stock Current Prices")
|
||||
print("------------------------------")
|
||||
for symbol in ["AAPL", "MSFT", "GOOGL"]:
|
||||
price_data = yf_tools.get_stock_price(symbol)
|
||||
if "error" not in price_data:
|
||||
print(f"{symbol}: ${price_data.get('price', 'N/A')}")
|
||||
else:
|
||||
print(f"{symbol}: Error fetching price")
|
||||
print()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {str(e)}")
|
||||
|
||||
print("==================================================")
|
||||
print("Analysis Complete")
|
||||
print("==================================================")
|
||||
@@ -0,0 +1,19 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import execute_code, analyze_code, format_code, lint_code, disassemble_code # Code Tools
|
||||
from praisonaiagents.tools import execute_command, list_processes, kill_process, get_system_info # Shell Tools
|
||||
from praisonaiagents.tools import duckduckgo # Web Search Tool
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Programming Agent", self_reflect=True, min_reflect=5, max_reflect=10,
|
||||
tools=[execute_code, analyze_code, format_code, lint_code, disassemble_code, execute_command, list_processes, kill_process, get_system_info, duckduckgo]
|
||||
)
|
||||
agent.start(
|
||||
"Write a python script using yfinance to find the stock price of Tesla"
|
||||
"First check if required packages are installed"
|
||||
"Run it using execute_code"
|
||||
"execute_command if you want to run any terminal command"
|
||||
"search internet using duckduckgo if you want to know update python package information"
|
||||
"Analyse the output using analyze_code and fix error if required"
|
||||
"if no package is installed, install it"
|
||||
"then run the code"
|
||||
)
|
||||
@@ -0,0 +1,91 @@
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.task import Task
|
||||
from praisonaiagents.agents import PraisonAIAgents
|
||||
from typing import List, Dict
|
||||
import time
|
||||
|
||||
def get_time_check():
|
||||
current_time = int(time.time())
|
||||
result = "even" if current_time % 2 == 0 else "odd"
|
||||
print(f"Time check: {current_time} is {result}")
|
||||
return result
|
||||
|
||||
def create_prompt_chain():
|
||||
# Create agents for each step in the chain
|
||||
agent1 = Agent(
|
||||
name="Time Checker",
|
||||
role="Time checker",
|
||||
goal="Check if the time is even or odd",
|
||||
instructions="Check if the time is even or odd",
|
||||
tools=[get_time_check]
|
||||
)
|
||||
|
||||
agent2 = Agent(
|
||||
name="Advanced Analyzer",
|
||||
role="Advanced data analyzer",
|
||||
goal="Perform in-depth analysis of processed data",
|
||||
instructions="Analyze the processed data in detail"
|
||||
)
|
||||
|
||||
agent3 = Agent(
|
||||
name="Final Processor",
|
||||
role="Final data processor",
|
||||
goal="Generate final output based on analysis",
|
||||
instructions="Create final output based on analyzed data"
|
||||
)
|
||||
|
||||
# Create tasks for each step
|
||||
initial_task = Task(
|
||||
name="time_check",
|
||||
description="Getting time check and checking if it is even or odd",
|
||||
expected_output="Getting time check and checking if it is even or odd",
|
||||
agent=agent1,
|
||||
is_start=True, # Mark as the starting task
|
||||
task_type="decision", # This task will make a decision
|
||||
next_tasks=["advanced_analysis"], # Next task if condition passes
|
||||
condition={
|
||||
"even": ["advanced_analysis"], # If passes, go to advanced analysis
|
||||
"odd": ["final_processing"] # If fails, exit the chain
|
||||
}
|
||||
)
|
||||
|
||||
analysis_task = Task(
|
||||
name="advanced_analysis",
|
||||
description="Perform advanced analysis on the processed data",
|
||||
expected_output="Analyzed data ready for final processing",
|
||||
agent=agent2,
|
||||
next_tasks=["final_processing"]
|
||||
)
|
||||
|
||||
final_task = Task(
|
||||
name="final_processing",
|
||||
description="Generate final output",
|
||||
expected_output="Final processed result",
|
||||
agent=agent3
|
||||
)
|
||||
|
||||
# Create the workflow manager
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[agent1, agent2, agent3],
|
||||
tasks=[initial_task, analysis_task, final_task],
|
||||
process="workflow", # Use workflow process type
|
||||
verbose=True
|
||||
)
|
||||
|
||||
return workflow
|
||||
|
||||
def main():
|
||||
# Create and run the prompt chain
|
||||
workflow = create_prompt_chain()
|
||||
|
||||
# Run the workflow
|
||||
results = workflow.start()
|
||||
|
||||
# Print results
|
||||
print("\nWorkflow Results:")
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
print(f"Task {task_id}: {result.raw}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,78 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import time
|
||||
|
||||
def get_time_check():
|
||||
current_time = int(time.time())
|
||||
result = "even" if current_time % 2 == 0 else "odd"
|
||||
print(f"Time check: {current_time} is {result}")
|
||||
return result
|
||||
|
||||
# Create agents for each step in the chain
|
||||
agent1 = Agent(
|
||||
name="Time Checker",
|
||||
role="Time checker",
|
||||
goal="Check if the time is even or odd",
|
||||
instructions="Check if the time is even or odd",
|
||||
tools=[get_time_check]
|
||||
)
|
||||
|
||||
agent2 = Agent(
|
||||
name="Advanced Analyzer",
|
||||
role="Advanced data analyzer",
|
||||
goal="Perform in-depth analysis of processed data",
|
||||
instructions="Analyze the processed data in detail"
|
||||
)
|
||||
|
||||
agent3 = Agent(
|
||||
name="Final Processor",
|
||||
role="Final data processor",
|
||||
goal="Generate final output based on analysis",
|
||||
instructions="Create final output based on analyzed data"
|
||||
)
|
||||
|
||||
# Create tasks for each step
|
||||
initial_task = Task(
|
||||
name="time_check",
|
||||
description="Getting time check and checking if it is even or odd",
|
||||
expected_output="Getting time check and checking if it is even or odd",
|
||||
agent=agent1,
|
||||
is_start=True, # Mark as the starting task
|
||||
task_type="decision", # This task will make a decision
|
||||
next_tasks=["advanced_analysis"], # Next task if condition passes
|
||||
condition={
|
||||
"even": ["advanced_analysis"], # If passes, go to advanced analysis
|
||||
"odd": "exit" # If fails, exit the chain
|
||||
}
|
||||
)
|
||||
|
||||
analysis_task = Task(
|
||||
name="advanced_analysis",
|
||||
description="Perform advanced analysis on the processed data",
|
||||
expected_output="Analyzed data ready for final processing",
|
||||
agent=agent2,
|
||||
next_tasks=["final_processing"]
|
||||
)
|
||||
|
||||
final_task = Task(
|
||||
name="final_processing",
|
||||
description="Generate final output",
|
||||
expected_output="Final processed result",
|
||||
agent=agent3
|
||||
)
|
||||
|
||||
# Create the workflow manager
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[agent1, agent2, agent3],
|
||||
tasks=[initial_task, analysis_task, final_task],
|
||||
process="workflow", # Use workflow process type
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Run the workflow
|
||||
results = workflow.start()
|
||||
|
||||
# Print results
|
||||
print("\nWorkflow Results:")
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
print(f"Task {task_id}: {result.raw}")
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.21"
|
||||
version = "0.0.36"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
authors = [
|
||||
{ name="Mervin Praison" }
|
||||
@@ -13,4 +13,9 @@ dependencies = [
|
||||
"pydantic",
|
||||
"rich",
|
||||
"openai"
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
memory = [
|
||||
"chromadb>=0.6.0"
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Recommendation Agent", tools=[duckduckgo])
|
||||
agent.start("Recommend me a good movie to watch in 2025")
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Research Agent", tools=[duckduckgo])
|
||||
agent.start("Research about AI 2024")
|
||||
@@ -0,0 +1,82 @@
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.task import Task
|
||||
from praisonaiagents.agents import PraisonAIAgents
|
||||
import time
|
||||
|
||||
current_time = int(time.time())
|
||||
result = "even" if current_time % 2 == 0 else "odd"
|
||||
print(f"Time check: {current_time} is {result}")
|
||||
|
||||
# Create specialized agents
|
||||
router = Agent(
|
||||
name="Router",
|
||||
role="Input Router",
|
||||
goal="Evaluate input and determine routing path",
|
||||
instructions="Analyze input and decide whether to proceed or exit",
|
||||
tools=[get_time_check]
|
||||
)
|
||||
|
||||
processor1 = Agent(
|
||||
name="Processor 1",
|
||||
role="Secondary Processor",
|
||||
goal="Process valid inputs that passed initial check",
|
||||
instructions="Process data that passed the routing check"
|
||||
)
|
||||
|
||||
processor2 = Agent(
|
||||
name="Processor 2",
|
||||
role="Final Processor",
|
||||
goal="Perform final processing on validated data",
|
||||
instructions="Generate final output for processed data"
|
||||
)
|
||||
|
||||
# Create tasks with routing logic
|
||||
routing_task = Task(
|
||||
name="initial_routing",
|
||||
description="check the time and return according to what is returned",
|
||||
expected_output="pass or fail based on what is returned",
|
||||
agent=router,
|
||||
is_start=True,
|
||||
task_type="decision",
|
||||
condition={
|
||||
"pass": ["process_valid"],
|
||||
"fail": "exit"
|
||||
}
|
||||
)
|
||||
|
||||
processing_task = Task(
|
||||
name="process_valid",
|
||||
description="Process validated input",
|
||||
expected_output="Processed data ready for final step",
|
||||
agent=processor1,
|
||||
next_tasks=["final_process"]
|
||||
)
|
||||
|
||||
final_task = Task(
|
||||
name="final_process",
|
||||
description="Generate final output",
|
||||
expected_output="Final processed result",
|
||||
agent=processor2
|
||||
)
|
||||
|
||||
# Create and run workflow
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[router, processor1, processor2],
|
||||
tasks=[routing_task, processing_task, final_task],
|
||||
process="workflow",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
print("\nStarting Routing Workflow...")
|
||||
print("=" * 50)
|
||||
|
||||
results = workflow.start()
|
||||
|
||||
print("\nWorkflow Results:")
|
||||
print("=" * 50)
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
task_name = result.description
|
||||
print(f"\nTask: {task_name}")
|
||||
print(f"Result: {result.raw}")
|
||||
print("-" * 50)
|
||||
@@ -0,0 +1,83 @@
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.task import Task
|
||||
from praisonaiagents.agents import PraisonAIAgents
|
||||
import time
|
||||
|
||||
def get_time_check():
|
||||
current_time = int(time.time())
|
||||
result = "even" if current_time % 2 == 0 else "odd"
|
||||
print(f"Time check: {current_time} is {result}")
|
||||
return result
|
||||
|
||||
# Create specialized agents
|
||||
router = Agent(
|
||||
name="Router",
|
||||
role="Input Router",
|
||||
goal="Evaluate input and determine routing path",
|
||||
instructions="Analyze input and decide whether to proceed or exit",
|
||||
tools=[get_time_check]
|
||||
)
|
||||
|
||||
processor1 = Agent(
|
||||
name="Processor 1",
|
||||
role="Secondary Processor",
|
||||
goal="Process valid inputs that passed initial check",
|
||||
instructions="Process data that passed the routing check"
|
||||
)
|
||||
|
||||
processor2 = Agent(
|
||||
name="Processor 2",
|
||||
role="Final Processor",
|
||||
goal="Perform final processing on validated data",
|
||||
instructions="Generate final output for processed data"
|
||||
)
|
||||
|
||||
# Create tasks with routing logic
|
||||
routing_task = Task(
|
||||
name="initial_routing",
|
||||
description="check the time and return according to what is returned",
|
||||
expected_output="pass or fail based on what is returned",
|
||||
agent=router,
|
||||
is_start=True,
|
||||
task_type="decision",
|
||||
condition={
|
||||
"pass": ["process_valid"],
|
||||
"fail": ["process_invalid"]
|
||||
}
|
||||
)
|
||||
|
||||
processing_task = Task(
|
||||
name="process_valid",
|
||||
description="Process validated input",
|
||||
expected_output="Processed data ready for final step",
|
||||
agent=processor1,
|
||||
)
|
||||
|
||||
final_task = Task(
|
||||
name="process_invalid",
|
||||
description="Generate final output",
|
||||
expected_output="Final processed result",
|
||||
agent=processor2
|
||||
)
|
||||
|
||||
# Create and run workflow
|
||||
workflow = PraisonAIAgents(
|
||||
agents=[router, processor1, processor2],
|
||||
tasks=[routing_task, processing_task, final_task],
|
||||
process="workflow",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
print("\nStarting Routing Workflow...")
|
||||
print("=" * 50)
|
||||
|
||||
results = workflow.start()
|
||||
|
||||
print("\nWorkflow Results:")
|
||||
print("=" * 50)
|
||||
for task_id, result in results["task_results"].items():
|
||||
if result:
|
||||
task_name = result.description
|
||||
print(f"\nTask: {task_name}")
|
||||
print(f"Result: {result.raw}")
|
||||
print("-" * 50)
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Shopping Agent", tools=[duckduckgo])
|
||||
agent.start("I want to buy iPhone 16 Pro Max, check 5 stores and give me price in table")
|
||||
@@ -0,0 +1,4 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(instructions="You are a Markdown Agent, output in markdown format")
|
||||
agent.start("Write a blog post about AI")
|
||||
@@ -0,0 +1,56 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
|
||||
from pydantic import BaseModel
|
||||
|
||||
class AnalysisReport(BaseModel):
|
||||
title: str
|
||||
findings: str
|
||||
summary: str
|
||||
|
||||
# Create a researcher agent
|
||||
researcher = Agent(
|
||||
name="AIResearcher",
|
||||
role="Technology Research Analyst",
|
||||
goal="Analyze and structure information about AI developments",
|
||||
backstory="Expert analyst specializing in AI technology trends",
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini",
|
||||
tools=[Tools.internet_search],
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Create an analyst agent
|
||||
analyst = Agent(
|
||||
name="DataAnalyst",
|
||||
role="Data Insights Specialist",
|
||||
goal="Structure and analyze research findings",
|
||||
backstory="Senior data analyst with expertise in pattern recognition",
|
||||
verbose=True,
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Define structured tasks
|
||||
research_task = Task(
|
||||
name="gather_research",
|
||||
description="Research recent AI developments in 2024",
|
||||
agent=researcher,
|
||||
expected_output="Research findings"
|
||||
)
|
||||
|
||||
analysis_task = Task(
|
||||
name="analyze_findings",
|
||||
description="Analyze research findings and create structured report. No additional text or explanation.",
|
||||
agent=analyst,
|
||||
output_json=AnalysisReport,
|
||||
expected_output="JSON object"
|
||||
)
|
||||
|
||||
# Initialize and run agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher, analyst],
|
||||
tasks=[research_task, analysis_task],
|
||||
process="sequential",
|
||||
verbose=True
|
||||
)
|
||||
result = agents.start()
|
||||
print(result)
|
||||
@@ -0,0 +1,95 @@
|
||||
from praisonaiagents import Agent
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
# Define structured output models
|
||||
class SearchResult(BaseModel):
|
||||
title: str
|
||||
url: Optional[str]
|
||||
snippet: str
|
||||
|
||||
class AnalysisReport(BaseModel):
|
||||
topic: str
|
||||
key_findings: List[str]
|
||||
search_results: List[SearchResult]
|
||||
summary: str
|
||||
confidence_score: float
|
||||
|
||||
def get_structured_analysis(query: str, verbose: bool = True) -> AnalysisReport:
|
||||
"""
|
||||
Performs a search and returns a structured analysis using an AI agent.
|
||||
|
||||
Args:
|
||||
query (str): The search query or topic to analyze
|
||||
verbose (bool): Whether to show detailed output
|
||||
|
||||
Returns:
|
||||
AnalysisReport: A structured report containing the analysis
|
||||
"""
|
||||
# Create an agent with search capabilities
|
||||
agent = Agent(
|
||||
name="StructuredAnalyst",
|
||||
role="Research Analyst",
|
||||
goal="Analyze topics and provide structured reports",
|
||||
backstory="Expert at gathering information and providing structured analysis",
|
||||
verbose=verbose,
|
||||
self_reflect=True, # Enable self-reflection for better quality
|
||||
markdown=True,
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create the analysis prompt
|
||||
prompt = f"""
|
||||
Analyze the following topic: "{query}"
|
||||
|
||||
Provide a structured analysis in JSON format that matches this schema:
|
||||
{{
|
||||
"topic": "string",
|
||||
"key_findings": ["string"],
|
||||
"search_results": [
|
||||
{{
|
||||
"title": "string",
|
||||
"url": "string",
|
||||
"snippet": "string"
|
||||
}}
|
||||
],
|
||||
"summary": "string",
|
||||
"confidence_score": float (0-1)
|
||||
}}
|
||||
|
||||
Requirements:
|
||||
1. Include at least 3 key findings
|
||||
2. Include at least 2 relevant search results
|
||||
3. Provide a comprehensive summary
|
||||
4. Set confidence score based on quality of sources (0-1)
|
||||
|
||||
Return ONLY the JSON object, no other text.
|
||||
"""
|
||||
|
||||
# Get structured response from agent
|
||||
response = agent.chat(
|
||||
prompt=prompt,
|
||||
output_json=AnalysisReport # This ensures response matches our model
|
||||
)
|
||||
|
||||
return AnalysisReport.model_validate_json(response)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
analysis = get_structured_analysis("Latest developments in AI agents and autonomous systems")
|
||||
print(analysis)
|
||||
# Print the structured results
|
||||
print("\n=== Structured Analysis Report ===")
|
||||
print(f"Topic: {analysis.topic}")
|
||||
print("\nKey Findings:")
|
||||
for i, finding in enumerate(analysis.key_findings, 1):
|
||||
print(f"{i}. {finding}")
|
||||
|
||||
print("\nSearch Results:")
|
||||
for result in analysis.search_results:
|
||||
print(f"\nTitle: {result.title}")
|
||||
print(f"URL: {result.url}")
|
||||
print(f"Snippet: {result.snippet}")
|
||||
|
||||
print(f"\nSummary: {analysis.summary}")
|
||||
print(f"Confidence Score: {analysis.confidence_score:.2f}")
|
||||
@@ -0,0 +1,758 @@
|
||||
"date","close","volume","open","high","low"
|
||||
"11:34","270.49","4,787,699","264.50","273.88","262.24"
|
||||
"2018/10/15","259.5900","6189026.0000","259.0600","263.2800","254.5367"
|
||||
"2018/10/12","258.7800","7189257.0000","261.0000","261.9900","252.0100"
|
||||
"2018/10/11","252.2300","8128184.0000","257.5300","262.2500","249.0300"
|
||||
"2018/10/10","256.8800","12781560.0000","264.6100","265.5100","247.7700"
|
||||
"2018/10/09","262.8000","12037780.0000","255.2500","266.7700","253.3000"
|
||||
"2018/10/08","250.5600","13371180.0000","264.5200","267.7599","249.0000"
|
||||
"2018/10/05","261.9500","17900710.0000","274.6500","274.8800","260.0000"
|
||||
"2018/10/04","281.8300","9638885.0000","293.9500","294.0000","277.6700"
|
||||
"2018/10/03","294.8000","7982272.0000","303.3300","304.6000","291.5700"
|
||||
"2018/10/02","301.0200","11699690.0000","313.9500","316.8400","299.1500"
|
||||
"2018/10/01","310.7000","21714210.0000","305.7700","311.4400","301.0500"
|
||||
"2018/09/28","264.7700","33597290.0000","270.2600","278.0000","260.5550"
|
||||
"2018/09/27","307.5200","7337760.0000","312.9000","314.9600","306.9100"
|
||||
"2018/09/26","309.5800","7835863.0000","301.9100","313.8900","301.1093"
|
||||
"2018/09/25","300.9900","4472287.0000","300.0000","304.6000","296.5000"
|
||||
"2018/09/24","299.6800","4834384.0000","298.4800","302.9993","293.5800"
|
||||
"2018/09/21","299.1000","5038497.0000","297.7000","300.5800","295.3700"
|
||||
"2018/09/20","298.3300","7332477.0000","303.5600","305.9800","293.3300"
|
||||
"2018/09/19","299.0200","8264353.0000","280.5100","300.0000","280.5000"
|
||||
"2018/09/18","284.9600","16494280.0000","296.6900","302.6400","275.5000"
|
||||
"2018/09/17","294.8400","6866208.0000","290.0400","300.8700","288.1300"
|
||||
"2018/09/14","295.2000","6757652.0000","288.7600","297.3256","286.5200"
|
||||
"2018/09/13","289.4600","6333281.0000","288.0200","295.0000","285.1800"
|
||||
"2018/09/12","290.5400","10002150.0000","281.4400","292.5000","278.6463"
|
||||
"2018/09/11","279.4400","9161061.0000","279.4700","282.0000","273.5500"
|
||||
"2018/09/10","285.5000","14232250.0000","273.2600","286.0300","271.0000"
|
||||
"2018/09/07","263.2400","22442310.0000","260.1000","268.3500","252.2548"
|
||||
"2018/09/06","280.9500","7472756.0000","284.8000","291.1700","278.8800"
|
||||
"2018/09/05","280.7400","7707342.0000","285.0500","286.7800","277.1800"
|
||||
"2018/09/04","288.9500","8304571.0000","296.9400","298.1900","288.0000"
|
||||
"2018/08/31","301.6600","5367048.0000","302.0000","305.3082","298.6000"
|
||||
"2018/08/30","303.1500","7210625.0000","302.2600","304.6000","297.7200"
|
||||
"2018/08/29","305.0100","7410538.0000","310.2700","311.8500","303.6900"
|
||||
"2018/08/28","311.8600","7639453.0000","318.4100","318.8800","311.1900"
|
||||
"2018/08/27","319.2700","13062650.0000","318.0000","322.4350","308.8100"
|
||||
"2018/08/24","322.8200","3592734.0000","320.7000","323.8500","319.4000"
|
||||
"2018/08/23","320.1000","5138685.0000","319.1400","327.3200","318.1000"
|
||||
"2018/08/22","321.6400","5935853.0000","320.8700","323.8800","314.6700"
|
||||
"2018/08/21","321.9000","13147770.0000","310.6100","324.7900","309.0000"
|
||||
"2018/08/20","308.4400","17380430.0000","291.7040","308.5000","288.2000"
|
||||
"2018/08/17","305.5000","18895880.0000","323.5000","326.7700","303.5300"
|
||||
"2018/08/16","335.4500","6045392.0000","339.9100","342.2800","333.8200"
|
||||
"2018/08/15","338.6900","9073033.0000","341.9060","344.4900","332.1400"
|
||||
"2018/08/14","347.6400","6948511.0000","358.4500","359.1995","347.1000"
|
||||
"2018/08/13","356.4100","10444920.0000","361.1300","363.1900","349.0200"
|
||||
"2018/08/10","355.4900","11530510.0000","354.0000","360.0000","346.0000"
|
||||
"2018/08/09","352.4500","16951800.0000","365.5500","367.0100","345.7300"
|
||||
"2018/08/08","370.3400","24535420.0000","369.0900","382.6400","367.1200"
|
||||
"2018/08/07","379.5700","30596400.0000","343.8400","387.4600","339.1501"
|
||||
"2018/08/06","341.9900","8536766.0000","345.4600","354.9800","341.8200"
|
||||
"2018/08/03","348.1700","13646640.0000","347.8100","355.0000","342.5300"
|
||||
"2018/08/02","349.5400","23159160.0000","328.4400","349.9900","323.1600"
|
||||
"2018/08/01","300.8400","8768099.0000","297.9900","303.0000","293.0000"
|
||||
"2018/07/31","298.1400","5055094.0000","292.2500","298.3200","289.0700"
|
||||
"2018/07/30","290.1700","6799581.0000","295.9000","296.1000","286.1300"
|
||||
"2018/07/27","297.1800","5694690.0000","307.2500","307.6933","295.3404"
|
||||
"2018/07/26","306.6500","4619567.0000","304.8500","310.7000","303.6411"
|
||||
"2018/07/25","308.7400","7031382.0000","296.7400","309.6200","294.5025"
|
||||
"2018/07/24","297.4300","9586186.0000","304.4209","307.7171","292.5452"
|
||||
"2018/07/23","303.2000","10974820.0000","301.8400","305.5000","292.8601"
|
||||
"2018/07/20","313.5800","5159831.0000","321.2300","323.2400","311.7093"
|
||||
"2018/07/19","320.2300","5907116.0000","316.3300","323.5400","314.0100"
|
||||
"2018/07/18","323.8500","5610085.0000","325.0000","325.5000","316.2500"
|
||||
"2018/07/17","322.6900","6987782.0000","308.8100","324.7400","308.5000"
|
||||
"2018/07/16","310.1000","7800454.0000","311.7100","315.1600","306.2500"
|
||||
"2018/07/13","318.8700","5866776.0000","315.5800","319.5849","309.2500"
|
||||
"2018/07/12","316.7100","5711851.0000","321.4300","323.2300","312.7700"
|
||||
"2018/07/11","318.9600","4878595.0000","315.8000","321.9400","315.0700"
|
||||
"2018/07/10","322.4700","9437557.0000","324.5600","327.6771","319.2000"
|
||||
"2018/07/09","318.5100","7579541.0000","311.9900","318.5200","308.0000"
|
||||
"2018/07/06","308.9000","8860198.0000","304.9550","312.0700","302.0000"
|
||||
"2018/07/05","309.1600","17457570.0000","313.7600","314.3900","296.2200"
|
||||
"2018/07/03","310.8600","12282640.0000","331.7500","332.4900","309.6900"
|
||||
"2018/07/02","335.0700","18732710.0000","360.0700","364.7800","329.8500"
|
||||
"2018/06/29","342.9500","6475903.0000","353.3300","353.8600","342.4100"
|
||||
"2018/06/28","349.9300","8388172.0000","348.6600","357.0200","346.1100"
|
||||
"2018/06/27","344.5000","8313817.0000","345.0000","350.7900","339.5000"
|
||||
"2018/06/26","342.0000","7434749.0000","336.0540","343.5500","325.7990"
|
||||
"2018/06/25","333.0100","6925089.0000","330.1200","338.4700","327.5000"
|
||||
"2018/06/22","333.6300","10253770.0000","351.5400","352.2500","332.0000"
|
||||
"2018/06/21","347.5100","7949846.0000","362.0000","366.2139","346.2700"
|
||||
"2018/06/20","362.2200","8370268.0000","358.0400","364.3800","352.0000"
|
||||
"2018/06/19","352.5500","12734840.0000","365.1600","370.0000","346.2500"
|
||||
"2018/06/18","370.8300","12025450.0000","355.4000","373.7300","354.5000"
|
||||
"2018/06/15","358.1700","10827560.0000","353.8400","364.6700","351.2500"
|
||||
"2018/06/14","357.7200","10961150.0000","347.6300","358.7500","346.6000"
|
||||
"2018/06/13","344.7800","9401467.0000","346.7060","347.2000","339.8000"
|
||||
"2018/06/12","342.7700","22256430.0000","344.7000","354.9700","338.0000"
|
||||
"2018/06/11","332.1000","13166050.0000","322.5100","334.6600","322.5000"
|
||||
"2018/06/08","317.6600","8192175.0000","319.0000","324.4800","317.1500"
|
||||
"2018/06/07","316.0900","14336110.0000","316.1500","330.0000","313.5800"
|
||||
"2018/06/06","319.5000","18742400.0000","300.5000","322.1700","297.4800"
|
||||
"2018/06/05","291.1300","5877873.0000","297.7000","297.8000","286.7400"
|
||||
"2018/06/04","296.7400","4793787.0000","294.3400","299.0000","293.5480"
|
||||
"2018/06/01","291.8200","5417984.0000","285.8600","291.9500","283.8390"
|
||||
"2018/05/31","284.7300","5912825.0000","287.2100","290.3700","282.9300"
|
||||
"2018/05/30","291.7200","7473590.0000","283.2900","295.0050","281.6000"
|
||||
"2018/05/29","283.7600","5657153.0000","278.5100","286.5000","276.1500"
|
||||
"2018/05/25","278.8500","3872621.0000","277.6250","279.6400","275.6100"
|
||||
"2018/05/24","277.8500","4170789.0000","278.4000","281.1100","274.8900"
|
||||
"2018/05/23","279.0700","5972237.0000","277.7600","279.9099","274.0000"
|
||||
"2018/05/22","275.0100","8938501.0000","287.7620","288.0000","273.4220"
|
||||
"2018/05/21","284.4900","9167844.0000","281.3300","291.4900","281.3000"
|
||||
"2018/05/18","276.8200","7243699.0000","284.6500","284.6500","274.0000"
|
||||
"2018/05/17","284.5400","4418258.0000","285.9000","289.1873","283.9700"
|
||||
"2018/05/16","286.4800","5669644.0000","283.8300","288.8100","281.5600"
|
||||
"2018/05/15","284.1800","9509753.0000","285.0100","286.9600","280.5000"
|
||||
"2018/05/14","291.9700","7265501.0000","303.3200","304.9400","291.6200"
|
||||
"2018/05/11","301.0600","4665321.0000","307.7000","308.8800","299.0800"
|
||||
"2018/05/10","305.0200","5642771.0000","307.4997","312.9900","304.1119"
|
||||
"2018/05/09","306.8500","5712255.0000","300.4100","307.0100","300.0500"
|
||||
"2018/05/08","301.9700","5927447.0000","300.7950","307.7500","299.0000"
|
||||
"2018/05/07","302.7700","8673090.0000","297.5000","305.9588","295.1700"
|
||||
"2018/05/04","294.0900","8565506.0000","283.0000","296.8600","279.5200"
|
||||
"2018/05/03","284.4500","17339090.0000","278.7900","288.0400","275.2300"
|
||||
"2018/05/02","301.1500","8346176.0000","298.5700","306.8500","297.7844"
|
||||
"2018/05/01","299.9200","4614946.0000","293.5100","300.8200","293.2200"
|
||||
"2018/04/30","293.9000","4224068.0000","293.6100","298.7300","292.5000"
|
||||
"2018/04/27","294.0750","4346854.0000","285.3700","294.4700","283.8300"
|
||||
"2018/04/26","285.4800","4348108.0000","278.7500","285.7900","276.5000"
|
||||
"2018/04/25","280.6900","4000421.0000","283.5000","285.1600","277.2500"
|
||||
"2018/04/24","283.4600","5678242.0000","285.0000","287.0900","278.4600"
|
||||
"2018/04/23","283.3700","4880743.0000","291.2900","291.6200","282.3300"
|
||||
"2018/04/20","290.2400","5622630.0000","295.1700","299.9800","289.7500"
|
||||
"2018/04/19","300.0800","6083800.0000","291.0800","301.0100","288.5500"
|
||||
"2018/04/18","293.3500","6531563.0000","291.0800","300.2400","288.1600"
|
||||
"2018/04/17","287.6900","6931152.0000","288.8700","292.1700","282.5100"
|
||||
"2018/04/16","291.2100","6289497.0000","299.0000","299.6600","289.0100"
|
||||
"2018/04/13","300.3400","7319225.0000","303.6000","303.9499","295.9800"
|
||||
"2018/04/12","294.0800","7595525.0000","302.3200","303.9500","293.6800"
|
||||
"2018/04/11","300.9300","7470204.0000","300.7400","308.9800","299.6600"
|
||||
"2018/04/10","304.7000","11017010.0000","298.9700","307.1000","293.6800"
|
||||
"2018/04/09","289.6600","10241410.0000","300.3700","309.5000","289.2100"
|
||||
"2018/04/06","299.3000","13516530.0000","301.0000","309.2800","295.5000"
|
||||
"2018/04/05","305.7200","19025860.0000","289.3400","306.2600","288.2000"
|
||||
"2018/04/04","286.9400","19880180.0000","252.7800","288.3700","252.0000"
|
||||
"2018/04/03","267.5300","18829650.0000","269.8200","273.3500","254.4900"
|
||||
"2018/04/02","252.4800","16093290.0000","256.2600","260.3318","244.5901"
|
||||
"2018/03/29","266.1300","15014440.0000","256.4900","270.9599","248.2100"
|
||||
"2018/03/28","257.7800","20962280.0000","264.5800","268.6800","252.1000"
|
||||
"2018/03/27","279.1800","13787060.0000","304.0000","304.2700","277.1800"
|
||||
"2018/03/26","304.1800","8364937.0000","307.3400","307.5900","291.3600"
|
||||
"2018/03/23","301.5400","6639960.0000","311.2500","311.6100","300.4500"
|
||||
"2018/03/22","309.1000","4924928.0000","313.8900","318.8200","308.1800"
|
||||
"2018/03/21","316.5300","5954247.0000","310.2500","322.4400","310.1900"
|
||||
"2018/03/20","310.5500","4761031.0000","314.8700","316.2500","308.7600"
|
||||
"2018/03/19","313.5600","7467890.0000","316.5000","320.7500","309.6700"
|
||||
"2018/03/16","321.3500","6113365.0000","325.9600","327.4000","319.0700"
|
||||
"2018/03/15","325.6000","6554568.0000","329.3800","332.8500","321.1000"
|
||||
"2018/03/14","326.6300","7952152.0000","336.7600","339.8100","323.9300"
|
||||
"2018/03/13","341.8400","5960636.0000","344.9200","345.1200","336.2635"
|
||||
"2018/03/12","345.5100","8252919.0000","328.6100","347.2100","326.5000"
|
||||
"2018/03/09","327.1700","5502779.0000","324.1000","328.4900","322.3700"
|
||||
"2018/03/08","329.1000","3514367.0000","332.8600","333.3000","326.2740"
|
||||
"2018/03/07","332.3000","5001940.0000","325.4400","332.5000","321.7400"
|
||||
"2018/03/06","328.2000","4236483.0000","333.7500","336.3700","327.0300"
|
||||
"2018/03/05","333.3500","3820702.0000","332.3900","337.7500","329.2929"
|
||||
"2018/03/02","335.1200","5088789.0000","326.9800","335.2200","322.9700"
|
||||
"2018/03/01","330.9300","6872552.0000","345.0100","348.6700","330.0700"
|
||||
"2018/02/28","343.0600","6066044.0000","352.5700","355.2400","342.2200"
|
||||
"2018/02/27","350.9900","4785022.0000","356.2500","359.9900","350.0100"
|
||||
"2018/02/26","357.4200","4334500.0000","353.5000","359.0000","352.3550"
|
||||
"2018/02/23","352.0500","5814177.0000","347.8300","354.9900","347.1000"
|
||||
"2018/02/22","346.1700","6961880.0000","335.5300","347.4400","334.7501"
|
||||
"2018/02/21","333.3000","3207250.0000","336.0300","339.6929","333.1700"
|
||||
"2018/02/20","334.7700","4005962.0000","334.4700","340.8400","331.5000"
|
||||
"2018/02/16","335.4900","5640184.0000","332.5000","343.1200","331.6400"
|
||||
"2018/02/15","334.0650","5909823.0000","324.5000","334.1200","322.4000"
|
||||
"2018/02/14","322.3100","3948077.0000","320.8400","326.1700","318.5200"
|
||||
"2018/02/13","323.6600","4554631.0000","315.0200","324.1900","312.5105"
|
||||
"2018/02/12","315.7300","6224479.0000","316.1300","318.0800","306.2500"
|
||||
"2018/02/09","310.4200","12924430.0000","319.9300","320.9845","294.7600"
|
||||
"2018/02/08","315.2300","10296420.0000","343.3100","348.6200","314.6000"
|
||||
"2018/02/07","345.0000","6729682.0000","338.9900","346.0000","335.6600"
|
||||
"2018/02/06","333.9700","5080032.0000","325.2100","336.2200","323.5000"
|
||||
"2018/02/05","333.1300","4441166.0000","337.9700","344.4700","333.0000"
|
||||
"2018/02/02","343.7500","3696157.0000","348.4400","351.9500","340.5100"
|
||||
"2018/02/01","349.2500","4187440.0000","351.0000","359.6600","348.6300"
|
||||
"2018/01/31","354.3100","6199373.0000","347.5100","356.1900","345.1900"
|
||||
"2018/01/30","345.8200","4710684.0000","345.1400","348.2700","342.1700"
|
||||
"2018/01/29","349.5300","4738208.0000","339.8500","350.8500","338.2800"
|
||||
"2018/01/26","342.8500","4535457.0000","341.5000","344.0000","335.7100"
|
||||
"2018/01/25","337.6400","6725208.0000","348.2700","349.2000","336.4000"
|
||||
"2018/01/24","345.8900","5277386.0000","354.5800","354.7500","343.5200"
|
||||
"2018/01/23","352.7900","5447622.0000","360.0000","360.5000","351.0000"
|
||||
"2018/01/22","351.5600","6199777.0000","349.4000","357.8300","349.2000"
|
||||
"2018/01/19","350.0200","4871181.0000","345.0000","350.5899","342.6000"
|
||||
"2018/01/18","344.5700","5679629.0000","345.6700","352.3000","343.7400"
|
||||
"2018/01/17","347.1600","7092554.0000","340.4700","349.0000","339.7500"
|
||||
"2018/01/16","340.0600","6429234.0000","337.5400","345.0000","334.8000"
|
||||
"2018/01/12","336.2200","4822509.0000","338.6300","340.4100","333.6700"
|
||||
"2018/01/11","337.9500","6640863.0000","335.2400","344.8099","333.2600"
|
||||
"2018/01/10","334.8000","4303053.0000","332.2000","337.0000","330.0000"
|
||||
"2018/01/09","333.6900","7134525.0000","335.1600","338.8000","327.4050"
|
||||
"2018/01/08","336.4100","9832610.0000","316.0000","337.0199","315.5000"
|
||||
"2018/01/05","316.5800","4588728.0000","316.6200","317.2400","312.0000"
|
||||
"2018/01/04","314.6200","9944593.0000","312.8700","318.5500","305.6800"
|
||||
"2018/01/03","317.2500","4438520.0000","321.0000","325.2500","315.5500"
|
||||
"2018/01/02","320.5300","4345615.0000","312.0000","322.1099","311.0000"
|
||||
"2017/12/29","311.3500","3769504.0000","316.1800","316.4100","310.0000"
|
||||
"2017/12/28","315.3600","4306746.0000","311.7500","315.8200","309.5400"
|
||||
"2017/12/27","311.6400","4705084.0000","316.0000","317.6800","310.7500"
|
||||
"2017/12/26","317.2900","4368699.0000","323.8300","323.9400","316.5800"
|
||||
"2017/12/22","325.2000","4207602.0000","329.5100","330.9214","324.8200"
|
||||
"2017/12/21","331.6600","4369994.0000","329.5900","333.7400","327.2100"
|
||||
"2017/12/20","328.9800","5907378.0000","332.6900","333.1000","325.0400"
|
||||
"2017/12/19","331.1000","6820868.0000","340.2600","341.4925","330.3000"
|
||||
"2017/12/18","338.8700","5466546.0000","344.9000","346.7300","337.5800"
|
||||
"2017/12/15","343.4500","6921246.0000","342.0400","343.9000","335.7600"
|
||||
"2017/12/14","337.8900","5792024.0000","341.0100","347.4400","336.9000"
|
||||
"2017/12/13","339.0300","6217701.0000","340.9300","344.2200","336.5000"
|
||||
"2017/12/12","341.0300","8715570.0000","330.4500","341.4400","330.0300"
|
||||
"2017/12/11","328.9100","7926090.0000","314.6300","329.0100","313.7500"
|
||||
"2017/12/08","315.1300","3466088.0000","314.6000","316.9800","311.2600"
|
||||
"2017/12/07","311.2400","4779590.0000","312.0000","318.6341","311.0500"
|
||||
"2017/12/06","313.2600","7184477.0000","300.1000","313.3900","300.0000"
|
||||
"2017/12/05","303.7000","4643708.0000","302.0000","308.0000","301.0000"
|
||||
"2017/12/04","305.2000","5834227.0000","306.5000","308.2650","300.6100"
|
||||
"2017/12/01","306.5300","4286082.0000","305.4400","310.3200","305.0500"
|
||||
"2017/11/30","308.8500","4345434.0000","308.5600","310.7000","304.5400"
|
||||
"2017/11/29","307.5400","8757463.0000","317.3000","318.0000","301.2300"
|
||||
"2017/11/28","317.5500","4941703.0000","316.3600","320.0000","313.9200"
|
||||
"2017/11/27","316.8100","4537779.0000","313.2500","317.3400","309.5100"
|
||||
"2017/11/24","315.5500","3244065.0000","313.7900","316.4100","311.0000"
|
||||
"2017/11/22","312.6000","4913283.0000","316.7700","317.4200","311.8400"
|
||||
"2017/11/21","317.8100","7256381.0000","310.8600","318.2300","308.7100"
|
||||
"2017/11/20","308.7400","8241821.0000","313.7900","315.5000","304.7500"
|
||||
"2017/11/17","315.0500","13720830.0000","325.6700","326.6700","313.1500"
|
||||
"2017/11/16","312.5000","5794409.0000","313.9900","318.1400","311.3000"
|
||||
"2017/11/15","311.3000","5951835.0000","306.0100","312.4900","301.5000"
|
||||
"2017/11/14","308.7000","5661904.0000","315.0000","316.3500","306.9000"
|
||||
"2017/11/13","315.4000","7565242.0000","300.1300","316.8000","299.1100"
|
||||
"2017/11/10","302.9900","4621080.0000","302.5000","308.3600","301.8500"
|
||||
"2017/11/09","302.9900","5440173.0000","302.5000","304.4600","296.3000"
|
||||
"2017/11/08","304.3900","4725355.0000","305.5000","306.8900","301.3001"
|
||||
"2017/11/07","306.0500","5284844.0000","301.0200","306.5000","300.0300"
|
||||
"2017/11/06","302.7800","6480689.0000","307.0000","307.5000","299.0100"
|
||||
"2017/11/03","306.0900","8885933.0000","299.5000","306.2500","295.1300"
|
||||
"2017/11/02","299.2600","19771280.0000","300.1300","308.6900","292.6300"
|
||||
"2017/11/01","321.0800","8242487.0000","332.2500","332.6089","320.2600"
|
||||
"2017/10/31","331.5300","5650062.0000","320.2300","331.9500","320.1800"
|
||||
"2017/10/30","320.0800","4247619.0000","319.1800","323.7800","317.2500"
|
||||
"2017/10/27","320.8700","6968365.0000","319.7500","324.5900","316.6600"
|
||||
"2017/10/26","326.1700","5010087.0000","327.7800","330.2300","323.2000"
|
||||
"2017/10/25","325.8400","8583619.0000","336.7000","337.5000","323.5600"
|
||||
"2017/10/24","337.3400","4485692.0000","338.8000","342.8000","336.1600"
|
||||
"2017/10/23","337.0200","5739598.0000","349.8800","349.9500","336.2500"
|
||||
"2017/10/20","345.1000","4925657.0000","352.6900","354.5500","344.3400"
|
||||
"2017/10/19","351.8100","5055715.0000","355.5600","357.1465","348.2000"
|
||||
"2017/10/18","359.6500","4936024.0000","355.9700","363.0000","354.1300"
|
||||
"2017/10/17","355.7500","3290290.0000","350.9100","356.2200","350.0700"
|
||||
"2017/10/16","350.6000","5373090.0000","353.7600","354.4800","347.1600"
|
||||
"2017/10/13","355.5700","3538807.0000","356.9800","358.4900","353.6800"
|
||||
"2017/10/12","355.6800","4073552.0000","352.9500","359.7800","352.6400"
|
||||
"2017/10/11","354.6000","4494733.0000","353.8900","357.6000","351.1500"
|
||||
"2017/10/10","355.5900","6972263.0000","346.8000","355.6300","345.5305"
|
||||
"2017/10/09","342.9400","7461461.0000","349.6500","351.7500","342.6700"
|
||||
"2017/10/06","356.8800","4272866.0000","353.1000","360.0992","352.2500"
|
||||
"2017/10/05","355.3300","4167913.0000","356.0000","357.4350","351.3500"
|
||||
"2017/10/04","355.0100","8147995.0000","351.2500","358.6200","349.6000"
|
||||
"2017/10/03","348.1400","10122550.0000","335.9000","348.5500","331.2800"
|
||||
"2017/10/02","341.5300","5227547.0000","342.5200","343.7000","335.5100"
|
||||
"2017/09/29","341.1000","5099051.0000","341.8600","344.6800","338.6010"
|
||||
"2017/09/28","339.6000","5309779.0000","339.8800","342.7500","335.4000"
|
||||
"2017/09/27","340.9700","6027375.0000","349.9000","351.4890","340.5000"
|
||||
"2017/09/26","345.2500","7149649.0000","350.9300","351.2400","340.9000"
|
||||
"2017/09/25","344.9900","7587158.0000","353.1500","357.4690","342.8800"
|
||||
"2017/09/22","351.0900","8142895.0000","366.4900","369.8999","350.8800"
|
||||
"2017/09/21","366.4800","4610872.0000","374.9000","376.8300","364.5100"
|
||||
"2017/09/20","373.9100","4909958.0000","373.0000","378.2490","371.0700"
|
||||
"2017/09/19","375.1000","6435208.0000","380.0000","382.3900","373.5700"
|
||||
"2017/09/18","385.0000","7177773.0000","380.2500","389.6100","377.6800"
|
||||
"2017/09/15","379.8100","5407227.0000","374.5100","380.0000","372.7000"
|
||||
"2017/09/14","377.6400","7170263.0000","364.3300","377.9600","362.6300"
|
||||
"2017/09/13","366.2300","4171185.0000","363.8200","368.0700","359.5900"
|
||||
"2017/09/12","362.7500","5966204.0000","364.4900","368.7600","360.4000"
|
||||
"2017/09/11","363.6900","7651000.0000","351.3500","363.7100","350.0000"
|
||||
"2017/09/08","343.4000","3261333.0000","348.9900","349.7800","342.3000"
|
||||
"2017/09/07","350.6100","4235045.0000","345.9800","352.4800","343.4500"
|
||||
"2017/09/06","344.5300","4082001.0000","349.5000","350.9790","341.5600"
|
||||
"2017/09/05","349.5900","3836402.0000","353.8000","355.4900","345.8900"
|
||||
"2017/09/01","355.4000","3048550.0000","356.1200","357.5900","353.6902"
|
||||
"2017/08/31","355.9000","4063330.0000","353.5500","358.4400","352.8200"
|
||||
"2017/08/30","353.1800","3404165.0000","349.6700","353.4700","347.0000"
|
||||
"2017/08/29","347.3600","4069303.0000","339.4800","349.0500","338.7500"
|
||||
"2017/08/28","345.6600","3746240.0000","347.2800","347.3500","339.7200"
|
||||
"2017/08/25","348.0500","3475186.0000","354.2400","355.6900","347.3000"
|
||||
"2017/08/24","352.9300","4571574.0000","352.5200","356.6600","349.7400"
|
||||
"2017/08/23","352.7700","4941947.0000","338.9900","353.4900","338.3041"
|
||||
"2017/08/22","341.3500","4314268.0000","341.1300","342.2400","337.3725"
|
||||
"2017/08/21","337.8600","6489850.0000","345.8200","345.8200","331.8500"
|
||||
"2017/08/18","347.4600","5390805.0000","352.9100","354.0000","345.8000"
|
||||
"2017/08/17","351.9200","5010825.0000","361.2100","363.3000","351.5900"
|
||||
"2017/08/16","362.9100","3406173.0000","363.0000","366.5000","362.5200"
|
||||
"2017/08/15","362.3300","3084601.0000","365.2000","365.4900","359.3700"
|
||||
"2017/08/14","363.8000","4507969.0000","364.6300","367.6600","362.6000"
|
||||
"2017/08/11","357.8700","4357562.0000","356.9700","361.2600","353.6200"
|
||||
"2017/08/10","355.4000","7063054.0000","361.6000","366.6504","354.6600"
|
||||
"2017/08/09","363.5300","6881182.0000","361.0000","370.0000","358.9500"
|
||||
"2017/08/08","365.2200","7431838.0000","357.5300","368.5800","357.4000"
|
||||
"2017/08/07","355.1700","6309331.0000","357.3500","359.4800","352.7500"
|
||||
"2017/08/04","356.9100","9233295.0000","347.0000","357.2700","343.3000"
|
||||
"2017/08/03","347.0900","13524270.0000","345.3300","350.0000","343.1500"
|
||||
"2017/08/02","325.8900","11942350.0000","318.9400","327.1200","311.2200"
|
||||
"2017/08/01","319.5700","8284997.0000","323.0000","324.4500","316.1300"
|
||||
"2017/07/31","323.4700","8525454.0000","335.5000","341.4900","321.0400"
|
||||
"2017/07/28","335.0700","4869891.0000","336.8900","339.6000","332.5100"
|
||||
"2017/07/27","334.4600","8285879.0000","346.0000","347.5000","326.2900"
|
||||
"2017/07/26","343.8500","4809514.0000","340.3600","345.5000","338.1200"
|
||||
"2017/07/25","339.6000","6977932.0000","345.0000","345.6000","334.1500"
|
||||
"2017/07/24","342.5200","8598292.0000","330.2400","343.3990","330.0100"
|
||||
"2017/07/21","328.4000","4871708.0000","329.4600","331.2575","325.8000"
|
||||
"2017/07/20","329.9200","5142516.0000","326.9000","330.2200","324.2000"
|
||||
"2017/07/19","325.2600","6345884.0000","328.2300","331.6500","323.2193"
|
||||
"2017/07/18","328.2400","6360710.0000","317.5000","329.1300","315.6600"
|
||||
"2017/07/17","319.5700","9827506.0000","325.5400","327.1000","313.4500"
|
||||
"2017/07/14","327.7800","5607924.0000","323.1900","328.4200","321.2200"
|
||||
"2017/07/13","323.4100","8582315.0000","330.1100","331.6000","319.9700"
|
||||
"2017/07/12","329.5200","10317240.0000","330.4000","333.1000","324.5000"
|
||||
"2017/07/11","327.2200","11526760.0000","316.0000","327.2800","314.3000"
|
||||
"2017/07/10","316.0500","13760290.0000","312.9000","317.9400","303.1300"
|
||||
"2017/07/07","313.2200","14129950.0000","313.5000","317.0000","307.3800"
|
||||
"2017/07/06","308.8300","19258040.0000","317.2600","320.7899","306.3000"
|
||||
"2017/07/05","327.0900","16998300.0000","347.2000","347.2400","326.3300"
|
||||
"2017/07/03","352.6200","6305401.0000","370.2400","371.3500","351.5000"
|
||||
"2017/06/30","361.6100","5823046.0000","363.7100","366.7674","359.6187"
|
||||
"2017/06/29","360.7500","8202463.0000","370.6100","371.0000","354.1000"
|
||||
"2017/06/28","371.2400","6290323.0000","366.6800","371.7400","362.5200"
|
||||
"2017/06/27","362.3700","6977494.0000","376.4000","376.4000","362.0200"
|
||||
"2017/06/26","377.4900","6596854.0000","386.6900","386.9500","373.1000"
|
||||
"2017/06/23","383.4500","6425180.0000","382.4500","386.9900","379.3450"
|
||||
"2017/06/22","382.6100","7510406.0000","377.9900","385.0000","373.5700"
|
||||
"2017/06/21","376.4000","4916770.0000","374.3500","376.9900","368.0200"
|
||||
"2017/06/20","372.2400","7419110.0000","376.6700","378.8800","369.7300"
|
||||
"2017/06/19","369.8000","6433434.0000","375.0000","376.7000","367.8000"
|
||||
"2017/06/16","371.4000","6347450.0000","377.9750","378.0100","370.1000"
|
||||
"2017/06/15","375.3400","10412650.0000","372.5000","375.4600","366.4900"
|
||||
"2017/06/14","380.6600","12778480.0000","381.0850","384.2500","376.3100"
|
||||
"2017/06/13","375.9500","11773370.0000","367.6200","376.0000","366.6100"
|
||||
"2017/06/12","359.0100","10507860.0000","357.9900","364.5000","350.6200"
|
||||
"2017/06/09","357.3200","17250060.0000","374.4200","376.8700","354.8000"
|
||||
"2017/06/08","370.0000","9028677.0000","363.7500","371.9000","360.2200"
|
||||
"2017/06/07","359.6500","9348692.0000","356.3400","360.5000","355.1400"
|
||||
"2017/06/06","352.8500","11031920.0000","344.7000","359.4929","339.9700"
|
||||
"2017/06/05","347.3200","6769174.0000","338.5000","348.4400","334.2100"
|
||||
"2017/06/02","339.8500","5583952.0000","339.7700","342.8800","335.9300"
|
||||
"2017/06/01","340.3700","7601764.0000","344.0000","344.8800","337.2900"
|
||||
"2017/05/31","341.0100","9937556.0000","337.6900","342.8900","335.1600"
|
||||
"2017/05/30","335.1000","7771536.0000","326.0000","336.2800","325.7600"
|
||||
"2017/05/26","325.1400","7793009.0000","317.2800","325.4900","316.3100"
|
||||
"2017/05/25","316.8300","5000432.0000","311.0200","316.9700","307.8100"
|
||||
"2017/05/24","310.2200","5035192.0000","306.5100","311.0000","305.4000"
|
||||
"2017/05/23","303.8600","4314267.0000","310.4600","310.7300","303.4800"
|
||||
"2017/05/22","310.3500","4324305.0000","312.8000","314.3700","306.8000"
|
||||
"2017/05/19","310.8300","4654580.0000","315.5000","316.5000","310.2000"
|
||||
"2017/05/18","313.0600","5609153.0000","307.0000","313.9400","305.3100"
|
||||
"2017/05/17","306.1100","6695657.0000","314.3900","314.6300","305.5000"
|
||||
"2017/05/16","317.0100","4141066.0000","317.5900","320.0600","315.1400"
|
||||
"2017/05/15","315.8800","7606854.0000","318.3800","320.2000","312.5300"
|
||||
"2017/05/12","324.8100","4118613.0000","325.4800","327.0000","321.5300"
|
||||
"2017/05/11","323.1000","4747172.0000","323.4000","326.0000","319.6000"
|
||||
"2017/05/10","325.2200","5734524.0000","321.5600","325.5000","318.1200"
|
||||
"2017/05/09","321.2600","9663374.0000","309.3800","321.9900","309.1000"
|
||||
"2017/05/08","307.1900","7002907.0000","310.9000","313.7900","305.8200"
|
||||
"2017/05/05","308.3500","8117449.0000","298.0000","308.5500","296.8000"
|
||||
"2017/05/04","295.4600","14135990.0000","307.4350","307.7700","290.7601"
|
||||
"2017/05/03","311.0200","6879259.0000","317.6700","321.5300","310.4500"
|
||||
"2017/05/02","318.8900","5316551.0000","324.0000","327.6599","316.5601"
|
||||
"2017/05/01","322.8300","8819888.0000","314.8800","327.2500","314.8100"
|
||||
"2017/04/28","314.0700","4496659.0000","309.8300","314.8000","308.0000"
|
||||
"2017/04/27","308.6300","3462663.0000","311.6900","313.0900","307.5000"
|
||||
"2017/04/26","310.1700","4683131.0000","312.3700","314.5000","309.0000"
|
||||
"2017/04/25","313.7900","6734162.0000","308.0000","313.9800","305.8600"
|
||||
"2017/04/24","308.0300","5077771.0000","309.2200","310.5500","306.0215"
|
||||
"2017/04/21","305.6000","4501958.0000","302.0000","306.4000","300.4200"
|
||||
"2017/04/20","302.5100","6145961.0000","306.5100","309.1500","300.2300"
|
||||
"2017/04/19","305.5200","3891145.0000","302.4600","306.6200","302.1100"
|
||||
"2017/04/18","300.2500","3034225.0000","299.7000","300.8399","297.9000"
|
||||
"2017/04/17","301.4400","4128067.0000","302.7000","304.0000","298.6800"
|
||||
"2017/04/13","304.0000","9275682.0000","296.7000","307.3900","295.3000"
|
||||
"2017/04/12","296.8400","6043648.0000","306.3400","308.4481","296.3200"
|
||||
"2017/04/11","308.7100","5718053.0000","313.3800","313.4700","305.5000"
|
||||
"2017/04/10","312.3900","7653623.0000","309.1500","313.7299","308.7100"
|
||||
"2017/04/07","302.5400","4566632.0000","297.5000","302.6900","297.1500"
|
||||
"2017/04/06","298.7000","5517731.0000","296.8800","301.9400","294.1000"
|
||||
"2017/04/05","295.0000","7858565.0000","302.0400","304.8800","294.2000"
|
||||
"2017/04/04","303.7000","10108230.0000","296.8900","304.8100","294.5300"
|
||||
"2017/04/03","298.5200","13864850.0000","286.9000","299.0000","284.5800"
|
||||
"2017/03/31","278.3000","3293698.0000","278.7300","279.6800","276.3197"
|
||||
"2017/03/30","277.9200","4141437.0000","278.0400","282.0000","277.2100"
|
||||
"2017/03/29","277.3800","3672526.0000","278.3400","279.6000","275.5400"
|
||||
"2017/03/28","277.4500","7978665.0000","277.0200","280.6800","275.0000"
|
||||
"2017/03/27","270.2200","6221361.0000","260.6000","270.5700","259.7500"
|
||||
"2017/03/24","263.1600","5637668.0000","255.7000","263.8900","255.0100"
|
||||
"2017/03/23","254.7800","3309844.0000","255.3900","257.6720","253.3000"
|
||||
"2017/03/22","255.0100","4056735.0000","251.5600","255.0700","250.5100"
|
||||
"2017/03/21","250.6800","6901555.0000","262.8300","264.8000","250.2400"
|
||||
"2017/03/20","261.9200","3601616.0000","260.6000","264.5500","258.8210"
|
||||
"2017/03/17","261.5000","6491018.0000","264.0000","265.3300","261.2000"
|
||||
"2017/03/16","262.0500","7127180.0000","262.4000","265.7500","259.0600"
|
||||
"2017/03/15","255.7300","5233365.0000","257.0000","261.0000","254.2700"
|
||||
"2017/03/14","258.0000","7581719.0000","246.1100","258.1200","246.0200"
|
||||
"2017/03/13","246.1700","3011280.0000","244.8200","246.8500","242.7810"
|
||||
"2017/03/10","243.6900","3062785.0000","246.2100","246.5000","243.0000"
|
||||
"2017/03/09","244.9000","3876494.0000","247.6300","248.6600","243.0000"
|
||||
"2017/03/08","246.8700","3726746.0000","247.0000","250.0700","245.3200"
|
||||
"2017/03/07","248.5900","3452587.0000","251.9200","253.8900","248.3200"
|
||||
"2017/03/06","251.2100","3353601.0000","247.9100","251.7000","247.5100"
|
||||
"2017/03/03","251.5700","2925481.0000","250.7400","251.9000","249.0000"
|
||||
"2017/03/02","250.4800","3345751.0000","249.7100","253.2800","248.2700"
|
||||
"2017/03/01","250.0200","4804963.0000","254.1800","254.8500","249.1100"
|
||||
"2017/02/28","249.9900","6073890.0000","244.1900","251.0000","243.9000"
|
||||
"2017/02/27","246.2300","11450160.0000","248.1700","248.3600","242.0100"
|
||||
"2017/02/24","257.0000","8166869.0000","252.6600","258.2500","250.2000"
|
||||
"2017/02/23","255.9900","14877090.0000","264.0000","264.6600","255.5600"
|
||||
"2017/02/22","273.5100","8537811.0000","280.3100","283.4500","272.6000"
|
||||
"2017/02/21","277.3900","5647575.0000","275.4500","281.4000","274.0100"
|
||||
"2017/02/17","272.2300","6251469.0000","265.8000","272.8900","264.1500"
|
||||
"2017/02/16","268.9500","7063860.0000","277.6000","280.0000","268.5000"
|
||||
"2017/02/15","279.7600","4943879.0000","280.0000","282.2400","276.4400"
|
||||
"2017/02/14","280.9800","7341450.0000","279.0300","287.3900","278.6100"
|
||||
"2017/02/13","280.6000","7023072.0000","270.7400","280.7899","270.5100"
|
||||
"2017/02/10","269.2300","3618336.0000","269.7900","270.9500","266.1100"
|
||||
"2017/02/09","269.2000","7812600.0000","266.2500","271.1800","266.1500"
|
||||
"2017/02/08","262.0800","3912428.0000","257.3500","263.3600","256.2000"
|
||||
"2017/02/07","257.4800","4244063.0000","258.1900","260.0000","256.4200"
|
||||
"2017/02/06","257.7700","3557600.0000","251.0000","257.8200","250.6300"
|
||||
"2017/02/03","251.3300","2185230.0000","251.9100","252.1790","249.6800"
|
||||
"2017/02/02","251.5500","2498799.0000","248.3400","252.4200","247.7100"
|
||||
"2017/02/01","249.2400","3953105.0000","253.0500","253.2000","249.0500"
|
||||
"2017/01/31","251.9300","4112013.0000","249.2400","255.8900","247.7000"
|
||||
"2017/01/30","250.6300","3798638.0000","252.5300","255.2899","247.1000"
|
||||
"2017/01/27","252.9500","3161774.0000","251.3800","253.0000","248.5200"
|
||||
"2017/01/26","252.5100","3143717.0000","254.2900","255.7400","250.7500"
|
||||
"2017/01/25","254.4700","5145301.0000","257.3100","258.4600","251.8000"
|
||||
"2017/01/24","254.6100","4958144.0000","250.0000","254.8000","249.6500"
|
||||
"2017/01/23","248.9200","6262097.0000","245.8500","250.8899","245.5000"
|
||||
"2017/01/20","244.7300","4199720.0000","245.4600","246.0000","243.0100"
|
||||
"2017/01/19","243.7600","7717244.0000","247.2500","248.6800","240.7500"
|
||||
"2017/01/18","238.3600","3767272.0000","236.6500","239.7100","235.5800"
|
||||
"2017/01/17","235.5800","4614731.0000","236.7000","239.9600","234.3700"
|
||||
"2017/01/13","237.7500","6085610.0000","230.0000","237.8500","229.5900"
|
||||
"2017/01/12","229.5900","3787547.0000","229.0600","230.7000","225.5800"
|
||||
"2017/01/11","229.7300","3649910.0000","229.0700","229.9800","226.6800"
|
||||
"2017/01/10","229.8700","3658981.0000","232.0000","232.0000","226.8900"
|
||||
"2017/01/09","231.2800","3972716.0000","228.9700","231.9200","228.0000"
|
||||
"2017/01/06","229.0100","5524153.0000","226.9300","230.3100","225.4500"
|
||||
"2017/01/05","226.7500","5908927.0000","226.4200","227.4800","221.9500"
|
||||
"2017/01/04","226.9900","11204560.0000","214.7500","228.0000","214.3100"
|
||||
"2017/01/03","216.9900","5901494.0000","214.8600","220.3300","210.9600"
|
||||
"2016/12/30","213.6900","4641288.0000","216.3000","217.5000","211.6800"
|
||||
"2016/12/29","214.6800","4043086.0000","218.5600","219.2000","214.1225"
|
||||
"2016/12/28","219.7400","3778544.0000","221.5300","223.8000","217.2000"
|
||||
"2016/12/27","219.5300","5913335.0000","214.8800","222.2500","214.4200"
|
||||
"2016/12/23","213.3400","4668263.0000","208.0000","213.4500","207.7100"
|
||||
"2016/12/22","208.4500","3108427.0000","208.2200","209.9900","206.5000"
|
||||
"2016/12/21","207.7000","5206143.0000","208.4500","212.2300","207.4100"
|
||||
"2016/12/20","208.7900","4684269.0000","203.0500","209.0000","202.5000"
|
||||
"2016/12/19","202.7300","3486937.0000","202.4900","204.4500","199.8400"
|
||||
"2016/12/16","202.4900","3795444.0000","198.0800","202.5900","197.6000"
|
||||
"2016/12/15","197.5800","3218653.0000","198.4100","200.7400","197.3900"
|
||||
"2016/12/14","198.6900","4147314.0000","198.7400","203.0000","196.7600"
|
||||
"2016/12/13","198.1500","6819322.0000","193.1800","201.2800","193.0000"
|
||||
"2016/12/12","192.4300","2436428.0000","192.8000","194.4200","191.0400"
|
||||
"2016/12/09","192.1800","2720937.0000","190.8700","193.8360","190.8100"
|
||||
"2016/12/08","192.2900","3193684.0000","192.0500","192.5000","189.5400"
|
||||
"2016/12/07","193.1500","5457071.0000","186.1500","193.4000","185.0000"
|
||||
"2016/12/06","185.8500","3389365.0000","185.5200","186.5800","182.6825"
|
||||
"2016/12/05","186.8000","4069480.0000","182.5100","188.8900","182.5100"
|
||||
"2016/12/02","181.4700","4040201.0000","182.8800","184.8800","180.0000"
|
||||
"2016/12/01","181.8800","5119105.0000","188.2500","188.5300","181.0000"
|
||||
"2016/11/30","189.4000","3544891.0000","191.0000","191.8900","187.5000"
|
||||
"2016/11/29","189.5700","4435312.0000","195.5600","196.7300","189.5000"
|
||||
"2016/11/28","196.1200","4521082.0000","195.4800","199.3500","194.5500"
|
||||
"2016/11/25","196.6500","2366098.0000","193.6400","197.2372","193.6400"
|
||||
"2016/11/23","193.1400","4890285.0000","190.6100","195.6440","189.0000"
|
||||
"2016/11/22","191.1700","5601238.0000","185.8400","191.4700","183.7100"
|
||||
"2016/11/21","184.5200","4355872.0000","185.0400","188.8900","184.4100"
|
||||
"2016/11/18","185.0200","5206473.0000","190.6500","193.0000","185.0000"
|
||||
"2016/11/17","188.6600","4880969.0000","183.4900","189.4900","182.1101"
|
||||
"2016/11/16","183.9300","3432668.0000","182.6500","184.7300","181.2100"
|
||||
"2016/11/15","183.7700","3899743.0000","182.7800","186.4300","182.0500"
|
||||
"2016/11/14","181.4500","6547913.0000","188.0000","188.2500","178.1900"
|
||||
"2016/11/11","188.5600","3987091.0000","184.2400","188.8800","183.0000"
|
||||
"2016/11/10","185.3500","6746290.0000","191.0500","191.6100","180.4200"
|
||||
"2016/11/09","190.0600","8166447.0000","186.8750","192.0000","183.9500"
|
||||
"2016/11/08","194.9400","3252927.0000","193.7900","197.4900","191.2600"
|
||||
"2016/11/07","193.2100","3857682.0000","193.5900","194.2900","190.0500"
|
||||
"2016/11/04","190.5600","5143915.0000","189.0000","193.4600","185.9600"
|
||||
"2016/11/03","187.4200","2644550.0000","189.0000","191.4700","187.0401"
|
||||
"2016/11/02","188.0200","4242250.0000","190.0500","192.6951","187.5050"
|
||||
"2016/11/01","190.7900","7023674.0000","198.0400","198.5000","188.1050"
|
||||
"2016/10/31","197.7300","4691320.0000","202.4900","202.4900","195.8100"
|
||||
"2016/10/28","199.9700","4273456.0000","204.0000","205.3200","199.8300"
|
||||
"2016/10/27","204.0100","13070990.0000","211.3400","213.7000","201.6500"
|
||||
"2016/10/26","202.2400","5346893.0000","201.0000","203.1900","200.1000"
|
||||
"2016/10/25","202.3400","2442118.0000","202.9000","204.6900","201.2000"
|
||||
"2016/10/24","202.7600","2747344.0000","201.0000","203.9452","200.2500"
|
||||
"2016/10/21","200.0900","2936908.0000","198.6000","201.5700","197.4100"
|
||||
"2016/10/20","199.1000","5069410.0000","202.1200","203.0000","197.0500"
|
||||
"2016/10/19","203.5600","6966185.0000","199.7400","206.6600","198.0600"
|
||||
"2016/10/18","199.1000","5676046.0000","195.9900","199.4700","193.2600"
|
||||
"2016/10/17","193.9600","4518331.0000","197.0500","198.3900","192.0000"
|
||||
"2016/10/14","196.5100","4265471.0000","200.6600","201.4300","196.3000"
|
||||
"2016/10/13","200.2400","2495140.0000","200.5000","200.8950","197.0500"
|
||||
"2016/10/12","201.5100","1969099.0000","200.9500","203.8800","200.4200"
|
||||
"2016/10/11","200.1000","2326754.0000","201.8500","202.2000","198.3100"
|
||||
"2016/10/10","200.9500","3311340.0000","201.3500","204.1400","199.6600"
|
||||
"2016/10/07","196.6100","3489480.0000","201.0000","201.3200","195.8000"
|
||||
"2016/10/06","201.0000","4697937.0000","202.4600","204.2099","200.2100"
|
||||
"2016/10/05","208.4600","1872456.0000","212.2400","213.1500","208.1200"
|
||||
"2016/10/04","211.4100","3537953.0000","213.1000","213.3200","208.8200"
|
||||
"2016/10/03","213.7000","5989342.0000","212.3000","215.6688","208.2500"
|
||||
"2016/09/30","204.0300","2581730.0000","202.2100","204.9800","199.5500"
|
||||
"2016/09/29","200.7000","2714040.0000","205.6000","207.3300","200.5800"
|
||||
"2016/09/28","206.2700","2079324.0000","207.5100","208.2500","205.2600"
|
||||
"2016/09/27","205.8100","3367907.0000","209.6500","209.9818","204.6093"
|
||||
"2016/09/26","208.9900","2393373.0000","206.5000","211.0000","206.5000"
|
||||
"2016/09/23","207.4500","2904125.0000","205.9900","210.1800","205.6700"
|
||||
"2016/09/22","206.4300","2381600.0000","206.4000","207.2800","203.0000"
|
||||
"2016/09/21","205.2200","2631285.0000","206.3700","207.0000","201.5600"
|
||||
"2016/09/20","204.6400","2408556.0000","206.8500","207.7500","203.9100"
|
||||
"2016/09/19","206.3400","2298318.0000","207.0000","209.4300","205.0000"
|
||||
"2016/09/16","205.4000","3104266.0000","200.4200","205.7000","199.0000"
|
||||
"2016/09/15","200.4200","3082942.0000","196.4900","202.5193","196.4000"
|
||||
"2016/09/14","196.4100","2256143.0000","195.7500","197.9248","194.8562"
|
||||
"2016/09/13","196.0500","3583140.0000","197.0600","198.4900","193.4500"
|
||||
"2016/09/12","198.3000","3714049.0000","195.0000","201.3690","194.1000"
|
||||
"2016/09/09","194.4700","3753734.0000","199.0900","199.9200","193.7000"
|
||||
"2016/09/08","197.3600","3372293.0000","199.5500","199.8900","196.3600"
|
||||
"2016/09/07","201.7100","3637918.0000","205.5000","206.4968","200.7100"
|
||||
"2016/09/06","202.8300","4386193.0000","199.0200","203.2500","199.0000"
|
||||
"2016/09/02","197.7800","5970154.0000","202.3300","203.2000","196.2000"
|
||||
"2016/09/01","200.7700","7932444.0000","209.0100","211.0999","200.5000"
|
||||
"2016/08/31","212.0100","3274854.0000","210.4300","212.6000","208.6500"
|
||||
"2016/08/30","211.3400","3164222.0000","216.1100","216.1100","210.5200"
|
||||
"2016/08/29","215.2000","3261381.0000","220.1500","220.4000","215.0000"
|
||||
"2016/08/26","219.9900","2238087.0000","222.1400","222.8550","218.8200"
|
||||
"2016/08/25","220.9600","1760212.0000","223.1100","223.8000","220.7700"
|
||||
"2016/08/24","222.6200","2566947.0000","227.0500","227.1500","222.2200"
|
||||
"2016/08/23","224.8400","4747745.0000","224.3200","228.4900","222.8000"
|
||||
"2016/08/22","222.9300","2061692.0000","224.1700","225.1100","222.6800"
|
||||
"2016/08/19","225.0000","1658616.0000","223.5400","225.1690","222.5300"
|
||||
"2016/08/18","223.5100","1699300.0000","223.8200","225.6600","222.2900"
|
||||
"2016/08/17","223.2400","1786408.0000","224.3300","224.8300","222.8000"
|
||||
"2016/08/16","223.6100","2251115.0000","225.4900","227.1900","223.4101"
|
||||
"2016/08/15","225.5900","2017964.0000","226.0200","229.5000","224.9300"
|
||||
"2016/08/12","225.6100","1811850.0000","225.4100","226.6500","224.0400"
|
||||
"2016/08/11","224.9100","1876959.0000","226.1700","227.5700","223.4100"
|
||||
"2016/08/10","225.6500","2336496.0000","228.2400","229.8700","224.6200"
|
||||
"2016/08/09","229.0800","2199766.0000","226.8200","231.5375","226.6500"
|
||||
"2016/08/08","226.1600","2259465.0000","228.0000","229.6000","226.0900"
|
||||
"2016/08/05","230.0300","3190342.0000","230.0000","232.0000","227.4000"
|
||||
"2016/08/04","230.6100","4142155.0000","225.6900","230.8600","222.0500"
|
||||
"2016/08/03","225.7900","3808747.0000","227.3700","229.6990","224.2100"
|
||||
"2016/08/02","227.2000","3928706.0000","229.3700","229.8700","221.4000"
|
||||
"2016/08/01","230.0100","4014048.0000","235.5000","236.6300","229.3800"
|
||||
"2016/07/29","234.7900","3061574.0000","230.7000","235.2800","230.2400"
|
||||
"2016/07/28","230.6100","2415462.0000","227.9500","230.7600","226.6000"
|
||||
"2016/07/27","228.4900","2887028.0000","229.3400","233.3600","226.9200"
|
||||
"2016/07/26","229.5100","3427251.0000","227.6900","230.0000","225.3000"
|
||||
"2016/07/25","230.0100","4479483.0000","222.2700","231.3900","221.3715"
|
||||
"2016/07/22","222.2700","2570462.0000","221.9900","224.5000","218.8800"
|
||||
"2016/07/21","220.5000","4425756.0000","226.0000","227.8470","219.1000"
|
||||
"2016/07/20","228.3600","2556119.0000","226.4700","229.8000","225.0000"
|
||||
"2016/07/19","225.2600","3047535.0000","225.0000","229.1000","224.7500"
|
||||
"2016/07/18","226.2500","3394588.0000","219.6400","227.0900","218.3000"
|
||||
"2016/07/15","220.4000","2230821.0000","222.5200","222.7499","219.6400"
|
||||
"2016/07/14","221.5300","2657899.0000","223.1200","224.9400","221.0500"
|
||||
"2016/07/13","222.5300","3540699.0000","225.5000","225.5900","220.2900"
|
||||
"2016/07/12","224.6500","4573875.0000","224.1000","227.5000","223.2200"
|
||||
"2016/07/11","224.7800","5369066.0000","219.9600","226.7800","219.5100"
|
||||
"2016/07/08","216.7800","4067131.0000","217.8000","219.8100","214.5000"
|
||||
"2016/07/07","215.9400","3603385.0000","213.1000","218.1200","213.0100"
|
||||
"2016/07/06","214.4400","4910285.0000","210.0000","215.2300","209.0000"
|
||||
"2016/07/05","213.9800","5169832.0000","209.7300","214.5441","208.0000"
|
||||
"2016/07/01","216.5000","5396030.0000","206.1400","218.2400","206.0000"
|
||||
"2016/06/30","212.2800","4794440.0000","212.9700","213.4999","209.0200"
|
||||
"2016/06/29","210.1900","5987120.0000","205.1300","211.7800","203.0000"
|
||||
"2016/06/28","201.7900","6158996.0000","201.8900","204.0500","199.4100"
|
||||
"2016/06/27","198.5500","7211823.0000","190.8600","198.8100","187.8700"
|
||||
"2016/06/24","193.1500","6988949.0000","190.0500","195.1200","189.7300"
|
||||
"2016/06/23","196.4000","10098190.0000","195.6900","197.5500","192.1300"
|
||||
"2016/06/22","196.6600","23706100.0000","199.4700","205.9500","195.7500"
|
||||
"2016/06/21","219.6100","2855619.0000","220.6800","222.5690","218.8100"
|
||||
"2016/06/20","219.7000","3549768.0000","219.5000","223.7500","218.2300"
|
||||
"2016/06/17","215.4700","3107734.0000","217.8100","219.9900","214.5000"
|
||||
"2016/06/16","217.9300","2438692.0000","217.4200","218.0400","213.5000"
|
||||
"2016/06/15","217.7000","2905002.0000","216.9500","221.9000","215.1300"
|
||||
"2016/06/14","214.9600","3575822.0000","218.8800","222.2000","212.5300"
|
||||
"2016/06/13","217.8700","4187833.0000","219.5000","225.7700","217.6600"
|
||||
"2016/06/10","218.7900","5994548.0000","227.3900","227.9700","218.4217"
|
||||
"2016/06/09","229.3600","4454222.0000","234.9800","235.3300","227.0600"
|
||||
"2016/06/08","235.5200","5965110.0000","233.8000","240.8450","232.6050"
|
||||
"2016/06/07","232.3400","6194154.0000","222.2400","234.4400","221.5200"
|
||||
"2016/06/06","220.6800","2242634.0000","218.0000","220.9000","215.4500"
|
||||
"2016/06/03","218.9900","2226110.0000","220.0000","221.9400","218.0100"
|
||||
"2016/06/02","218.9600","2017279.0000","219.5900","219.9090","217.1100"
|
||||
"2016/06/01","219.5600","2982270.0000","221.4800","222.4000","216.8900"
|
||||
"2016/05/31","223.2300","2786682.0000","223.0400","224.7497","221.5001"
|
||||
"2016/05/27","223.0400","3645792.0000","224.9900","225.9300","220.7500"
|
||||
"2016/05/26","225.1200","4066512.0000","220.5000","225.2600","219.0500"
|
||||
"2016/05/25","219.5800","3131279.0000","217.9100","221.3600","216.5100"
|
||||
"2016/05/24","217.9100","3009327.0000","216.6000","218.7400","215.1800"
|
||||
"2016/05/23","216.2200","5096751.0000","219.8700","222.6000","215.8600"
|
||||
"2016/05/20","220.2800","8996546.0000","216.9900","220.5500","216.3500"
|
||||
"2016/05/19","215.2100","6817922.0000","213.6200","216.7900","207.3000"
|
||||
"2016/05/18","211.1700","5552692.0000","209.1500","215.3100","207.7500"
|
||||
"2016/05/17","204.6600","2842028.0000","209.0500","209.8199","204.0200"
|
||||
"2016/05/16","208.2900","2945754.0000","208.1500","213.1500","207.9200"
|
||||
"2016/05/13","207.6100","2820616.0000","207.7800","211.2000","206.7000"
|
||||
"2016/05/12","207.2800","3650075.0000","211.4400","211.6700","203.6572"
|
||||
"2016/05/11","208.9600","5159640.0000","207.5900","215.4800","206.0500"
|
||||
"2016/05/10","208.6900","4068053.0000","207.5500","209.4700","205.0000"
|
||||
"2016/05/09","208.9200","4772992.0000","215.7200","216.1500","206.8000"
|
||||
"2016/05/06","214.9300","5683675.0000","210.8700","216.3700","208.1100"
|
||||
"2016/05/05","211.5300","11248340.0000","228.4600","228.6400","209.7900"
|
||||
"2016/05/04","222.5600","8550004.0000","230.2900","234.4600","220.4000"
|
||||
"2016/05/03","232.3200","4296325.0000","237.3600","238.9100","231.6200"
|
||||
"2016/05/02","241.8000","3838958.0000","241.5000","243.1900","234.8200"
|
||||
"2016/04/29","240.7600","5405164.0000","248.1400","248.4300","237.8100"
|
||||
"2016/04/28","247.7100","2515595.0000","249.8500","253.4300","247.4400"
|
||||
"2016/04/27","251.4700","3198105.0000","252.7500","255.0000","249.4000"
|
||||
"2016/04/26","253.7400","3217779.0000","252.0500","255.7300","249.3900"
|
||||
"2016/04/25","251.8200","3665088.0000","253.0100","257.3800","250.7600"
|
||||
"2016/04/22","253.7500","3783126.0000","248.8900","254.0000","245.7100"
|
||||
"2016/04/21","248.2900","2777079.0000","248.9900","250.9000","246.9100"
|
||||
"2016/04/20","249.9700","5192343.0000","246.2600","253.6600","241.5000"
|
||||
"2016/04/19","247.3700","6351360.0000","253.1200","254.3699","241.2510"
|
||||
"2016/04/18","253.8800","4263246.0000","252.2300","258.3100","251.6600"
|
||||
"2016/04/15","254.5100","3748593.0000","251.3100","254.6000","249.1200"
|
||||
"2016/04/14","251.8600","4129258.0000","253.0000","256.8390","251.0501"
|
||||
"2016/04/13","254.5300","4921554.0000","248.5100","255.5000","247.3300"
|
||||
"2016/04/12","247.8200","5755351.0000","249.5000","251.8000","243.6300"
|
||||
"2016/04/11","249.9200","9156404.0000","251.0000","258.9900","245.3000"
|
||||
"2016/04/08","250.0700","7352707.0000","260.5000","260.8200","248.0201"
|
||||
"2016/04/07","257.2000","8845567.0000","266.4500","269.3400","254.5100"
|
||||
"2016/04/06","265.4200","11695760.0000","253.9700","267.7400","253.4500"
|
||||
"2016/04/05","255.4700","9931470.0000","240.5000","256.5600","240.0000"
|
||||
"2016/04/04","246.9900","13381160.0000","249.1200","252.1200","243.6400"
|
||||
"2016/04/01","237.5900","15979910.0000","244.8250","247.9000","233.2500"
|
||||
"2016/03/31","229.7700","7975638.0000","229.3400","237.4200","225.0100"
|
||||
"2016/03/30","226.8900","4025670.0000","235.0900","235.5000","226.5000"
|
||||
"2016/03/29","230.1300","4006037.0000","229.8900","232.3800","225.3300"
|
||||
"2016/03/28","230.2600","3878599.0000","231.6100","234.8100","225.0000"
|
||||
"2016/03/24","227.7500","4956604.0000","215.7800","228.8877","215.0000"
|
||||
"2016/03/23","222.5800","4936203.0000","232.3700","234.7300","222.0300"
|
||||
"2016/03/22","234.2400","4303052.0000","237.2100","238.9900","232.5580"
|
||||
"2016/03/21","238.3200","5289014.0000","235.3400","239.8800","235.0000"
|
||||
"2016/03/18","232.7400","4704761.0000","229.1000","234.4800","228.0600"
|
||||
"2016/03/17","226.3800","3769083.0000","221.4700","228.5000","220.0000"
|
||||
"2016/03/16","221.9300","3511873.0000","218.0000","222.5800","217.0200"
|
||||
"2016/03/15","218.3400","3133230.0000","214.2700","218.9700","211.5000"
|
||||
"2016/03/14","215.1500","4062304.0000","212.6500","216.7200","210.6400"
|
||||
"2016/03/11","207.5000","3339148.0000","207.9300","209.4200","205.3300"
|
||||
"2016/03/10","205.1800","5191602.0000","210.0000","213.2900","200.6710"
|
||||
"2016/03/09","208.7200","3207597.0000","204.5200","209.3726","202.7900"
|
||||
"2016/03/08","202.6000","4175498.0000","203.5000","207.5000","202.2000"
|
||||
"2016/03/07","205.2900","5331097.0000","197.6800","209.7000","197.4000"
|
||||
"2016/03/04","201.0400","6479858.0000","198.0000","204.0300","197.5001"
|
||||
"2016/03/03","195.7400","4822993.0000","188.2800","197.4200","184.2200"
|
||||
"2016/03/02","188.3400","4858631.0000","183.7300","188.5200","181.5000"
|
||||
"2016/03/01","186.3500","6695704.0000","194.2500","195.9484","182.7000"
|
||||
"2016/02/29","191.9300","4495402.0000","192.4000","196.3500","189.2220"
|
||||
"2016/02/26","190.3400","6054061.0000","188.7000","192.0000","185.0000"
|
||||
"2016/02/25","187.4300","5727008.0000","178.6500","188.5192","175.2000"
|
||||
"2016/02/24","179.0000","5385369.0000","172.7500","179.5000","167.8400"
|
||||
"2016/02/23","177.2100","5982993.0000","176.1600","181.7300","173.6800"
|
||||
"2016/02/22","177.7400","5055340.0000","170.1200","178.9100","169.8500"
|
||||
"2016/02/19","166.5800","2955832.0000","163.6600","167.4900","162.5000"
|
||||
"2016/02/18","166.7700","3883107.0000","172.4200","172.9500","164.7700"
|
||||
"2016/02/17","168.6800","5820264.0000","159.0000","169.3400","156.6800"
|
||||
"2016/02/16","155.1700","5561638.0000","158.7000","162.9500","154.1100"
|
||||
"2016/02/12","151.0400","7233264.0000","155.0000","157.0100","143.7000"
|
||||
"2016/02/11","150.4700","14236900.0000","152.0000","163.2600","147.0000"
|
||||
"2016/02/10","143.6700","10104690.0000","150.5000","154.9700","141.7400"
|
||||
"2016/02/09","148.2500","8639661.0000","142.3200","159.7900","141.0500"
|
||||
"2016/02/08","147.9900","9306754.0000","157.1050","157.1500","146.0000"
|
||||
"2016/02/05","162.6000","9434495.0000","171.3000","173.0000","157.7442"
|
||||
"2016/02/04","175.3300","4379442.0000","170.7000","175.9800","166.9900"
|
||||
"2016/02/03","173.4800","7927640.0000","183.5900","183.9400","170.1800"
|
||||
"2016/02/02","182.7800","5764817.0000","192.4200","193.1200","180.2300"
|
||||
"2016/02/01","196.9400","5295685.0000","188.7600","199.5200","182.7500"
|
||||
"2016/01/29","191.2000","2848929.0000","189.9500","193.7400","188.0800"
|
||||
"2016/01/28","189.7000","4586328.0000","190.7900","191.2800","182.4100"
|
||||
"2016/01/27","188.0700","3577353.0000","192.3800","193.2600","185.7700"
|
||||
"2016/01/26","193.5600","4956918.0000","196.7000","197.8200","188.8800"
|
||||
"2016/01/25","196.3800","2695408.0000","200.0600","203.5700","195.8800"
|
||||
"2016/01/22","202.5500","3122066.0000","204.8010","205.5000","199.0300"
|
||||
"2016/01/21","199.9700","3164424.0000","201.5500","203.2300","195.0200"
|
||||
"2016/01/20","198.7000","5826110.0000","199.4000","201.2800","191.2500"
|
||||
"2016/01/19","204.7200","4031271.0000","208.7100","210.4700","200.7800"
|
||||
"2016/01/15","204.9900","5325773.0000","198.9700","205.0700","197.2500"
|
||||
"2016/01/14","206.1800","6490579.0000","202.2100","210.0000","193.3800"
|
||||
"2016/01/13","200.3100","4120993.0000","212.0100","212.6500","200.0000"
|
||||
"2016/01/12","209.9700","3079051.0000","211.6000","213.7395","205.3100"
|
||||
"2016/01/11","207.8500","4090843.0000","214.0100","214.4500","203.0000"
|
||||
"2016/01/08","211.0000","3572360.0000","217.8600","220.4400","210.7700"
|
||||
"2016/01/07","215.6500","3550704.0000","214.1900","218.4400","213.6700"
|
||||
"2016/01/06","219.0400","3774353.0000","220.0000","220.0500","215.9800"
|
||||
"2016/01/05","223.4300","3185525.0000","226.3600","226.8900","220.0000"
|
||||
"2016/01/04","223.4100","6823510.0000","230.7200","231.3800","219.0000"
|
||||
"2015/12/31","240.0100","2713817.0000","238.5100","243.4500","238.3700"
|
||||
"2015/12/30","238.0900","3694448.0000","236.6000","243.6340","235.6707"
|
||||
"2015/12/29","237.1900","2402438.0000","230.0600","237.7200","229.5470"
|
||||
"2015/12/28","228.9500","1900171.0000","231.4900","231.9800","225.5400"
|
||||
"2015/12/24","230.5700","710277.0000","230.5600","231.8800","228.2800"
|
||||
"2015/12/23","229.7000","1551031.0000","232.1800","233.4500","228.1300"
|
||||
"2015/12/22","229.9500","1958836.0000","234.9900","236.5500","229.6300"
|
||||
"2015/12/21","232.5600","1951581.0000","231.6900","235.8300","231.0800"
|
||||
"2015/12/18","230.4600","3008057.0000","232.8900","235.9000","229.2900"
|
||||
"2015/12/17","233.3900","3294460.0000","233.9400","237.7600","229.8149"
|
||||
"2015/12/16","234.5100","5100443.0000","222.1000","234.8800","220.7300"
|
||||
"2015/12/15","221.0900","2241686.0000","221.8200","222.2200","218.0000"
|
||||
"2015/12/14","218.5800","2827797.0000","217.5100","220.9200","214.8700"
|
||||
"2015/12/11","217.0200","3259114.0000","225.2400","225.7500","216.6400"
|
||||
"2015/12/10","227.0700","2070088.0000","224.7100","228.4900","223.6400"
|
||||
"2015/12/09","224.5200","3057036.0000","226.7000","227.5000","220.7200"
|
||||
"2015/12/08","226.7200","2682259.0000","227.5200","228.8000","224.2000"
|
||||
"2015/12/07","231.1300","3140864.0000","227.7000","235.6300","226.1500"
|
||||
"2015/12/04","230.3800","2572014.0000","232.4600","233.2700","227.6600"
|
||||
"2015/12/03","232.7100","2937233.0000","235.4800","237.4500","230.0000"
|
||||
"2015/12/02","231.9900","2980644.0000","237.0000","238.6000","231.2300"
|
||||
"2015/12/01","237.1900","3728562.0000","231.0600","238.0000","231.0500"
|
||||
"2015/11/30","230.2600","2591205.0000","231.7900","234.2800","229.0800"
|
||||
"2015/11/27","231.6100","1949353.0000","231.0600","232.2500","227.0100"
|
||||
"2015/11/25","229.6400","3986945.0000","221.3400","230.8250","220.3750"
|
||||
"2015/11/24","218.2500","2478389.0000","215.3700","221.0000","215.0000"
|
||||
"2015/11/23","217.7500","2525196.0000","217.3500","219.1800","214.6798"
|
||||
"2015/11/20","220.0100","4394199.0000","223.4900","225.0000","213.5800"
|
||||
"2015/11/19","221.8000","2499593.0000","220.5400","226.1900","220.3000"
|
||||
"2015/11/18","221.0700","2810200.0000","214.5000","221.3800","212.5200"
|
||||
"2015/11/17","214.0000","2148326.0000","215.2000","216.0000","211.4000"
|
||||
"2015/11/16","214.3100","2920362.0000","206.0900","214.9800","205.8000"
|
||||
"2015/11/13","207.1900","3423139.0000","212.9500","212.9900","206.5200"
|
||||
"2015/11/12","212.9400","2913511.0000","217.8500","219.0000","212.6600"
|
||||
"2015/11/11","219.0800","3337501.0000","217.7700","219.4800","213.6300"
|
||||
"2015/11/10","216.5000","4612408.0000","223.4800","223.7000","216.0800"
|
||||
"2015/11/09","225.3300","3846179.0000","232.9900","232.9900","224.3100"
|
||||
"2015/11/06","232.3600","2444264.0000","230.7000","233.3590","229.5000"
|
||||
"2015/11/05","231.7700","4494944.0000","230.5800","234.5843","229.1900"
|
||||
"2015/11/04","231.6300","12713530.0000","227.0000","232.7400","225.2000"
|
||||
"2015/11/03","208.3500","8065091.0000","213.8500","214.4400","207.7500"
|
||||
"2015/11/02","213.7900","3924514.0000","208.9200","215.8000","207.2200"
|
||||
"2015/10/30","206.9300","4432433.0000","210.4000","211.6300","203.8900"
|
||||
"2015/10/29","211.6300","1794743.0000","211.7500","213.7481","210.6400"
|
||||
"2015/10/28","212.9600","2726880.0000","211.3100","213.4500","208.3000"
|
||||
"2015/10/27","210.3500","3512832.0000","214.8400","217.1000","207.5100"
|
||||
"2015/10/26","215.2600","3388227.0000","211.3800","215.8800","210.0000"
|
||||
"2015/10/23","209.0900","4226411.0000","215.0000","215.3500","207.6900"
|
||||
"2015/10/22","211.7200","2818801.0000","211.5600","215.7500","209.4000"
|
||||
"2015/10/21","210.0900","4177956.0000","211.9900","214.8100","208.8000"
|
||||
"2015/10/20","213.0300","14877020.0000","227.7200","228.6000","202.0000"
|
||||
"2015/10/19","228.1000","2506836.0000","226.5000","231.1500","224.9400"
|
||||
"2015/10/16","227.0100","4327574.0000","223.0400","230.4805","222.8700"
|
||||
"2015/10/15","221.3100","2835920.0000","216.4300","221.7300","213.7000"
|
||||
|
@@ -0,0 +1,115 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import os
|
||||
import requests
|
||||
from typing import Any, Dict, List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class EXASearchTool(BaseModel):
|
||||
"""Wrapper for EXA Search API."""
|
||||
search_url: str = "https://api.exa.ai/search"
|
||||
headers: Dict = {
|
||||
"accept": "application/json",
|
||||
"content-type": "application/json",
|
||||
}
|
||||
max_results: Optional[int] = None
|
||||
|
||||
def run(self, query: str) -> str:
|
||||
"""Run query through EXA and return concatenated results.
|
||||
|
||||
Args:
|
||||
query (str): The search query to use
|
||||
|
||||
Returns:
|
||||
str: The concatenated search results
|
||||
"""
|
||||
payload = {
|
||||
"query": query,
|
||||
"type": "magic",
|
||||
}
|
||||
|
||||
headers = self.headers.copy()
|
||||
headers["x-api-key"] = os.environ['EXA_API_KEY']
|
||||
|
||||
response = requests.post(self.search_url, json=payload, headers=headers)
|
||||
results = response.json()
|
||||
|
||||
if 'results' in results:
|
||||
return self._parse_results(results['results'])
|
||||
return ""
|
||||
|
||||
def results(self, query: str, max_results: Optional[int] = None) -> List[Dict[str, Any]]:
|
||||
"""Run query through EXA and return metadata.
|
||||
|
||||
Args:
|
||||
query (str): The search query to use
|
||||
max_results (Optional[int]): Maximum number of results to return
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: List of result dictionaries
|
||||
"""
|
||||
payload = {
|
||||
"query": query,
|
||||
"type": "magic",
|
||||
}
|
||||
|
||||
headers = self.headers.copy()
|
||||
headers["x-api-key"] = os.environ['EXA_API_KEY']
|
||||
|
||||
response = requests.post(self.search_url, json=payload, headers=headers)
|
||||
results = response.json()
|
||||
|
||||
if 'results' in results:
|
||||
return results['results'][:max_results] if max_results else results['results']
|
||||
return []
|
||||
|
||||
def _parse_results(self, results: List[Dict[str, Any]]) -> str:
|
||||
"""Parse results into a readable string format.
|
||||
|
||||
Args:
|
||||
results (List[Dict[str, Any]]): List of result dictionaries
|
||||
|
||||
Returns:
|
||||
str: Formatted string of results
|
||||
"""
|
||||
strings = []
|
||||
for result in results:
|
||||
try:
|
||||
strings.append('\n'.join([
|
||||
f"Title: {result['title']}",
|
||||
f"Score: {result['score']}",
|
||||
f"Url: {result['url']}",
|
||||
f"ID: {result['id']}",
|
||||
"---"
|
||||
]))
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
content = '\n'.join(strings)
|
||||
return f"\nSearch results: {content}\n"
|
||||
|
||||
# Create an agent with the tool
|
||||
agent = Agent(
|
||||
name="SearchAgent",
|
||||
role="Research Assistant",
|
||||
goal="Search for information about 'AI Agents Framework'",
|
||||
backstory="I am an AI assistant that can search GitHub.",
|
||||
tools=[EXASearchTool],
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Create tasks to demonstrate both tools
|
||||
task = Task(
|
||||
name="search_task",
|
||||
description="Search for information about 'AI Agents Framework'",
|
||||
expected_output="Information about AI Agents Framework",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Create and start the workflow
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
agents.start()
|
||||
@@ -0,0 +1,30 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import get_article, get_news_sources, get_articles_from_source, get_trending_topics
|
||||
|
||||
# Create Wikipedia agent
|
||||
news_agent = Agent(
|
||||
name="NewsAgent",
|
||||
role="News Analyst",
|
||||
goal="Collect and analyze news articles from various sources.",
|
||||
backstory="Expert in news gathering and content analysis.",
|
||||
tools=[get_article, get_news_sources, get_articles_from_source, get_trending_topics],
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
|
||||
# Define research task
|
||||
news_task = Task(
|
||||
description="Analyze news articles about 'AI developments' from major tech news sources.",
|
||||
expected_output="Summary of key AI developments with source articles.",
|
||||
agent=news_agent,
|
||||
name="ai_news"
|
||||
)
|
||||
|
||||
|
||||
# Run agent
|
||||
agents = PraisonAIAgents(
|
||||
agents=[news_agent],
|
||||
tasks=[news_task],
|
||||
process="sequential"
|
||||
)
|
||||
agents.start()
|
||||
Generated
+1271
-2
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,36 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create Video Analysis Agent
|
||||
video_agent = Agent(
|
||||
name="VideoAnalyst",
|
||||
role="Video Analysis Specialist",
|
||||
goal="Analyze images and videos to extract meaningful information",
|
||||
backstory="""You are an expert in computer vision and image analysis.
|
||||
You excel at describing images, detecting objects, and understanding visual content.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Task with Video File
|
||||
task1 = Task(
|
||||
name="analyze_video",
|
||||
description="""Watch this video and provide:
|
||||
1. A summary of the main events
|
||||
2. Key objects and people visible
|
||||
3. Any text or important information shown
|
||||
4. The overall context and setting""",
|
||||
expected_output="Comprehensive analysis of the video content",
|
||||
agent=video_agent,
|
||||
images=["video.mp4"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[video_agent],
|
||||
tasks=[task1],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run all tasks
|
||||
agents.start()
|
||||
@@ -0,0 +1,5 @@
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Web Search Agent", tools=[duckduckgo])
|
||||
agent.start("Search about AI 2024")
|
||||
@@ -0,0 +1,16 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Wikipedia Agent",
|
||||
tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language],
|
||||
self_reflect=True,
|
||||
min_reflect=3,
|
||||
max_reflect=5,
|
||||
)
|
||||
agent.start(
|
||||
"What is the history of AI?"
|
||||
"First search the history of AI"
|
||||
"Read the page of the history of AI"
|
||||
"Get the summary of the page"
|
||||
)
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: "Data Analyst Agent"
|
||||
sidebarTitle: "Data Analyst"
|
||||
description: "Learn how to create AI agents for data analysis and insights generation."
|
||||
icon: "chart-line"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Data Source] --> Reader[Data Reader]
|
||||
Reader --> Analyzer[Data Analyzer]
|
||||
Analyzer --> Generator[Insights Generator]
|
||||
Generator --> Out[Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Reader fill:#2E8B57,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Data Analyst Agent can read data from various sources, analyze it, and generate insights.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `data_analysis.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import (
|
||||
read_csv, read_excel, write_csv, write_excel,
|
||||
filter_data, get_summary, group_by, pivot_table
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Data Analyst Agent",
|
||||
tools=[
|
||||
read_csv, read_excel, write_csv, write_excel,
|
||||
filter_data, get_summary, group_by, pivot_table
|
||||
]
|
||||
)
|
||||
|
||||
# Start analysis with a specific task
|
||||
agent.start(f"""
|
||||
Read the data from the csv file {os.path.join(os.path.dirname(__file__), "tesla-stock-price.csv")}
|
||||
Analyse the data and give me the insights
|
||||
read_csv to read the file
|
||||
""")
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Data Analysis Workflow
|
||||
|
||||
The Data Analyst Agent is designed to perform comprehensive data analysis tasks using a suite of specialized tools. Here's how it works:
|
||||
|
||||
1. **Data Reading**: The agent can read data from various sources:
|
||||
- CSV files using `read_csv`
|
||||
- Excel files using `read_excel`
|
||||
|
||||
2. **Data Analysis**: Multiple analysis tools are available:
|
||||
- `filter_data`: Filter datasets based on conditions
|
||||
- `get_summary`: Generate statistical summaries
|
||||
- `group_by`: Group data by specific columns
|
||||
- `pivot_table`: Create pivot tables for analysis
|
||||
|
||||
3. **Data Export**: Results can be exported to:
|
||||
- CSV format using `write_csv`
|
||||
- Excel format using `write_excel`
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Multiple Data Sources" icon="table">
|
||||
Support for both CSV and Excel file formats.
|
||||
</Card>
|
||||
<Card title="Analysis Tools" icon="calculator">
|
||||
Comprehensive suite of analysis tools including filtering, summarization, and pivoting.
|
||||
</Card>
|
||||
<Card title="Data Export" icon="file-export">
|
||||
Export capabilities to various formats.
|
||||
</Card>
|
||||
<Card title="Automated Insights" icon="lightbulb">
|
||||
Automatic generation of data insights and patterns.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Analyzing stock data
|
||||
agent.start("""
|
||||
1. Read 'stock_data.csv'
|
||||
2. Filter data for the last 30 days
|
||||
3. Calculate daily returns
|
||||
4. Generate summary statistics
|
||||
5. Export results to 'analysis_results.xlsx'
|
||||
""")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex analysis workflows
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving analysis accuracy
|
||||
- Check out other specialized agents like the [Finance Agent](/agents/finance) for specific use cases
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "Finance Agent"
|
||||
sidebarTitle: "Finance"
|
||||
description: "Learn how to create AI agents for financial analysis and investment recommendations."
|
||||
icon: "money-bill-trend-up"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Request] --> Price[Stock Price]
|
||||
Price --> Info[Stock Info]
|
||||
Info --> History[Historical Data]
|
||||
History --> Out[Investment Advice]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Price fill:#2E8B57,color:#fff
|
||||
style Info fill:#2E8B57,color:#fff
|
||||
style History fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Finance Agent can analyze stock market data and provide investment recommendations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `finance_analysis.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import get_stock_price, get_stock_info, get_historical_data
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Research Agent",
|
||||
tools=[get_stock_price, get_stock_info, get_historical_data]
|
||||
)
|
||||
agent.start("Understand current stock price and historical data of Apple and Google. Tell me if I can invest in them")
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Financial Analysis
|
||||
|
||||
The Finance Agent specializes in stock market analysis and investment recommendations using three key components:
|
||||
|
||||
1. **Real-time Price Data**: Uses `get_stock_price` to fetch current market prices
|
||||
2. **Company Information**: Retrieves company details using `get_stock_info`
|
||||
3. **Historical Analysis**: Analyzes trends with `get_historical_data`
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Real-time Data" icon="clock">
|
||||
Access to current stock market prices and information.
|
||||
</Card>
|
||||
<Card title="Company Analysis" icon="building">
|
||||
Detailed company information and fundamentals.
|
||||
</Card>
|
||||
<Card title="Historical Trends" icon="chart-line">
|
||||
Analysis of historical price movements and patterns.
|
||||
</Card>
|
||||
<Card title="Investment Advice" icon="money-bill">
|
||||
AI-powered investment recommendations.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Analyzing specific stocks
|
||||
agent.start("""
|
||||
1. Get current prices for AAPL and GOOGL
|
||||
2. Retrieve company information
|
||||
3. Analyze 6-month historical data
|
||||
4. Provide investment recommendations
|
||||
""")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex financial analysis
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving recommendation accuracy
|
||||
- Check out the [Data Analyst Agent](/agents/data-analyst) for detailed data analysis capabilities
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: "Image to Text Agent"
|
||||
sidebarTitle: "Image to Text"
|
||||
description: "Learn how to create AI agents for converting images to textual descriptions and extracting text from images."
|
||||
icon: "image-to-text"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Image Input] --> OCR[Text Extractor]
|
||||
OCR --> Analyzer[Content Analyzer]
|
||||
Analyzer --> Generator[Description Generator]
|
||||
Generator --> Out[Text Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style OCR fill:#2E8B57,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Image-to-Text Agent can extract text from images and generate comprehensive descriptions.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `image_to_text.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create Image-to-Text Agent
|
||||
image_text_agent = Agent(
|
||||
name="ImageTextConverter",
|
||||
role="Image Text Extraction Specialist",
|
||||
goal="Convert image content to textual descriptions and extract text",
|
||||
backstory="""You are an expert in OCR and image understanding.
|
||||
You excel at extracting text from images and generating detailed descriptions.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Create text extraction task
|
||||
extraction_task = Task(
|
||||
name="extract_text",
|
||||
description="Extract all text from this image and describe its layout.",
|
||||
expected_output="Extracted text and layout description",
|
||||
agent=image_text_agent,
|
||||
images=["document.jpg"]
|
||||
)
|
||||
|
||||
# Create description task
|
||||
description_task = Task(
|
||||
name="generate_description",
|
||||
description="Generate a detailed description of the image content.",
|
||||
expected_output="Comprehensive description of visual elements",
|
||||
agent=image_text_agent,
|
||||
images=["scene.jpg"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[image_text_agent],
|
||||
tasks=[extraction_task, description_task],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run analysis
|
||||
agents.start()
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Image-to-Text Conversion
|
||||
|
||||
The Image-to-Text Agent combines multiple capabilities to convert visual content into textual form:
|
||||
|
||||
1. **OCR Processing**: Extracts text from images using optical character recognition
|
||||
2. **Layout Analysis**: Understands the spatial arrangement of text and visual elements
|
||||
3. **Content Description**: Generates natural language descriptions of image content
|
||||
4. **Text Formatting**: Preserves text formatting and structure where possible
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Text Extraction" icon="text">
|
||||
Advanced OCR capabilities for text extraction.
|
||||
</Card>
|
||||
<Card title="Layout Understanding" icon="table-layout">
|
||||
Analysis of text and content layout.
|
||||
</Card>
|
||||
<Card title="Content Description" icon="pen-to-square">
|
||||
Detailed descriptions of visual content.
|
||||
</Card>
|
||||
<Card title="Format Preservation" icon="paragraph">
|
||||
Maintains text formatting and structure.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Processing a document image
|
||||
document_task = Task(
|
||||
name="process_document",
|
||||
description="Extract text and analyze document layout",
|
||||
expected_output="Extracted text with layout information",
|
||||
agent=image_text_agent,
|
||||
images=["business_document.jpg"]
|
||||
)
|
||||
|
||||
# Run single task
|
||||
agents = PraisonAIAgents(
|
||||
agents=[image_text_agent],
|
||||
tasks=[document_task],
|
||||
process="sequential"
|
||||
)
|
||||
agents.start()
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex document processing
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving text extraction accuracy
|
||||
- Check out the [Image Agent](/agents/image) for pure image analysis capabilities
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: "Image Analysis Agent"
|
||||
sidebarTitle: "Image Analysis"
|
||||
description: "Learn how to create AI agents for image analysis and visual content understanding."
|
||||
icon: "image"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Image Input] --> Analyzer[Image Analyzer]
|
||||
Analyzer --> Detector[Object Detector]
|
||||
Detector --> Generator[Description Generator]
|
||||
Generator --> Out[Analysis Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Detector fill:#2E8B57,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Image Analysis Agent can process and analyze visual content, detect objects, and generate detailed descriptions.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `image_analysis.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create Image Analysis Agent
|
||||
image_agent = Agent(
|
||||
name="ImageAnalyst",
|
||||
role="Image Analysis Specialist",
|
||||
goal="Analyze images and videos to extract meaningful information",
|
||||
backstory="""You are an expert in computer vision and image analysis.
|
||||
You excel at describing images, detecting objects, and understanding visual content.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Create tasks for different types of analysis
|
||||
task1 = Task(
|
||||
name="analyze_landmark",
|
||||
description="Describe this famous landmark and its architectural features.",
|
||||
expected_output="Detailed description of the landmark's architecture and significance",
|
||||
agent=image_agent,
|
||||
images=["https://example.com/landmark.jpg"]
|
||||
)
|
||||
|
||||
task2 = Task(
|
||||
name="analyze_local_image",
|
||||
description="What objects can you see in this image? Describe their arrangement.",
|
||||
expected_output="Detailed description of objects and their spatial relationships",
|
||||
agent=image_agent,
|
||||
images=["local_image.jpg"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[image_agent],
|
||||
tasks=[task1, task2],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run analysis
|
||||
agents.start()
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Image Analysis
|
||||
|
||||
The Image Analysis Agent is designed to process and understand visual content through several key capabilities:
|
||||
|
||||
1. **Image Input**: Supports both URL and local file inputs
|
||||
2. **Object Detection**: Identifies and locates objects within images
|
||||
3. **Architectural Analysis**: Specialized in analyzing architectural features
|
||||
4. **Spatial Understanding**: Describes spatial relationships between objects
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Multiple Input Sources" icon="images">
|
||||
Support for both URL and local image files.
|
||||
</Card>
|
||||
<Card title="Object Detection" icon="magnifying-glass">
|
||||
Advanced object detection and recognition.
|
||||
</Card>
|
||||
<Card title="Architectural Analysis" icon="building">
|
||||
Specialized analysis of architectural features.
|
||||
</Card>
|
||||
<Card title="Detailed Descriptions" icon="pen">
|
||||
Generation of comprehensive visual descriptions.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Analyzing a landmark image
|
||||
landmark_task = Task(
|
||||
name="analyze_famous_landmark",
|
||||
description="Analyze this historical landmark",
|
||||
expected_output="Architectural details and historical significance",
|
||||
agent=image_agent,
|
||||
images=["https://example.com/landmark.jpg"]
|
||||
)
|
||||
|
||||
# Run single task
|
||||
agents = PraisonAIAgents(
|
||||
agents=[image_agent],
|
||||
tasks=[landmark_task],
|
||||
process="sequential"
|
||||
)
|
||||
agents.start()
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex image analysis workflows
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving analysis accuracy
|
||||
- Check out the [Video Agent](/agents/video) for video content analysis
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: "Markdown Agent"
|
||||
sidebarTitle: "Markdown"
|
||||
description: "Learn how to create AI agents for generating and formatting content in Markdown."
|
||||
icon: "markdown"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Content Request] --> Generator[Content Generator]
|
||||
Generator --> Formatter[Markdown Formatter]
|
||||
Formatter --> Validator[Format Validator]
|
||||
Validator --> Out[Markdown Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Formatter fill:#2E8B57,color:#fff
|
||||
style Validator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Markdown Agent can generate and format content in Markdown syntax.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `markdown_generator.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Create Markdown Agent
|
||||
markdown_agent = Agent(
|
||||
name="MarkdownWriter",
|
||||
role="Markdown Content Specialist",
|
||||
goal="Generate well-formatted content in Markdown syntax",
|
||||
instructions="You are a Markdown Agent, output in markdown format",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Generate content
|
||||
response = markdown_agent.start(
|
||||
"Write a blog post about artificial intelligence"
|
||||
)
|
||||
|
||||
# Save to file
|
||||
with open('blog_post.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Markdown Generation
|
||||
|
||||
The Markdown Agent specializes in creating properly formatted Markdown content:
|
||||
|
||||
1. **Content Generation**: Creates original content based on prompts
|
||||
2. **Markdown Formatting**: Applies proper Markdown syntax
|
||||
3. **Structure Validation**: Ensures correct formatting
|
||||
4. **Document Organization**: Creates well-structured documents
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Content Creation" icon="pen-to-square">
|
||||
Generates original, well-structured content.
|
||||
</Card>
|
||||
<Card title="Markdown Syntax" icon="code">
|
||||
Proper implementation of Markdown formatting.
|
||||
</Card>
|
||||
<Card title="Document Structure" icon="sitemap">
|
||||
Organized document hierarchy and sections.
|
||||
</Card>
|
||||
<Card title="Format Validation" icon="check">
|
||||
Ensures correct Markdown syntax usage.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Generate a technical documentation
|
||||
agent = Agent(
|
||||
instructions="You are a Markdown Agent, output in markdown format"
|
||||
)
|
||||
|
||||
# Generate API documentation
|
||||
response = agent.start("""
|
||||
Create technical documentation for a REST API with:
|
||||
- Introduction
|
||||
- Authentication
|
||||
- Endpoints
|
||||
- Examples
|
||||
""")
|
||||
|
||||
# Save documentation
|
||||
with open('api_docs.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex document generation
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving content quality
|
||||
- Check out the [Documentation Agent](/agents/documentation) for specialized documentation generation
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: "Planning Agent"
|
||||
sidebarTitle: "Planning"
|
||||
description: "Learn how to create AI agents for trip planning and itinerary generation."
|
||||
icon: "calendar"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Request] --> Search[Web Search]
|
||||
Search --> Analyzer[Options Analyzer]
|
||||
Analyzer --> Generator[Plan Generator]
|
||||
Generator --> Out[Itinerary]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Search fill:#2E8B57,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Planning Agent can search for travel options and create detailed itineraries.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `travel_planner.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create Planning Agent
|
||||
planning_agent = Agent(
|
||||
name="TravelPlanner",
|
||||
role="Travel Planning Specialist",
|
||||
goal="Create comprehensive travel plans and itineraries",
|
||||
instructions="You are a Planning Agent",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Generate travel plan
|
||||
response = planning_agent.start(
|
||||
"I want to go to London next week, find me a good hotel and flight"
|
||||
)
|
||||
|
||||
# Save plan
|
||||
with open('travel_plan.txt', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Travel Planning
|
||||
|
||||
The Planning Agent combines multiple capabilities to create comprehensive travel plans:
|
||||
|
||||
1. **Web Search**: Uses DuckDuckGo to find current travel options
|
||||
2. **Options Analysis**: Evaluates and compares different choices
|
||||
3. **Plan Generation**: Creates detailed itineraries
|
||||
4. **Recommendations**: Provides personalized suggestions
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Flight Search" icon="plane">
|
||||
Finds and compares flight options.
|
||||
</Card>
|
||||
<Card title="Hotel Booking" icon="hotel">
|
||||
Recommends suitable accommodations.
|
||||
</Card>
|
||||
<Card title="Itinerary Creation" icon="calendar-days">
|
||||
Generates detailed travel schedules.
|
||||
</Card>
|
||||
<Card title="Real-time Data" icon="clock">
|
||||
Uses current pricing and availability.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Create a weekend trip plan
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Planning Agent", tools=[duckduckgo])
|
||||
agent.start("I want to go London next week, find me a good hotel and flight")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex travel planning
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving recommendations
|
||||
- Check out the [Research Agent](/agents/research) for detailed destination research
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: "Programming Agent"
|
||||
sidebarTitle: "Programming"
|
||||
description: "Learn how to create AI agents for code development, analysis, and execution."
|
||||
icon: "code"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Code Request] --> Analyzer[Code Analyzer]
|
||||
Analyzer --> Generator[Code Generator]
|
||||
Generator --> Executor[Code Executor]
|
||||
Executor --> Debugger[Code Debugger]
|
||||
Debugger --> Out[Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Executor fill:#2E8B57,color:#fff
|
||||
style Debugger fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Programming Agent can analyze, generate, execute, and debug code.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `code_assistant.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import execute_code, analyze_code, format_code, lint_code, disassemble_code # Code Tools
|
||||
from praisonaiagents.tools import execute_command, list_processes, kill_process, get_system_info # Shell Tools
|
||||
from praisonaiagents.tools import duckduckgo # Web Search Tool
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Programming Agent", self_reflect=True, min_reflect=5, max_reflect=10,
|
||||
tools=[execute_code, analyze_code, format_code, lint_code, disassemble_code, execute_command, list_processes, kill_process, get_system_info, duckduckgo]
|
||||
)
|
||||
agent.start(
|
||||
"Write a python script using yfinance to find the stock price of Tesla"
|
||||
"First check if required packages are installed"
|
||||
"Run it using execute_code"
|
||||
"execute_command if you want to run any terminal command"
|
||||
"search internet using duckduckgo if you want to know update python package information"
|
||||
"Analyse the output using analyze_code and fix error if required"
|
||||
"if no package is installed, install it"
|
||||
"then run the code"
|
||||
)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Code Development
|
||||
|
||||
The Programming Agent combines multiple tools for comprehensive code development:
|
||||
|
||||
1. **Code Tools**:
|
||||
- `execute_code`: Run Python code
|
||||
- `analyze_code`: Analyze code structure
|
||||
- `format_code`: Format code to standards
|
||||
- `lint_code`: Check code quality
|
||||
- `disassemble_code`: View bytecode
|
||||
|
||||
2. **Shell Tools**:
|
||||
- `execute_command`: Run terminal commands
|
||||
- `list_processes`: View running processes
|
||||
- `kill_process`: Terminate processes
|
||||
- `get_system_info`: System information
|
||||
|
||||
3. **Web Tools**:
|
||||
- `duckduckgo`: Search for programming resources
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Code Execution" icon="play">
|
||||
Run and test code directly.
|
||||
</Card>
|
||||
<Card title="Code Analysis" icon="magnifying-glass-code">
|
||||
Analyze and improve code quality.
|
||||
</Card>
|
||||
<Card title="System Integration" icon="terminal">
|
||||
Execute system commands and manage processes.
|
||||
</Card>
|
||||
<Card title="Package Management" icon="box">
|
||||
Handle dependencies and installations.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Create and run a data analysis script
|
||||
response = agent.start("""
|
||||
1. Create a script that:
|
||||
- Uses pandas for data analysis
|
||||
- Reads a CSV file
|
||||
- Performs basic statistics
|
||||
- Creates a visualization
|
||||
2. Check and install required packages
|
||||
3. Execute and analyze the results
|
||||
""")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex development workflows
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving code quality
|
||||
- Check out the [Data Analyst Agent](/agents/data-analyst) for specialized data analysis
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: "Recommendation Agent"
|
||||
sidebarTitle: "Recommendation"
|
||||
description: "Learn how to create AI agents for personalized recommendations across various domains."
|
||||
icon: "thumbs-up"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[User Preferences] --> Search[Content Search]
|
||||
Search --> Analyzer[Preference Analyzer]
|
||||
Analyzer --> Generator[Recommendation Generator]
|
||||
Generator --> Out[Recommendations]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Search fill:#2E8B57,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Recommendation Agent can analyze preferences and generate personalized recommendations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `recommendation_system.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create Recommendation Agent
|
||||
recommendation_agent = Agent(
|
||||
name="RecommendationEngine",
|
||||
role="Recommendation Specialist",
|
||||
goal="Generate personalized recommendations based on preferences",
|
||||
instructions="You are a Recommendation Agent",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Get recommendations
|
||||
response = recommendation_agent.start(
|
||||
"Recommend me a good movie to watch in 2025"
|
||||
)
|
||||
|
||||
# Save recommendations
|
||||
with open('recommendations.txt', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Recommendation System
|
||||
|
||||
The Recommendation Agent uses multiple approaches to generate personalized suggestions:
|
||||
|
||||
1. **Content Search**: Uses DuckDuckGo to find current options
|
||||
2. **Preference Analysis**: Understands user preferences
|
||||
3. **Recommendation Generation**: Creates personalized suggestions
|
||||
4. **Content Filtering**: Filters based on relevance and quality
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Personalization" icon="user">
|
||||
Tailored recommendations based on preferences.
|
||||
</Card>
|
||||
<Card title="Real-time Data" icon="clock">
|
||||
Up-to-date content and information.
|
||||
</Card>
|
||||
<Card title="Multi-domain" icon="layer-group">
|
||||
Recommendations across various categories.
|
||||
</Card>
|
||||
<Card title="Content Filtering" icon="filter">
|
||||
Quality-based content selection.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Get personalized recommendations
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Recommendation Agent", tools=[duckduckgo])
|
||||
agent.start("Recommend me a good movie to watch in 2025")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex recommendation systems
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving recommendation accuracy
|
||||
- Check out the [Research Agent](/agents/research) for detailed content research
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "Research Agent"
|
||||
sidebarTitle: "Research"
|
||||
description: "Learn how to create AI agents for conducting comprehensive research and analysis."
|
||||
icon: "magnifying-glass-chart"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Research Query] --> Search[Web Search]
|
||||
Search --> Analyzer[Content Analyzer]
|
||||
Analyzer --> Synthesizer[Information Synthesizer]
|
||||
Synthesizer --> Out[Research Report]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Search fill:#2E8B57,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Synthesizer fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Research Agent can gather, analyze, and synthesize information from various sources.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `research_assistant.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create Research Agent
|
||||
research_agent = Agent(
|
||||
name="ResearchAssistant",
|
||||
role="Research Specialist",
|
||||
goal="Conduct comprehensive research and analysis",
|
||||
instructions="You are a Research Agent",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Conduct research
|
||||
response = research_agent.start(
|
||||
"Research about AI developments in 2024"
|
||||
)
|
||||
|
||||
# Save research findings
|
||||
with open('research_report.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Research Process
|
||||
|
||||
The Research Agent employs a systematic approach to information gathering and analysis:
|
||||
|
||||
1. **Web Search**: Uses DuckDuckGo to find relevant sources
|
||||
2. **Content Analysis**: Evaluates source credibility and relevance
|
||||
3. **Information Synthesis**: Combines findings into coherent insights
|
||||
4. **Report Generation**: Creates structured research reports
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Web Research" icon="globe">
|
||||
Comprehensive online information gathering.
|
||||
</Card>
|
||||
<Card title="Content Analysis" icon="microscope">
|
||||
In-depth evaluation of sources.
|
||||
</Card>
|
||||
<Card title="Information Synthesis" icon="layer-group">
|
||||
Integration of multiple sources.
|
||||
</Card>
|
||||
<Card title="Report Generation" icon="file-lines">
|
||||
Structured research documentation.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Research Agent", tools=[duckduckgo])
|
||||
agent.start("Research about AI 2024")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex research projects
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving research quality
|
||||
- Check out the [Data Analyst Agent](/agents/data-analyst) for data-driven research
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: "Shopping Agent"
|
||||
sidebarTitle: "Shopping"
|
||||
description: "Learn how to create AI agents for price comparison and shopping assistance."
|
||||
icon: "shopping-cart"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Product Query] --> Search[Store Search]
|
||||
Search --> Analyzer[Price Analyzer]
|
||||
Analyzer --> Comparator[Price Comparator]
|
||||
Comparator --> Out[Price Report]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Search fill:#2E8B57,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Comparator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Shopping Agent can search for products, compare prices, and provide shopping recommendations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `shopping_assistant.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create Shopping Agent
|
||||
shopping_agent = Agent(
|
||||
name="ShoppingAssistant",
|
||||
role="Shopping Specialist",
|
||||
goal="Find the best prices and deals across stores",
|
||||
instructions="You are a Shopping Agent",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Search for product prices
|
||||
response = shopping_agent.start(
|
||||
"I want to buy iPhone 16 Pro Max, check 5 stores and give me price in table"
|
||||
)
|
||||
|
||||
# Save price comparison
|
||||
with open('price_comparison.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Shopping Assistant
|
||||
|
||||
The Shopping Agent combines multiple capabilities for effective price comparison:
|
||||
|
||||
1. **Store Search**: Uses DuckDuckGo to find retailers
|
||||
2. **Price Analysis**: Gathers pricing information
|
||||
3. **Price Comparison**: Compares prices across stores
|
||||
4. **Deal Finding**: Identifies special offers and discounts
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Price Comparison" icon="scale-balanced">
|
||||
Compare prices across multiple stores.
|
||||
</Card>
|
||||
<Card title="Deal Detection" icon="tag">
|
||||
Find special offers and discounts.
|
||||
</Card>
|
||||
<Card title="Store Analysis" icon="store">
|
||||
Evaluate retailer reliability.
|
||||
</Card>
|
||||
<Card title="Price Tracking" icon="chart-line">
|
||||
Monitor price changes over time.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Compare product prices
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(instructions="You are a Shopping Agent", tools=[duckduckgo])
|
||||
agent.start("I want to buy iPhone 16 Pro Max, check 5 stores and give me price in table")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex shopping research
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving price accuracy
|
||||
- Check out the [Research Agent](/agents/research) for detailed product research
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: "Single Agent"
|
||||
sidebarTitle: "Single"
|
||||
description: "Learn how to create a basic single-purpose AI agent for simple tasks."
|
||||
icon: "circle-1"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Input] --> Agent[Single Agent]
|
||||
Agent --> Out[Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Agent fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how to create and use a simple single-purpose agent.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `simple_agent.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
|
||||
agent = Agent(instructions="You are a Markdown Agent, output in markdown format")
|
||||
agent.start("Write a blog post about AI")
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Single Agent
|
||||
|
||||
The Single Agent is the simplest form of PraisonAI agent:
|
||||
|
||||
1. **Simple Setup**: Minimal configuration required
|
||||
2. **Single Purpose**: Focused on one specific task
|
||||
3. **Direct Execution**: Straightforward input-output flow
|
||||
4. **No Dependencies**: No external tools required
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Simple Setup" icon="square-1">
|
||||
Minimal configuration needed.
|
||||
</Card>
|
||||
<Card title="Focused Task" icon="bullseye">
|
||||
Single-purpose execution.
|
||||
</Card>
|
||||
<Card title="Quick Start" icon="bolt">
|
||||
Rapid implementation.
|
||||
</Card>
|
||||
<Card title="No Tools" icon="toolbox">
|
||||
No external dependencies.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Create a content generation agent
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Create content agent
|
||||
agent = Agent(
|
||||
instructions="You are a Content Generation Agent, create engaging content"
|
||||
)
|
||||
|
||||
# Generate content
|
||||
response = agent.start("""
|
||||
Write a short story about:
|
||||
- A future world
|
||||
- With advanced AI
|
||||
- And human collaboration
|
||||
""")
|
||||
|
||||
# Save the story
|
||||
with open('ai_story.txt', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for more complex workflows
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving output quality
|
||||
- Check out other specialized agents like the [Markdown Agent](/agents/markdown) for specific use cases
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
title: "Video Agent"
|
||||
sidebarTitle: "Video"
|
||||
description: "Learn how to create AI agents for video analysis and content understanding."
|
||||
icon: "video"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Video Input] --> Analyzer[Content Analyzer]
|
||||
Analyzer --> Detector[Object Detector]
|
||||
Detector --> Transcriber[Text Transcriber]
|
||||
Transcriber --> Out[Analysis Report]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Analyzer fill:#2E8B57,color:#fff
|
||||
style Detector fill:#2E8B57,color:#fff
|
||||
style Transcriber fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Video Agent can analyze video content, detect objects, and extract meaningful information.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `video_analyzer.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create Video Analysis Agent
|
||||
video_agent = Agent(
|
||||
name="VideoAnalyst",
|
||||
role="Video Analysis Specialist",
|
||||
goal="Analyze videos to extract meaningful information",
|
||||
backstory="""You are an expert in computer vision and video analysis.
|
||||
You excel at describing content, detecting objects, and understanding context.""",
|
||||
llm="gpt-4o-mini",
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# Create video analysis task
|
||||
analysis_task = Task(
|
||||
name="analyze_video",
|
||||
description="""Analyze this video and provide:
|
||||
1. Summary of main events
|
||||
2. Key objects and people
|
||||
3. Text and important information
|
||||
4. Context and setting""",
|
||||
expected_output="Comprehensive video analysis",
|
||||
agent=video_agent,
|
||||
images=["video.mp4"]
|
||||
)
|
||||
|
||||
# Create PraisonAIAgents instance
|
||||
agents = PraisonAIAgents(
|
||||
agents=[video_agent],
|
||||
tasks=[analysis_task],
|
||||
process="sequential",
|
||||
verbose=1
|
||||
)
|
||||
|
||||
# Run analysis
|
||||
agents.start()
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Video Analysis
|
||||
|
||||
The Video Agent combines multiple capabilities for comprehensive video understanding:
|
||||
|
||||
1. **Content Analysis**: Analyzes video scenes and events
|
||||
2. **Object Detection**: Identifies objects and people
|
||||
3. **Text Extraction**: Captures text shown in videos
|
||||
4. **Context Understanding**: Interprets settings and situations
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Scene Analysis" icon="film">
|
||||
Detailed analysis of video scenes.
|
||||
</Card>
|
||||
<Card title="Object Detection" icon="cube">
|
||||
Identification of objects and people.
|
||||
</Card>
|
||||
<Card title="Text Extraction" icon="closed-captioning">
|
||||
Capture of text and captions.
|
||||
</Card>
|
||||
<Card title="Context Analysis" icon="sitemap">
|
||||
Understanding of video context.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Analyze a presentation video
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
video_agent = Agent(
|
||||
name="VideoAnalyst",
|
||||
role="Video Analysis Specialist",
|
||||
goal="Extract information from presentation videos",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create presentation analysis task
|
||||
presentation_task = Task(
|
||||
name="analyze_presentation",
|
||||
description="""Analyze this presentation video:
|
||||
1. Extract key points
|
||||
2. Capture slide content
|
||||
3. Note speaker's main arguments
|
||||
4. Summarize Q&A session""",
|
||||
expected_output="Detailed presentation summary",
|
||||
agent=video_agent,
|
||||
images=["presentation.mp4"]
|
||||
)
|
||||
|
||||
# Run analysis
|
||||
agents = PraisonAIAgents(
|
||||
agents=[video_agent],
|
||||
tasks=[presentation_task],
|
||||
process="sequential"
|
||||
)
|
||||
agents.start()
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex video analysis
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving analysis accuracy
|
||||
- Check out the [Image Agent](/agents/image) for still image analysis
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: "Web Search Agent"
|
||||
sidebarTitle: "Web Search"
|
||||
description: "Learn how to create AI agents for intelligent web searching and information gathering."
|
||||
icon: "globe"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Search Query] --> Search[Web Search]
|
||||
Search --> Filter[Content Filter]
|
||||
Filter --> Summarizer[Content Summarizer]
|
||||
Summarizer --> Out[Search Results]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Search fill:#2E8B57,color:#fff
|
||||
style Filter fill:#2E8B57,color:#fff
|
||||
style Summarizer fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Web Search Agent can perform intelligent searches and process web content.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `web_search.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create Web Search Agent
|
||||
search_agent = Agent(
|
||||
name="WebSearcher",
|
||||
role="Web Search Specialist",
|
||||
goal="Perform intelligent web searches and gather information",
|
||||
instructions="You are a Web Search Agent",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Perform search
|
||||
response = search_agent.start(
|
||||
"Search about AI developments in 2024"
|
||||
)
|
||||
|
||||
# Save search results
|
||||
with open('search_results.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Web Search
|
||||
|
||||
The Web Search Agent employs sophisticated search strategies:
|
||||
|
||||
1. **Query Processing**: Optimizes search queries
|
||||
2. **Content Filtering**: Filters relevant results
|
||||
3. **Information Extraction**: Extracts key information
|
||||
4. **Result Summarization**: Summarizes findings
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Smart Search" icon="magnifying-glass">
|
||||
Intelligent query processing.
|
||||
</Card>
|
||||
<Card title="Content Filtering" icon="filter">
|
||||
Relevance-based filtering.
|
||||
</Card>
|
||||
<Card title="Information Extraction" icon="scissors">
|
||||
Key information extraction.
|
||||
</Card>
|
||||
<Card title="Result Summary" icon="list">
|
||||
Concise result summaries.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Perform topic research
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Web Search Agent",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Research specific topic
|
||||
response = agent.start("""
|
||||
Search for information about quantum computing:
|
||||
- Recent breakthroughs
|
||||
- Leading companies
|
||||
- Current applications
|
||||
- Future predictions
|
||||
""")
|
||||
|
||||
# Save research results
|
||||
with open('quantum_computing_research.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex search workflows
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving search relevance
|
||||
- Check out the [Research Agent](/agents/research) for comprehensive research capabilities
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: "Wikipedia Agent"
|
||||
sidebarTitle: "Wikipedia"
|
||||
description: "Learn how to create AI agents for searching and extracting information from Wikipedia."
|
||||
icon: "book"
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Query] --> Search[Wiki Search]
|
||||
Search --> Page[Page Retrieval]
|
||||
Page --> Summary[Content Summary]
|
||||
Summary --> Out[Knowledge Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Search fill:#2E8B57,color:#fff
|
||||
style Page fill:#2E8B57,color:#fff
|
||||
style Summary fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
A workflow demonstrating how the Wikipedia Agent can search, retrieve, and summarize Wikipedia content.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create Script">
|
||||
Create a new file `wikipedia_assistant.py`:
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import (
|
||||
wiki_search,
|
||||
wiki_summary,
|
||||
wiki_page,
|
||||
wiki_random,
|
||||
wiki_language
|
||||
)
|
||||
|
||||
# Create Wikipedia Agent
|
||||
wiki_agent = Agent(
|
||||
name="WikipediaAssistant",
|
||||
role="Wikipedia Research Specialist",
|
||||
goal="Extract and summarize Wikipedia content",
|
||||
instructions="You are a Wikipedia Agent",
|
||||
tools=[
|
||||
wiki_search,
|
||||
wiki_summary,
|
||||
wiki_page,
|
||||
wiki_random,
|
||||
wiki_language
|
||||
],
|
||||
self_reflect=True,
|
||||
min_reflect=3,
|
||||
max_reflect=5
|
||||
)
|
||||
|
||||
# Research a topic
|
||||
response = wiki_agent.start("""
|
||||
What is the history of AI?
|
||||
First search the history of AI
|
||||
Read the page of the history of AI
|
||||
Get the summary of the page
|
||||
""")
|
||||
|
||||
# Save research results
|
||||
with open('ai_history.md', 'w') as f:
|
||||
f.write(response)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding Wikipedia Research
|
||||
|
||||
The Wikipedia Agent provides comprehensive Wikipedia access through multiple tools:
|
||||
|
||||
1. **Wiki Search**: `wiki_search` for finding relevant articles
|
||||
2. **Page Access**: `wiki_page` for retrieving full articles
|
||||
3. **Summarization**: `wiki_summary` for concise overviews
|
||||
4. **Random Articles**: `wiki_random` for discovering content
|
||||
5. **Language Support**: `wiki_language` for multilingual access
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Article Search" icon="magnifying-glass">
|
||||
Find relevant Wikipedia articles.
|
||||
</Card>
|
||||
<Card title="Content Retrieval" icon="book-open">
|
||||
Access complete article content.
|
||||
</Card>
|
||||
<Card title="Summarization" icon="compress">
|
||||
Generate concise article summaries.
|
||||
</Card>
|
||||
<Card title="Multilingual" icon="language">
|
||||
Access content in multiple languages.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
# Example: Research a scientific topic
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a Wikipedia Agent",
|
||||
tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language],
|
||||
self_reflect=True,
|
||||
min_reflect=3,
|
||||
max_reflect=5,
|
||||
)
|
||||
agent.start(
|
||||
"What is the history of AI?"
|
||||
"First search the history of AI"
|
||||
"Read the page of the history of AI"
|
||||
"Get the summary of the page"
|
||||
)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Prompt Chaining](/features/promptchaining) for complex Wikipedia research
|
||||
- Explore [Evaluator Optimizer](/features/evaluator-optimiser) for improving research quality
|
||||
- Check out the [Research Agent](/agents/research) for broader research capabilities
|
||||
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.0.29 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.0.46 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: "AutoAgents Module"
|
||||
description: "Documentation for the praisonaiagents.agents.autoagents module"
|
||||
icon: "robot"
|
||||
---
|
||||
|
||||
# Module praisonaiagents.agents.autoagents
|
||||
|
||||
The AutoAgents module provides automatic creation and management of AI agents and tasks based on high-level instructions.
|
||||
|
||||
## Classes
|
||||
|
||||
### AutoAgents
|
||||
The main class for automatically creating and managing AI agents and tasks.
|
||||
|
||||
#### Parameters
|
||||
- `instructions: str` - High-level task description for the agents
|
||||
- `tools: Optional[List[Any]] = None` - List of tools available to the agents
|
||||
- `verbose: bool = False` - Enable detailed logging
|
||||
- `process: str = "sequential"` - Process type (sequential or hierarchical)
|
||||
- `manager_llm: Optional[str] = None` - Language model for manager agent
|
||||
- `max_retries: int = 5` - Maximum retry attempts
|
||||
- `completion_checker: Optional[Any] = None` - Custom completion checker
|
||||
- `allow_code_execution: bool = False` - Allow code execution
|
||||
- `memory: bool = True` - Enable agent memory
|
||||
- `markdown: bool = True` - Enable markdown formatting
|
||||
- `self_reflect: bool = False` - Enable agent self-reflection
|
||||
- `max_reflect: int = 3` - Maximum reflection iterations
|
||||
- `min_reflect: int = 1` - Minimum reflection iterations
|
||||
- `llm: Optional[str] = None` - Language model for agents
|
||||
- `function_calling_llm: Optional[str] = None` - Language model for tool calling
|
||||
- `respect_context_window: bool = True` - Respect model context window
|
||||
- `code_execution_mode: str = "safe"` - Code execution mode (safe/unsafe)
|
||||
- `embedder_config: Optional[Dict[str, Any]] = None` - Embedder configuration
|
||||
- `knowledge_sources: Optional[List[Any]] = None` - Knowledge sources
|
||||
- `use_system_prompt: bool = True` - Use system prompts
|
||||
- `cache: bool = True` - Enable caching
|
||||
- `allow_delegation: bool = False` - Allow task delegation
|
||||
- `step_callback: Optional[Any] = None` - Callback for each step
|
||||
- `system_template: Optional[str] = None` - Custom system template
|
||||
- `prompt_template: Optional[str] = None` - Custom prompt template
|
||||
- `response_template: Optional[str] = None` - Custom response template
|
||||
- `max_rpm: Optional[int] = None` - Maximum requests per minute
|
||||
- `max_execution_time: Optional[int] = None` - Maximum execution time
|
||||
- `max_iter: int = 20` - Maximum iterations
|
||||
- `reflect_llm: Optional[str] = None` - Language model for reflection
|
||||
- `max_agents: int = 3` - Maximum number of agents to create
|
||||
|
||||
#### Methods
|
||||
|
||||
##### start()
|
||||
Start the agents synchronously.
|
||||
|
||||
```python
|
||||
def start(self):
|
||||
"""
|
||||
Creates tasks based on the instructions, then starts execution.
|
||||
Returns the task status and results dictionary.
|
||||
"""
|
||||
return super().start()
|
||||
```
|
||||
|
||||
##### astart()
|
||||
Start the agents asynchronously.
|
||||
|
||||
```python
|
||||
async def astart(self):
|
||||
"""
|
||||
Async version of start() method.
|
||||
Creates tasks based on the instructions, then starts execution.
|
||||
Returns the task status and results dictionary.
|
||||
"""
|
||||
return await super().astart()
|
||||
```
|
||||
|
||||
#### Internal Methods
|
||||
|
||||
##### _generate_config()
|
||||
Generate the configuration for agents and tasks.
|
||||
|
||||
```python
|
||||
def _generate_config(self) -> AutoAgentsConfig:
|
||||
"""
|
||||
Generate the configuration for agents and tasks based on instructions.
|
||||
Returns AutoAgentsConfig object containing agent and task configurations.
|
||||
"""
|
||||
```
|
||||
|
||||
##### _create_agents_and_tasks()
|
||||
Create agents and tasks from configuration.
|
||||
|
||||
```python
|
||||
def _create_agents_and_tasks(self, config: AutoAgentsConfig) -> tuple[List[Agent], List[Task]]:
|
||||
"""
|
||||
Create agents and tasks based on the generated configuration.
|
||||
Returns tuple of (agents, tasks).
|
||||
"""
|
||||
```
|
||||
|
||||
##### _assign_tools_to_agent()
|
||||
Assign appropriate tools to an agent.
|
||||
|
||||
```python
|
||||
def _assign_tools_to_agent(self, agent_config: AgentConfig) -> List[Any]:
|
||||
"""
|
||||
Assign tools to an agent based on its role and tasks.
|
||||
Returns list of assigned tools.
|
||||
"""
|
||||
```
|
||||
|
||||
## Pydantic Models
|
||||
|
||||
### TaskConfig
|
||||
Configuration for a task.
|
||||
|
||||
#### Attributes
|
||||
- `name: str` - Task name
|
||||
- `description: str` - Task description
|
||||
- `expected_output: str` - Expected output description
|
||||
- `tools: List[str]` - Required tools for the task
|
||||
|
||||
### AgentConfig
|
||||
Configuration for an agent.
|
||||
|
||||
#### Attributes
|
||||
- `name: str` - Agent name
|
||||
- `role: str` - Agent role
|
||||
- `goal: str` - Agent goal
|
||||
- `backstory: str` - Agent backstory
|
||||
- `tools: List[str]` - Required tools
|
||||
- `tasks: List[TaskConfig]` - Tasks assigned to the agent
|
||||
|
||||
### AutoAgentsConfig
|
||||
Overall configuration for AutoAgents.
|
||||
|
||||
#### Attributes
|
||||
- `main_instruction: str` - Main instruction for the agents
|
||||
- `process_type: str` - Process type (sequential/hierarchical)
|
||||
- `agents: List[AgentConfig]` - List of agent configurations
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Basic Usage
|
||||
```python
|
||||
from praisonaiagents import AutoAgents
|
||||
from praisonaiagents.tools import SerperDevTool
|
||||
|
||||
agents = AutoAgents(
|
||||
instructions="Research recent AI developments",
|
||||
tools=[SerperDevTool()],
|
||||
verbose=True
|
||||
)
|
||||
result = agents.start()
|
||||
```
|
||||
|
||||
### Async Usage
|
||||
```python
|
||||
async def main():
|
||||
agents = AutoAgents(
|
||||
instructions="Research recent AI developments",
|
||||
tools=[SerperDevTool()],
|
||||
process="hierarchical"
|
||||
)
|
||||
result = await agents.astart()
|
||||
|
||||
import asyncio
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
```python
|
||||
agents = AutoAgents(
|
||||
instructions="Complex research task",
|
||||
tools=[SerperDevTool()],
|
||||
max_agents=5,
|
||||
process="hierarchical",
|
||||
manager_llm="gpt-4o",
|
||||
memory=True,
|
||||
allow_delegation=True,
|
||||
max_execution_time=600,
|
||||
self_reflect=True
|
||||
)
|
||||
```
|
||||
|
||||
<Note>
|
||||
For optimal results, provide clear instructions and appropriate tools for your use case.
|
||||
</Note>
|
||||
@@ -1,69 +0,0 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Set up PraisonAI in your development environment
|
||||
icon: download
|
||||
---
|
||||
|
||||
<Note>
|
||||
**Requirements**
|
||||
|
||||
- Python 3.10 or higher
|
||||
- pip package manager
|
||||
- Virtual environment (recommended)
|
||||
</Note>
|
||||
|
||||
# Installing PraisonAI
|
||||
|
||||
Follow these steps to set up PraisonAI in your development environment.
|
||||
|
||||
<Steps>
|
||||
<Step title="Create Virtual Environment (Optional)">
|
||||
First, create and activate a virtual environment:
|
||||
<CodeGroup>
|
||||
```bash Mac/Linux
|
||||
python -m venv praisonai-env
|
||||
source praisonai-env/bin/activate
|
||||
```
|
||||
|
||||
```bash Windows
|
||||
python -m venv praisonai-env
|
||||
.\praisonai-env\Scripts\activate
|
||||
```
|
||||
</CodeGroup>
|
||||
</Step>
|
||||
|
||||
<Step title="Install PraisonAI">
|
||||
Install the core package and tools:
|
||||
```bash Terminal
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Environment">
|
||||
```bash Terminal
|
||||
export OPENAI_API_KEY=your_openai_key
|
||||
```
|
||||
Generate your OpenAI API key from [OpenAI](https://platform.openai.com/api-keys)
|
||||
You can also use other LLM providers like Anthropic, Google, etc. Please refer to the [Models](/models) for more information.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Quick Start Guide"
|
||||
icon="bolt"
|
||||
href="/code/quickstart"
|
||||
>
|
||||
Build your first AI agent
|
||||
</Card>
|
||||
<Card
|
||||
title="API Reference"
|
||||
icon="code"
|
||||
href="/api/praisonaiagents/index"
|
||||
>
|
||||
Explore the API documentation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,188 +0,0 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "Welcome to PraisonAI - The Next Generation AI Agent Framework"
|
||||
icon: "book-open"
|
||||
---
|
||||
|
||||
<Note>
|
||||
PraisonAI is a powerful framework for building and deploying AI agents that can understand, reason, and execute complex tasks autonomously.
|
||||
</Note>
|
||||
|
||||
# What is PraisonAI?
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
%% Define the main flow
|
||||
Start([▶ Start]) --> Agent1
|
||||
Agent1 --> Process[⚙ Process]
|
||||
Process --> Agent2
|
||||
Agent2 --> Output([✓ Output])
|
||||
Process -.-> Agent1
|
||||
|
||||
%% Styling for main elements
|
||||
style Start fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
style Output fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
style Process fill:#5C80BC,stroke:#7C90A0,color:#fff
|
||||
|
||||
%% Define subgraphs for agents and their tasks
|
||||
subgraph Agent1[" "]
|
||||
Task1[📋 Task]
|
||||
AgentIcon1[🤖 AI Agent]
|
||||
Tools1[🔧 Tools]
|
||||
|
||||
Task1 --- AgentIcon1
|
||||
AgentIcon1 --- Tools1
|
||||
|
||||
style Task1 fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
style AgentIcon1 fill:#5C80BC,stroke:#7C90A0,color:#fff
|
||||
style Tools1 fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
end
|
||||
|
||||
subgraph Agent2[" "]
|
||||
Task2[📋 Task]
|
||||
AgentIcon2[🤖 AI Agent]
|
||||
Tools2[🔧 Tools]
|
||||
|
||||
Task2 --- AgentIcon2
|
||||
AgentIcon2 --- Tools2
|
||||
|
||||
style Task2 fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
style AgentIcon2 fill:#5C80BC,stroke:#7C90A0,color:#fff
|
||||
style Tools2 fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
end
|
||||
|
||||
%% Style for agent containers
|
||||
style Agent1 fill:#1B1F3B,stroke:#7C90A0,color:#fff
|
||||
style Agent2 fill:#1B1F3B,stroke:#7C90A0,color:#fff
|
||||
|
||||
%% Global styles
|
||||
classDef default fill:#2D3047,stroke:#7C90A0,color:#fff
|
||||
linkStyle default stroke:#7C90A0,stroke-width:2px
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Agents" icon="user-robot">
|
||||
Autonomous AI entities that can understand context and execute tasks
|
||||
</Card>
|
||||
<Card title="Tasks" icon="list-check">
|
||||
Units of work that agents can execute independently or collaboratively
|
||||
</Card>
|
||||
<Card title="Process" icon="diagram-project">
|
||||
Workflow management for coordinating agent activities
|
||||
</Card>
|
||||
<Card title="Tools" icon="toolbox">
|
||||
Extensible capabilities that agents can leverage
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Key Features
|
||||
|
||||
<Steps>
|
||||
<Step title="Autonomous Agents">
|
||||
Create intelligent agents that can:
|
||||
- Understand natural language instructions
|
||||
- Make decisions based on context
|
||||
- Execute complex tasks autonomously
|
||||
</Step>
|
||||
<Step title="Flexible Architecture">
|
||||
Build with:
|
||||
- Modular components
|
||||
- Extensible tools
|
||||
- Customizable workflows
|
||||
</Step>
|
||||
<Step title="Advanced Capabilities">
|
||||
Leverage:
|
||||
- Multi-agent collaboration
|
||||
- Memory and context management
|
||||
- Tool integration
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Use Cases
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Customer Service" icon="headset">
|
||||
Build intelligent support agents that can handle customer inquiries and resolve issues autonomously.
|
||||
</Card>
|
||||
<Card title="Data Analysis" icon="chart-line">
|
||||
Create agents that can process, analyze, and derive insights from complex datasets.
|
||||
</Card>
|
||||
<Card title="Content Creation" icon="pen-nib">
|
||||
Deploy agents that can generate, edit, and optimize content across various formats.
|
||||
</Card>
|
||||
<Card title="Process Automation" icon="gears">
|
||||
Automate complex workflows with intelligent agents that can coordinate and execute tasks.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Getting Started
|
||||
|
||||
<Warning>
|
||||
Before proceeding, ensure you have Python 3.9+ installed on your system.
|
||||
</Warning>
|
||||
|
||||
### Quick Setup
|
||||
|
||||
<CodeGroup>
|
||||
```bash pip
|
||||
pip install praisonai
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Basic Example
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create an agent
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Senior Research Analyst",
|
||||
goal="Uncover cutting-edge developments in AI",
|
||||
backstory="You are an expert at a technology research group",
|
||||
verbose=True,
|
||||
llm="gpt-4o"
|
||||
)
|
||||
|
||||
# Define a task
|
||||
task = Task(
|
||||
name="research_task",
|
||||
description="Analyze 2024's AI advancements",
|
||||
expected_output="A detailed report",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
# Run the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher],
|
||||
tasks=[task],
|
||||
verbose=False
|
||||
)
|
||||
|
||||
result = agents.start()
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Installation"
|
||||
icon="download"
|
||||
href="/code/installation"
|
||||
>
|
||||
Complete installation guide and setup instructions
|
||||
</Card>
|
||||
<Card
|
||||
title="Quick Start"
|
||||
icon="play"
|
||||
href="/code/quickstart"
|
||||
>
|
||||
Build your first AI agent in minutes
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Tip>
|
||||
Join our community on [Discord](https://www.youtube.com/redirect?event=channel_description&redir_token=QUFFLUhqbUpiZE00YlF1MjB1LVNIeTFxTnV6MWRRc18wZ3xBQ3Jtc0ttQVZ0aFJ4RXRDanlBQU1Vd3lHU3BBcml3bzhoek1pTmlkZkFkWjlNVDBpRkxZd1dTOWhPc20xbHJOb1NibmNYeTEydklJT0pZOVJFQW93MmJOQXU0WkJNVzNsd29SZ3FBNFVwMTFGNThNSHBWVUhjSQ&q=https%3A%2F%2Fdiscord.gg%2FnNZu5gGT59) to connect with other developers and get help!
|
||||
</Tip>
|
||||
@@ -1,195 +0,0 @@
|
||||
---
|
||||
title: Quick Start
|
||||
description: Build your first AI agent with PraisonAI
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Install PraisonAI">
|
||||
Install the core package:
|
||||
```bash Terminal
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Environment">
|
||||
```bash Terminal
|
||||
export OPENAI_API_KEY=your_openai_key
|
||||
```
|
||||
Generate your OpenAI API key from [OpenAI](https://platform.openai.com/api-keys)
|
||||
You can also use other LLM providers like Anthropic, Google, etc. Please refer to the [Models](/models) for more information.
|
||||
</Step>
|
||||
|
||||
<Step title="Create Agent">
|
||||
Create `app.py`:
|
||||
## Code Example
|
||||
|
||||
<CodeGroup>
|
||||
```python Basic
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create an agent
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Senior Research Analyst",
|
||||
goal="Uncover cutting-edge developments in AI",
|
||||
backstory="You are an expert at a technology research group",
|
||||
verbose=True,
|
||||
llm="gpt-4o"
|
||||
)
|
||||
|
||||
# Define a task
|
||||
task = Task(
|
||||
name="research_task",
|
||||
description="Analyze 2024's AI advancements",
|
||||
expected_output="A detailed report",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
# Run the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher],
|
||||
tasks=[task],
|
||||
verbose=False
|
||||
)
|
||||
|
||||
result = agents.start()
|
||||
```
|
||||
|
||||
```python Advanced
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create multiple agents
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Senior Research Analyst",
|
||||
goal="Uncover cutting-edge developments in AI",
|
||||
backstory="You are an expert at a technology research group",
|
||||
verbose=True,
|
||||
llm="gpt-4o",
|
||||
markdown=True
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="Writer",
|
||||
role="Tech Content Strategist",
|
||||
goal="Craft compelling content on tech advancements",
|
||||
backstory="You are a content strategist",
|
||||
llm="gpt-4o",
|
||||
markdown=True
|
||||
)
|
||||
|
||||
# Define multiple tasks
|
||||
task1 = Task(
|
||||
name="research_task",
|
||||
description="Analyze 2024's AI advancements",
|
||||
expected_output="A detailed report",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
task2 = Task(
|
||||
name="writing_task",
|
||||
description="Create a blog post about AI advancements",
|
||||
expected_output="A blog post",
|
||||
agent=writer
|
||||
)
|
||||
|
||||
# Run with hierarchical process
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher, writer],
|
||||
tasks=[task1, task2],
|
||||
verbose=False,
|
||||
process="hierarchical",
|
||||
manager_llm="gpt-4o"
|
||||
)
|
||||
|
||||
result = agents.start()
|
||||
```
|
||||
</CodeGroup>
|
||||
</Step>
|
||||
|
||||
<Step title="Run the Program">
|
||||
Execute your script:
|
||||
```bash Terminal
|
||||
python app.py
|
||||
```
|
||||
|
||||
You should see:
|
||||
- Agent initialization
|
||||
- Agents progress
|
||||
- Final results
|
||||
- Generated report
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Creating Custom Tool
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from duckduckgo_search import DDGS
|
||||
from typing import List, Dict
|
||||
|
||||
# 1. Tool
|
||||
def internet_search_tool(query: str) -> List[Dict]:
|
||||
"""
|
||||
Perform Internet Search
|
||||
"""
|
||||
results = []
|
||||
ddgs = DDGS()
|
||||
for result in ddgs.text(keywords=query, max_results=5):
|
||||
results.append({
|
||||
"title": result.get("title", ""),
|
||||
"url": result.get("href", ""),
|
||||
"snippet": result.get("body", "")
|
||||
})
|
||||
return results
|
||||
|
||||
# 2. Agent
|
||||
data_agent = Agent(
|
||||
name="DataCollector",
|
||||
role="Search Specialist",
|
||||
goal="Perform internet searches to collect relevant information.",
|
||||
backstory="Expert in finding and organising internet data.",
|
||||
tools=[internet_search_tool],
|
||||
self_reflect=False
|
||||
)
|
||||
|
||||
# 3. Tasks
|
||||
collect_task = Task(
|
||||
description="Perform an internet search using the query: 'AI job trends in 2024'. Return results as a list of title, URL, and snippet.",
|
||||
expected_output="List of search results with titles, URLs, and snippets.",
|
||||
agent=data_agent,
|
||||
name="collect_data",
|
||||
)
|
||||
|
||||
# 4. Start Agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[data_agent],
|
||||
tasks=[collect_task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
agents.start()
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Core Concepts"
|
||||
icon="book"
|
||||
href="/concepts/agents"
|
||||
>
|
||||
Learn about agents, tasks, and processes
|
||||
</Card>
|
||||
<Card
|
||||
title="API Reference"
|
||||
icon="code"
|
||||
href="/api/praisonaiagents/index"
|
||||
>
|
||||
Explore detailed API documentation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
+237
-26
@@ -105,43 +105,254 @@ graph LR
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Agent Types
|
||||
|
||||
<Steps>
|
||||
<Step title="Basic Agent">
|
||||
- Single-purpose focus
|
||||
- Direct user interaction
|
||||
- Limited tool set
|
||||
|
||||
<Step title="Install PraisonAI">
|
||||
Install the core package:
|
||||
```bash Terminal
|
||||
pip install praisonaiagents
|
||||
```
|
||||
</Step>
|
||||
<Step title="Specialized Agent">
|
||||
- Domain expertise
|
||||
- Advanced capabilities
|
||||
- Custom tools
|
||||
|
||||
<Step title="Configure Environment">
|
||||
```bash Terminal
|
||||
export OPENAI_API_KEY=your_openai_key
|
||||
```
|
||||
Generate your OpenAI API key from [OpenAI](https://platform.openai.com/api-keys)
|
||||
Use other LLM providers like Ollama, Anthropic, Groq, Google, etc. Please refer to the [Models](/models) for more information.
|
||||
</Step>
|
||||
<Step title="Collaborative Agent">
|
||||
- Team interaction
|
||||
- Task delegation
|
||||
- Shared context
|
||||
|
||||
<Step title="Create Agent">
|
||||
Create `app.py`:
|
||||
<CodeGroup>
|
||||
```python Single Agent
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create an agent
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Senior Research Analyst",
|
||||
goal="Uncover cutting-edge developments in AI",
|
||||
backstory="You are an expert at a technology research group",
|
||||
verbose=True,
|
||||
llm="gpt-4o"
|
||||
)
|
||||
|
||||
# Define a task
|
||||
task = Task(
|
||||
name="research_task",
|
||||
description="Analyze 2024's AI advancements",
|
||||
expected_output="A detailed report",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
# Run the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher],
|
||||
tasks=[task],
|
||||
verbose=False
|
||||
)
|
||||
|
||||
result = agents.start()
|
||||
```
|
||||
|
||||
```python Multiple Agents
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create multiple agents
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
role="Senior Research Analyst",
|
||||
goal="Uncover cutting-edge developments in AI",
|
||||
backstory="You are an expert at a technology research group",
|
||||
verbose=True,
|
||||
llm="gpt-4o",
|
||||
markdown=True
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="Writer",
|
||||
role="Tech Content Strategist",
|
||||
goal="Craft compelling content on tech advancements",
|
||||
backstory="You are a content strategist",
|
||||
llm="gpt-4o",
|
||||
markdown=True
|
||||
)
|
||||
|
||||
# Define multiple tasks
|
||||
task1 = Task(
|
||||
name="research_task",
|
||||
description="Analyze 2024's AI advancements",
|
||||
expected_output="A detailed report",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
task2 = Task(
|
||||
name="writing_task",
|
||||
description="Create a blog post about AI advancements",
|
||||
expected_output="A blog post",
|
||||
agent=writer
|
||||
)
|
||||
|
||||
# Run with hierarchical process
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher, writer],
|
||||
tasks=[task1, task2],
|
||||
verbose=False,
|
||||
process="hierarchical",
|
||||
manager_llm="gpt-4o"
|
||||
)
|
||||
|
||||
result = agents.start()
|
||||
```
|
||||
</CodeGroup>
|
||||
</Step>
|
||||
|
||||
<Step title="Start Agents">
|
||||
Execute your script:
|
||||
```bash Terminal
|
||||
python app.py
|
||||
```
|
||||
|
||||
You should see:
|
||||
- Agent initialization
|
||||
- Agents progress
|
||||
- Final results
|
||||
- Generated report
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Agent Types
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Basic Agent" icon="user" iconType="solid">
|
||||
<AccordionGroup>
|
||||
<Accordion title="Overview">
|
||||
Perfect for straightforward tasks and direct interactions
|
||||
</Accordion>
|
||||
<Accordion title="Features">
|
||||
- Single-purpose focus
|
||||
- Direct user interaction
|
||||
- Limited tool set
|
||||
- Ideal for simple tasks
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Card>
|
||||
|
||||
<Card title="Specialized Agent" icon="user-gear" iconType="solid">
|
||||
<AccordionGroup>
|
||||
<Accordion title="Overview">
|
||||
Experts in specific domains with advanced capabilities
|
||||
</Accordion>
|
||||
<Accordion title="Features">
|
||||
- Domain expertise
|
||||
- Advanced capabilities
|
||||
- Custom tools
|
||||
- Deep knowledge base
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Card>
|
||||
|
||||
<Card title="Collaborative Agent" icon="users" iconType="solid">
|
||||
<AccordionGroup>
|
||||
<Accordion title="Overview">
|
||||
Designed for team-based operations and complex workflows
|
||||
</Accordion>
|
||||
<Accordion title="Features">
|
||||
- Team interaction
|
||||
- Task delegation
|
||||
- Shared context
|
||||
- Coordinated actions
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Best Practices
|
||||
|
||||
<Warning>
|
||||
Always implement proper error handling and resource management in your agent configurations.
|
||||
</Warning>
|
||||
<Note>
|
||||
Always implement proper error handling and resource management in your agent configurations.
|
||||
</Note>
|
||||
|
||||
### Agent Design
|
||||
1. Clear role definition
|
||||
2. Specific, measurable goals
|
||||
3. Relevant tool selection
|
||||
4. Appropriate memory configuration
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Agent Design" icon="compass-drafting" iconType="solid">
|
||||
<Steps>
|
||||
<Step title="Role Definition">
|
||||
Define clear, specific roles for each agent
|
||||
</Step>
|
||||
<Step title="Goal Setting">
|
||||
Set specific, measurable goals
|
||||
</Step>
|
||||
<Step title="Tool Selection">
|
||||
Choose relevant tools for the task
|
||||
</Step>
|
||||
<Step title="Memory Setup">
|
||||
Configure appropriate memory settings
|
||||
</Step>
|
||||
</Steps>
|
||||
</Card>
|
||||
|
||||
### Agent Interaction
|
||||
1. Clear communication protocols
|
||||
2. Defined delegation rules
|
||||
3. Error handling mechanisms
|
||||
4. Resource management
|
||||
<Card title="Agent Interaction" icon="network-wired" iconType="solid">
|
||||
<Steps>
|
||||
<Step title="Communication">
|
||||
Establish clear communication protocols
|
||||
</Step>
|
||||
<Step title="Delegation">
|
||||
Define explicit delegation rules
|
||||
</Step>
|
||||
<Step title="Error Handling">
|
||||
Implement robust error handling
|
||||
</Step>
|
||||
<Step title="Resource Management">
|
||||
Set up efficient resource allocation
|
||||
</Step>
|
||||
</Steps>
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Async Capabilities
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Key Features" icon="bolt" iconType="solid">
|
||||
<Tabs>
|
||||
<Tab title="Async Support">
|
||||
```python
|
||||
async def main():
|
||||
agent = Agent(name="AsyncAgent")
|
||||
result = await agent.aprocess_task()
|
||||
```
|
||||
- Full async/await support
|
||||
- Non-blocking operations
|
||||
- Enhanced performance
|
||||
</Tab>
|
||||
|
||||
<Tab title="Parallel Execution">
|
||||
```python
|
||||
async def parallel_tasks():
|
||||
tasks = [agent1.task(), agent2.task()]
|
||||
results = await asyncio.gather(*tasks)
|
||||
```
|
||||
- Parallel task execution
|
||||
- Efficient resource usage
|
||||
- Improved throughput
|
||||
</Tab>
|
||||
|
||||
<Tab title="Integration">
|
||||
```python
|
||||
@agent.async_tool
|
||||
async def custom_tool():
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# Async operations
|
||||
pass
|
||||
```
|
||||
- Async tool integration
|
||||
- Callback support
|
||||
- Mixed sync/async operations
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Advanced Features
|
||||
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
---
|
||||
title: "AI Agents with Memory"
|
||||
sidebarTitle: "Memory"
|
||||
description: "Learn how to create AI agents with memory capabilities for maintaining context and information across tasks."
|
||||
icon: "brain"
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Code">
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
First, install the PraisonAI Agents package:
|
||||
```bash
|
||||
pip install "praisonaiagents[memory]" duckduckgo_search
|
||||
```
|
||||
duckduckgo_search is a tool that allows agents to search the web.
|
||||
It is required for the multiple agents example shown below.
|
||||
</Step>
|
||||
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable in your terminal:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create a file">
|
||||
Create a new file `app.py` with the basic setup:
|
||||
<CodeGroup>
|
||||
```python Single Agent
|
||||
from praisonaiagents.agents.agents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Create blog writer agent
|
||||
blog_agent = Agent(
|
||||
role="Blog Writer",
|
||||
goal="Write a blog post about AI",
|
||||
backstory="Expert at writing blog posts",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create blog writing task
|
||||
blog_task = Task(
|
||||
description="Write a blog post about AI trends",
|
||||
expected_output="Well-written blog post about AI trends",
|
||||
agent=blog_agent
|
||||
)
|
||||
|
||||
# Create and start the agents with memory enabled
|
||||
agents = PraisonAIAgents(
|
||||
agents=[blog_agent],
|
||||
tasks=[blog_task],
|
||||
memory=True
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
print(result)
|
||||
```
|
||||
|
||||
```python Multiple Agents
|
||||
from praisonaiagents.agents.agents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create research agent with memory
|
||||
research_agent = Agent(
|
||||
role="Research Analyst",
|
||||
goal="Research and document key information about topics",
|
||||
backstory="Expert at analyzing and storing information in memory",
|
||||
llm="gpt-4o-mini",
|
||||
tools=[duckduckgo]
|
||||
)
|
||||
|
||||
# Create blog writer agent
|
||||
blog_agent = Agent(
|
||||
role="Blog Writer",
|
||||
goal="Write a blog post about the research",
|
||||
backstory="Expert at writing blog posts",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create research task
|
||||
research_task = Task(
|
||||
description="Research and document key information about AI trends",
|
||||
expected_output="Detailed research findings about AI trends",
|
||||
agent=research_agent
|
||||
)
|
||||
|
||||
# Create blog writing task
|
||||
blog_task = Task(
|
||||
description="Write a blog post about the research findings",
|
||||
expected_output="Well-written blog post based on research",
|
||||
agent=blog_agent
|
||||
)
|
||||
|
||||
# Create and start the agents with advanced memory configuration
|
||||
agents = PraisonAIAgents(
|
||||
agents=[research_agent, blog_agent],
|
||||
tasks=[research_task, blog_task],
|
||||
memory=True
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
print(result)
|
||||
```
|
||||
</CodeGroup>
|
||||
</Step>
|
||||
|
||||
<Step title="Start Agents">
|
||||
Type this in your terminal to run your agents:
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="No Code">
|
||||
<Steps>
|
||||
<Step title="Install Package">
|
||||
Install the PraisonAI package:
|
||||
```bash
|
||||
pip install praisonai
|
||||
```
|
||||
</Step>
|
||||
<Step title="Set API Key">
|
||||
Set your OpenAI API key as an environment variable in your terminal:
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
</Step>
|
||||
<Step title="Create a file">
|
||||
Create a new file `agents.yaml` with the basic setup:
|
||||
```yaml
|
||||
framework: praisonai
|
||||
process: sequential
|
||||
memory: true
|
||||
roles:
|
||||
researcher:
|
||||
backstory: Expert at analyzing and storing information in memory.
|
||||
goal: Research and document key information about topics
|
||||
role: Research Analyst
|
||||
llm: gpt-4o-mini
|
||||
tools:
|
||||
- duckduckgo
|
||||
tasks:
|
||||
research_task:
|
||||
description: Research and document key information about topics.
|
||||
expected_output: Detailed research findings.
|
||||
|
||||
writer:
|
||||
backstory: Expert at writing blog posts.
|
||||
goal: Write a blog post about the research
|
||||
role: Blog Writer
|
||||
llm: gpt-4o-mini
|
||||
tasks:
|
||||
blog_task:
|
||||
description: Write a blog post about the research.
|
||||
expected_output: Well-written blog post based on research.
|
||||
```
|
||||
</Step>
|
||||
<Step title="Start Agents">
|
||||
Type this in your terminal to run your agents:
|
||||
```bash
|
||||
praisonai agents.yaml
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
**Requirements**
|
||||
- Python 3.10 or higher
|
||||
- OpenAI API key. Generate OpenAI API key [here](https://platform.openai.com/api-keys). Use Other models using [this guide](/models).
|
||||
- Basic understanding of Python
|
||||
</Note>
|
||||
|
||||
## Understanding Memory
|
||||
|
||||
<Card title="What is Agent Memory?" icon="question">
|
||||
Memory in AI agents enables them to:
|
||||
- Maintain context across multiple tasks
|
||||
- Remember previous interactions and findings
|
||||
- Build upon past knowledge
|
||||
- Share information between agents
|
||||
- Create more coherent and contextual responses
|
||||
</Card>
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Context Retention" icon="brain">
|
||||
Maintain information across multiple interactions.
|
||||
</Card>
|
||||
<Card title="Information Sharing" icon="share-nodes">
|
||||
Share knowledge between multiple agents.
|
||||
</Card>
|
||||
<Card title="Long-term Storage" icon="database">
|
||||
Store and retrieve information over extended periods.
|
||||
</Card>
|
||||
<Card title="Memory Types" icon="layer-group">
|
||||
Support for different memory types (short-term, long-term).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Multi-Agent Memory
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Code">
|
||||
```python
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
# Create first agent for research
|
||||
researcher = Agent(
|
||||
role="Research Analyst",
|
||||
goal="Research and analyze market trends",
|
||||
backstory="Expert in market research and data analysis",
|
||||
tools=[duckduckgo],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Create second agent for report writing
|
||||
writer = Agent(
|
||||
role="Report Writer",
|
||||
goal="Create comprehensive market reports",
|
||||
backstory="Expert in technical writing and report creation",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Create research task
|
||||
research_task = Task(
|
||||
description="Research current market trends",
|
||||
expected_output="Detailed market analysis",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
# Create writing task
|
||||
report_task = Task(
|
||||
description="Create a market report based on research",
|
||||
expected_output="Comprehensive market report",
|
||||
agent=writer
|
||||
)
|
||||
|
||||
# Create and start the agents with memory
|
||||
agents = PraisonAIAgents(
|
||||
agents=[researcher, writer],
|
||||
tasks=[research_task, report_task],
|
||||
memory=True,
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="No Code">
|
||||
```yaml
|
||||
framework: praisonai
|
||||
process: sequential
|
||||
memory: true
|
||||
roles:
|
||||
researcher:
|
||||
backstory: Expert in market research and data analysis.
|
||||
goal: Research and analyze market trends
|
||||
role: Research Analyst
|
||||
tools:
|
||||
- duckduckgo
|
||||
tasks:
|
||||
research_task:
|
||||
description: Research current market trends.
|
||||
expected_output: Detailed market analysis.
|
||||
|
||||
writer:
|
||||
backstory: Expert in technical writing and report creation.
|
||||
goal: Create comprehensive market reports
|
||||
role: Report Writer
|
||||
tasks:
|
||||
report_task:
|
||||
description: Create a market report based on research.
|
||||
expected_output: Comprehensive market report.
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Configuration Options
|
||||
|
||||
```python
|
||||
# Create an agent with memory configuration
|
||||
agent = Agent(
|
||||
role="Research Analyst",
|
||||
goal="Research and retain information",
|
||||
backstory="Expert in research and analysis",
|
||||
tools=[duckduckgo],
|
||||
verbose=True, # Enable detailed logging
|
||||
llm="gpt-4o" # Language model to use
|
||||
)
|
||||
|
||||
# Create agents with memory options
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
memory=True, # Enable memory
|
||||
memory_config={
|
||||
"provider": "rag", # Use RAG for semantic search
|
||||
"use_embedding": True, # Enable embeddings for better search
|
||||
"short_db": ".praison/short_term.db", # Path for short-term memory
|
||||
"long_db": ".praison/long_term.db", # Path for long-term memory
|
||||
"rag_db_path": ".praison/chroma_db" # Path for vector database
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Memory Issues" icon="triangle-exclamation">
|
||||
If memory isn't working as expected:
|
||||
- Check memory configuration
|
||||
- Enable verbose mode for debugging
|
||||
- Verify memory provider settings
|
||||
</Card>
|
||||
|
||||
<Card title="Context Flow" icon="code">
|
||||
If context isn't being maintained:
|
||||
- Review task dependencies
|
||||
- Check memory configuration
|
||||
- Verify agent communication
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="AutoAgents" icon="robot" href="../features/autoagents">
|
||||
Learn about automatically created and managed AI agents
|
||||
</Card>
|
||||
<Card title="Mini Agents" icon="microchip" href="../features/mini">
|
||||
Explore lightweight, focused AI agents
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
For optimal results, configure memory settings based on your specific use case requirements and expected interaction patterns.
|
||||
</Note>
|
||||
|
||||
### Memory Configuration Options
|
||||
|
||||
The memory system in PraisonAI supports various configuration options to customize how agents store and retrieve information:
|
||||
|
||||
```python
|
||||
memory_config = {
|
||||
# Memory Provider
|
||||
"provider": "rag", # Options: "rag", "mem0", "none"
|
||||
"use_embedding": True, # Enable semantic search with embeddings
|
||||
|
||||
# Storage Paths
|
||||
"short_db": ".praison/short_term.db", # Short-term memory SQLite DB
|
||||
"long_db": ".praison/long_term.db", # Long-term memory SQLite DB
|
||||
"rag_db_path": ".praison/chroma_db", # Vector database path
|
||||
|
||||
# Memory Settings
|
||||
"ttl": 3600, # Time to live for memory items (in seconds)
|
||||
|
||||
# Optional Mem0 Config (if using mem0 provider)
|
||||
"config": {
|
||||
"api_key": "...", # Mem0 API key
|
||||
"org_id": "...", # Organization ID
|
||||
"project_id": "..." # Project ID
|
||||
}
|
||||
}
|
||||
|
||||
# Create agents with memory configuration
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
memory=True,
|
||||
memory_config=memory_config
|
||||
)
|
||||
```
|
||||
|
||||
### Memory Types
|
||||
|
||||
PraisonAI's memory system includes several types of memory:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Short-term Memory" icon="bolt">
|
||||
- Temporary storage for current context
|
||||
- Automatically cleared between sessions
|
||||
- Fast access for immediate task context
|
||||
</Card>
|
||||
<Card title="Long-term Memory" icon="database">
|
||||
- Persistent storage for important information
|
||||
- Semantic search capabilities with RAG
|
||||
- Quality-based storage decisions
|
||||
</Card>
|
||||
<Card title="Entity Memory" icon="diagram-project">
|
||||
- Store information about specific entities
|
||||
- Track relationships and attributes
|
||||
- Specialized search for entity information
|
||||
</Card>
|
||||
<Card title="User Memory" icon="user">
|
||||
- Store user-specific information
|
||||
- Personalized context for each user
|
||||
- Persistent across sessions
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Memory Quality Control
|
||||
|
||||
PraisonAI includes built-in quality control for memory storage:
|
||||
|
||||
```python
|
||||
# Example of storing with quality metrics
|
||||
agents.memory.store_long_term(
|
||||
text="Important information to remember",
|
||||
metadata={
|
||||
"task_id": "task_123",
|
||||
"agent": "research_agent"
|
||||
},
|
||||
completeness=0.9, # How complete is the information
|
||||
relevance=0.85, # How relevant to the task
|
||||
clarity=0.95, # How clear and well-structured
|
||||
accuracy=0.9, # How accurate is the information
|
||||
weights={ # Custom weights for quality score
|
||||
"completeness": 0.3,
|
||||
"relevance": 0.3,
|
||||
"clarity": 0.2,
|
||||
"accuracy": 0.2
|
||||
}
|
||||
)
|
||||
|
||||
# Search with quality filter
|
||||
results = agents.memory.search_long_term(
|
||||
query="search query",
|
||||
min_quality=0.8, # Only return high-quality matches
|
||||
limit=5 # Maximum number of results
|
||||
)
|
||||
```
|
||||
Loaded 100 of 224 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user