mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
024eaed456 | ||
|
|
4fd3a572cf | ||
|
|
baaf8b6256 | ||
|
|
fe4550d2bc | ||
|
|
db09a94425 | ||
|
|
2db36dc932 | ||
|
|
8c0e4277a4 | ||
|
|
41448617d5 | ||
|
|
49ea23c676 | ||
|
|
bb0dc63680 | ||
|
|
7774f2c565 | ||
|
|
5533fd3476 | ||
|
|
262a90a235 | ||
|
|
60928dcb90 | ||
|
|
c8552e1158 | ||
|
|
c57d7db9bb | ||
|
|
236abc2c83 | ||
|
|
76c5f8fe3f | ||
|
|
82750020d2 | ||
|
|
f49ad9fbc2 | ||
|
|
c086c60fd6 | ||
|
|
4f8f4f2036 | ||
|
|
8e91c20967 | ||
|
|
6f092c1507 | ||
|
|
8f553d3a54 | ||
|
|
2d647e25b5 | ||
|
|
039bb772f5 | ||
|
|
459c2f2b70 | ||
|
|
ee59706357 | ||
|
|
2a51644773 | ||
|
|
cce908fa22 | ||
|
|
acf476f715 | ||
|
|
e5d77de79c | ||
|
|
9ab444b54c | ||
|
|
e13f543c6d | ||
|
|
4f235d358c | ||
|
|
b7847feb1e | ||
|
|
7ac730462c | ||
|
|
9cbc8d082d | ||
|
|
7fb0aed7ac | ||
|
|
206f0d01e7 | ||
|
|
e19a310314 | ||
|
|
1ea572689e | ||
|
|
96df649b05 | ||
|
|
5aed75837a | ||
|
|
9c8f68a05f | ||
|
|
9778a69c4d | ||
|
|
73c7994380 | ||
|
|
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 |
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.33 gunicorn markdown
|
||||
RUN pip install flask praisonai==2.0.55 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.
|
||||
@@ -0,0 +1,43 @@
|
||||
FROM mcr.microsoft.com/devcontainers/python:3.11
|
||||
|
||||
# Prevent apt from showing prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create a non-root user
|
||||
ARG USERNAME=vscode
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=$USER_UID
|
||||
|
||||
# Create the user and group only if they don't exist
|
||||
RUN if ! getent group $USER_GID > /dev/null 2>&1; then \
|
||||
groupadd --gid $USER_GID $USERNAME; \
|
||||
fi && \
|
||||
if ! getent passwd $USER_UID > /dev/null 2>&1; then \
|
||||
useradd --uid $USER_UID --gid $USER_GID -m $USERNAME; \
|
||||
fi && \
|
||||
apt-get update && \
|
||||
apt-get install -y sudo && \
|
||||
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \
|
||||
chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
|
||||
# Set Python environment variables
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Install Python dependencies
|
||||
COPY requirements.txt /tmp/pip-tmp/
|
||||
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
|
||||
&& rm -rf /tmp/pip-tmp
|
||||
|
||||
# Switch to non-root user
|
||||
USER $USERNAME
|
||||
|
||||
# Set the default shell to bash
|
||||
ENV SHELL=/bin/bash
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "PraisonAI Development",
|
||||
"dockerFile": "Dockerfile",
|
||||
"context": "..",
|
||||
"runArgs": [
|
||||
"--env-file",
|
||||
".env",
|
||||
"--network=host"
|
||||
],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-python.black-formatter",
|
||||
"ms-toolsai.jupyter",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
],
|
||||
"settings": {
|
||||
"python.defaultInterpreterPath": "/usr/local/bin/python",
|
||||
"python.linting.enabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [88],
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"remoteUser": "vscode",
|
||||
"updateRemoteUserUID": false,
|
||||
"forwardPorts": [],
|
||||
"appPort": ["56040:56040"],
|
||||
"overrideCommand": false,
|
||||
"init": true
|
||||
}
|
||||
+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,14 @@
|
||||
from praisonaiagents.knowledge import Chunking
|
||||
|
||||
# No heavy imports or initialization happens here
|
||||
chunking = Chunking(chunker_type='token')
|
||||
|
||||
# Chunker is only initialized when first used
|
||||
chunks = chunking.chunk("Your text here")
|
||||
|
||||
# Embedding model is only loaded if using semantic chunking
|
||||
semantic_chunking = Chunking(chunker_type='recursive')
|
||||
# Embedding model and chunker are loaded here on first use
|
||||
semantic_chunks = semantic_chunking.chunk("Your text here")
|
||||
|
||||
print(semantic_chunks)
|
||||
@@ -0,0 +1,97 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
from gitingest import ingest
|
||||
|
||||
class CodeMetrics(BaseModel):
|
||||
category: str
|
||||
score: int
|
||||
findings: List[str]
|
||||
|
||||
class CodeAnalysisReport(BaseModel):
|
||||
overall_quality: int
|
||||
code_metrics: List[CodeMetrics]
|
||||
architecture_score: int
|
||||
maintainability_score: int
|
||||
performance_score: int
|
||||
security_score: int
|
||||
test_coverage: int
|
||||
key_strengths: List[str]
|
||||
improvement_areas: List[str]
|
||||
tech_stack: List[str]
|
||||
recommendations: List[str]
|
||||
complexity_metrics: Dict[str, int]
|
||||
best_practices: List[Dict[str, str]]
|
||||
potential_risks: List[str]
|
||||
documentation_quality: int
|
||||
|
||||
code_analyzer = Agent(
|
||||
role="Code Analysis Expert",
|
||||
goal="Provide comprehensive code evaluation and recommendations",
|
||||
backstory="""Expert code analyst specializing in architecture review,
|
||||
best practices, and technical debt assessment.""",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
code_analysis_task = Task(
|
||||
description="""Analyze code repository and provide structured evaluation:
|
||||
|
||||
1. Overall Quality (0-100)
|
||||
2. Core Metrics Analysis:
|
||||
- Architecture and Design
|
||||
- Code Maintainability
|
||||
- Performance Optimization
|
||||
- Security Practices
|
||||
- Test Coverage
|
||||
3. Technical Assessment:
|
||||
- Technology Stack Review
|
||||
- Code Complexity Analysis
|
||||
- Best Practices Adherence
|
||||
- Risk Assessment
|
||||
4. Recommendations:
|
||||
- Key Improvements
|
||||
- Architecture Suggestions
|
||||
- Security Enhancements""",
|
||||
expected_output="Detailed code analysis report with metrics and recommendations",
|
||||
agent=code_analyzer,
|
||||
output_pydantic=CodeAnalysisReport
|
||||
)
|
||||
|
||||
def analyze_code(code_source: str) -> CodeAnalysisReport:
|
||||
"""
|
||||
Analyze code from directory path or GitHub URL
|
||||
"""
|
||||
# Ingest code content
|
||||
summary, tree, content = ingest(code_source)
|
||||
|
||||
# Concatenate context into structured format
|
||||
context_text = f"""
|
||||
CODE REPOSITORY ANALYSIS
|
||||
=======================
|
||||
|
||||
SUMMARY
|
||||
-------
|
||||
{summary}
|
||||
|
||||
REPOSITORY STRUCTURE
|
||||
-------------------
|
||||
{tree}
|
||||
|
||||
SOURCE CODE
|
||||
-----------
|
||||
{content}
|
||||
"""
|
||||
|
||||
# Initialize and run analysis
|
||||
agents = PraisonAIAgents(
|
||||
agents=[code_analyzer],
|
||||
tasks=[code_analysis_task]
|
||||
)
|
||||
|
||||
return agents.start(context_text)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
code_source = "https://github.com/openai/openai-python/tree/main/src/openai/cli/_api/chat" # GitHub URL or local directory
|
||||
result = analyze_code(code_source)
|
||||
print(result)
|
||||
@@ -0,0 +1,217 @@
|
||||
import streamlit as st
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict, Any
|
||||
from gitingest import ingest
|
||||
|
||||
# Import the same classes and code from code-analysis-agents.py
|
||||
class CodeMetrics(BaseModel):
|
||||
category: str
|
||||
score: int
|
||||
findings: List[str]
|
||||
|
||||
class CodeAnalysisReport(BaseModel):
|
||||
overall_quality: int
|
||||
code_metrics: List[CodeMetrics]
|
||||
architecture_score: int
|
||||
maintainability_score: int
|
||||
performance_score: int
|
||||
security_score: int
|
||||
test_coverage: int
|
||||
key_strengths: List[str]
|
||||
improvement_areas: List[str]
|
||||
tech_stack: List[str]
|
||||
recommendations: List[str]
|
||||
complexity_metrics: Dict[str, int]
|
||||
best_practices: List[Dict[str, str]]
|
||||
potential_risks: List[str]
|
||||
documentation_quality: int
|
||||
|
||||
def analyze_code(code_source: str) -> CodeAnalysisReport:
|
||||
"""
|
||||
Analyze code from directory path or GitHub URL
|
||||
"""
|
||||
# Create code analyzer agent
|
||||
code_analyzer = Agent(
|
||||
role="Code Analysis Expert",
|
||||
goal="Provide comprehensive code evaluation and recommendations",
|
||||
backstory="""Expert code analyst specializing in architecture review,
|
||||
best practices, and technical debt assessment.""",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Create analysis task
|
||||
code_analysis_task = Task(
|
||||
description="""Analyze code repository and provide structured evaluation:
|
||||
|
||||
1. Overall Quality (0-100)
|
||||
2. Core Metrics Analysis:
|
||||
- Architecture and Design
|
||||
- Code Maintainability
|
||||
- Performance Optimization
|
||||
- Security Practices
|
||||
- Test Coverage
|
||||
3. Technical Assessment:
|
||||
- Technology Stack Review
|
||||
- Code Complexity Analysis
|
||||
- Best Practices Adherence
|
||||
- Risk Assessment
|
||||
4. Recommendations:
|
||||
- Key Improvements
|
||||
- Architecture Suggestions
|
||||
- Security Enhancements""",
|
||||
expected_output="Detailed code analysis report with metrics and recommendations",
|
||||
agent=code_analyzer,
|
||||
output_pydantic=CodeAnalysisReport
|
||||
)
|
||||
|
||||
# Ingest code content
|
||||
summary, tree, content = ingest(code_source)
|
||||
|
||||
# Concatenate context into structured format
|
||||
context_text = f"""
|
||||
CODE REPOSITORY ANALYSIS
|
||||
=======================
|
||||
|
||||
SUMMARY
|
||||
-------
|
||||
{summary}
|
||||
|
||||
REPOSITORY STRUCTURE
|
||||
-------------------
|
||||
{tree}
|
||||
|
||||
SOURCE CODE
|
||||
-----------
|
||||
{content}
|
||||
"""
|
||||
|
||||
# Initialize and run analysis
|
||||
agents = PraisonAIAgents(
|
||||
agents=[code_analyzer],
|
||||
tasks=[code_analysis_task]
|
||||
)
|
||||
|
||||
result = agents.start(context_text)
|
||||
|
||||
# Extract the Pydantic model from the result
|
||||
if isinstance(result, dict) and 'task_results' in result:
|
||||
# Get the first task result's pydantic output
|
||||
analysis_result = result['task_results'][0].pydantic
|
||||
if isinstance(analysis_result, CodeAnalysisReport):
|
||||
return analysis_result
|
||||
|
||||
# If we can't get the Pydantic model, create one from the raw data
|
||||
return CodeAnalysisReport(**result)
|
||||
|
||||
def display_code_metrics(metrics, cols):
|
||||
"""Display code metrics in columns with color-coded scores"""
|
||||
for i, metric in enumerate(metrics):
|
||||
score = metric.score
|
||||
color = "red" if score < 60 else "orange" if score < 75 else "green"
|
||||
|
||||
# Use modulo to alternate between columns
|
||||
with cols[i % len(cols)]:
|
||||
st.markdown(f"### {metric.category}")
|
||||
st.markdown(f"**Score:** :{color}[{score}%]")
|
||||
for finding in metric.findings:
|
||||
st.markdown(f"• {finding}")
|
||||
|
||||
def display_section(title: str, items: list, icon: str = "•"):
|
||||
"""Display a section with items in a consistent format"""
|
||||
st.markdown(f"### {title}")
|
||||
for item in items:
|
||||
if isinstance(item, dict):
|
||||
for key, value in item.items():
|
||||
st.markdown(f"{icon} **{key}**: {value}")
|
||||
else:
|
||||
st.markdown(f"{icon} {item}")
|
||||
|
||||
def main():
|
||||
st.set_page_config(
|
||||
page_title="Code Analysis Agent",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded"
|
||||
)
|
||||
|
||||
st.title("Code Analysis Agent")
|
||||
|
||||
with st.sidebar:
|
||||
st.header("Input")
|
||||
code_source = st.text_input(
|
||||
"GitHub URL or Local Path",
|
||||
placeholder="https://github.com/username/repo or /path/to/directory"
|
||||
)
|
||||
analyze_button = st.button("Analyze Code", type="primary")
|
||||
|
||||
if analyze_button and code_source:
|
||||
try:
|
||||
with st.spinner("Analyzing code..."):
|
||||
result = analyze_code(code_source)
|
||||
|
||||
# Overall Metrics
|
||||
st.header("Overall Metrics")
|
||||
cols = st.columns(6)
|
||||
metrics = {
|
||||
"Overall Quality": result.overall_quality,
|
||||
"Architecture": result.architecture_score,
|
||||
"Maintainability": result.maintainability_score,
|
||||
"Performance": result.performance_score,
|
||||
"Security": result.security_score,
|
||||
"Test Coverage": result.test_coverage
|
||||
}
|
||||
|
||||
for (metric, value), col in zip(metrics.items(), cols):
|
||||
color = "red" if value < 60 else "orange" if value < 75 else "green"
|
||||
col.metric(metric, f"{value}%")
|
||||
col.markdown(f":{color}[{'●' * (value // 20)}]")
|
||||
|
||||
# Detailed Analysis
|
||||
st.header("Detailed Analysis")
|
||||
metric_cols = st.columns(2)
|
||||
display_code_metrics(result.code_metrics, metric_cols)
|
||||
|
||||
# Technology Stack
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
st.header("Technology Stack")
|
||||
for tech in result.tech_stack:
|
||||
st.markdown(f"🔧 {tech}")
|
||||
|
||||
with col2:
|
||||
st.header("Complexity Metrics")
|
||||
for metric, value in result.complexity_metrics.items():
|
||||
st.metric(metric.replace('_', ' ').title(), value)
|
||||
|
||||
# Key Findings
|
||||
st.header("Key Findings")
|
||||
cols = st.columns(2)
|
||||
|
||||
with cols[0]:
|
||||
display_section("✅ Strengths", result.key_strengths)
|
||||
display_section("🔄 Best Practices", result.best_practices)
|
||||
|
||||
with cols[1]:
|
||||
display_section("⚠️ Areas for Improvement", result.improvement_areas)
|
||||
display_section("❗ Potential Risks", result.potential_risks)
|
||||
|
||||
# Recommendations
|
||||
st.header("Recommendations")
|
||||
for i, rec in enumerate(result.recommendations, 1):
|
||||
st.markdown(f"**{i}.** {rec}")
|
||||
|
||||
# Documentation Quality
|
||||
st.header("Documentation")
|
||||
doc_score = result.documentation_quality
|
||||
color = "red" if doc_score < 60 else "orange" if doc_score < 75 else "green"
|
||||
st.markdown(f"Documentation Quality: :{color}[{doc_score}%]")
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Error analyzing code: {str(e)}")
|
||||
st.exception(e)
|
||||
|
||||
elif analyze_button:
|
||||
st.warning("Please enter a GitHub URL or local path")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -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,98 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
|
||||
class SkillMetrics(BaseModel):
|
||||
name: str
|
||||
score: int
|
||||
comments: str
|
||||
|
||||
class CVAnalysisReport(BaseModel):
|
||||
overall_score: int
|
||||
skill_metrics: List[SkillMetrics]
|
||||
experience_score: int
|
||||
education_score: int
|
||||
presentation_score: int
|
||||
achievement_impact_score: int
|
||||
market_readiness: int
|
||||
key_strengths: List[str]
|
||||
areas_for_improvement: List[str]
|
||||
market_fit: str
|
||||
recommendations: List[str]
|
||||
technical_skills: List[Dict[str, int]]
|
||||
soft_skills: List[Dict[str, int]]
|
||||
skill_gaps: List[str]
|
||||
key_achievements: List[str]
|
||||
impact_metrics: List[Dict[str, str]]
|
||||
areas_of_expertise: List[str]
|
||||
|
||||
# Create single CV analysis agent
|
||||
cv_analyzer = Agent(
|
||||
role="CV Analysis Expert",
|
||||
goal="Provide comprehensive CV evaluation and recommendations",
|
||||
backstory="""You are an expert CV analyst with deep knowledge of industry requirements,
|
||||
market trends, and talent assessment. You excel at evaluating skills, experience,
|
||||
and providing actionable recommendations.""",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Single comprehensive analysis task
|
||||
cv_analysis_task = Task(
|
||||
description="""Perform a detailed CV analysis with scoring metrics and provide output in the following format:
|
||||
|
||||
1. Overall Score (0-100):
|
||||
- Calculate based on weighted average of all metrics
|
||||
|
||||
2. Individual Skill Metrics:
|
||||
- For each major skill found in CV:
|
||||
* Name of skill
|
||||
* Score (0-100)
|
||||
* Specific feedback/comments
|
||||
|
||||
3. Core Metrics (0-100 each):
|
||||
- Experience Score: Based on relevance and years
|
||||
- Education Score: Based on relevance and level
|
||||
- Presentation Score: CV format, clarity, structure
|
||||
- Achievement Impact Score: Measurable results
|
||||
- Market Readiness: Current market fit
|
||||
|
||||
4. Technical Skills:
|
||||
- List each skill with proficiency score (0-100)
|
||||
- Example: {"Python": 85, "AWS": 70}
|
||||
|
||||
5. Soft Skills:
|
||||
- List each skill with proficiency score (0-100)
|
||||
- Example: {"Communication": 90, "Leadership": 75}
|
||||
|
||||
6. Detailed Analysis:
|
||||
- Key Strengths: Top 3-5 standout points
|
||||
- Areas for Improvement: 3-5 specific points
|
||||
- Market Fit: Current industry alignment
|
||||
- Skill Gaps: Missing critical skills for target role
|
||||
- Key Achievements: Quantifiable accomplishments
|
||||
- Impact Metrics: {"metric": "value"}
|
||||
- Areas of Expertise: Primary domains of expertise
|
||||
|
||||
7. Recommendations:
|
||||
- 3-5 actionable steps for improvement
|
||||
|
||||
Provide clear justification for all scores and ensure feedback is specific and actionable.""",
|
||||
expected_output="Structured CV analysis report with detailed metrics and scoring justification",
|
||||
agent=cv_analyzer,
|
||||
output_pydantic=CVAnalysisReport
|
||||
)
|
||||
|
||||
# Create and run the agent
|
||||
agents = PraisonAIAgents(
|
||||
agents=[cv_analyzer],
|
||||
tasks=[cv_analysis_task]
|
||||
)
|
||||
|
||||
# Read and pass CV content through start()
|
||||
with open('cv.txt', 'r') as file:
|
||||
cv_content = file.read()
|
||||
|
||||
# Start the analysis by passing CV content
|
||||
result = agents.start(cv_content)
|
||||
|
||||
print(result)
|
||||
@@ -0,0 +1,202 @@
|
||||
import streamlit as st
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
import json
|
||||
import asyncio
|
||||
import nest_asyncio
|
||||
from functools import partial
|
||||
|
||||
# Enable nested event loops
|
||||
nest_asyncio.apply()
|
||||
|
||||
class SkillMetrics(BaseModel):
|
||||
name: str
|
||||
score: int
|
||||
comments: str
|
||||
|
||||
class CVAnalysisReport(BaseModel):
|
||||
overall_score: int
|
||||
skill_metrics: List[SkillMetrics]
|
||||
experience_score: int
|
||||
education_score: int
|
||||
presentation_score: int
|
||||
achievement_impact_score: int
|
||||
market_readiness: int
|
||||
key_strengths: List[str]
|
||||
areas_for_improvement: List[str]
|
||||
market_fit: str
|
||||
recommendations: List[str]
|
||||
technical_skills: List[Dict[str, int]]
|
||||
soft_skills: List[Dict[str, int]]
|
||||
skill_gaps: List[str]
|
||||
key_achievements: List[str]
|
||||
impact_metrics: List[Dict[str, str]]
|
||||
areas_of_expertise: List[str]
|
||||
|
||||
async def analyze_cv_async(cv_content):
|
||||
cv_analyzer = Agent(
|
||||
role="CV Analysis Expert",
|
||||
goal="Provide comprehensive CV evaluation and recommendations",
|
||||
backstory="""You are an expert CV analyst with deep knowledge of industry requirements,
|
||||
market trends, and talent assessment. You excel at evaluating skills, experience,
|
||||
and providing actionable recommendations.""",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
cv_analysis_task = Task(
|
||||
description="""Perform a detailed CV analysis with scoring metrics and provide output in the following format:
|
||||
1. Overall Score (0-100):
|
||||
- Calculate based on weighted average of all metrics
|
||||
|
||||
2. Individual Skill Metrics:
|
||||
- For each major skill found in CV:
|
||||
* Name of skill
|
||||
* Score (0-100)
|
||||
* Specific feedback/comments
|
||||
|
||||
3. Core Metrics (0-100 each):
|
||||
- Experience Score: Based on relevance and years
|
||||
- Education Score: Based on relevance and level
|
||||
- Presentation Score: CV format, clarity, structure
|
||||
- Achievement Impact Score: Measurable results
|
||||
- Market Readiness: Current market fit
|
||||
|
||||
4. Technical Skills:
|
||||
- List each skill with proficiency score (0-100)
|
||||
|
||||
5. Soft Skills:
|
||||
- List each skill with proficiency score (0-100)
|
||||
|
||||
6. Detailed Analysis sections
|
||||
|
||||
7. Recommendations:
|
||||
- 3-5 actionable steps for improvement""",
|
||||
expected_output="Structured CV analysis report with detailed metrics and scoring justification",
|
||||
agent=cv_analyzer,
|
||||
output_pydantic=CVAnalysisReport
|
||||
)
|
||||
|
||||
agents = PraisonAIAgents(
|
||||
agents=[cv_analyzer],
|
||||
tasks=[cv_analysis_task]
|
||||
)
|
||||
|
||||
try:
|
||||
result = await agents.astart(cv_content)
|
||||
return result['task_results'][0].pydantic
|
||||
except Exception as e:
|
||||
print(f"Error during analysis: {str(e)}")
|
||||
raise e
|
||||
|
||||
def analyze_cv(cv_content):
|
||||
async def run_analysis():
|
||||
return await analyze_cv_async(cv_content)
|
||||
|
||||
try:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
result = loop.run_until_complete(run_analysis())
|
||||
return result
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
loop.close()
|
||||
|
||||
def main():
|
||||
st.set_page_config(page_title="CV Analyzer", layout="wide")
|
||||
st.title("Professional CV Analyzer")
|
||||
|
||||
# File upload
|
||||
cv_file = st.file_uploader("Upload your CV (TXT format)", type=['txt'])
|
||||
|
||||
if cv_file:
|
||||
cv_content = cv_file.read().decode()
|
||||
|
||||
with st.spinner('Analyzing your CV...'):
|
||||
try:
|
||||
analysis = analyze_cv(cv_content)
|
||||
|
||||
# Display Overall Score
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
st.metric("Overall Score", f"{analysis.overall_score}/100")
|
||||
with col2:
|
||||
st.metric("Market Readiness", f"{analysis.market_readiness}/100")
|
||||
with col3:
|
||||
st.metric("Experience Score", f"{analysis.experience_score}/100")
|
||||
|
||||
# Core Metrics
|
||||
st.subheader("Core Metrics")
|
||||
metrics_cols = st.columns(4)
|
||||
with metrics_cols[0]:
|
||||
st.metric("Education", f"{analysis.education_score}/100")
|
||||
with metrics_cols[1]:
|
||||
st.metric("Presentation", f"{analysis.presentation_score}/100")
|
||||
with metrics_cols[2]:
|
||||
st.metric("Achievement Impact", f"{analysis.achievement_impact_score}/100")
|
||||
with metrics_cols[3]:
|
||||
st.metric("Market Readiness", f"{analysis.market_readiness}/100")
|
||||
|
||||
# Skill Metrics
|
||||
st.subheader("Detailed Skill Analysis")
|
||||
for skill in analysis.skill_metrics:
|
||||
with st.expander(f"{skill.name} - Score: {skill.score}/100"):
|
||||
st.write(skill.comments)
|
||||
|
||||
# Technical and Soft Skills
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
st.subheader("Technical Skills")
|
||||
for skill in analysis.technical_skills:
|
||||
for skill_name, score in skill.items():
|
||||
st.progress(score/100)
|
||||
st.caption(f"{skill_name}: {score}/100")
|
||||
|
||||
with col2:
|
||||
st.subheader("Soft Skills")
|
||||
for skill in analysis.soft_skills:
|
||||
for skill_name, score in skill.items():
|
||||
st.progress(score/100)
|
||||
st.caption(f"{skill_name}: {score}/100")
|
||||
|
||||
# Key Information
|
||||
st.subheader("Key Analysis")
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
st.write("🎯 Key Strengths")
|
||||
for strength in analysis.key_strengths:
|
||||
st.write(f"• {strength}")
|
||||
|
||||
st.write("🎮 Areas of Expertise")
|
||||
for expertise in analysis.areas_of_expertise:
|
||||
st.write(f"• {expertise}")
|
||||
|
||||
with col2:
|
||||
st.write("🔄 Areas for Improvement")
|
||||
for area in analysis.areas_for_improvement:
|
||||
st.write(f"• {area}")
|
||||
|
||||
st.write("⚠️ Skill Gaps")
|
||||
for gap in analysis.skill_gaps:
|
||||
st.write(f"• {gap}")
|
||||
|
||||
# Impact Metrics
|
||||
st.subheader("Impact Metrics")
|
||||
for metric in analysis.impact_metrics:
|
||||
for key, value in metric.items():
|
||||
st.metric(key, value)
|
||||
|
||||
# Recommendations
|
||||
st.subheader("📋 Recommendations")
|
||||
for rec in analysis.recommendations:
|
||||
st.write(f"• {rec}")
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"An error occurred during analysis: {str(e)}")
|
||||
|
||||
else:
|
||||
st.info("Please upload your CV in TXT format to begin the analysis")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,61 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
|
||||
class CVAnalysisReport(BaseModel):
|
||||
overall_score: int
|
||||
key_strengths: List[str]
|
||||
areas_for_improvement: List[str]
|
||||
market_fit: str
|
||||
recommendations: List[str]
|
||||
technical_skills: List[str]
|
||||
soft_skills: List[str]
|
||||
skill_gaps: List[str]
|
||||
key_achievements: List[str]
|
||||
impact_metrics: List[Dict[str, str]]
|
||||
areas_of_expertise: List[str]
|
||||
|
||||
# Create single CV analysis agent
|
||||
cv_analyzer = Agent(
|
||||
role="CV Analysis Expert",
|
||||
goal="Provide comprehensive CV evaluation and recommendations",
|
||||
backstory="""You are an expert CV analyst with deep knowledge of industry requirements,
|
||||
market trends, and talent assessment. You excel at evaluating skills, experience,
|
||||
and providing actionable recommendations.""",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Read CV content first
|
||||
with open('cv.txt', 'r') as file:
|
||||
cv_content = file.read()
|
||||
|
||||
cv_key_strengths = Task(
|
||||
description="List the key strengths to look",
|
||||
expected_output="List of key strengths",
|
||||
agent=cv_analyzer,
|
||||
)
|
||||
|
||||
# Single comprehensive analysis task with CV content in context
|
||||
cv_analysis_task = Task(
|
||||
description="""Analyze the CV comprehensively including:
|
||||
1. Technical and soft skills assessment
|
||||
2. Experience and achievements evaluation
|
||||
3. Overall CV evaluation with recommendations
|
||||
Provide detailed insights and actionable recommendations.""",
|
||||
expected_output="Complete CV analysis report",
|
||||
agent=cv_analyzer,
|
||||
output_pydantic=CVAnalysisReport,
|
||||
context=[cv_content, cv_key_strengths] # Pass CV content directly in task context
|
||||
)
|
||||
|
||||
# Create and run the agent
|
||||
agents = PraisonAIAgents(
|
||||
agents=[cv_analyzer],
|
||||
tasks=[cv_key_strengths,cv_analysis_task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
# Start the analysis - no need to pass content again since it's in task context
|
||||
result = agents.start()
|
||||
|
||||
print(result)
|
||||
@@ -0,0 +1,27 @@
|
||||
# example tools.py
|
||||
from duckduckgo_search import DDGS
|
||||
from praisonai_tools import BaseTool
|
||||
|
||||
class InternetSearchTool(BaseTool):
|
||||
name: str = "InternetSearchTool"
|
||||
description: str = "Search Internet for relevant information based on a query or latest news"
|
||||
|
||||
def _run(self, query: str):
|
||||
ddgs = DDGS()
|
||||
results = ddgs.text(keywords=query, region='wt-wt', safesearch='moderate', max_results=5)
|
||||
return results
|
||||
|
||||
# Example agent
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents import PraisonAIAgents
|
||||
agent = Agent(
|
||||
name="Internet Search Agent",
|
||||
tools=[InternetSearchTool],
|
||||
instructions="""
|
||||
You are an agent that can search the internet for relevant information based on a query or latest news.
|
||||
"""
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
result = PraisonAIAgents(agents=[agent], verbose=10).start()
|
||||
print(result)
|
||||
@@ -0,0 +1,57 @@
|
||||
Name: Alex Johnson
|
||||
Location: London, UK
|
||||
|
||||
Professional Summary
|
||||
Dynamic and results-oriented leader with a proven track record of driving innovation, scaling operations, and achieving transformational growth. Over 15 years of experience in engineering, strategic management, and executive leadership in the automotive and renewable energy sectors. Passionate about sustainable technologies and visionary leadership.
|
||||
|
||||
Professional Experience
|
||||
Chief Operating Officer (COO)
|
||||
NextGen Mobility Ltd. | Palo Alto, CA | 2020 – Present
|
||||
|
||||
Spearheaded global operations for an electric vehicle company, scaling production by 200% in three years.
|
||||
Introduced automation-driven supply chain strategies, reducing costs by 15%.
|
||||
Collaborated with R&D to launch three new EV models, driving 25% revenue growth annually.
|
||||
Vice President of Engineering
|
||||
ElectroDrive Systems | San Francisco, CA | 2015 – 2020
|
||||
|
||||
Led a team of 500+ engineers in developing next-generation EV batteries, achieving a 30% increase in energy efficiency.
|
||||
Secured $50M in government funding for sustainable energy innovation.
|
||||
Drove cross-functional collaboration, aligning product development with market needs.
|
||||
Director of Renewable Energy Initiatives
|
||||
GreenTech Innovations | London, UK | 2011 – 2015
|
||||
|
||||
Oversaw £100M projects in solar and wind energy deployment across Europe.
|
||||
Established partnerships with policymakers to influence renewable energy legislation.
|
||||
Pioneered a smart energy grid system, enhancing energy distribution efficiency by 40%.
|
||||
Senior Project Manager
|
||||
Innovate Auto Ltd. | Munich, Germany | 2008 – 2011
|
||||
|
||||
Managed R&D projects for autonomous driving technologies.
|
||||
Delivered projects 20% under budget through strategic resource allocation.
|
||||
Filed 10+ patents in automotive AI and renewable integration.
|
||||
Mechanical Engineer
|
||||
AutoFusion Technologies | Berlin, Germany | 2005 – 2008
|
||||
|
||||
Designed core systems for EV powertrains, optimising energy consumption by 15%.
|
||||
Collaborated with suppliers to improve material efficiency, cutting costs by £5M annually.
|
||||
Education
|
||||
MBA in Strategic Management
|
||||
Harvard Business School | Cambridge, MA | 2015
|
||||
|
||||
MSc in Renewable Energy Engineering
|
||||
Imperial College London | London, UK | 2007
|
||||
|
||||
BSc in Mechanical Engineering
|
||||
Technical University of Munich | Munich, Germany | 2005
|
||||
|
||||
Skills
|
||||
Strategic Leadership & Visionary Thinking
|
||||
Electric Vehicle Engineering
|
||||
Renewable Energy Systems
|
||||
Global Operations Management
|
||||
Policy Advocacy & Government Relations
|
||||
Supply Chain Optimisation
|
||||
Achievements
|
||||
Featured in Forbes 30 Under 40 for innovations in EV technology.
|
||||
Author of The Future of Sustainable Mobility.
|
||||
Keynote speaker at World Energy Summit, 2023.
|
||||
@@ -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,39 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
# Define the configuration for the Knowledge instance
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": ".praison"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create an agent
|
||||
rag_agent = Agent(
|
||||
name="RAG Agent",
|
||||
role="Information Specialist",
|
||||
goal="Retrieve knowledge efficiently",
|
||||
llm="deepseek-r1"
|
||||
)
|
||||
|
||||
# Define a task for the agent
|
||||
rag_task = Task(
|
||||
name="RAG Task",
|
||||
description="What is KAG?",
|
||||
expected_output="Answer to the question",
|
||||
agent=rag_agent,
|
||||
context=[config] # Vector Database provided as context
|
||||
)
|
||||
|
||||
# Build Agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[rag_agent],
|
||||
tasks=[rag_task],
|
||||
user_id="user1"
|
||||
)
|
||||
|
||||
# Start Agents
|
||||
agents.start()
|
||||
@@ -0,0 +1,3 @@
|
||||
I am Mervin Praison, a software engineer and entrepreneur.
|
||||
|
||||
I am the founder of Praison AI, a company that is building AI agents to help people with their daily tasks.
|
||||
@@ -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,34 @@
|
||||
import gradio as gr
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
def research(query):
|
||||
agent = Agent(instructions="You are a Research Agent", tools=[duckduckgo])
|
||||
result = agent.start(query)
|
||||
# Format the result with enhanced markdown
|
||||
formatted_result = f"""
|
||||
{result}
|
||||
----
|
||||
*Generated by PraisonAI Research Assistant*
|
||||
"""
|
||||
return formatted_result
|
||||
|
||||
# Create a simple Gradio interface
|
||||
demo = gr.Interface(
|
||||
fn=research,
|
||||
inputs=gr.Textbox(
|
||||
label="Research Query",
|
||||
placeholder="Enter your research topic...",
|
||||
lines=2
|
||||
),
|
||||
outputs=gr.Markdown(
|
||||
show_copy_button=True,
|
||||
height=500,
|
||||
container=True
|
||||
),
|
||||
title="AI Research Assistant",
|
||||
description="Enter your research query below to get started!",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
||||
Whitespace-only changes.
@@ -0,0 +1,159 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
import os
|
||||
import json
|
||||
|
||||
class ProjectEvaluation(BaseModel):
|
||||
innovation_score: int # 0-100
|
||||
technical_complexity: int # 0-100
|
||||
presentation_quality: int # 0-100
|
||||
user_experience: int # 0-100
|
||||
completeness: int # 0-100
|
||||
overall_score: int # 0-100
|
||||
key_strengths: List[str]
|
||||
areas_for_improvement: List[str]
|
||||
notable_features: List[str]
|
||||
technical_highlights: List[str]
|
||||
recommendations: List[str]
|
||||
market_potential: str
|
||||
scalability_assessment: str
|
||||
|
||||
# Create Vision Analysis Agent
|
||||
hackathon_judge = Agent(
|
||||
name="HackathonJudge",
|
||||
role="Technical Project Evaluator",
|
||||
goal="Evaluate hackathon projects through video demonstrations",
|
||||
backstory="""You are an experienced hackathon judge and technical expert.
|
||||
You excel at evaluating innovation, technical implementation, and presentation quality.
|
||||
You provide constructive feedback and identify both strengths and areas for improvement.""",
|
||||
llm="gpt-4o-mini", # Using vision-capable model
|
||||
self_reflect=False,
|
||||
knowledge=""
|
||||
)
|
||||
|
||||
def evaluate_project(video_path: str) -> ProjectEvaluation:
|
||||
"""
|
||||
Evaluate a hackathon project based on its video demonstration
|
||||
"""
|
||||
evaluation_task = Task(
|
||||
name="project_evaluation",
|
||||
description="""Analyze this hackathon project video demonstration and provide a comprehensive evaluation:
|
||||
|
||||
1. Score the following aspects (0-100):
|
||||
- Innovation and Creativity
|
||||
- Technical Complexity
|
||||
- Presentation Quality
|
||||
- User Experience
|
||||
- Project Completeness
|
||||
|
||||
2. Identify:
|
||||
- Key strengths and standout features
|
||||
- Areas that could be improved
|
||||
- Notable technical implementations
|
||||
- Market potential and scalability
|
||||
|
||||
3. Provide:
|
||||
- Specific recommendations for improvement
|
||||
- Technical suggestions
|
||||
- Potential future enhancements""",
|
||||
expected_output="Detailed project evaluation with scores and feedback",
|
||||
agent=hackathon_judge,
|
||||
output_pydantic=ProjectEvaluation,
|
||||
images=[video_path] # Video input for multimodal analysis
|
||||
)
|
||||
|
||||
# Initialize and run evaluation
|
||||
agents = PraisonAIAgents(
|
||||
agents=[hackathon_judge],
|
||||
tasks=[evaluation_task],
|
||||
process="sequential",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
response = agents.start()
|
||||
|
||||
try:
|
||||
# If response contains task_results, extract the Pydantic model directly
|
||||
if isinstance(response, dict) and 'task_results' in response:
|
||||
task_output = response['task_results'][0]
|
||||
if hasattr(task_output, 'pydantic'):
|
||||
return task_output.pydantic
|
||||
elif hasattr(task_output, 'raw'):
|
||||
# Extract JSON from raw string if it's wrapped in ```json
|
||||
raw_text = task_output.raw
|
||||
if raw_text.startswith('```json'):
|
||||
raw_text = raw_text.split('\n', 1)[1].rsplit('\n', 1)[0]
|
||||
evaluation_data = json.loads(raw_text)
|
||||
else:
|
||||
evaluation_data = json.loads(task_output) if isinstance(task_output, str) else task_output
|
||||
# If response is a string, try to parse it as JSON
|
||||
elif isinstance(response, str):
|
||||
evaluation_data = json.loads(response)
|
||||
# If response is a dict with task_status
|
||||
elif isinstance(response, dict) and 'task_status' in response:
|
||||
content = response['task_status']
|
||||
if isinstance(content, dict):
|
||||
evaluation_data = content
|
||||
else:
|
||||
evaluation_data = json.loads(content) if isinstance(content, str) else content
|
||||
else:
|
||||
evaluation_data = response
|
||||
|
||||
print(f"Debug - Parsed evaluation_data: {evaluation_data}")
|
||||
|
||||
# Create and return ProjectEvaluation instance
|
||||
return ProjectEvaluation(
|
||||
innovation_score=int(evaluation_data.get('innovation_score', 0)),
|
||||
technical_complexity=int(evaluation_data.get('technical_complexity', 0)),
|
||||
presentation_quality=int(evaluation_data.get('presentation_quality', 0)),
|
||||
user_experience=int(evaluation_data.get('user_experience', 0)),
|
||||
completeness=int(evaluation_data.get('completeness', 0)),
|
||||
overall_score=int(evaluation_data.get('overall_score', 0)),
|
||||
key_strengths=evaluation_data.get('key_strengths', []),
|
||||
areas_for_improvement=evaluation_data.get('areas_for_improvement', []),
|
||||
notable_features=evaluation_data.get('notable_features', []),
|
||||
technical_highlights=evaluation_data.get('technical_highlights', []),
|
||||
recommendations=evaluation_data.get('recommendations', []),
|
||||
market_potential=str(evaluation_data.get('market_potential', '')),
|
||||
scalability_assessment=str(evaluation_data.get('scalability_assessment', ''))
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Debug - Raw response: {response}")
|
||||
print(f"Error processing response: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
video_path = os.path.join(current_dir, "presentation.mp4")
|
||||
result = evaluate_project(video_path) # Now returns ProjectEvaluation directly
|
||||
|
||||
print("\nHackathon Project Evaluation")
|
||||
print("===========================")
|
||||
print(f"\nOverall Score: {result.overall_score}/100")
|
||||
print("\nDetailed Scores:")
|
||||
print(f"Innovation: {result.innovation_score}/100")
|
||||
print(f"Technical Complexity: {result.technical_complexity}/100")
|
||||
print(f"Presentation: {result.presentation_quality}/100")
|
||||
print(f"User Experience: {result.user_experience}/100")
|
||||
print(f"Completeness: {result.completeness}/100")
|
||||
|
||||
print("\nKey Strengths:")
|
||||
for strength in result.key_strengths:
|
||||
print(f"- {strength}")
|
||||
|
||||
print("\nAreas for Improvement:")
|
||||
for area in result.areas_for_improvement:
|
||||
print(f"- {area}")
|
||||
|
||||
print("\nTechnical Highlights:")
|
||||
for highlight in result.technical_highlights:
|
||||
print(f"- {highlight}")
|
||||
|
||||
print("\nRecommendations:")
|
||||
for rec in result.recommendations:
|
||||
print(f"- {rec}")
|
||||
|
||||
print(f"\nMarket Potential: {result.market_potential}")
|
||||
print(f"\nScalability Assessment: {result.scalability_assessment}")
|
||||
@@ -0,0 +1,159 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
import os
|
||||
import json
|
||||
|
||||
class ProjectEvaluation(BaseModel):
|
||||
innovation_score: int # 0-100
|
||||
technical_complexity: int # 0-100
|
||||
presentation_quality: int # 0-100
|
||||
user_experience: int # 0-100
|
||||
completeness: int # 0-100
|
||||
overall_score: int # 0-100
|
||||
key_strengths: List[str]
|
||||
areas_for_improvement: List[str]
|
||||
notable_features: List[str]
|
||||
technical_highlights: List[str]
|
||||
recommendations: List[str]
|
||||
market_potential: str
|
||||
scalability_assessment: str
|
||||
|
||||
# Create Vision Analysis Agent
|
||||
hackathon_judge = Agent(
|
||||
name="HackathonJudge",
|
||||
role="Technical Project Evaluator",
|
||||
goal="Evaluate hackathon projects through video demonstrations",
|
||||
backstory="""You are an experienced hackathon judge and technical expert.
|
||||
You excel at evaluating innovation, technical implementation, and presentation quality.
|
||||
You provide constructive feedback and identify both strengths and areas for improvement.""",
|
||||
llm="gpt-4o-mini", # Using vision-capable model
|
||||
self_reflect=False,
|
||||
knowledge=""
|
||||
)
|
||||
|
||||
def evaluate_project(video_path: str) -> ProjectEvaluation:
|
||||
"""
|
||||
Evaluate a hackathon project based on its video demonstration
|
||||
"""
|
||||
evaluation_task = Task(
|
||||
name="project_evaluation",
|
||||
description="""Analyze this hackathon project video demonstration and provide a comprehensive evaluation:
|
||||
|
||||
1. Score the following aspects (0-100):
|
||||
- Innovation and Creativity
|
||||
- Technical Complexity
|
||||
- Presentation Quality
|
||||
- User Experience
|
||||
- Project Completeness
|
||||
|
||||
2. Identify:
|
||||
- Key strengths and standout features
|
||||
- Areas that could be improved
|
||||
- Notable technical implementations
|
||||
- Market potential and scalability
|
||||
|
||||
3. Provide:
|
||||
- Specific recommendations for improvement
|
||||
- Technical suggestions
|
||||
- Potential future enhancements""",
|
||||
expected_output="Detailed project evaluation with scores and feedback",
|
||||
agent=hackathon_judge,
|
||||
output_pydantic=ProjectEvaluation,
|
||||
images=[video_path] # Video input for multimodal analysis
|
||||
)
|
||||
|
||||
# Initialize and run evaluation
|
||||
agents = PraisonAIAgents(
|
||||
agents=[hackathon_judge],
|
||||
tasks=[evaluation_task],
|
||||
process="sequential",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
response = agents.start()
|
||||
|
||||
try:
|
||||
# If response contains task_results, extract the Pydantic model directly
|
||||
if isinstance(response, dict) and 'task_results' in response:
|
||||
task_output = response['task_results'][0]
|
||||
if hasattr(task_output, 'pydantic'):
|
||||
return task_output.pydantic
|
||||
elif hasattr(task_output, 'raw'):
|
||||
# Extract JSON from raw string if it's wrapped in ```json
|
||||
raw_text = task_output.raw
|
||||
if raw_text.startswith('```json'):
|
||||
raw_text = raw_text.split('\n', 1)[1].rsplit('\n', 1)[0]
|
||||
evaluation_data = json.loads(raw_text)
|
||||
else:
|
||||
evaluation_data = json.loads(task_output) if isinstance(task_output, str) else task_output
|
||||
# If response is a string, try to parse it as JSON
|
||||
elif isinstance(response, str):
|
||||
evaluation_data = json.loads(response)
|
||||
# If response is a dict with task_status
|
||||
elif isinstance(response, dict) and 'task_status' in response:
|
||||
content = response['task_status']
|
||||
if isinstance(content, dict):
|
||||
evaluation_data = content
|
||||
else:
|
||||
evaluation_data = json.loads(content) if isinstance(content, str) else content
|
||||
else:
|
||||
evaluation_data = response
|
||||
|
||||
print(f"Debug - Parsed evaluation_data: {evaluation_data}")
|
||||
|
||||
# Create and return ProjectEvaluation instance
|
||||
return ProjectEvaluation(
|
||||
innovation_score=int(evaluation_data.get('innovation_score', 0)),
|
||||
technical_complexity=int(evaluation_data.get('technical_complexity', 0)),
|
||||
presentation_quality=int(evaluation_data.get('presentation_quality', 0)),
|
||||
user_experience=int(evaluation_data.get('user_experience', 0)),
|
||||
completeness=int(evaluation_data.get('completeness', 0)),
|
||||
overall_score=int(evaluation_data.get('overall_score', 0)),
|
||||
key_strengths=evaluation_data.get('key_strengths', []),
|
||||
areas_for_improvement=evaluation_data.get('areas_for_improvement', []),
|
||||
notable_features=evaluation_data.get('notable_features', []),
|
||||
technical_highlights=evaluation_data.get('technical_highlights', []),
|
||||
recommendations=evaluation_data.get('recommendations', []),
|
||||
market_potential=str(evaluation_data.get('market_potential', '')),
|
||||
scalability_assessment=str(evaluation_data.get('scalability_assessment', ''))
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Debug - Raw response: {response}")
|
||||
print(f"Error processing response: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
video_path = os.path.join(current_dir, "presentation.mp4")
|
||||
result = evaluate_project(video_path) # Now returns ProjectEvaluation directly
|
||||
|
||||
print("\nHackathon Project Evaluation")
|
||||
print("===========================")
|
||||
print(f"\nOverall Score: {result.overall_score}/100")
|
||||
print("\nDetailed Scores:")
|
||||
print(f"Innovation: {result.innovation_score}/100")
|
||||
print(f"Technical Complexity: {result.technical_complexity}/100")
|
||||
print(f"Presentation: {result.presentation_quality}/100")
|
||||
print(f"User Experience: {result.user_experience}/100")
|
||||
print(f"Completeness: {result.completeness}/100")
|
||||
|
||||
print("\nKey Strengths:")
|
||||
for strength in result.key_strengths:
|
||||
print(f"- {strength}")
|
||||
|
||||
print("\nAreas for Improvement:")
|
||||
for area in result.areas_for_improvement:
|
||||
print(f"- {area}")
|
||||
|
||||
print("\nTechnical Highlights:")
|
||||
for highlight in result.technical_highlights:
|
||||
print(f"- {highlight}")
|
||||
|
||||
print("\nRecommendations:")
|
||||
for rec in result.recommendations:
|
||||
print(f"- {rec}")
|
||||
|
||||
print(f"\nMarket Potential: {result.market_potential}")
|
||||
print(f"\nScalability Assessment: {result.scalability_assessment}")
|
||||
@@ -0,0 +1,210 @@
|
||||
import streamlit as st
|
||||
import os
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
import tempfile
|
||||
import json
|
||||
|
||||
class ProjectEvaluation(BaseModel):
|
||||
innovation_score: int # 0-100
|
||||
technical_complexity: int # 0-100
|
||||
presentation_quality: int # 0-100
|
||||
user_experience: int # 0-100
|
||||
completeness: int # 0-100
|
||||
overall_score: int # 0-100
|
||||
key_strengths: List[str]
|
||||
areas_for_improvement: List[str]
|
||||
notable_features: List[str]
|
||||
technical_highlights: List[str]
|
||||
recommendations: List[str]
|
||||
market_potential: str
|
||||
scalability_assessment: str
|
||||
|
||||
# Set page config
|
||||
st.set_page_config(
|
||||
page_title="Hackathon Project Evaluator",
|
||||
page_icon="🏆",
|
||||
layout="wide"
|
||||
)
|
||||
|
||||
# Create Vision Analysis Agent
|
||||
@st.cache_resource
|
||||
def get_hackathon_judge():
|
||||
return Agent(
|
||||
name="HackathonJudge",
|
||||
role="Technical Project Evaluator",
|
||||
goal="Evaluate hackathon projects through video demonstrations",
|
||||
backstory="""You are an experienced hackathon judge and technical expert.
|
||||
You excel at evaluating innovation, technical implementation, and presentation quality.
|
||||
You provide constructive feedback and identify both strengths and areas for improvement.""",
|
||||
llm="gpt-4o-mini", # Using vision-capable model
|
||||
self_reflect=False,
|
||||
knowledge=""
|
||||
)
|
||||
|
||||
def evaluate_project(video_path: str) -> ProjectEvaluation:
|
||||
"""
|
||||
Evaluate a hackathon project based on its video demonstration
|
||||
"""
|
||||
hackathon_judge = get_hackathon_judge()
|
||||
|
||||
evaluation_task = Task(
|
||||
name="project_evaluation",
|
||||
description="""Analyze this hackathon project video demonstration and provide a comprehensive evaluation:
|
||||
|
||||
1. Score the following aspects (0-100):
|
||||
- Innovation and Creativity
|
||||
- Technical Complexity
|
||||
- Presentation Quality
|
||||
- User Experience
|
||||
- Project Completeness
|
||||
|
||||
2. Identify:
|
||||
- Key strengths and standout features
|
||||
- Areas that could be improved
|
||||
- Notable technical implementations
|
||||
- Market potential and scalability
|
||||
|
||||
3. Provide:
|
||||
- Specific recommendations for improvement
|
||||
- Technical suggestions
|
||||
- Potential future enhancements""",
|
||||
expected_output="Detailed project evaluation with scores and feedback",
|
||||
agent=hackathon_judge,
|
||||
output_pydantic=ProjectEvaluation,
|
||||
images=[video_path] # Video input for multimodal analysis
|
||||
)
|
||||
|
||||
# Initialize and run evaluation
|
||||
agents = PraisonAIAgents(
|
||||
agents=[hackathon_judge],
|
||||
tasks=[evaluation_task],
|
||||
process="sequential",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
response = agents.start()
|
||||
|
||||
try:
|
||||
# If response contains task_results, extract the Pydantic model directly
|
||||
if isinstance(response, dict) and 'task_results' in response:
|
||||
task_output = response['task_results'][0]
|
||||
if hasattr(task_output, 'pydantic'):
|
||||
return task_output.pydantic
|
||||
elif hasattr(task_output, 'raw'):
|
||||
# Extract JSON from raw string if it's wrapped in ```json
|
||||
raw_text = task_output.raw
|
||||
if raw_text.startswith('```json'):
|
||||
raw_text = raw_text.split('\n', 1)[1].rsplit('\n', 1)[0]
|
||||
evaluation_data = json.loads(raw_text)
|
||||
else:
|
||||
evaluation_data = json.loads(task_output) if isinstance(task_output, str) else task_output
|
||||
elif isinstance(response, str):
|
||||
evaluation_data = json.loads(response)
|
||||
elif isinstance(response, dict) and 'task_status' in response:
|
||||
content = response['task_status']
|
||||
if isinstance(content, dict):
|
||||
evaluation_data = content
|
||||
else:
|
||||
evaluation_data = json.loads(content) if isinstance(content, str) else content
|
||||
else:
|
||||
evaluation_data = response
|
||||
|
||||
# Create and return ProjectEvaluation instance
|
||||
return ProjectEvaluation(
|
||||
innovation_score=int(evaluation_data.get('innovation_score', 0)),
|
||||
technical_complexity=int(evaluation_data.get('technical_complexity', 0)),
|
||||
presentation_quality=int(evaluation_data.get('presentation_quality', 0)),
|
||||
user_experience=int(evaluation_data.get('user_experience', 0)),
|
||||
completeness=int(evaluation_data.get('completeness', 0)),
|
||||
overall_score=int(evaluation_data.get('overall_score', 0)),
|
||||
key_strengths=evaluation_data.get('key_strengths', []),
|
||||
areas_for_improvement=evaluation_data.get('areas_for_improvement', []),
|
||||
notable_features=evaluation_data.get('notable_features', []),
|
||||
technical_highlights=evaluation_data.get('technical_highlights', []),
|
||||
recommendations=evaluation_data.get('recommendations', []),
|
||||
market_potential=str(evaluation_data.get('market_potential', '')),
|
||||
scalability_assessment=str(evaluation_data.get('scalability_assessment', ''))
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Debug - Raw response: {response}")
|
||||
print(f"Error processing response: {e}")
|
||||
raise
|
||||
|
||||
# Title and description
|
||||
st.title("🏆 Hackathon Judge Agent")
|
||||
st.markdown("""
|
||||
Upload your hackathon project demonstration video for an AI-powered evaluation.
|
||||
Get comprehensive feedback on various aspects of your project.
|
||||
""")
|
||||
|
||||
# File uploader
|
||||
uploaded_file = st.file_uploader("Choose a video file", type=['mp4', 'avi', 'mov', 'mkv'])
|
||||
|
||||
if uploaded_file:
|
||||
# Create a temporary file to store the video
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix='.'+uploaded_file.name.split('.')[-1]) as tmp_file:
|
||||
tmp_file.write(uploaded_file.getvalue())
|
||||
video_path = tmp_file.name
|
||||
|
||||
with st.spinner("🤖 AI is evaluating your project..."):
|
||||
try:
|
||||
# Evaluate the project
|
||||
result = evaluate_project(video_path)
|
||||
|
||||
# Display results
|
||||
st.header("Overall Score")
|
||||
st.metric("Overall Score", f"{result.overall_score}/100")
|
||||
|
||||
# Display detailed scores
|
||||
st.header("Detailed Scores")
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
st.metric("Innovation", f"{result.innovation_score}/100")
|
||||
st.metric("Technical Complexity", f"{result.technical_complexity}/100")
|
||||
with col2:
|
||||
st.metric("Presentation", f"{result.presentation_quality}/100")
|
||||
st.metric("User Experience", f"{result.user_experience}/100")
|
||||
with col3:
|
||||
st.metric("Completeness", f"{result.completeness}/100")
|
||||
|
||||
# Display qualitative feedback
|
||||
st.header("Key Strengths")
|
||||
for strength in result.key_strengths:
|
||||
st.write(f"• {strength}")
|
||||
|
||||
st.header("Areas for Improvement")
|
||||
for area in result.areas_for_improvement:
|
||||
st.write(f"• {area}")
|
||||
|
||||
st.header("Technical Highlights")
|
||||
for highlight in result.technical_highlights:
|
||||
st.write(f"• {highlight}")
|
||||
|
||||
st.header("Notable Features")
|
||||
for feature in result.notable_features:
|
||||
st.write(f"• {feature}")
|
||||
|
||||
st.header("Recommendations")
|
||||
for rec in result.recommendations:
|
||||
st.write(f"• {rec}")
|
||||
|
||||
# Market Analysis
|
||||
st.header("Market Analysis")
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
st.subheader("Market Potential")
|
||||
st.write(result.market_potential)
|
||||
with col2:
|
||||
st.subheader("Scalability Assessment")
|
||||
st.write(result.scalability_assessment)
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Error evaluating the project: {str(e)}")
|
||||
finally:
|
||||
# Clean up the temporary file
|
||||
os.unlink(video_path)
|
||||
else:
|
||||
# Display placeholder content
|
||||
st.info("👆 Upload a video file to get started!")
|
||||
@@ -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,70 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Define the configuration for the Knowledge instance
|
||||
config = {
|
||||
"version": "v1.1",
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": os.path.abspath("./.praison"),
|
||||
"host": None,
|
||||
"port": None
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"embedding_dims": 1536
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0,
|
||||
"max_tokens": 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create an agent with knowledge capabilities
|
||||
knowledge_agent = Agent(
|
||||
name="KnowledgeAgent",
|
||||
role="Information Specialist",
|
||||
goal="Store and retrieve knowledge efficiently",
|
||||
backstory="Expert in managing and utilizing stored knowledge",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Define a task for the agent
|
||||
knowledge_task = Task(
|
||||
name="knowledge_task",
|
||||
description="KAG",
|
||||
expected_output="Answer to the question",
|
||||
agent=knowledge_agent,
|
||||
context=[
|
||||
{
|
||||
"embedding_db_config": json.dumps(config["vector_store"])
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
# Create and start the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[knowledge_agent],
|
||||
tasks=[knowledge_task],
|
||||
process="sequential",
|
||||
user_id="user1"
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
@@ -0,0 +1,21 @@
|
||||
from praisonaiagents import Agent
|
||||
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": ".praison",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
agent = Agent(
|
||||
name="Knowledge Agent",
|
||||
instructions="You answer questions based on the provided knowledge.",
|
||||
knowledge=["large.pdf"],
|
||||
knowledge_config=config,
|
||||
user_id="user1"
|
||||
)
|
||||
|
||||
agent.start("What is KAG in one line?")
|
||||
@@ -0,0 +1,44 @@
|
||||
import logging
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
import os
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
config = {
|
||||
"version": "v1.1",
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": os.path.abspath("./.praison"),
|
||||
"host": None,
|
||||
"port": None
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"embedding_dims": 1536
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0,
|
||||
"max_tokens": 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
knowledge = Knowledge(config)
|
||||
|
||||
# Search for memories based on a query
|
||||
query = "KAG"
|
||||
logger.info(f"Searching for memories with query: {query}")
|
||||
search_results = knowledge.search(query, user_id="user1")
|
||||
logger.info(f"Search results: {search_results}")
|
||||
print(f"\nSearch results for '{query}':")
|
||||
print(search_results)
|
||||
@@ -0,0 +1,171 @@
|
||||
import logging
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
import os
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "test",
|
||||
"path": ".praison",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
knowledge = Knowledge(config)
|
||||
|
||||
# Store memories
|
||||
logger.info("Storing memories...")
|
||||
cricket_memory = knowledge.store("Likes to play cricket on weekends", user_id="alice", metadata={"category": "hobbies"})
|
||||
logger.info(f"Stored cricket memory: {cricket_memory}")
|
||||
tennis_memory = knowledge.store("Enjoys playing tennis with friends", user_id="alice", metadata={"category": "hobbies"})
|
||||
logger.info(f"Stored tennis memory: {tennis_memory}")
|
||||
work_memory = knowledge.store("Works as a software engineer", user_id="alice", metadata={"category": "work"})
|
||||
logger.info(f"Stored work memory: {work_memory}")
|
||||
|
||||
print("Stored memories:")
|
||||
print(cricket_memory)
|
||||
print(tennis_memory)
|
||||
print(work_memory)
|
||||
|
||||
# Retrieve all memories for a user
|
||||
logger.info("Retrieving all memories for Alice...")
|
||||
alice_memories = knowledge.get_all(user_id="alice")
|
||||
logger.info(f"Retrieved memories for Alice: {alice_memories}")
|
||||
print("\nAll memories for Alice:")
|
||||
for memory in alice_memories:
|
||||
print(memory)
|
||||
|
||||
# Retrieve a specific memory by ID
|
||||
logger.info(f"Retrieving memory with ID: {cricket_memory['results'][0]['id']}")
|
||||
retrieved_memory = knowledge.get(cricket_memory['results'][0]["id"])
|
||||
logger.info(f"Retrieved memory: {retrieved_memory}")
|
||||
print("\nRetrieved memory:")
|
||||
print(retrieved_memory)
|
||||
|
||||
# Search for memories based on a query
|
||||
query = "What are Alice's hobbies?"
|
||||
logger.info(f"Searching for memories with query: {query}")
|
||||
search_results = knowledge.search(query, user_id="alice")
|
||||
logger.info(f"Search results: {search_results}")
|
||||
print(f"\nSearch results for '{query}':")
|
||||
for memory in search_results:
|
||||
print(memory)
|
||||
|
||||
# Update a memory
|
||||
logger.info(f"Updating memory with ID: {tennis_memory['results'][0]['id']}")
|
||||
updated_memory = knowledge.update(memory_id=tennis_memory['results'][0]["id"], data="Loves playing tennis on weekends")
|
||||
logger.info(f"Updated memory: {updated_memory}")
|
||||
print("\nUpdated memory:")
|
||||
print(updated_memory)
|
||||
|
||||
# Get memory history
|
||||
logger.info(f"Retrieving memory history for ID: {tennis_memory['results'][0]['id']}")
|
||||
memory_history = knowledge.history(memory_id=tennis_memory['results'][0]["id"])
|
||||
logger.info(f"Memory history: {memory_history}")
|
||||
print("\nMemory history:")
|
||||
for entry in memory_history:
|
||||
print(entry)
|
||||
|
||||
# Delete a memory
|
||||
logger.info(f"Deleting memory with ID: {work_memory['results'][0]['id']}")
|
||||
knowledge.delete(memory_id=work_memory['results'][0]["id"])
|
||||
logger.info("Memory deleted")
|
||||
print("\nDeleted memory")
|
||||
|
||||
# Retrieve memories after deletion
|
||||
logger.info("Retrieving memories after deletion...")
|
||||
alice_memories = knowledge.get_all(user_id="alice")
|
||||
logger.info(f"Retrieved memories after deletion: {alice_memories}")
|
||||
print("\nMemories after deletion:")
|
||||
for memory in alice_memories:
|
||||
print(memory)
|
||||
|
||||
# Delete all memories for a user
|
||||
logger.info("Deleting all memories for Alice...")
|
||||
knowledge.delete_all(user_id="alice")
|
||||
logger.info("All memories for Alice deleted")
|
||||
print("\nAll memories for Alice deleted")
|
||||
|
||||
# Retrieve memories after deleting all
|
||||
logger.info("Retrieving memories after deleting all...")
|
||||
alice_memories = knowledge.get_all(user_id="alice")
|
||||
logger.info(f"Retrieved memories after deleting all: {alice_memories}")
|
||||
print("\nMemories after deleting all:")
|
||||
print(alice_memories)
|
||||
|
||||
# Reset all memories
|
||||
logger.info("Resetting all memories...")
|
||||
knowledge.reset()
|
||||
logger.info("All memories reset")
|
||||
print("\nAll memories reset")
|
||||
|
||||
# Test file handling
|
||||
logger.info("Testing file handling...")
|
||||
|
||||
try:
|
||||
# Get absolute path to test files
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
pdf_file = os.path.join(current_dir, "small.pdf")
|
||||
txt_file = os.path.join(current_dir, "sample.txt")
|
||||
|
||||
# Test text file
|
||||
logger.info(f"Testing text file: {txt_file}")
|
||||
# Read and display content
|
||||
with open(txt_file, 'r') as file:
|
||||
txt_content = file.read()
|
||||
print("\nText file content:")
|
||||
print(txt_content)
|
||||
# Store the text file using add
|
||||
txt_memory = knowledge.add(txt_file, user_id="bob", metadata={"type": "text"})
|
||||
logger.info(f"Stored text file: {txt_memory}")
|
||||
print("\nStored text content:")
|
||||
if txt_memory and 'results' in txt_memory and txt_memory['results']:
|
||||
for mem in txt_memory['results']:
|
||||
print(mem)
|
||||
else:
|
||||
print("No valid memories were stored from the text file")
|
||||
|
||||
# Verify text memories are stored
|
||||
if txt_memory and 'results' in txt_memory and txt_memory['results']:
|
||||
logger.info("Verifying text memories...")
|
||||
for mem in txt_memory['results']:
|
||||
if isinstance(mem, dict) and 'id' in mem:
|
||||
stored = knowledge.get(mem['id'])
|
||||
logger.info(f"Verified text memory: {stored}")
|
||||
else:
|
||||
logger.warning(f"Unexpected memory format: {mem}")
|
||||
else:
|
||||
logger.warning("No valid memories were stored from the text file")
|
||||
|
||||
# Test pdf file
|
||||
logger.info(f"Testing pdf file: {pdf_file}")
|
||||
pdf_memory = knowledge.add(pdf_file, user_id="bob", metadata={"type": "pdf"})
|
||||
logger.info(f"Stored pdf file: {pdf_memory}")
|
||||
print("\nStored pdf content:")
|
||||
if pdf_memory and 'results' in pdf_memory and pdf_memory['results']:
|
||||
for mem in pdf_memory['results']:
|
||||
print(mem)
|
||||
else:
|
||||
print("No valid memories were stored from the PDF file")
|
||||
|
||||
# Verify stored content
|
||||
if pdf_memory and 'results' in pdf_memory and pdf_memory['results']:
|
||||
for memory in pdf_memory['results']:
|
||||
if isinstance(memory, dict) and 'id' in memory:
|
||||
stored_memory = knowledge.get(memory['id'])
|
||||
logger.info(f"Verified stored memory: {stored_memory}")
|
||||
print("\nVerified content:")
|
||||
print(stored_memory)
|
||||
else:
|
||||
logger.warning(f"Unexpected memory format: {memory}")
|
||||
else:
|
||||
logger.warning("No valid memories were stored from the PDF file")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during file handling test: {str(e)}")
|
||||
raise
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import logging
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
import os
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
knowledge = Knowledge()
|
||||
try:
|
||||
# Get absolute path to test files
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
pdf_file = os.path.join(current_dir, "large.pdf")
|
||||
|
||||
# Test pdf file
|
||||
logger.info(f"Testing pdf file: {pdf_file}")
|
||||
pdf_memory = knowledge.add(pdf_file, user_id="bob", metadata={"type": "pdf"})
|
||||
logger.info(f"Stored pdf file: {pdf_memory}")
|
||||
print("\nStored pdf content:")
|
||||
if pdf_memory and 'results' in pdf_memory and pdf_memory['results']:
|
||||
for mem in pdf_memory['results']:
|
||||
print(mem)
|
||||
else:
|
||||
print("No valid memories were stored from the PDF file")
|
||||
|
||||
# Verify stored content
|
||||
if pdf_memory and 'results' in pdf_memory and pdf_memory['results']:
|
||||
for memory in pdf_memory['results']:
|
||||
if isinstance(memory, dict) and 'id' in memory:
|
||||
stored_memory = knowledge.get(memory['id'])
|
||||
logger.info(f"Verified stored memory: {stored_memory}")
|
||||
print("\nVerified content:")
|
||||
print(stored_memory)
|
||||
else:
|
||||
logger.warning(f"Unexpected memory format: {memory}")
|
||||
else:
|
||||
logger.warning("No valid memories were stored from the PDF file")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during file handling test: {str(e)}")
|
||||
raise
|
||||
@@ -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()
|
||||
Binary file not shown.
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,5 @@
|
||||
from markitdown import MarkItDown
|
||||
|
||||
md = MarkItDown()
|
||||
result = md.convert("sample.txt")
|
||||
print(result.text_content)
|
||||
@@ -0,0 +1,220 @@
|
||||
from mem0 import Memory
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class MemoryManager:
|
||||
def __init__(self):
|
||||
"""Initialize Mem0 Memory manager with configuration"""
|
||||
# Define the complete configuration with only supported fields
|
||||
config = {
|
||||
"version": "v1.1",
|
||||
"custom_prompt": None,
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": os.path.abspath("./.praison/storage"),
|
||||
"host": None,
|
||||
"port": None
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"embedding_dims": 1536
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0,
|
||||
"max_tokens": 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure storage directory exists
|
||||
storage_path = os.path.abspath("./.praison/storage")
|
||||
if os.path.exists(storage_path):
|
||||
logger.info(f"Clearing existing storage at {storage_path}")
|
||||
import shutil
|
||||
shutil.rmtree(storage_path)
|
||||
os.makedirs(storage_path, exist_ok=True)
|
||||
logger.info(f"Created storage path: {storage_path}")
|
||||
|
||||
# Initialize memory
|
||||
self.memory = Memory.from_config(config)
|
||||
logger.info("Memory manager initialized")
|
||||
|
||||
def pretty_print(self, data: Any) -> None:
|
||||
"""Helper function to print memory data in a readable format"""
|
||||
if isinstance(data, (dict, list)):
|
||||
print(json.dumps(data, indent=2))
|
||||
else:
|
||||
print(data)
|
||||
|
||||
def extract_memory_id(self, result: Any) -> Optional[str]:
|
||||
"""Extract memory ID from the result, handling different formats"""
|
||||
try:
|
||||
if isinstance(result, dict):
|
||||
# v1.1 format
|
||||
memories = result.get('results', [])
|
||||
return memories[0]['id'] if memories else None
|
||||
elif isinstance(result, list):
|
||||
# v1.0 format
|
||||
return result[0]['id'] if result else None
|
||||
return None
|
||||
except (KeyError, IndexError) as e:
|
||||
logger.warning(f"Could not extract memory ID from result: {e}")
|
||||
return None
|
||||
|
||||
def prepare_metadata(self, metadata: Optional[Dict]) -> Dict:
|
||||
"""Prepare metadata by converting values to acceptable types"""
|
||||
if not metadata:
|
||||
return {}
|
||||
|
||||
clean_metadata = {}
|
||||
for key, value in metadata.items():
|
||||
if isinstance(value, (str, int, float, bool)):
|
||||
clean_metadata[key] = value
|
||||
elif isinstance(value, list):
|
||||
clean_metadata[key] = ','.join(map(str, value))
|
||||
elif isinstance(value, dict):
|
||||
clean_metadata[key] = json.dumps(value)
|
||||
else:
|
||||
clean_metadata[key] = str(value)
|
||||
return clean_metadata
|
||||
|
||||
def verify_memory_addition(self, user_id: str, max_retries: int = 3) -> bool:
|
||||
"""Verify that memory was added successfully"""
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
time.sleep(0.5) # Brief pause to allow indexing
|
||||
memories = self.memory.get_all(user_id=user_id)
|
||||
if len(memories.get('results', [])) > 0:
|
||||
return True
|
||||
logger.warning(f"Memory not found on attempt {attempt + 1}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Verification attempt {attempt + 1} failed: {e}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
def add_memory(self, text: str, user_id: Optional[str] = None, metadata: Optional[Dict] = None) -> Dict:
|
||||
"""Add a new memory with error handling and verification"""
|
||||
try:
|
||||
logger.info(f"Adding memory for user {user_id}")
|
||||
clean_metadata = self.prepare_metadata(metadata)
|
||||
logger.debug(f"Prepared metadata: {clean_metadata}")
|
||||
|
||||
messages = [{"role": "user", "content": text}]
|
||||
result = self.memory.add(
|
||||
messages=messages,
|
||||
user_id=user_id,
|
||||
metadata=clean_metadata
|
||||
)
|
||||
|
||||
if self.verify_memory_addition(user_id):
|
||||
logger.info("Memory added and verified successfully")
|
||||
else:
|
||||
logger.warning("Memory addition could not be verified")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add memory: {str(e)}")
|
||||
raise
|
||||
|
||||
def get_all_memories(self, user_id: Optional[str] = None) -> Dict:
|
||||
"""Retrieve all memories for a user"""
|
||||
try:
|
||||
logger.info(f"Retrieving all memories for user {user_id}")
|
||||
memories = self.memory.get_all(user_id=user_id)
|
||||
count = len(memories.get('results', []))
|
||||
logger.info(f"Retrieved {count} memories")
|
||||
return memories
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve memories: {str(e)}")
|
||||
raise
|
||||
|
||||
def search_memories(self, query: str, user_id: Optional[str] = None, limit: int = 5) -> Dict:
|
||||
"""Search memories with a query"""
|
||||
try:
|
||||
logger.info(f"Searching memories with query: {query}")
|
||||
results = self.memory.search(
|
||||
query=query,
|
||||
user_id=user_id,
|
||||
limit=limit
|
||||
)
|
||||
count = len(results.get('results', []))
|
||||
logger.info(f"Found {count} matching memories")
|
||||
return results
|
||||
except Exception as e:
|
||||
logger.error(f"Search failed: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
try:
|
||||
# Initialize memory manager
|
||||
manager = MemoryManager()
|
||||
|
||||
# Add a test memory
|
||||
print("\n1. Adding a new memory:")
|
||||
metadata = {
|
||||
"category": "hobbies",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"tags": "tennis,learning",
|
||||
"priority": 1,
|
||||
"source": "user_input"
|
||||
}
|
||||
|
||||
result = manager.add_memory(
|
||||
text="I am working on improving my tennis skills. Suggest some online courses.",
|
||||
user_id="alice",
|
||||
metadata=metadata
|
||||
)
|
||||
manager.pretty_print(result)
|
||||
|
||||
# Wait briefly for indexing
|
||||
time.sleep(1)
|
||||
|
||||
print("\n2. Retrieving all memories:")
|
||||
all_memories = manager.get_all_memories(user_id="alice")
|
||||
manager.pretty_print(all_memories)
|
||||
|
||||
print("\n3. Searching memories:")
|
||||
search_results = manager.search_memories(
|
||||
query="What are Alice's hobbies?",
|
||||
user_id="alice",
|
||||
limit=5
|
||||
)
|
||||
manager.pretty_print(search_results)
|
||||
|
||||
# Print summary
|
||||
print("\nMemory Store Summary:")
|
||||
total_memories = len(all_memories.get('results', []))
|
||||
print(f"Total memories stored: {total_memories}")
|
||||
if total_memories > 0:
|
||||
print("Available memories:")
|
||||
for mem in all_memories['results']:
|
||||
print(f"- {mem.get('memory', 'No content')}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Main process failed: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,143 @@
|
||||
from mem0 import Memory
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class MemorySearcher:
|
||||
def __init__(self):
|
||||
"""Initialize Mem0 Memory searcher with configuration"""
|
||||
# Define the configuration
|
||||
config = {
|
||||
"version": "v1.1",
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": os.path.abspath("./.praison/storage"),
|
||||
"host": None,
|
||||
"port": None
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"embedding_dims": 1536
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0,
|
||||
"max_tokens": 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize memory
|
||||
self.memory = Memory.from_config(config)
|
||||
logger.info("Memory searcher initialized")
|
||||
|
||||
def pretty_print(self, data: Any) -> None:
|
||||
"""Helper function to print data in a readable format"""
|
||||
if isinstance(data, (dict, list)):
|
||||
print(json.dumps(data, indent=2))
|
||||
else:
|
||||
print(data)
|
||||
|
||||
def get_all_memories(self, user_id: Optional[str] = None) -> Dict:
|
||||
"""Retrieve all memories for a user"""
|
||||
try:
|
||||
logger.info(f"Retrieving all memories for user {user_id}")
|
||||
memories = self.memory.get_all(user_id=user_id)
|
||||
count = len(memories.get('results', []))
|
||||
logger.info(f"Retrieved {count} memories")
|
||||
return memories
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to retrieve memories: {str(e)}")
|
||||
raise
|
||||
|
||||
def search_memories(self, query: str, user_id: Optional[str] = None, limit: int = 5) -> Dict:
|
||||
"""Search memories with a query"""
|
||||
try:
|
||||
logger.info(f"Searching memories with query: {query}")
|
||||
results = self.memory.search(
|
||||
query=query,
|
||||
user_id=user_id,
|
||||
limit=limit
|
||||
)
|
||||
count = len(results.get('results', []))
|
||||
logger.info(f"Found {count} matching memories")
|
||||
return results
|
||||
except Exception as e:
|
||||
logger.error(f"Search failed: {str(e)}")
|
||||
raise
|
||||
|
||||
def list_unique_users(self) -> list:
|
||||
"""List all unique users in the memory store"""
|
||||
try:
|
||||
all_memories = self.memory.get_all()
|
||||
users = set()
|
||||
for memory in all_memories.get('results', []):
|
||||
if 'user_id' in memory:
|
||||
users.add(memory['user_id'])
|
||||
return sorted(list(users))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to list users: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
try:
|
||||
# Initialize memory searcher
|
||||
searcher = MemorySearcher()
|
||||
|
||||
# Get list of users
|
||||
print("\n1. Available users in the memory store:")
|
||||
users = searcher.list_unique_users()
|
||||
searcher.pretty_print(users)
|
||||
|
||||
if not users:
|
||||
print("No users found in the memory store. Please add some memories first.")
|
||||
return
|
||||
|
||||
# Example user (using first user found)
|
||||
example_user = users[0]
|
||||
|
||||
print(f"\n2. All memories for user '{example_user}':")
|
||||
all_memories = searcher.get_all_memories(user_id=example_user)
|
||||
searcher.pretty_print(all_memories)
|
||||
|
||||
print(f"\n3. Searching memories for user '{example_user}':")
|
||||
search_results = searcher.search_memories(
|
||||
query="What are their hobbies?",
|
||||
user_id=example_user,
|
||||
limit=5
|
||||
)
|
||||
searcher.pretty_print(search_results)
|
||||
|
||||
# Print summary
|
||||
print("\nMemory Store Summary:")
|
||||
total_memories = len(all_memories.get('results', []))
|
||||
print(f"Total memories for {example_user}: {total_memories}")
|
||||
if total_memories > 0:
|
||||
print("Available memories:")
|
||||
for mem in all_memories['results']:
|
||||
print(f"- {mem.get('memory', 'No content')}")
|
||||
if 'metadata' in mem:
|
||||
print(f" Metadata: {mem['metadata']}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Main process failed: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,26 @@
|
||||
from mem0 import Memory
|
||||
import os
|
||||
import json
|
||||
|
||||
# Basic configuration
|
||||
config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": ".praison",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize memory
|
||||
memory = Memory.from_config(config)
|
||||
|
||||
# Search Alice's hobbies
|
||||
search_results = memory.search(
|
||||
query="KAG",
|
||||
user_id="user1",
|
||||
limit=5
|
||||
)
|
||||
|
||||
print(search_results)
|
||||
@@ -0,0 +1,46 @@
|
||||
from mem0 import Memory
|
||||
import os
|
||||
import json
|
||||
|
||||
# Basic configuration
|
||||
config = {
|
||||
"version": "v1.1",
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": "praison",
|
||||
"path": os.path.abspath("./.praison/storage")
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "text-embedding-3-small"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-4o-mini"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize memory
|
||||
memory = Memory.from_config(config)
|
||||
|
||||
# Search Alice's hobbies
|
||||
search_results = memory.search(
|
||||
query="What are Alice's hobbies?",
|
||||
user_id="alice",
|
||||
limit=5
|
||||
)
|
||||
|
||||
# Print results in a readable format
|
||||
print("\nAlice's Memories:")
|
||||
for memory in search_results['results']:
|
||||
print(f"\nMemory: {memory['memory']}")
|
||||
print(f"Score: {memory['score']}")
|
||||
if 'metadata' in memory:
|
||||
print("Metadata:")
|
||||
print(json.dumps(memory['metadata'], indent=2))
|
||||
@@ -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,9 @@ 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 .knowledge.knowledge import Knowledge
|
||||
from .knowledge.chunking import Chunking
|
||||
from .main import (
|
||||
TaskOutput,
|
||||
ReflectionOutput,
|
||||
@@ -33,6 +36,7 @@ __all__ = [
|
||||
'Task',
|
||||
'TaskOutput',
|
||||
'ReflectionOutput',
|
||||
'AutoAgents',
|
||||
'display_interaction',
|
||||
'display_self_reflection',
|
||||
'display_instruction',
|
||||
@@ -44,4 +48,6 @@ __all__ = [
|
||||
'register_display_callback',
|
||||
'sync_display_callbacks',
|
||||
'async_display_callbacks',
|
||||
'Knowledge',
|
||||
'Chunking'
|
||||
]
|
||||
@@ -16,8 +16,11 @@ from ..main import (
|
||||
display_self_reflection,
|
||||
ReflectionOutput,
|
||||
client,
|
||||
error_logs
|
||||
error_logs,
|
||||
adisplay_instruction
|
||||
)
|
||||
import inspect
|
||||
import uuid
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..task.task import Task
|
||||
@@ -67,9 +70,29 @@ class Agent:
|
||||
return None
|
||||
|
||||
import inspect
|
||||
# Langchain tools
|
||||
if inspect.isclass(func) and hasattr(func, 'run') and not hasattr(func, '_run'):
|
||||
original_func = func
|
||||
func = func.run
|
||||
function_name = original_func.__name__
|
||||
# CrewAI tools
|
||||
elif 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 +118,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:
|
||||
@@ -146,7 +169,7 @@ class Agent:
|
||||
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,
|
||||
@@ -159,14 +182,27 @@ class Agent:
|
||||
respect_context_window: bool = True,
|
||||
code_execution_mode: Literal["safe", "unsafe"] = "safe",
|
||||
embedder_config: Optional[Dict[str, Any]] = None,
|
||||
knowledge_sources: Optional[List[Any]] = None,
|
||||
knowledge: Optional[List[str]] = None,
|
||||
knowledge_config: Optional[Dict[str, 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
|
||||
reflect_llm: Optional[str] = None,
|
||||
user_id: Optional[str] = None
|
||||
):
|
||||
# Add check at start if memory is requested
|
||||
if memory is not None:
|
||||
try:
|
||||
from ..memory.memory import Memory
|
||||
MEMORY_AVAILABLE = True
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Memory features requested in Agent but memory dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[memory]\""
|
||||
)
|
||||
|
||||
# Handle backward compatibility for required fields
|
||||
if all(x is None for x in [name, role, goal, backstory, instructions]):
|
||||
raise ValueError("At least one of name, role, goal, backstory, or instructions must be provided")
|
||||
@@ -209,7 +245,7 @@ class Agent:
|
||||
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.knowledge = knowledge
|
||||
self.use_system_prompt = use_system_prompt
|
||||
self.chat_history = []
|
||||
self.markdown = markdown
|
||||
@@ -225,6 +261,40 @@ Your Role: {self.role}\n
|
||||
Your Goal: {self.goal}
|
||||
"""
|
||||
|
||||
# Generate unique IDs
|
||||
self.agent_id = str(uuid.uuid4())
|
||||
|
||||
# Store user_id
|
||||
self.user_id = user_id
|
||||
|
||||
# Check if knowledge parameter has any values
|
||||
if not knowledge:
|
||||
self.knowledge = None
|
||||
else:
|
||||
# Initialize Knowledge with provided or default config
|
||||
from praisonaiagents.knowledge import Knowledge
|
||||
self.knowledge = Knowledge(knowledge_config or None)
|
||||
|
||||
# Handle knowledge
|
||||
if knowledge:
|
||||
for source in knowledge:
|
||||
self._process_knowledge(source)
|
||||
|
||||
def _process_knowledge(self, knowledge_item):
|
||||
"""Process and store knowledge from a file path, URL, or string."""
|
||||
try:
|
||||
if os.path.exists(knowledge_item):
|
||||
# It's a file path
|
||||
self.knowledge.add(knowledge_item, user_id=self.user_id, agent_id=self.agent_id)
|
||||
elif knowledge_item.startswith("http://") or knowledge_item.startswith("https://"):
|
||||
# It's a URL
|
||||
pass
|
||||
else:
|
||||
# It's a string content
|
||||
self.knowledge.store(knowledge_item, user_id=self.user_id, agent_id=self.agent_id)
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing knowledge item: {knowledge_item}, error: {e}")
|
||||
|
||||
def generate_task(self) -> 'Task':
|
||||
"""Generate a Task object from the agent's instructions"""
|
||||
from ..task.task import Task
|
||||
@@ -249,25 +319,39 @@ 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)
|
||||
# Langchain: If it's a class with run but not _run, instantiate and call run
|
||||
if inspect.isclass(func) and hasattr(func, 'run') and not hasattr(func, '_run'):
|
||||
instance = func()
|
||||
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)
|
||||
|
||||
# CrewAI: If it's a class with an _run method, instantiate and call _run
|
||||
elif inspect.isclass(func) and hasattr(func, '_run'):
|
||||
instance = func()
|
||||
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}")
|
||||
@@ -359,7 +443,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:
|
||||
@@ -395,15 +479,30 @@ Your Goal: {self.goal}
|
||||
return None
|
||||
|
||||
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
|
||||
# Search for existing knowledge if any knowledge is provided
|
||||
if self.knowledge:
|
||||
search_results = self.knowledge.search(prompt, agent_id=self.agent_id)
|
||||
if search_results:
|
||||
# Check if search_results is a list of dictionaries or strings
|
||||
if isinstance(search_results, dict) and 'results' in search_results:
|
||||
# Extract memory content from the results
|
||||
knowledge_content = "\n".join([result['memory'] for result in search_results['results']])
|
||||
else:
|
||||
# If search_results is a list of strings, join them directly
|
||||
knowledge_content = "\n".join(search_results)
|
||||
|
||||
# Append found knowledge to the prompt
|
||||
prompt = f"{prompt}\n\nKnowledge: {knowledge_content}"
|
||||
|
||||
if self.use_system_prompt:
|
||||
system_prompt = f"""{self.backstory}\n
|
||||
Your Role: {self.role}\n
|
||||
Your Goal: {self.goal}
|
||||
"""
|
||||
if output_json:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_json.model_json_schema())}"
|
||||
elif output_pydantic:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_pydantic.model_json_schema())}"
|
||||
else:
|
||||
system_prompt = None
|
||||
|
||||
@@ -444,7 +543,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:
|
||||
@@ -503,7 +610,7 @@ Your Goal: {self.goal}
|
||||
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
|
||||
|
||||
@@ -585,9 +692,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
# 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()}"
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_json.model_json_schema())}"
|
||||
elif output_pydantic:
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
|
||||
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_pydantic.model_json_schema())}"
|
||||
|
||||
# Build messages
|
||||
if isinstance(prompt, str):
|
||||
@@ -608,6 +715,22 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
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:
|
||||
@@ -711,4 +834,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']
|
||||
@@ -12,6 +12,10 @@ from ..agent.agent import Agent
|
||||
from ..task.task import Task
|
||||
from ..process.process import Process, LoopItems
|
||||
import asyncio
|
||||
import uuid
|
||||
|
||||
# Set up logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def encode_file_to_base64(file_path: str) -> str:
|
||||
"""Base64-encode a file."""
|
||||
@@ -41,10 +45,29 @@ 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, user_id=None, max_iter=10):
|
||||
# Add check at the start if memory is requested
|
||||
if memory:
|
||||
try:
|
||||
from ..memory.memory import Memory
|
||||
MEMORY_AVAILABLE = True
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Memory features requested but memory dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[memory]\""
|
||||
)
|
||||
|
||||
if not agents:
|
||||
raise ValueError("At least one agent must be provided")
|
||||
|
||||
|
||||
self.run_id = str(uuid.uuid4()) # Auto-generate run_id
|
||||
self.user_id = user_id or "praison" # Optional user_id
|
||||
self.max_iter = max_iter # Add max_iter parameter
|
||||
|
||||
# Pass user_id to each agent
|
||||
for agent in agents:
|
||||
agent.user_id = self.user_id
|
||||
|
||||
self.agents = agents
|
||||
self.tasks = {}
|
||||
if max_retries < 3:
|
||||
@@ -57,8 +80,21 @@ class PraisonAIAgents:
|
||||
|
||||
# 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:
|
||||
logging.info(f"Using model {self.manager_llm} for manager")
|
||||
logger.info(f"Using model {self.manager_llm} for manager")
|
||||
|
||||
# If no tasks provided, generate them from agents
|
||||
if tasks is None:
|
||||
@@ -66,12 +102,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:
|
||||
@@ -87,9 +123,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
|
||||
@@ -139,26 +231,62 @@ 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:
|
||||
if context_task.result:
|
||||
context_results += f"Result of previous task {context_task.name if context_task.name else context_task.description}: {context_task.result.raw}\n"
|
||||
else:
|
||||
context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
|
||||
context_results = [] # Use list to avoid duplicates
|
||||
for context_item in task.context:
|
||||
if isinstance(context_item, str):
|
||||
context_results.append(f"Input Content:\n{context_item}")
|
||||
elif isinstance(context_item, list):
|
||||
context_results.append(f"Input Content: {' '.join(str(x) for x in context_item)}")
|
||||
elif hasattr(context_item, 'result'): # Task object
|
||||
if context_item.result:
|
||||
context_results.append(
|
||||
f"Result of previous task {context_item.name if context_item.name else context_item.description}:\n{context_item.result.raw}"
|
||||
)
|
||||
else:
|
||||
context_results.append(
|
||||
f"Previous task {context_item.name if context_item.name else context_item.description} has no result yet."
|
||||
)
|
||||
elif isinstance(context_item, dict) and "vector_store" in context_item:
|
||||
from ..knowledge.knowledge import Knowledge
|
||||
try:
|
||||
# Handle both string and dict configs
|
||||
cfg = context_item["vector_store"]
|
||||
if isinstance(cfg, str):
|
||||
cfg = json.loads(cfg)
|
||||
|
||||
knowledge = Knowledge(config={"vector_store": cfg}, verbose=self.verbose)
|
||||
|
||||
# Only use user_id as filter
|
||||
db_results = knowledge.search(
|
||||
task.description,
|
||||
user_id=self.user_id if self.user_id else None
|
||||
)
|
||||
context_results.append(f"[DB Context]: {str(db_results)}")
|
||||
except Exception as e:
|
||||
context_results.append(f"[Vector DB Error]: {e}")
|
||||
|
||||
# Join unique context results
|
||||
unique_contexts = list(dict.fromkeys(context_results)) # Remove duplicates
|
||||
task_prompt += f"""
|
||||
Here are the results of previous tasks that might be useful:\n
|
||||
{context_results}
|
||||
Context:
|
||||
|
||||
{' '.join(unique_contexts)}
|
||||
"""
|
||||
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):
|
||||
@@ -195,14 +323,14 @@ 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,
|
||||
tools=tools,
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic
|
||||
)
|
||||
@@ -223,8 +351,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)
|
||||
@@ -234,8 +362,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
|
||||
@@ -250,37 +378,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"""
|
||||
@@ -288,15 +432,30 @@ Here are the results of previous tasks that might be useful:\n
|
||||
tasks=self.tasks,
|
||||
agents=self.agents,
|
||||
manager_llm=self.manager_llm,
|
||||
verbose=self.verbose
|
||||
verbose=self.verbose,
|
||||
max_iter=self.max_iter
|
||||
)
|
||||
|
||||
if self.process == "workflow":
|
||||
# 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:
|
||||
@@ -312,8 +471,16 @@ Here are the results of previous tasks that might be useful:\n
|
||||
else:
|
||||
self.run_task(task_id)
|
||||
|
||||
async def astart(self):
|
||||
async def astart(self, content=None, **kwargs):
|
||||
"""Async version of start method"""
|
||||
if content:
|
||||
# Add content to context of all tasks
|
||||
for task in self.tasks.values():
|
||||
if isinstance(content, (str, list)):
|
||||
if not task.context:
|
||||
task.context = []
|
||||
task.context.append(content)
|
||||
|
||||
await self.arun_all_tasks()
|
||||
return {
|
||||
"task_status": self.get_all_tasks_status(),
|
||||
@@ -328,7 +495,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}")
|
||||
|
||||
@@ -339,6 +506,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:
|
||||
@@ -361,21 +537,62 @@ 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:
|
||||
if context_task.result:
|
||||
context_results += f"Result of previous task {context_task.name if context_task.name else context_task.description}: {context_task.result.raw}\n"
|
||||
else:
|
||||
context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
|
||||
context_results = [] # Use list to avoid duplicates
|
||||
for context_item in task.context:
|
||||
if isinstance(context_item, str):
|
||||
context_results.append(f"Input Content:\n{context_item}")
|
||||
elif isinstance(context_item, list):
|
||||
context_results.append(f"Input Content: {' '.join(str(x) for x in context_item)}")
|
||||
elif hasattr(context_item, 'result'): # Task object
|
||||
if context_item.result:
|
||||
context_results.append(
|
||||
f"Result of previous task {context_item.name if context_item.name else context_item.description}:\n{context_item.result.raw}"
|
||||
)
|
||||
else:
|
||||
context_results.append(
|
||||
f"Previous task {context_item.name if context_item.name else context_item.description} has no result yet."
|
||||
)
|
||||
elif isinstance(context_item, dict) and "vector_store" in context_item:
|
||||
from ..knowledge.knowledge import Knowledge
|
||||
try:
|
||||
# Handle both string and dict configs
|
||||
cfg = context_item["vector_store"]
|
||||
if isinstance(cfg, str):
|
||||
cfg = json.loads(cfg)
|
||||
|
||||
knowledge = Knowledge(config={"vector_store": cfg}, verbose=self.verbose)
|
||||
|
||||
# Only use user_id as filter
|
||||
db_results = knowledge.search(
|
||||
task.description,
|
||||
user_id=self.user_id if self.user_id else None
|
||||
)
|
||||
context_results.append(f"[DB Context]: {str(db_results)}")
|
||||
except Exception as e:
|
||||
context_results.append(f"[Vector DB Error]: {e}")
|
||||
|
||||
# Join unique context results
|
||||
unique_contexts = list(dict.fromkeys(context_results)) # Remove duplicates
|
||||
task_prompt += f"""
|
||||
Here are the results of previous tasks that might be useful:\n
|
||||
{context_results}
|
||||
Context:
|
||||
|
||||
{' '.join(unique_contexts)}
|
||||
"""
|
||||
|
||||
# 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):
|
||||
@@ -425,6 +642,17 @@ Here are the results of previous tasks that might be useful:\n
|
||||
)
|
||||
|
||||
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],
|
||||
@@ -440,8 +668,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)
|
||||
@@ -451,8 +679,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
|
||||
@@ -467,37 +695,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"""
|
||||
@@ -505,7 +759,8 @@ Here are the results of previous tasks that might be useful:\n
|
||||
tasks=self.tasks,
|
||||
agents=self.agents,
|
||||
manager_llm=self.manager_llm,
|
||||
verbose=self.verbose
|
||||
verbose=self.verbose,
|
||||
max_iter=self.max_iter
|
||||
)
|
||||
|
||||
if self.process == "workflow":
|
||||
@@ -544,12 +799,24 @@ Here are the results of previous tasks that might be useful:\n
|
||||
return str(agent[0])
|
||||
return None
|
||||
|
||||
def start(self):
|
||||
def start(self, content=None, **kwargs):
|
||||
"""Start agent execution with optional content and config"""
|
||||
if content:
|
||||
# Add content to context of all tasks
|
||||
for task in self.tasks.values():
|
||||
if isinstance(content, (str, list)):
|
||||
# If context is empty, initialize it
|
||||
if not task.context:
|
||||
task.context = []
|
||||
# Add content to context
|
||||
task.context.append(content)
|
||||
|
||||
# Run tasks as before
|
||||
self.run_all_tasks()
|
||||
return {
|
||||
"task_status": self.get_all_tasks_status(),
|
||||
"task_results": {task_id: self.get_task_result(task_id) for task_id in self.tasks}
|
||||
}
|
||||
}
|
||||
|
||||
def set_state(self, key: str, value: Any) -> None:
|
||||
"""Set a state value"""
|
||||
|
||||
@@ -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()
|
||||
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
PraisonAI Knowledge - Advanced knowledge management system with configurable features
|
||||
"""
|
||||
|
||||
from praisonaiagents.knowledge.knowledge import Knowledge
|
||||
from praisonaiagents.knowledge.chunking import Chunking
|
||||
|
||||
__all__ = ["Knowledge", "Chunking"]
|
||||
@@ -0,0 +1,182 @@
|
||||
from typing import List, Union, Optional, Dict, Any
|
||||
from functools import cached_property
|
||||
import importlib
|
||||
|
||||
class Chunking:
|
||||
"""A unified class for text chunking with various chunking strategies."""
|
||||
|
||||
CHUNKER_PARAMS = {
|
||||
'token': ['chunk_size', 'chunk_overlap', 'tokenizer'],
|
||||
'word': ['chunk_size', 'chunk_overlap', 'tokenizer'],
|
||||
'sentence': ['chunk_size', 'chunk_overlap', 'tokenizer'],
|
||||
'semantic': ['chunk_size', 'embedding_model', 'tokenizer'],
|
||||
'sdpm': ['chunk_size', 'embedding_model', 'tokenizer'],
|
||||
'late': ['chunk_size', 'embedding_model', 'tokenizer'],
|
||||
'recursive': ['chunk_size', 'tokenizer']
|
||||
}
|
||||
|
||||
@cached_property
|
||||
def SUPPORTED_CHUNKERS(self) -> Dict[str, Any]:
|
||||
"""Lazy load chunker classes."""
|
||||
try:
|
||||
from chonkie.chunker import (
|
||||
TokenChunker,
|
||||
WordChunker,
|
||||
SentenceChunker,
|
||||
SemanticChunker,
|
||||
SDPMChunker,
|
||||
LateChunker,
|
||||
RecursiveChunker
|
||||
)
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"chonkie package not found. Please install it using: pip install 'praisonaiagents[knowledge]'"
|
||||
)
|
||||
|
||||
return {
|
||||
'token': TokenChunker,
|
||||
'word': WordChunker,
|
||||
'sentence': SentenceChunker,
|
||||
'semantic': SemanticChunker,
|
||||
'sdpm': SDPMChunker,
|
||||
'late': LateChunker,
|
||||
'recursive': RecursiveChunker
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chunker_type: str = 'token',
|
||||
chunk_size: int = 512,
|
||||
chunk_overlap: int = 128,
|
||||
tokenizer: str = "gpt2",
|
||||
embedding_model: Optional[Union[str, Any]] = None,
|
||||
**kwargs
|
||||
):
|
||||
"""Initialize the Chunking class."""
|
||||
if chunker_type not in self.CHUNKER_PARAMS:
|
||||
raise ValueError(
|
||||
f"Unsupported chunker type: {chunker_type}. "
|
||||
f"Must be one of: {list(self.CHUNKER_PARAMS.keys())}"
|
||||
)
|
||||
|
||||
self.chunker_type = chunker_type
|
||||
self.chunk_size = chunk_size
|
||||
self.chunk_overlap = chunk_overlap
|
||||
self.tokenizer = tokenizer
|
||||
self._embedding_model = embedding_model
|
||||
self.kwargs = kwargs
|
||||
|
||||
# Initialize these as None for lazy loading
|
||||
self._chunker = None
|
||||
self._embeddings = None
|
||||
|
||||
@cached_property
|
||||
def embedding_model(self):
|
||||
"""Lazy load the embedding model."""
|
||||
if self._embedding_model is None and self.chunker_type in ['semantic', 'sdpm', 'late']:
|
||||
from chonkie.embeddings import AutoEmbeddings
|
||||
return AutoEmbeddings.get_embeddings("all-MiniLM-L6-v2")
|
||||
elif isinstance(self._embedding_model, str):
|
||||
from chonkie.embeddings import AutoEmbeddings
|
||||
return AutoEmbeddings.get_embeddings(self._embedding_model)
|
||||
return self._embedding_model
|
||||
|
||||
def _get_chunker_params(self) -> Dict[str, Any]:
|
||||
"""Get the appropriate parameters for the current chunker type."""
|
||||
allowed_params = self.CHUNKER_PARAMS[self.chunker_type]
|
||||
params = {'chunk_size': self.chunk_size}
|
||||
|
||||
if 'chunk_overlap' in allowed_params:
|
||||
params['chunk_overlap'] = self.chunk_overlap
|
||||
|
||||
if 'tokenizer' in allowed_params:
|
||||
if self.chunker_type in ['semantic', 'sdpm', 'late']:
|
||||
params['tokenizer'] = self.embedding_model.get_tokenizer_or_token_counter()
|
||||
else:
|
||||
params['tokenizer'] = self.tokenizer
|
||||
|
||||
if 'embedding_model' in allowed_params:
|
||||
params['embedding_model'] = self.embedding_model
|
||||
|
||||
# Add any additional kwargs that are in allowed_params
|
||||
for key, value in self.kwargs.items():
|
||||
if key in allowed_params:
|
||||
params[key] = value
|
||||
|
||||
return params
|
||||
|
||||
@cached_property
|
||||
def chunker(self):
|
||||
"""Lazy load the chunker instance."""
|
||||
if self._chunker is None:
|
||||
chunker_cls = self.SUPPORTED_CHUNKERS[self.chunker_type]
|
||||
common_params = self._get_chunker_params()
|
||||
self._chunker = chunker_cls(**common_params)
|
||||
|
||||
return self._chunker
|
||||
|
||||
def _get_overlap_refinery(self, context_size: Optional[int] = None, **kwargs):
|
||||
"""Lazy load the overlap refinery."""
|
||||
try:
|
||||
from chonkie.refinery import OverlapRefinery
|
||||
except ImportError:
|
||||
raise ImportError("Failed to import OverlapRefinery from chonkie.refinery")
|
||||
|
||||
if context_size is None:
|
||||
context_size = self.chunk_overlap
|
||||
|
||||
return OverlapRefinery(
|
||||
context_size=context_size,
|
||||
tokenizer=self.chunker.tokenizer,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def add_overlap_context(
|
||||
self,
|
||||
chunks: List[Any],
|
||||
context_size: int = None,
|
||||
mode: str = "suffix",
|
||||
merge_context: bool = True
|
||||
) -> List[Any]:
|
||||
"""Add overlap context to chunks using OverlapRefinery."""
|
||||
refinery = self._get_overlap_refinery(
|
||||
context_size=context_size,
|
||||
mode=mode,
|
||||
merge_context=merge_context
|
||||
)
|
||||
return refinery.refine(chunks)
|
||||
|
||||
def chunk(
|
||||
self,
|
||||
text: Union[str, List[str]],
|
||||
add_context: bool = False,
|
||||
context_params: Optional[Dict[str, Any]] = None
|
||||
) -> Union[List[Any], List[List[Any]]]:
|
||||
"""Chunk text using the configured chunking strategy."""
|
||||
chunks = self.chunker(text)
|
||||
|
||||
if add_context:
|
||||
context_params = context_params or {}
|
||||
if isinstance(text, str):
|
||||
chunks = self.add_overlap_context(chunks, **context_params)
|
||||
else:
|
||||
chunks = [self.add_overlap_context(c, **context_params) for c in chunks]
|
||||
|
||||
return chunks
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
text: Union[str, List[str]],
|
||||
add_context: bool = False,
|
||||
context_params: Optional[Dict[str, Any]] = None
|
||||
) -> Union[List[Any], List[List[Any]]]:
|
||||
"""Make the Chunking instance callable."""
|
||||
return self.chunk(text, add_context, context_params)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""String representation of the Chunking instance."""
|
||||
return (
|
||||
f"Chunking(chunker_type='{self.chunker_type}', "
|
||||
f"chunk_size={self.chunk_size}, "
|
||||
f"chunk_overlap={self.chunk_overlap})"
|
||||
)
|
||||
@@ -0,0 +1,321 @@
|
||||
import os
|
||||
import logging
|
||||
import uuid
|
||||
import time
|
||||
from .chunking import Chunking
|
||||
from functools import cached_property
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class CustomMemory:
|
||||
@classmethod
|
||||
def from_config(cls, config):
|
||||
from mem0 import Memory
|
||||
return type('CustomMemory', (Memory,), {
|
||||
'_add_to_vector_store': cls._add_to_vector_store
|
||||
}).from_config(config)
|
||||
|
||||
@staticmethod
|
||||
def _add_to_vector_store(self, messages, metadata, filters):
|
||||
# Custom implementation that doesn't use LLM
|
||||
parsed_messages = "\n".join([msg["content"] for msg in messages])
|
||||
|
||||
# Create a simple fact without using LLM
|
||||
new_retrieved_facts = [parsed_messages]
|
||||
|
||||
# Process embeddings and continue with vector store operations
|
||||
new_message_embeddings = {}
|
||||
for new_mem in new_retrieved_facts:
|
||||
messages_embeddings = self.embedding_model.embed(new_mem)
|
||||
new_message_embeddings[new_mem] = messages_embeddings
|
||||
|
||||
# Create the memory
|
||||
memory_id = self._create_memory(
|
||||
data=parsed_messages,
|
||||
existing_embeddings=new_message_embeddings,
|
||||
metadata=metadata
|
||||
)
|
||||
|
||||
return [{
|
||||
"id": memory_id,
|
||||
"memory": parsed_messages,
|
||||
"event": "ADD"
|
||||
}]
|
||||
|
||||
class Knowledge:
|
||||
def __init__(self, config=None, verbose=None):
|
||||
self._config = config
|
||||
self._verbose = verbose or 0
|
||||
os.environ['ANONYMIZED_TELEMETRY'] = 'False' # Chromadb
|
||||
|
||||
# Configure logging levels based on verbose setting
|
||||
if not self._verbose:
|
||||
# Suppress logs from all relevant dependencies
|
||||
for logger_name in [
|
||||
'mem0',
|
||||
'chromadb',
|
||||
'local_persistent_hnsw',
|
||||
'_client',
|
||||
'main'
|
||||
]:
|
||||
logging.getLogger(logger_name).setLevel(logging.WARNING)
|
||||
|
||||
# Disable OpenAI API request logging
|
||||
logging.getLogger('openai').setLevel(logging.WARNING)
|
||||
|
||||
# Set root logger to warning to catch any uncategorized logs
|
||||
logging.getLogger().setLevel(logging.WARNING)
|
||||
|
||||
@cached_property
|
||||
def _deps(self):
|
||||
try:
|
||||
from markitdown import MarkItDown
|
||||
import chromadb
|
||||
return {
|
||||
'chromadb': chromadb,
|
||||
'markdown': MarkItDown()
|
||||
}
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Required packages not installed. Please install using: "
|
||||
'pip install "praisonaiagents[knowledge]"'
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def config(self):
|
||||
# Generate unique collection name for each instance (only if not provided in config)
|
||||
default_collection = f"test_{int(time.time())}_{str(uuid.uuid4())[:8]}"
|
||||
persist_dir = ".praison"
|
||||
|
||||
# Create persistent client config
|
||||
base_config = {
|
||||
"vector_store": {
|
||||
"provider": "chroma",
|
||||
"config": {
|
||||
"collection_name": default_collection,
|
||||
"path": persist_dir,
|
||||
"client": self._deps['chromadb'].PersistentClient(path=persist_dir),
|
||||
"host": None,
|
||||
"port": None
|
||||
}
|
||||
},
|
||||
"version": "v1.1",
|
||||
"custom_prompt": "Return {{\"facts\": [text]}} where text is the exact input provided and json response"
|
||||
}
|
||||
|
||||
# If config is provided, merge it with base config
|
||||
if self._config:
|
||||
# Merge version if provided
|
||||
if "version" in self._config:
|
||||
base_config["version"] = self._config["version"]
|
||||
|
||||
# Merge vector_store config
|
||||
if "vector_store" in self._config:
|
||||
if "provider" in self._config["vector_store"]:
|
||||
base_config["vector_store"]["provider"] = self._config["vector_store"]["provider"]
|
||||
|
||||
if "config" in self._config["vector_store"]:
|
||||
config_copy = self._config["vector_store"]["config"].copy()
|
||||
# Only exclude client as it's managed internally
|
||||
if "client" in config_copy:
|
||||
del config_copy["client"]
|
||||
base_config["vector_store"]["config"].update(config_copy)
|
||||
|
||||
# Merge embedder config if provided
|
||||
if "embedder" in self._config:
|
||||
base_config["embedder"] = self._config["embedder"]
|
||||
|
||||
# Merge llm config if provided
|
||||
if "llm" in self._config:
|
||||
base_config["llm"] = self._config["llm"]
|
||||
return base_config
|
||||
|
||||
@cached_property
|
||||
def memory(self):
|
||||
try:
|
||||
return CustomMemory.from_config(self.config)
|
||||
except (NotImplementedError, ValueError) as e:
|
||||
if "list_collections" in str(e) or "Extra fields not allowed" in str(e):
|
||||
# Keep only allowed fields
|
||||
vector_store_config = {
|
||||
"collection_name": self.config["vector_store"]["config"]["collection_name"],
|
||||
"path": self.config["vector_store"]["config"]["path"]
|
||||
}
|
||||
self.config["vector_store"]["config"] = vector_store_config
|
||||
from mem0 import Memory
|
||||
return Memory.from_config(self.config)
|
||||
raise
|
||||
|
||||
@cached_property
|
||||
def markdown(self):
|
||||
return self._deps['markdown']
|
||||
|
||||
@cached_property
|
||||
def chunker(self):
|
||||
return Chunking(
|
||||
chunker_type='recursive',
|
||||
chunk_size=512,
|
||||
chunk_overlap=50
|
||||
)
|
||||
|
||||
def _log(self, message, level=2):
|
||||
"""Internal logging helper"""
|
||||
if self._verbose and self._verbose >= level:
|
||||
logger.info(message)
|
||||
|
||||
def store(self, content, user_id=None, agent_id=None, run_id=None, metadata=None):
|
||||
"""Store a memory."""
|
||||
try:
|
||||
if isinstance(content, str):
|
||||
if any(content.lower().endswith(ext) for ext in ['.pdf', '.doc', '.docx', '.txt']):
|
||||
self._log(f"Content appears to be a file path, processing file: {content}")
|
||||
return self.add(content, user_id=user_id, agent_id=agent_id, run_id=run_id, metadata=metadata)
|
||||
|
||||
content = content.strip()
|
||||
if not content:
|
||||
return []
|
||||
|
||||
result = self.memory.add(content, user_id=user_id, agent_id=agent_id, run_id=run_id, metadata=metadata)
|
||||
self._log(f"Store operation result: {result}")
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing content: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_all(self, user_id=None, agent_id=None, run_id=None):
|
||||
"""Retrieve all memories."""
|
||||
return self.memory.get_all(user_id=user_id, agent_id=agent_id, run_id=run_id)
|
||||
|
||||
def get(self, memory_id):
|
||||
"""Retrieve a specific memory by ID."""
|
||||
return self.memory.get(memory_id)
|
||||
|
||||
def search(self, query, user_id=None, agent_id=None, run_id=None):
|
||||
"""Search for memories related to a query."""
|
||||
return self.memory.search(query, user_id=user_id, agent_id=agent_id, run_id=run_id)
|
||||
|
||||
def update(self, memory_id, data):
|
||||
"""Update a memory."""
|
||||
return self.memory.update(memory_id, data)
|
||||
|
||||
def history(self, memory_id):
|
||||
"""Get the history of changes for a memory."""
|
||||
return self.memory.history(memory_id)
|
||||
|
||||
def delete(self, memory_id):
|
||||
"""Delete a memory."""
|
||||
self.memory.delete(memory_id)
|
||||
|
||||
def delete_all(self, user_id=None, agent_id=None, run_id=None):
|
||||
"""Delete all memories."""
|
||||
self.memory.delete_all(user_id=user_id, agent_id=agent_id, run_id=run_id)
|
||||
|
||||
def reset(self):
|
||||
"""Reset all memories."""
|
||||
self.memory.reset()
|
||||
|
||||
def normalize_content(self, content):
|
||||
"""Normalize content for consistent storage."""
|
||||
# Example normalization: strip whitespace, convert to lowercase
|
||||
return content.strip().lower()
|
||||
|
||||
def add(self, file_path, user_id=None, agent_id=None, run_id=None, metadata=None):
|
||||
"""Read file content and store it in memory.
|
||||
|
||||
Args:
|
||||
file_path: Can be:
|
||||
- A string path to local file
|
||||
- A URL string
|
||||
- A list containing file paths and/or URLs
|
||||
"""
|
||||
if isinstance(file_path, (list, tuple)):
|
||||
results = []
|
||||
for path in file_path:
|
||||
result = self._process_single_input(path, user_id, agent_id, run_id, metadata)
|
||||
results.extend(result.get('results', []))
|
||||
return {'results': results, 'relations': []}
|
||||
|
||||
return self._process_single_input(file_path, user_id, agent_id, run_id, metadata)
|
||||
|
||||
def _process_single_input(self, input_path, user_id=None, agent_id=None, run_id=None, metadata=None):
|
||||
"""Process a single input which can be a file path or URL."""
|
||||
try:
|
||||
# Define supported file extensions
|
||||
DOCUMENT_EXTENSIONS = {
|
||||
'document': ('.pdf', '.ppt', '.pptx', '.doc', '.docx', '.xls', '.xlsx'),
|
||||
'media': ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.mp3', '.wav', '.ogg', '.m4a'),
|
||||
'text': ('.txt', '.csv', '.json', '.xml', '.md', '.html', '.htm'),
|
||||
'archive': '.zip'
|
||||
}
|
||||
|
||||
# Check if input is URL
|
||||
if isinstance(input_path, str) and (input_path.startswith('http://') or input_path.startswith('https://')):
|
||||
self._log(f"Processing URL: {input_path}")
|
||||
raise NotImplementedError("URL processing not yet implemented")
|
||||
|
||||
# Check if input ends with any supported extension
|
||||
is_supported_file = any(input_path.lower().endswith(ext)
|
||||
for exts in DOCUMENT_EXTENSIONS.values()
|
||||
for ext in (exts if isinstance(exts, tuple) else (exts,)))
|
||||
|
||||
if is_supported_file:
|
||||
self._log(f"Processing as file path: {input_path}")
|
||||
if not os.path.exists(input_path):
|
||||
logger.error(f"File not found: {input_path}")
|
||||
raise FileNotFoundError(f"File not found: {input_path}")
|
||||
|
||||
file_ext = '.' + input_path.lower().split('.')[-1] # Get extension reliably
|
||||
|
||||
# Process file based on type
|
||||
if file_ext in DOCUMENT_EXTENSIONS['text']:
|
||||
with open(input_path, 'r', encoding='utf-8') as file:
|
||||
content = file.read().strip()
|
||||
if not content:
|
||||
raise ValueError("Empty text file")
|
||||
memories = [self.normalize_content(content)]
|
||||
else:
|
||||
# Use MarkItDown for documents and media
|
||||
result = self.markdown.convert(input_path)
|
||||
content = result.text_content
|
||||
if not content:
|
||||
raise ValueError("No content could be extracted from file")
|
||||
chunks = self.chunker.chunk(content)
|
||||
memories = [chunk.text.strip() if hasattr(chunk, 'text') else str(chunk).strip()
|
||||
for chunk in chunks if chunk]
|
||||
|
||||
# Set metadata for file
|
||||
if not metadata:
|
||||
metadata = {}
|
||||
metadata['file_type'] = file_ext.lstrip('.')
|
||||
metadata['filename'] = os.path.basename(input_path)
|
||||
else:
|
||||
# Treat as raw text content only if no file extension
|
||||
memories = [self.normalize_content(input_path)]
|
||||
|
||||
# Create progress display
|
||||
progress = Progress(
|
||||
SpinnerColumn(),
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
TaskProgressColumn(),
|
||||
transient=True
|
||||
)
|
||||
|
||||
# Store memories with progress bar
|
||||
all_results = []
|
||||
with progress:
|
||||
store_task = progress.add_task(f"Adding to Knowledge from {os.path.basename(input_path)}", total=len(memories))
|
||||
for memory in memories:
|
||||
if memory:
|
||||
memory_result = self.store(memory, user_id=user_id, agent_id=agent_id,
|
||||
run_id=run_id, metadata=metadata)
|
||||
if memory_result:
|
||||
all_results.extend(memory_result.get('results', []))
|
||||
progress.advance(store_task)
|
||||
|
||||
return {'results': all_results, 'relations': []}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing input {input_path}: {str(e)}", exc_info=True)
|
||||
raise
|
||||
@@ -23,6 +23,10 @@ logging.basicConfig(
|
||||
handlers=[RichHandler(rich_tracebacks=True)]
|
||||
)
|
||||
|
||||
# Add these lines to suppress markdown parser debug logs
|
||||
logging.getLogger('markdown_it').setLevel(logging.WARNING)
|
||||
logging.getLogger('rich.markdown').setLevel(logging.WARNING)
|
||||
|
||||
# Global list to store error logs
|
||||
error_logs = []
|
||||
|
||||
@@ -116,10 +120,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 +139,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 +150,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 +238,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 +257,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 +268,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
|
||||
pass
|
||||
|
||||
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
|
||||
@@ -5,19 +5,23 @@ from pydantic import BaseModel
|
||||
from ..agent.agent import Agent
|
||||
from ..task.task import Task
|
||||
from ..main import display_error, client
|
||||
import csv
|
||||
import os
|
||||
|
||||
class LoopItems(BaseModel):
|
||||
items: List[Any]
|
||||
|
||||
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 +45,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}")
|
||||
|
||||
@@ -55,46 +64,47 @@ class Process:
|
||||
if prev_task and prev_task.result:
|
||||
# Handle loop data
|
||||
if current_task.task_type == "loop":
|
||||
# create a loop manager Agent
|
||||
loop_manager = Agent(
|
||||
name="Loop Manager",
|
||||
role="Loop data processor",
|
||||
goal="Process loop data and convert it to list format",
|
||||
backstory="Expert at handling loop data and converting it to proper format",
|
||||
llm=self.manager_llm,
|
||||
verbose=self.verbose,
|
||||
markdown=True
|
||||
)
|
||||
# # create a loop manager Agent
|
||||
# loop_manager = Agent(
|
||||
# name="Loop Manager",
|
||||
# role="Loop data processor",
|
||||
# goal="Process loop data and convert it to list format",
|
||||
# backstory="Expert at handling loop data and converting it to proper format",
|
||||
# llm=self.manager_llm,
|
||||
# verbose=self.verbose,
|
||||
# markdown=True
|
||||
# )
|
||||
|
||||
# get the loop data convert it to list using calling Agent class chat
|
||||
loop_prompt = f"""
|
||||
Process this data into a list format:
|
||||
{prev_task.result.raw}
|
||||
# # get the loop data convert it to list using calling Agent class chat
|
||||
# loop_prompt = f"""
|
||||
# Process this data into a list format:
|
||||
# {prev_task.result.raw}
|
||||
|
||||
Return a JSON object with an 'items' array containing the items to process.
|
||||
"""
|
||||
if current_task.async_execution:
|
||||
loop_data_str = await loop_manager.achat(
|
||||
prompt=loop_prompt,
|
||||
output_json=LoopItems
|
||||
)
|
||||
else:
|
||||
loop_data_str = loop_manager.chat(
|
||||
prompt=loop_prompt,
|
||||
output_json=LoopItems
|
||||
)
|
||||
# Return a JSON object with an 'items' array containing the items to process.
|
||||
# """
|
||||
# if current_task.async_execution:
|
||||
# loop_data_str = await loop_manager.achat(
|
||||
# prompt=loop_prompt,
|
||||
# output_json=LoopItems
|
||||
# )
|
||||
# else:
|
||||
# loop_data_str = loop_manager.chat(
|
||||
# prompt=loop_prompt,
|
||||
# output_json=LoopItems
|
||||
# )
|
||||
|
||||
try:
|
||||
# The response will already be parsed into LoopItems model
|
||||
loop_data[f"loop_{current_task.name}"] = {
|
||||
"items": loop_data_str.items,
|
||||
"index": 0,
|
||||
"remaining": len(loop_data_str.items)
|
||||
}
|
||||
context += f"\nCurrent loop item: {loop_data_str.items[0]}"
|
||||
except Exception as e:
|
||||
display_error(f"Failed to process loop data: {e}")
|
||||
context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
# try:
|
||||
# # The response will already be parsed into LoopItems model
|
||||
# loop_data[f"loop_{current_task.name}"] = {
|
||||
# "items": loop_data_str.items,
|
||||
# "index": 0,
|
||||
# "remaining": len(loop_data_str.items)
|
||||
# }
|
||||
# context += f"\nCurrent loop item: {loop_data_str.items[0]}"
|
||||
# except Exception as e:
|
||||
# display_error(f"Failed to process loop data: {e}")
|
||||
# context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
else:
|
||||
context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
|
||||
@@ -111,6 +121,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 +145,32 @@ 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()
|
||||
# MINIMAL CHANGE: use pydantic decision if present
|
||||
decision_str = current_task.result.raw.lower()
|
||||
if current_task.result.pydantic and hasattr(current_task.result.pydantic, "decision"):
|
||||
decision_str = current_task.result.pydantic.decision.lower()
|
||||
|
||||
# Check conditions
|
||||
for condition, tasks in current_task.condition.items():
|
||||
if condition.lower() in result and tasks:
|
||||
next_task_name = tasks[0]
|
||||
if condition.lower() == decision_str:
|
||||
# Handle both list and direct string values
|
||||
task_value = tasks[0] if isinstance(tasks, list) else tasks
|
||||
if not task_value or task_value == "exit":
|
||||
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 +310,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:
|
||||
@@ -303,12 +329,86 @@ Provide a JSON with the structure:
|
||||
if not start_task:
|
||||
start_task = list(self.tasks.values())[0]
|
||||
logging.info("No start task marked, using first task")
|
||||
|
||||
|
||||
# If loop type and no input_file, default to tasks.csv
|
||||
if start_task and start_task.task_type == "loop" and not start_task.input_file:
|
||||
start_task.input_file = "tasks.csv"
|
||||
|
||||
# --- If loop + input_file, read file & create tasks
|
||||
if start_task and start_task.task_type == "loop" and getattr(start_task, "input_file", None):
|
||||
try:
|
||||
file_ext = os.path.splitext(start_task.input_file)[1].lower()
|
||||
new_tasks = []
|
||||
|
||||
if file_ext == ".csv":
|
||||
# existing CSV reading logic
|
||||
with open(start_task.input_file, "r", encoding="utf-8") as f:
|
||||
# Try as simple CSV first
|
||||
reader = csv.reader(f)
|
||||
previous_task = None
|
||||
for i, row in enumerate(reader):
|
||||
if row: # Skip empty rows
|
||||
task_desc = row[0] # Take first column
|
||||
row_task = Task(
|
||||
description=task_desc, # Keep full row as description
|
||||
agent=start_task.agent,
|
||||
name=task_desc, # Use first column as name
|
||||
is_start=(i == 0),
|
||||
task_type="task",
|
||||
condition={
|
||||
"complete": ["next"],
|
||||
"retry": ["current"]
|
||||
}
|
||||
)
|
||||
self.tasks[row_task.id] = row_task
|
||||
new_tasks.append(row_task)
|
||||
|
||||
if previous_task:
|
||||
previous_task.next_tasks = [row_task.name]
|
||||
previous_task.condition["complete"] = [row_task.name]
|
||||
previous_task = row_task
|
||||
else:
|
||||
# If not CSV, read lines
|
||||
with open(start_task.input_file, "r", encoding="utf-8") as f:
|
||||
lines = f.read().splitlines()
|
||||
previous_task = None
|
||||
for i, line in enumerate(lines):
|
||||
row_task = Task(
|
||||
description=line.strip(),
|
||||
agent=start_task.agent,
|
||||
name=line.strip(),
|
||||
is_start=(i == 0),
|
||||
task_type="task",
|
||||
condition={
|
||||
"complete": ["next"],
|
||||
"retry": ["current"]
|
||||
}
|
||||
)
|
||||
self.tasks[row_task.id] = row_task
|
||||
new_tasks.append(row_task)
|
||||
|
||||
if previous_task:
|
||||
previous_task.next_tasks = [row_task.name]
|
||||
previous_task.condition["complete"] = [row_task.name]
|
||||
previous_task = row_task
|
||||
|
||||
if new_tasks:
|
||||
start_task = new_tasks[0]
|
||||
logging.info(f"Created {len(new_tasks)} tasks from: {start_task.input_file}")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to read file tasks: {e}")
|
||||
|
||||
# end of the new block
|
||||
current_task = start_task
|
||||
visited_tasks = set()
|
||||
loop_data = {} # Store loop-specific data
|
||||
|
||||
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}")
|
||||
|
||||
@@ -322,40 +422,41 @@ Provide a JSON with the structure:
|
||||
if prev_task and prev_task.result:
|
||||
# Handle loop data
|
||||
if current_task.task_type == "loop":
|
||||
# create a loop manager Agent
|
||||
loop_manager = Agent(
|
||||
name="Loop Manager",
|
||||
role="Loop data processor",
|
||||
goal="Process loop data and convert it to list format",
|
||||
backstory="Expert at handling loop data and converting it to proper format",
|
||||
llm=self.manager_llm,
|
||||
verbose=self.verbose,
|
||||
markdown=True
|
||||
)
|
||||
# # create a loop manager Agent
|
||||
# loop_manager = Agent(
|
||||
# name="Loop Manager",
|
||||
# role="Loop data processor",
|
||||
# goal="Process loop data and convert it to list format",
|
||||
# backstory="Expert at handling loop data and converting it to proper format",
|
||||
# llm=self.manager_llm,
|
||||
# verbose=self.verbose,
|
||||
# markdown=True
|
||||
# )
|
||||
|
||||
# get the loop data convert it to list using calling Agent class chat
|
||||
loop_prompt = f"""
|
||||
Process this data into a list format:
|
||||
{prev_task.result.raw}
|
||||
# # get the loop data convert it to list using calling Agent class chat
|
||||
# loop_prompt = f"""
|
||||
# Process this data into a list format:
|
||||
# {prev_task.result.raw}
|
||||
|
||||
Return a JSON object with an 'items' array containing the items to process.
|
||||
"""
|
||||
loop_data_str = loop_manager.chat(
|
||||
prompt=loop_prompt,
|
||||
output_json=LoopItems
|
||||
)
|
||||
# Return a JSON object with an 'items' array containing the items to process.
|
||||
# """
|
||||
# loop_data_str = loop_manager.chat(
|
||||
# prompt=loop_prompt,
|
||||
# output_json=LoopItems
|
||||
# )
|
||||
|
||||
try:
|
||||
# The response will already be parsed into LoopItems model
|
||||
loop_data[f"loop_{current_task.name}"] = {
|
||||
"items": loop_data_str.items,
|
||||
"index": 0,
|
||||
"remaining": len(loop_data_str.items)
|
||||
}
|
||||
context += f"\nCurrent loop item: {loop_data_str.items[0]}"
|
||||
except Exception as e:
|
||||
display_error(f"Failed to process loop data: {e}")
|
||||
context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
# try:
|
||||
# # The response will already be parsed into LoopItems model
|
||||
# loop_data[f"loop_{current_task.name}"] = {
|
||||
# "items": loop_data_str.items,
|
||||
# "index": 0,
|
||||
# "remaining": len(loop_data_str.items)
|
||||
# }
|
||||
# context += f"\nCurrent loop item: {loop_data_str.items[0]}"
|
||||
# except Exception as e:
|
||||
# display_error(f"Failed to process loop data: {e}")
|
||||
# context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
else:
|
||||
context += f"\n{prev_name}: {prev_task.result.raw}"
|
||||
|
||||
@@ -372,6 +473,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: Only for workflow because some tasks may be revisited
|
||||
if self.tasks[task_id].status == "completed":
|
||||
logging.debug(f"Task {task_id} was completed, resetting to 'not started' for next iteration.")
|
||||
self.tasks[task_id].status = "not started"
|
||||
|
||||
# Handle loop progression
|
||||
if current_task.task_type == "loop":
|
||||
loop_key = f"loop_{current_task.name}"
|
||||
@@ -391,22 +497,32 @@ 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()
|
||||
# MINIMAL CHANGE: use pydantic decision if present
|
||||
decision_str = current_task.result.raw.lower()
|
||||
if current_task.result.pydantic and hasattr(current_task.result.pydantic, "decision"):
|
||||
decision_str = current_task.result.pydantic.decision.lower()
|
||||
|
||||
# Check conditions
|
||||
for condition, tasks in current_task.condition.items():
|
||||
if condition.lower() in result and tasks:
|
||||
next_task_name = tasks[0]
|
||||
if condition.lower() == decision_str:
|
||||
# Handle both list and direct string values
|
||||
task_value = tasks[0] if isinstance(tasks, list) else tasks
|
||||
if not task_value or task_value == "exit":
|
||||
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:
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import logging
|
||||
import asyncio
|
||||
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine
|
||||
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine, Literal
|
||||
from pydantic import BaseModel
|
||||
from ..main import TaskOutput
|
||||
from ..agent.agent import Agent
|
||||
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,
|
||||
context: Optional[List["Task"]] = None,
|
||||
context: Optional[List[Union[str, List, 'Task']]] = None,
|
||||
async_execution: Optional[bool] = False,
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
output_file: Optional[str] = None,
|
||||
@@ -30,12 +35,27 @@ 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,
|
||||
input_file: Optional[str] = None
|
||||
):
|
||||
# Add check if memory config is provided
|
||||
if memory is not None or (config and config.get('memory_config')):
|
||||
try:
|
||||
from ..memory.memory import Memory
|
||||
MEMORY_AVAILABLE = True
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Memory features requested in Task but memory dependencies not installed. "
|
||||
"Please install with: pip install \"praisonaiagents[memory]\""
|
||||
)
|
||||
|
||||
self.input_file = input_file
|
||||
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 +74,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")
|
||||
@@ -61,14 +96,210 @@ class Task:
|
||||
# Track previous tasks based on next_tasks relationships
|
||||
self.previous_tasks = []
|
||||
|
||||
# If task_type="decision" and output_pydantic is not set
|
||||
if self.task_type == "decision" and not self.output_pydantic:
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal
|
||||
|
||||
# Gather condition keys for the "decision" field
|
||||
condition_keys = list(self.condition.keys())
|
||||
if not condition_keys:
|
||||
# Fall back to placeholders if nothing is specified
|
||||
condition_keys = ["next_task", "exit"]
|
||||
|
||||
# Create a dynamic literal type from condition keys
|
||||
DecisionLiteral = Literal.__getitem__(tuple(condition_keys))
|
||||
|
||||
class DecisionModel(BaseModel):
|
||||
response: str
|
||||
decision: DecisionLiteral
|
||||
|
||||
self.output_pydantic = DecisionModel
|
||||
|
||||
# If task_type="loop" and output_pydantic is not set
|
||||
if self.task_type == "loop" and not self.output_pydantic:
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal
|
||||
|
||||
# Gather condition keys for the "decision" field
|
||||
condition_keys = list(self.condition.keys())
|
||||
if not condition_keys:
|
||||
# Fall back to placeholders if nothing is specified
|
||||
condition_keys = ["next_item", "exit"]
|
||||
|
||||
# Create a dynamic literal type
|
||||
LoopLiteral = Literal.__getitem__(tuple(condition_keys))
|
||||
|
||||
class LoopModel(BaseModel):
|
||||
response: str
|
||||
decision: LoopLiteral
|
||||
loop_id: str # Additional field for loop
|
||||
|
||||
self.output_pydantic = LoopModel
|
||||
|
||||
def __str__(self):
|
||||
return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"
|
||||
|
||||
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)
|
||||
|
||||
task_prompt = f"""
|
||||
You need to do the following task: {self.description}.
|
||||
Expected Output: {self.expected_output}.
|
||||
"""
|
||||
if self.context:
|
||||
context_results = [] # Use list to avoid duplicates
|
||||
for context_item in self.context:
|
||||
if isinstance(context_item, str):
|
||||
context_results.append(f"Input Content:\n{context_item}")
|
||||
elif isinstance(context_item, list):
|
||||
context_results.append(f"Input Content: {' '.join(str(x) for x in context_item)}")
|
||||
elif hasattr(context_item, 'result'): # Task object
|
||||
if context_item.result:
|
||||
context_results.append(
|
||||
f"Result of previous task {context_item.name if context_item.name else context_item.description}:\n{context_item.result.raw}"
|
||||
)
|
||||
else:
|
||||
context_results.append(
|
||||
f"Previous task {context_item.name if context_item.name else context_item.description} has no result yet."
|
||||
)
|
||||
|
||||
# Join unique context results
|
||||
unique_contexts = list(dict.fromkeys(context_results)) # Remove duplicates
|
||||
task_prompt += f"""
|
||||
Context:
|
||||
|
||||
{' '.join(unique_contexts)}
|
||||
"""
|
||||
@@ -8,6 +8,59 @@ TOOL_MAPPINGS = {
|
||||
'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'),
|
||||
@@ -68,14 +121,6 @@ TOOL_MAPPINGS = {
|
||||
'transform_yaml': ('.yaml_tools', 'YAMLTools'),
|
||||
'yaml_tools': ('.yaml_tools', 'YAMLTools'),
|
||||
|
||||
# Calculator Tools
|
||||
'evaluate': ('.calculator_tools', 'CalculatorTools'),
|
||||
'solve_equation': ('.calculator_tools', 'CalculatorTools'),
|
||||
'convert_units': ('.calculator_tools', 'CalculatorTools'),
|
||||
'calculate_statistics': ('.calculator_tools', 'CalculatorTools'),
|
||||
'calculate_financial': ('.calculator_tools', 'CalculatorTools'),
|
||||
'calculator_tools': ('.calculator_tools', 'CalculatorTools'),
|
||||
|
||||
# Python Tools
|
||||
'execute_code': ('.python_tools', 'PythonTools'),
|
||||
'analyze_code': ('.python_tools', 'PythonTools'),
|
||||
@@ -90,51 +135,6 @@ TOOL_MAPPINGS = {
|
||||
'group_by': ('.pandas_tools', 'PandasTools'),
|
||||
'pivot_table': ('.pandas_tools', 'PandasTools'),
|
||||
'pandas_tools': ('.pandas_tools', 'PandasTools'),
|
||||
|
||||
# Wikipedia Tools
|
||||
'search': ('.wikipedia_tools', 'WikipediaTools'),
|
||||
'get_wikipedia_summary': ('.wikipedia_tools', 'WikipediaTools'),
|
||||
'get_wikipedia_page': ('.wikipedia_tools', 'WikipediaTools'),
|
||||
'get_random_wikipedia': ('.wikipedia_tools', 'WikipediaTools'),
|
||||
'set_wikipedia_language': ('.wikipedia_tools', 'WikipediaTools'),
|
||||
'wikipedia_tools': ('.wikipedia_tools', 'WikipediaTools'),
|
||||
|
||||
# Newspaper Tools
|
||||
'get_article': ('.newspaper_tools', 'NewspaperTools'),
|
||||
'get_news_sources': ('.newspaper_tools', 'NewspaperTools'),
|
||||
'get_articles_from_source': ('.newspaper_tools', 'NewspaperTools'),
|
||||
'get_trending_topics': ('.newspaper_tools', 'NewspaperTools'),
|
||||
'newspaper_tools': ('.newspaper_tools', 'NewspaperTools'),
|
||||
|
||||
# arXiv Tools
|
||||
'search_arxiv': ('.arxiv_tools', 'ArxivTools'),
|
||||
'get_arxiv_paper': ('.arxiv_tools', 'ArxivTools'),
|
||||
'get_papers_by_author': ('.arxiv_tools', 'ArxivTools'),
|
||||
'get_papers_by_category': ('.arxiv_tools', 'ArxivTools'),
|
||||
'arxiv_tools': ('.arxiv_tools', 'ArxivTools'),
|
||||
|
||||
# Spider Tools
|
||||
'scrape_page': ('.spider_tools', 'SpiderTools'),
|
||||
'extract_links': ('.spider_tools', 'SpiderTools'),
|
||||
'crawl': ('.spider_tools', 'SpiderTools'),
|
||||
'extract_text': ('.spider_tools', 'SpiderTools'),
|
||||
'spider_tools': ('.spider_tools', 'SpiderTools'),
|
||||
|
||||
# DuckDB Tools
|
||||
'query': ('.duckdb_tools', 'DuckDBTools'),
|
||||
'create_table': ('.duckdb_tools', 'DuckDBTools'),
|
||||
'load_data': ('.duckdb_tools', 'DuckDBTools'),
|
||||
'export_data': ('.duckdb_tools', 'DuckDBTools'),
|
||||
'get_table_info': ('.duckdb_tools', 'DuckDBTools'),
|
||||
'analyze_data': ('.duckdb_tools', 'DuckDBTools'),
|
||||
'duckdb_tools': ('.duckdb_tools', 'DuckDBTools'),
|
||||
|
||||
# Shell Tools
|
||||
'execute_command': ('.shell_tools', 'ShellTools'),
|
||||
'list_processes': ('.shell_tools', 'ShellTools'),
|
||||
'kill_process': ('.shell_tools', 'ShellTools'),
|
||||
'get_system_info': ('.shell_tools', 'ShellTools'),
|
||||
'shell_tools': ('.shell_tools', 'ShellTools'),
|
||||
}
|
||||
|
||||
_instances = {} # Cache for class instances
|
||||
@@ -149,7 +149,18 @@ def __getattr__(name: str) -> Any:
|
||||
if class_name is None:
|
||||
# Direct function import
|
||||
module = import_module(module_path, __package__)
|
||||
if name in ['duckduckgo', 'file_tools', 'pandas_tools', 'wikipedia_tools',
|
||||
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)
|
||||
|
||||
@@ -12,7 +12,6 @@ results = duckduckgo("AI news")
|
||||
from typing import List, Dict
|
||||
import logging
|
||||
from importlib import util
|
||||
import sys
|
||||
|
||||
def internet_search(query: str) -> List[Dict]:
|
||||
"""Perform an internet search using DuckDuckGo."""
|
||||
@@ -40,8 +39,9 @@ def internet_search(query: str) -> List[Dict]:
|
||||
logging.error(error_msg)
|
||||
return [{"error": error_msg}]
|
||||
|
||||
# Make the module callable
|
||||
sys.modules[__name__].__call__ = internet_search
|
||||
def duckduckgo(query: str) -> List[Dict]:
|
||||
"""Alias for internet_search function."""
|
||||
return internet_search(query)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
|
||||
@@ -160,6 +160,9 @@ class PandasTools:
|
||||
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:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Tools module for PraisonAI Agents"""
|
||||
from .internet_search import internet_search
|
||||
from .duckduckgo_tools import internet_search
|
||||
|
||||
class Tools:
|
||||
"""Tools class for backward compatibility"""
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import wikipedia_tools
|
||||
summary = wikipedia_tools.get_summary("Python programming language")
|
||||
page = wikipedia_tools.get_page("Python programming language")
|
||||
results = wikipedia_tools.search("Python programming")
|
||||
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 get_wikipedia_summary, get_wikipedia_page
|
||||
summary = get_wikipedia_summary("Python programming language")
|
||||
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page
|
||||
summary = wiki_summary("Python programming language")
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -32,7 +32,7 @@ class WikipediaTools:
|
||||
# Set default language to English
|
||||
wikipedia.set_lang("en")
|
||||
|
||||
def search(
|
||||
def wiki_search(
|
||||
self,
|
||||
query: str,
|
||||
results: int = 10,
|
||||
@@ -63,7 +63,7 @@ class WikipediaTools:
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_summary(
|
||||
def wiki_summary(
|
||||
self,
|
||||
title: str,
|
||||
sentences: int = 5,
|
||||
@@ -92,7 +92,7 @@ class WikipediaTools:
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def get_page(
|
||||
def wiki_page(
|
||||
self,
|
||||
title: str,
|
||||
auto_suggest: bool = True
|
||||
@@ -168,7 +168,7 @@ class WikipediaTools:
|
||||
|
||||
return sections
|
||||
|
||||
def get_random(self, pages: int = 1) -> Union[List[str], Dict[str, str]]:
|
||||
def wiki_random(self, pages: int = 1) -> Union[List[str], Dict[str, str]]:
|
||||
"""
|
||||
Get random Wikipedia page titles.
|
||||
|
||||
@@ -185,7 +185,7 @@ class WikipediaTools:
|
||||
logging.error(error_msg)
|
||||
return {"error": error_msg}
|
||||
|
||||
def set_language(self, language: str) -> bool:
|
||||
def wiki_language(self, language: str) -> bool:
|
||||
"""
|
||||
Set the language for Wikipedia searches.
|
||||
|
||||
@@ -205,11 +205,11 @@ class WikipediaTools:
|
||||
|
||||
# Create instance for direct function access
|
||||
_wikipedia_tools = WikipediaTools()
|
||||
search = _wikipedia_tools.search
|
||||
get_summary = _wikipedia_tools.get_summary
|
||||
get_page = _wikipedia_tools.get_page
|
||||
get_random = _wikipedia_tools.get_random
|
||||
set_language = _wikipedia_tools.set_language
|
||||
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
|
||||
@@ -220,7 +220,7 @@ if __name__ == "__main__":
|
||||
# 1. Search for a topic
|
||||
print("1. Searching Wikipedia")
|
||||
print("------------------------------")
|
||||
search_results = search("Python programming language")
|
||||
search_results = wiki_search("Python programming language")
|
||||
print("Search results:")
|
||||
print(json.dumps(search_results, indent=2))
|
||||
print()
|
||||
@@ -228,7 +228,7 @@ if __name__ == "__main__":
|
||||
# 2. Get a page summary
|
||||
print("2. Getting Page Summary")
|
||||
print("------------------------------")
|
||||
summary = get_summary("Python programming language", sentences=3)
|
||||
summary = wiki_summary("Python programming language", sentences=3)
|
||||
print("Summary:")
|
||||
print(summary)
|
||||
print()
|
||||
@@ -236,7 +236,7 @@ if __name__ == "__main__":
|
||||
# 3. Get detailed page information
|
||||
print("3. Getting Full Page Information")
|
||||
print("------------------------------")
|
||||
page = get_page("Python programming language")
|
||||
page = wiki_page("Python programming language")
|
||||
if isinstance(page, dict) and "error" not in page:
|
||||
print("Page sections:")
|
||||
for section in page["sections"]:
|
||||
@@ -252,7 +252,7 @@ if __name__ == "__main__":
|
||||
# 4. Get random pages
|
||||
print("4. Getting Random Pages")
|
||||
print("------------------------------")
|
||||
random_pages = get_random(3)
|
||||
random_pages = wiki_random(3)
|
||||
print("Random page titles:")
|
||||
print(json.dumps(random_pages, indent=2))
|
||||
print()
|
||||
@@ -260,9 +260,9 @@ if __name__ == "__main__":
|
||||
# 5. Try different language
|
||||
print("5. Changing Language")
|
||||
print("------------------------------")
|
||||
success = set_language("es")
|
||||
success = wiki_language("es")
|
||||
if success:
|
||||
summary = get_summary("Python (lenguaje de programación)", sentences=1)
|
||||
summary = wiki_summary("Python (lenguaje de programación)", sentences=1)
|
||||
print("Spanish summary:")
|
||||
print(summary)
|
||||
print()
|
||||
|
||||
@@ -145,7 +145,15 @@ class YFinanceTools:
|
||||
return [{"error": "yfinance package not available"}]
|
||||
|
||||
hist = ticker.history(period=period, interval=interval, start=start, end=end)
|
||||
return hist.reset_index().to_dict('records')
|
||||
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)
|
||||
|
||||
@@ -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}")
|
||||
+20
-2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.24"
|
||||
version = "0.0.46"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
authors = [
|
||||
{ name="Mervin Praison" }
|
||||
@@ -13,4 +13,22 @@ dependencies = [
|
||||
"pydantic",
|
||||
"rich",
|
||||
"openai"
|
||||
]
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
memory = [
|
||||
"chromadb>=0.5.23"
|
||||
]
|
||||
|
||||
knowledge = [
|
||||
"mem0ai>=0.1.0",
|
||||
"chromadb==0.5.23",
|
||||
"markitdown",
|
||||
"chonkie"
|
||||
]
|
||||
|
||||
# Combined features
|
||||
all = [
|
||||
"praisonaiagents[memory]",
|
||||
"praisonaiagents[knowledge]"
|
||||
]
|
||||
@@ -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,22 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a loop agent that creating a loop of tasks.",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description="Create the list of tasks to be looped through.",
|
||||
agent=agent,
|
||||
task_type="loop",
|
||||
input_file="tasks.csv"
|
||||
)
|
||||
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="workflow",
|
||||
max_iter=30
|
||||
)
|
||||
|
||||
agents.start()
|
||||
@@ -0,0 +1,139 @@
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import read_csv, write_csv
|
||||
import pandas as pd
|
||||
|
||||
def read_csv_smart(csv_path: str) -> pd.DataFrame:
|
||||
"""Read CSV and use first column as task, handling both header/no-header cases"""
|
||||
# Try reading with headers first
|
||||
df = pd.read_csv(csv_path)
|
||||
|
||||
# Check if first row looks like data
|
||||
if not any(col.lower() in ['task', 'description', 'status', 'state'] for col in df.columns):
|
||||
# If no header-like names found, read without headers
|
||||
df = pd.read_csv(csv_path, header=None)
|
||||
|
||||
# Rename columns consistently
|
||||
df.columns = [*['task'], *[f'col_{i}' for i in range(1, len(df.columns))]]
|
||||
if 'status' not in df.columns:
|
||||
df['status'] = 'pending'
|
||||
if 'loop_id' not in df.columns:
|
||||
# Start loop_id from 1 instead of 0
|
||||
df['loop_id'] = (df.index + 1).astype(str)
|
||||
return df[['task', 'status', 'loop_id']]
|
||||
|
||||
def has_pending_tasks(df: pd.DataFrame) -> bool:
|
||||
"""Check if there are any pending tasks in the DataFrame"""
|
||||
return not df[df['status'] == 'pending'].empty
|
||||
|
||||
def check_task_status(csv_path: str) -> dict:
|
||||
"""Read task status from CSV file"""
|
||||
try:
|
||||
# Always read directly from CSV file
|
||||
df = pd.read_csv(csv_path, keep_default_na=False)
|
||||
|
||||
# Get only pending tasks from original status
|
||||
pending_tasks = df[df['status'].str.lower() == 'pending']
|
||||
|
||||
if not pending_tasks.empty:
|
||||
task_data = pending_tasks.iloc[0].to_dict()
|
||||
return {
|
||||
'has_pending': True,
|
||||
'next_task': task_data
|
||||
}
|
||||
return {'has_pending': False, 'next_task': None}
|
||||
except Exception as e:
|
||||
print(f"Error reading CSV: {e}")
|
||||
return {'has_pending': False, 'next_task': None, 'error': str(e)}
|
||||
|
||||
def get_next_task() -> dict:
|
||||
"""Get the next pending task from CSV"""
|
||||
status = check_task_status("tasks.csv")
|
||||
if status['has_pending'] and status['next_task']:
|
||||
task = status['next_task']
|
||||
if task['status'].lower() == 'pending':
|
||||
return {
|
||||
'loop_data': {
|
||||
'task': task['task'],
|
||||
'loop_id': task['loop_id'],
|
||||
'status': task['status']
|
||||
}
|
||||
}
|
||||
return None
|
||||
|
||||
def update_task_status(loop_id: str, new_status: str) -> bool:
|
||||
"""Update task status in CSV file using loop_id as identifier"""
|
||||
try:
|
||||
# Read directly from CSV file
|
||||
df = pd.read_csv("tasks.csv", keep_default_na=False)
|
||||
df['loop_id'] = df['loop_id'].astype(str)
|
||||
mask = df['loop_id'] == str(loop_id)
|
||||
if mask.any():
|
||||
df.loc[mask, 'status'] = new_status
|
||||
df.to_csv("tasks.csv", index=False)
|
||||
return True
|
||||
print(f"No task found with loop_id: {loop_id}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error updating status: {e}")
|
||||
return False
|
||||
|
||||
identify_agent = Agent(
|
||||
name="Identify Agent",
|
||||
role="Pending Task Identifier",
|
||||
goal="Identify the next pending task",
|
||||
tools=[get_next_task],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Create the repetitive agent
|
||||
repetitive_agent = Agent(
|
||||
name="TaskProcessor",
|
||||
role="Task Processing Specialist",
|
||||
goal="Complete the task response",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
update_status_agent = Agent(
|
||||
name="UpdateStatusAgent",
|
||||
role="Status Updater",
|
||||
goal="Update the status of the task in CSV",
|
||||
tools=[update_task_status],
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
# Define the repetitive task
|
||||
identify_task = Task(
|
||||
name="identify_task",
|
||||
description="Identify the next pending task. If there are no pending tasks decision is completed.",
|
||||
agent=identify_agent,
|
||||
task_type="loop",
|
||||
condition={
|
||||
"pending": ["process_task"],
|
||||
"completed": "exit"
|
||||
}
|
||||
)
|
||||
|
||||
process_task = Task(
|
||||
name="process_task",
|
||||
description="Complete the task mentioned in the response",
|
||||
agent=repetitive_agent,
|
||||
context=[identify_task],
|
||||
task_type="loop",
|
||||
next_tasks=["update_task"]
|
||||
)
|
||||
|
||||
update_task = Task(
|
||||
name="update_task",
|
||||
description="Update the task status",
|
||||
agent=update_status_agent,
|
||||
next_tasks= ["identify_task"]
|
||||
)
|
||||
|
||||
# Create and run agents manager
|
||||
agents = PraisonAIAgents(
|
||||
agents=[identify_agent, repetitive_agent],
|
||||
tasks=[identify_task, process_task, update_task],
|
||||
process="workflow"
|
||||
)
|
||||
|
||||
agents.start()
|
||||
@@ -0,0 +1,15 @@
|
||||
# Core dependencies
|
||||
pydantic
|
||||
rich
|
||||
openai>=1.30.0
|
||||
|
||||
# Memory dependencies
|
||||
chromadb>=0.5.23
|
||||
|
||||
# Knowledge dependencies
|
||||
mem0ai>=0.1.0
|
||||
markitdown
|
||||
chonkie
|
||||
|
||||
# Development dependencies
|
||||
python-dotenv>=0.19.0
|
||||
@@ -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)
|
||||
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
# Sample Markdown
|
||||
## Section 1
|
||||
This is a test markdown file.
|
||||
## Section 2
|
||||
It contains some formatted text and sections.
|
||||
@@ -0,0 +1,3 @@
|
||||
This is a sample text file.
|
||||
It contains multiple lines.
|
||||
This is for testing purposes.
|
||||
@@ -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")
|
||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
import streamlit as st
|
||||
from praisonaiagents import Agent, Tools
|
||||
from praisonaiagents.tools import duckduckgo
|
||||
|
||||
st.title("AI Research Assistant")
|
||||
st.write("Enter your research query below to get started!")
|
||||
|
||||
# Initialize the research agent
|
||||
agent = Agent(instructions="You are a Research Agent", tools=[duckduckgo])
|
||||
|
||||
# Create the input field
|
||||
query = st.text_input("Research Query", placeholder="Enter your research topic...")
|
||||
|
||||
# Add a search button
|
||||
if st.button("Search"):
|
||||
if query:
|
||||
with st.spinner("Researching..."):
|
||||
result = agent.start(query)
|
||||
st.write(result)
|
||||
else:
|
||||
st.warning("Please enter a research query")
|
||||
Whitespace-only changes.
@@ -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,100 @@
|
||||
Write a product description for a smartwatch in 2 lines.
|
||||
Create a user manual for a blender in 2 lines.
|
||||
Draft an apology email for delayed delivery in 2 lines.
|
||||
Write a blog post on "Benefits of Meditation" in 2 lines.
|
||||
Create a privacy policy for a website in 2 lines.
|
||||
Write instructions for setting up a Wi-Fi router in 2 lines.
|
||||
Draft an email to follow up after a job interview in 2 lines.
|
||||
Create a FAQ page for an e-commerce store in 2 lines.
|
||||
Write an invitation email for a webinar in 2 lines.
|
||||
Create a terms and conditions page for an app in 2 lines.
|
||||
Write a press release for a product launch in 2 lines.
|
||||
Create a checklist for house cleaning tasks in 2 lines.
|
||||
Draft a resignation letter template in 2 lines.
|
||||
Write a cover letter for a software developer role in 2 lines.
|
||||
Create onboarding instructions for new employees in 2 lines.
|
||||
Write a memo announcing a new policy in a company in 2 lines.
|
||||
Create a safety guidelines document for a workplace in 2 lines.
|
||||
Write a pitch email to potential investors in 2 lines.
|
||||
Draft a thank-you email for attending a conference in 2 lines.
|
||||
Create a refund policy template for an online store in 2 lines.
|
||||
Write a blog on "How to Improve Time Management" in 2 lines.
|
||||
Create step-by-step instructions for changing a car tyre in 2 lines.
|
||||
Write a welcome email for new subscribers in 2 lines.
|
||||
Draft an event agenda template in 2 lines.
|
||||
Create a code of conduct for an online forum in 2 lines.
|
||||
Write a guide for beginners to start a blog in 2 lines.
|
||||
Create a checklist for organising a party in 2 lines.
|
||||
Draft an email to request a product review in 2 lines.
|
||||
Write a policy for remote work in a company in 2 lines.
|
||||
Create a simple tutorial for using Excel formulas in 2 lines.
|
||||
Write a thank-you letter for a donation in 2 lines.
|
||||
Draft a meeting minutes template in 2 lines.
|
||||
Create an FAQ for a gym membership in 2 lines.
|
||||
Write a blog post about "Healthy Snacks for Kids" in 2 lines.
|
||||
Create a guide for securing your social media accounts in 2 lines.
|
||||
Draft an email to request a testimonial from a client in 2 lines.
|
||||
Write a safety checklist for camping trips in 2 lines.
|
||||
Create a script for a customer service call in 2 lines.
|
||||
Write a tutorial for creating a simple PowerPoint presentation in 2 lines.
|
||||
Create a packing list for a beach vacation in 2 lines.
|
||||
Draft an email to cancel a service subscription in 2 lines.
|
||||
Write a personal statement for university admission in 2 lines.
|
||||
Create a template for tracking monthly expenses in 2 lines.
|
||||
Write a guide for planting and caring for succulents in 2 lines.
|
||||
Create a simple user guide for a mobile app in 2 lines.
|
||||
Draft an email to announce a team-building activity in 2 lines.
|
||||
Write a step-by-step guide for baking a cake in 2 lines.
|
||||
Create a list of house maintenance tasks in 2 lines.
|
||||
Draft a partnership proposal email in 2 lines.
|
||||
Write a guide for basic photography tips in 2 lines.
|
||||
Create a feedback form template for an online course in 2 lines.
|
||||
Write a blog on "Top 5 Budget Travel Destinations" in 2 lines.
|
||||
Draft a letter of recommendation for a colleague in 2 lines.
|
||||
Create a workout plan for beginners in 2 lines.
|
||||
Write a guide for troubleshooting common tech issues in 2 lines.
|
||||
Draft a reminder email for an upcoming appointment in 2 lines.
|
||||
Create a guide for setting personal goals in 2 lines.
|
||||
Write a blog post about "How to Start Freelancing" in 2 lines.
|
||||
Create a template for event planning in 2 lines.
|
||||
Draft an email to notify users about system maintenance in 2 lines.
|
||||
Write a guide for choosing the right laptop in 2 lines.
|
||||
Create a checklist for moving to a new house in 2 lines.
|
||||
Draft an email to update a client on project status in 2 lines.
|
||||
Write a guide for reducing screen time in 2 lines.
|
||||
Create a shopping list template for weekly groceries in 2 lines.
|
||||
Draft a thank-you email for completing a survey in 2 lines.
|
||||
Write a blog on "Simple DIY Crafts for Beginners" in 2 lines.
|
||||
Create a checklist for starting a small business in 2 lines.
|
||||
Draft an email to apologise for a customer complaint in 2 lines.
|
||||
Write a guide for setting up a personal budget in 2 lines.
|
||||
Create a policy for using company-provided equipment in 2 lines.
|
||||
Draft an email to notify about a price increase in 2 lines.
|
||||
Write a tutorial for creating a resume using Word in 2 lines.
|
||||
Create a simple guide for organising a charity event in 2 lines.
|
||||
Draft a formal invitation letter for a conference in 2 lines.
|
||||
Write a blog post about "Top Productivity Apps" in 2 lines.
|
||||
Create a checklist for first-time pet owners in 2 lines.
|
||||
Draft an email to confirm a service appointment in 2 lines.
|
||||
Write a beginner’s guide for learning to code in 2 lines.
|
||||
Create a guide for assembling a basic toolkit in 2 lines.
|
||||
Draft a welcome email for a new team member in 2 lines.
|
||||
Write a guide for preparing a home for winter in 2 lines.
|
||||
Create a schedule template for meal planning in 2 lines.
|
||||
Draft an email to request feedback from a customer in 2 lines.
|
||||
Write a guide on "How to Build a Morning Routine" in 2 lines.
|
||||
Create a checklist for hosting a virtual meeting in 2 lines.
|
||||
Draft a newsletter for a non-profit organisation in 2 lines.
|
||||
Write a guide for beginners to use Canva in 2 lines.
|
||||
Create a guide for organising your workspace in 2 lines.
|
||||
Draft an email to invite users to a beta program in 2 lines.
|
||||
Write a step-by-step guide for creating a podcast in 2 lines.
|
||||
Create a simple guide for buying your first car in 2 lines.
|
||||
Draft an email to announce a new team member in 2 lines.
|
||||
Write a tutorial on creating basic charts in Excel in 2 lines.
|
||||
Create a guide for teaching basic first aid in 2 lines.
|
||||
Draft an email to follow up after a sales call in 2 lines.
|
||||
Write a blog post on "How to Save Money on Utilities" in 2 lines.
|
||||
Create a guide for designing a basic website in 2 lines.
|
||||
Draft a thank-you email for a business referral in 2 lines.
|
||||
Write a guide for setting up a new smartphone in 2 lines.
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 22.
|
@@ -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
+2168
-1
File diff suppressed because it is too large.
Load diff
Loaded 100 of 297 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user