The expiry date of the run
- is {(new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) == "Invalid Da" ? expiryDate : (new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) }
+ is {(new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) == "Invalid Da" ? expiryDate : (new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10)}
@@ -209,13 +211,14 @@ export default function App() {
Continue with Github
- By continuing, you agree to Super AGI’s Terms of Service and Privacy Policy, and to receive important updates.
+ By continuing, you agree to Super AGI’s Terms of Service and Privacy Policy, and to receive important
+ updates.
-
:
-
{loadingText}
+
:
+
{loadingText}
}
-
) : true }
+
) : true}
);
}
diff --git a/gui/pages/api/DashboardService.js b/gui/pages/api/DashboardService.js
index cf976ab64..8adb26bc7 100644
--- a/gui/pages/api/DashboardService.js
+++ b/gui/pages/api/DashboardService.js
@@ -131,6 +131,10 @@ export const updatePermissions = (permissionId, data) => {
return api.put(`/agentexecutionpermissions/update/status/${permissionId}`, data)
}
+export const deleteAgent = (agentId) => {
+ return api.put(`/agents/delete/${agentId}`)
+}
+
export const authenticateGoogleCred = (toolKitId) => {
return api.get(`/google/get_google_creds/toolkit_id/${toolKitId}`);
}
@@ -186,15 +190,19 @@ export const getDateTime = (agentId) => {
export const getMetrics = () => {
return api.get(`/analytics/metrics`)
}
+
export const getAllAgents = () => {
return api.get(`/analytics/agents/all`)
}
+
export const getAgentRuns = (agent_id) => {
return api.get(`analytics/agents/${agent_id}`);
}
+
export const getActiveRuns = () => {
return api.get(`analytics/runs/active`);
}
+
export const getToolsUsage = () => {
return api.get(`analytics/tools/used`);
}
\ No newline at end of file
diff --git a/gui/public/images/duckduckgo_icon.png b/gui/public/images/duckduckgo_icon.png
new file mode 100644
index 000000000..a98719646
Binary files /dev/null and b/gui/public/images/duckduckgo_icon.png differ
diff --git a/gui/utils/utils.js b/gui/utils/utils.js
index e3781c9e7..2375d56cc 100644
--- a/gui/utils/utils.js
+++ b/gui/utils/utils.js
@@ -1,5 +1,5 @@
-import { formatDistanceToNow, parseISO } from 'date-fns';
-import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
+import {formatDistanceToNow} from 'date-fns';
+import {utcToZonedTime} from 'date-fns-tz';
import {baseUrl} from "@/pages/api/apiConfig";
import {EventBus} from "@/utils/eventBus";
import JSZip from "jszip";
@@ -48,6 +48,7 @@ export const formatNumber = (number) => {
return scaledNumber.toFixed(1) + suffix;
};
+
export const formatTime = (lastExecutionTime) => {
try {
const parsedTime = new Date(lastExecutionTime + 'Z'); // append 'Z' to indicate UTC
@@ -62,15 +63,16 @@ export const formatTime = (lastExecutionTime) => {
addSuffix: true,
includeSeconds: true
}).replace(/about\s/, '')
- .replace(/minutes?/, 'min')
- .replace(/hours?/, 'hrs')
- .replace(/days?/, 'day')
- .replace(/weeks?/, 'week');
+ .replace(/minutes?/, 'min')
+ .replace(/hours?/, 'hrs')
+ .replace(/days?/, 'day')
+ .replace(/weeks?/, 'week');
} catch (error) {
console.error('Error formatting time:', error);
return 'Invalid Time';
}
};
+
export const formatBytes = (bytes, decimals = 2) => {
if (bytes === 0) {
return '0 Bytes';
@@ -94,7 +96,7 @@ export const downloadFile = (fileId, fileName = null) => {
Authorization: `Bearer ${authToken}`,
};
- return fetch(url, { headers })
+ return fetch(url, {headers})
.then((response) => response.blob())
.then((blob) => {
if (fileName) {
@@ -124,7 +126,7 @@ export const downloadFile = (fileId, fileName = null) => {
}
};
-export const downloadAllFiles = (files,run_name) => {
+export const downloadAllFiles = (files, run_name) => {
const zip = new JSZip();
const promises = [];
const fileNamesCount = {};
@@ -155,21 +157,21 @@ export const downloadAllFiles = (files,run_name) => {
});
Promise.all(promises)
- .then(() => {
- zip.generateAsync({ type: "blob" })
- .then((content) => {
- const now = new Date();
- const timestamp = `${now.getFullYear()}-${("0" + (now.getMonth() + 1)).slice(-2)}-${("0" + now.getDate()).slice(-2)}_${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`.replace(/:/g, '-');
- const zipFilename = `${run_name}_${timestamp}.zip`;
- const downloadLink = document.createElement("a");
- downloadLink.href = URL.createObjectURL(content);
- downloadLink.download = zipFilename;
- downloadLink.click();
- })
- .catch((error) => {
- console.error("Error generating zip:", error);
- });
- });
+ .then(() => {
+ zip.generateAsync({type: "blob"})
+ .then((content) => {
+ const now = new Date();
+ const timestamp = `${now.getFullYear()}-${("0" + (now.getMonth() + 1)).slice(-2)}-${("0" + now.getDate()).slice(-2)}_${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`.replace(/:/g, '-');
+ const zipFilename = `${run_name}_${timestamp}.zip`;
+ const downloadLink = document.createElement("a");
+ downloadLink.href = URL.createObjectURL(content);
+ downloadLink.download = zipFilename;
+ downloadLink.click();
+ })
+ .catch((error) => {
+ console.error("Error generating zip:", error);
+ });
+ });
};
export const refreshUrl = () => {
@@ -339,6 +341,7 @@ export const returnToolkitIcon = (toolkitName) => {
{name: 'File Toolkit', imageSrc: '/images/filemanager_icon.svg'},
{name: 'CodingToolkit', imageSrc: '/images/app-logo-light.png'},
{name: 'Image Generation Toolkit', imageSrc: '/images/app-logo-light.png'},
+ {name: 'DuckDuckGo Search Toolkit', imageSrc: '/images/duckduckgo_icon.png'},
];
const toolkit = toolkitData.find((tool) => tool.name === toolkitName);
@@ -346,18 +349,24 @@ export const returnToolkitIcon = (toolkitName) => {
}
export const returnResourceIcon = (file) => {
+ let fileIcon;
const fileTypeIcons = {
'application/pdf': '/images/pdf_file.svg',
'application/txt': '/images/txt_file.svg',
'text/plain': '/images/txt_file.svg',
- 'image': '/images/img_file.svg',
};
- return fileTypeIcons[file.type] || '/images/default_file.svg';
+ if (file.type.includes('image')) {
+ fileIcon = '/images/img_file.svg';
+ } else {
+ fileIcon = fileTypeIcons[file.type] || '/images/default_file.svg';
+ }
+
+ return fileIcon;
};
export const convertToTitleCase = (str) => {
- if(str === null || str === '') {
+ if (str === null || str === '') {
return '';
}
diff --git a/migrations/versions/cac478732572_delete_agent_feature.py b/migrations/versions/cac478732572_delete_agent_feature.py
new file mode 100644
index 000000000..e0c483975
--- /dev/null
+++ b/migrations/versions/cac478732572_delete_agent_feature.py
@@ -0,0 +1,23 @@
+"""delete_agent_feature
+
+Revision ID: cac478732572
+Revises: e39295ec089c
+Create Date: 2023-07-13 17:18:42.003412
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'cac478732572'
+down_revision = 'e39295ec089c'
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+ op.add_column('agents', sa.Column('is_deleted', sa.Boolean(), nullable=True))
+
+def downgrade() -> None:
+ op.drop_column('agents', 'is_deleted')
diff --git a/superagi/agent/output_parser.py b/superagi/agent/output_parser.py
index a2a7fcefd..bd3747580 100644
--- a/superagi/agent/output_parser.py
+++ b/superagi/agent/output_parser.py
@@ -23,11 +23,14 @@ class BaseOutputParser(ABC):
def parse(self, text: str) -> AgentGPTAction:
"""Return AgentGPTAction"""
+
class AgentSchemaOutputParser(BaseOutputParser):
def parse(self, response: str) -> AgentGPTAction:
if response.startswith("```") and response.endswith("```"):
response = "```".join(response.split("```")[1:-1])
- response = JsonCleaner.extract_json_section(response)
+ response = JsonCleaner.extract_json_section(response)
+ # ast throws error if true/false params passed in json
+ response = JsonCleaner.clean_boolean(response)
# OpenAI returns `str(content_dict)`, literal_eval reverses this
try:
@@ -40,76 +43,3 @@ def parse(self, response: str) -> AgentGPTAction:
except BaseException as e:
logger.info(f"AgentSchemaOutputParser: Error parsing JSON respons {e}")
return {}
-
-class AgentOutputParser(BaseOutputParser):
- def parse(self, text: str) -> AgentGPTAction:
- try:
- logger.info(text)
- text = JsonCleaner.check_and_clean_json(text)
- parsed = json5.loads(text)
- except json.JSONDecodeError:
- return AgentGPTAction(
- name="ERROR",
- args={"error": f"Could not parse invalid json: {text}"},
- )
- try:
- format_prefix_yellow = "\033[93m\033[1m"
- format_suffix_yellow = "\033[0m\033[0m"
- format_prefix_green = "\033[92m\033[1m"
- format_suffix_green = "\033[0m\033[0m"
- logger.info(format_prefix_green + "Intelligence : " + format_suffix_green)
- if "text" in parsed["thoughts"]:
- logger.info(format_prefix_yellow + "Thoughts: " + format_suffix_yellow + parsed["thoughts"]["text"] + "\n")
-
- if "reasoning" in parsed["thoughts"]:
- logger.info(format_prefix_yellow + "Reasoning: " + format_suffix_yellow + parsed["thoughts"]["reasoning"] + "\n")
-
- if "plan" in parsed["thoughts"]:
- logger.info(format_prefix_yellow + "Plan: " + format_suffix_yellow + str(parsed["thoughts"]["plan"]) + "\n")
-
- if "criticism" in parsed["thoughts"]:
- logger.info(format_prefix_yellow + "Criticism: " + format_suffix_yellow + parsed["thoughts"]["criticism"] + "\n")
-
- logger.info(format_prefix_green + "Action : " + format_suffix_green)
- # print(format_prefix_yellow + "Args: "+ format_suffix_yellow + parsed["tool"]["args"] + "\n")
- if "tool" not in parsed:
- raise Exception("No tool found in the response..")
- if parsed["tool"] is None or not parsed["tool"]:
- return AgentGPTAction(name="", args="")
- if "name" in parsed["tool"]:
- logger.info(format_prefix_yellow + "Tool: " + format_suffix_yellow + parsed["tool"]["name"] + "\n")
- args = {}
- if "args" in parsed["tool"]:
- args = parsed["tool"]["args"]
- return AgentGPTAction(
- name=parsed["tool"]["name"],
- args=args,
- )
- except (KeyError, TypeError) as e:
- logger.error(f"Error parsing output:", e)
- # If the tool is null or incomplete, return an erroneous tool
- return AgentGPTAction(
- name="ERROR", args={"error": f"Unable to parse the output: {parsed}"}
- )
-
- def parse_tasks(self, text: str) -> AgentTasks:
- try:
- parsed = json.loads(text, strict=False)
- except json.JSONDecodeError:
- preprocessed_text = JsonCleaner.preprocess_json_input(text)
- try:
- parsed = json.loads(preprocessed_text, strict=False)
- except Exception:
- return AgentTasks(
- error=f"Could not parse invalid json: {text}",
- )
- try:
- logger.info("Tasks: ", parsed["tasks"])
- return AgentTasks(
- tasks=parsed["tasks"]
- )
- except (KeyError, TypeError):
- # If the command is null or incomplete, return an erroneous tool
- return AgentTasks(
- error=f"Incomplete tool args: {parsed}",
- )
\ No newline at end of file
diff --git a/superagi/agent/super_agi.py b/superagi/agent/super_agi.py
index 0eda8afdd..cbcda55ca 100644
--- a/superagi/agent/super_agi.py
+++ b/superagi/agent/super_agi.py
@@ -6,7 +6,7 @@
import time
from typing import Any
from typing import Tuple
-import json
+
import numpy as np
from pydantic import ValidationError
from pydantic.types import List
@@ -14,9 +14,10 @@
from sqlalchemy.orm import sessionmaker
from superagi.agent.agent_prompt_builder import AgentPromptBuilder
-from superagi.agent.output_parser import BaseOutputParser, AgentOutputParser, AgentSchemaOutputParser
+from superagi.agent.output_parser import BaseOutputParser, AgentSchemaOutputParser
from superagi.agent.task_queue import TaskQueue
from superagi.apm.event_handler import EventHandler
+from superagi.config.config import get_config
from superagi.helper.token_counter import TokenCounter
from superagi.lib.logger import logger
from superagi.llms.base_llm import BaseLlm
@@ -65,23 +66,6 @@ def __init__(self,
self.agent_config = agent_config
self.agent_execution_config = agent_execution_config
- @classmethod
- def from_llm_and_tools(
- cls,
- ai_name: str,
- ai_role: str,
- memory: VectorStore,
- tools: List[BaseTool],
- llm: BaseLlm
- ) -> SuperAgi:
- return cls(
- ai_name=ai_name,
- ai_role=ai_role,
- llm=llm,
- memory=memory,
- output_parser=AgentOutputParser(),
- tools=tools
- )
def fetch_agent_feeds(self, session, agent_execution_id):
agent_feeds = session.query(AgentExecutionFeed.role, AgentExecutionFeed.feed) \
@@ -114,23 +98,23 @@ def execute(self, workflow_step: AgentWorkflowStep):
if len(agent_feeds) <= 0:
task_queue.clear_tasks()
messages = []
- max_token_limit = 600
+ max_output_token_limit = int(get_config("MAX_TOOL_TOKEN_LIMIT", 600))
# adding history to the messages
if workflow_step.history_enabled:
prompt = self.build_agent_prompt(workflow_step.prompt, task_queue=task_queue,
- max_token_limit=max_token_limit)
+ max_token_limit=max_output_token_limit)
messages.append({"role": "system", "content": prompt})
messages.append({"role": "system", "content": f"The current time and date is {time.strftime('%c')}"})
base_token_limit = TokenCounter.count_message_tokens(messages, self.llm.get_model())
full_message_history = [{'role': role, 'content': feed} for role, feed in agent_feeds]
past_messages, current_messages = self.split_history(full_message_history,
- token_limit - base_token_limit - max_token_limit)
+ token_limit - base_token_limit - max_output_token_limit)
for history in current_messages:
messages.append({"role": history["role"], "content": history["content"]})
messages.append({"role": "user", "content": workflow_step.completion_prompt})
else:
prompt = self.build_agent_prompt(workflow_step.prompt, task_queue=task_queue,
- max_token_limit=max_token_limit)
+ max_token_limit=max_output_token_limit)
messages.append({"role": "system", "content": prompt})
# agent_execution_feed = AgentExecutionFeed(agent_execution_id=self.agent_config["agent_execution_id"],
# agent_id=self.agent_config["agent_id"], feed=template_step.prompt,
diff --git a/superagi/agent/task_queue.py b/superagi/agent/task_queue.py
index b44bfb532..5885f3b5f 100644
--- a/superagi/agent/task_queue.py
+++ b/superagi/agent/task_queue.py
@@ -4,7 +4,7 @@
from superagi.config.config import get_config
-redis_url = get_config('REDIS_URL')
+redis_url = get_config('REDIS_URL') or "localhost:6379"
"""TaskQueue manages current tasks and past tasks in Redis """
class TaskQueue:
def __init__(self, queue_name: str):
diff --git a/superagi/apm/__init__.py b/superagi/apm/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/superagi/apm/analytics_helper.py b/superagi/apm/analytics_helper.py
index 3fadff23e..154fa5778 100644
--- a/superagi/apm/analytics_helper.py
+++ b/superagi/apm/analytics_helper.py
@@ -1,11 +1,10 @@
-from typing import List, Dict, Tuple, Optional, Iterator, Union, Any
+from typing import List, Dict, Union, Any
+
+from sqlalchemy import text, func, and_
from sqlalchemy.orm import Session
-from sqlalchemy.orm.query import Query
+
from superagi.models.events import Event
-from sqlalchemy.exc import SQLAlchemyError
-from sqlalchemy import text, func, Integer, and_
-from collections import defaultdict
-import logging
+
class AnalyticsHelper:
diff --git a/superagi/apm/tools_handler.py b/superagi/apm/tools_handler.py
index 71e3702af..71b0937ce 100644
--- a/superagi/apm/tools_handler.py
+++ b/superagi/apm/tools_handler.py
@@ -1,8 +1,11 @@
from typing import List, Dict
-from sqlalchemy.orm import Session
-from sqlalchemy.orm.query import Query
+
from sqlalchemy import func
+from sqlalchemy.orm import Session
+
from superagi.models.events import Event
+
+
class ToolsHandler:
def __init__(self, session: Session, organisation_id: int):
diff --git a/superagi/controllers/agent.py b/superagi/controllers/agent.py
index 11b98643e..0b7169330 100644
--- a/superagi/controllers/agent.py
+++ b/superagi/controllers/agent.py
@@ -10,6 +10,7 @@
from jsonmerge import merge
from pytz import timezone
from sqlalchemy import func
+from superagi.models.agent_execution_permission import AgentExecutionPermission
from superagi.worker import execute_agent
from superagi.helper.auth import check_auth
from superagi.models.agent import Agent
@@ -105,13 +106,17 @@ def get_agent(agent_id: int,
Agent: An object of Agent representing the retrieved Agent.
Raises:
- HTTPException (Status Code=404): If the Agent is not found.
+ HTTPException (Status Code=404): If the Agent is not found or deleted.
"""
- db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first()
- if not db_agent:
+ if (
+ db_agent := db.session.query(Agent)
+ .filter(Agent.id == agent_id, Agent.is_deleted == False)
+ .first()
+ ):
+ return db_agent
+ else:
raise HTTPException(status_code=404, detail="agent not found")
- return db_agent
@router.put("/update/{agent_id}", response_model=AgentOut)
@@ -136,15 +141,15 @@ def update_agent(agent_id: int, agent: AgentIn,
HTTPException (Status Code=404): If the Agent or associated Project is not found.
"""
- db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first()
+ db_agent = db.session.query(Agent).filter(Agent.id == agent_id, Agent.is_deleted == False).first()
if not db_agent:
raise HTTPException(status_code=404, detail="agent not found")
if agent.project_id:
- project = db.session.query(Project).get(agent.project_id)
- if not project:
+ if project := db.session.query(Project).get(agent.project_id):
+ db_agent.project_id = project.id
+ else:
raise HTTPException(status_code=404, detail="Project not found")
- db_agent.project_id = project.id
db_agent.name = agent.name
db_agent.description = agent.description
@@ -369,7 +374,7 @@ def get_schedule_data(agent_id: int, Authorize: AuthJWT = Depends(check_auth)):
current_datetime = datetime.now(tzone).strftime("%d/%m/%Y %I:%M %p")
- response_data = {
+ return {
"current_datetime": current_datetime,
"start_date": agent.start_time.astimezone(tzone).strftime("%d %b %Y"),
"start_time": agent.start_time.astimezone(tzone).strftime("%I:%M %p"),
@@ -378,8 +383,6 @@ def get_schedule_data(agent_id: int, Authorize: AuthJWT = Depends(check_auth)):
"expiry_runs": agent.expiry_runs if agent.expiry_runs != -1 else None
}
- return response_data
-
@router.get("/get/project/{project_id}")
def get_agents_by_project_id(project_id: int,
@@ -403,11 +406,12 @@ def get_agents_by_project_id(project_id: int,
if not project:
raise HTTPException(status_code=404, detail="Project not found")
- agents = db.session.query(Agent).filter(Agent.project_id == project_id).all()
+ agents = db.session.query(Agent).filter(Agent.project_id == project_id, Agent.is_deleted == False).all()
new_agents, new_agents_sorted = [], []
for agent in agents:
agent_dict = vars(agent)
+
agent_id = agent.id
# Query the AgentExecution table using the agent ID
@@ -445,12 +449,12 @@ def get_agent_configuration(agent_id: int,
dict: Agent configuration including its details.
Raises:
- HTTPException (status_code=404): If the agent is not found.
+ HTTPException (status_code=404): If the agent is not found or deleted.
"""
# Define the agent_config keys to fetch
keys_to_fetch = AgentTemplate.main_keys()
- agent = db.session.query(Agent).filter(agent_id == Agent.id).first()
+ agent = db.session.query(Agent).filter(agent_id == Agent.id, Agent.is_deleted == False).first()
if not agent:
raise HTTPException(status_code=404, detail="Agent not found")
@@ -480,3 +484,36 @@ def get_agent_configuration(agent_id: int,
db.session.close()
return response
+
+@router.put("/delete/{agent_id}", status_code = 200)
+def delete_agent(agent_id: int, Authorize: AuthJWT = Depends(check_auth)):
+ """
+ Delete an existing Agent
+ - Updates the is_deleted flag: Executes a soft delete
+ - AgentExecutions are updated to: "TERMINATED" if agentexecution is created, All the agent executions are updated
+ - AgentExecutionPermission is set to: "REJECTED" if agentexecutionpersmision is created
+
+ Args:
+ agent_id (int): Identifier of the Agent to delete
+
+ Returns:
+ A dictionary containing a "success" key with the value True to indicate a successful delete.
+
+ Raises:
+ HTTPException (Status Code=404): If the Agent or associated Project is not found or deleted already.
+ """
+
+ db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first()
+ db_agent_executions = db.session.query(AgentExecution).filter(AgentExecution.agent_id == agent_id).all()
+
+ if not db_agent or db_agent.is_deleted:
+ raise HTTPException(status_code=404, detail="agent not found")
+
+ # Deletion Procedure
+ db_agent.is_deleted = True
+ if db_agent_executions:
+ # Updating all the RUNNING executions to TERMINATED
+ for db_agent_execution in db_agent_executions:
+ db_agent_execution.status = "TERMINATED"
+
+ db.session.commit()
diff --git a/superagi/controllers/agent_config.py b/superagi/controllers/agent_config.py
index d57d4943e..f3ba396c0 100644
--- a/superagi/controllers/agent_config.py
+++ b/superagi/controllers/agent_config.py
@@ -52,10 +52,10 @@ def create_agent_config(agent_config: AgentConfigurationIn,
AgentConfiguration: The created agent configuration.
Raises:
- HTTPException (Status Code=404): If the associated agent is not found.
+ HTTPException (Status Code=404): If the associated agent is not found or deleted.
"""
- agent = db.session.query(Agent).get(agent_config.agent_id)
+ agent = db.session.query(Agent).filter(Agent.id == agent_config.agent_id, Agent.is_deleted == False).first()
if not agent:
raise HTTPException(status_code=404, detail="Agent not found")
@@ -82,10 +82,14 @@ def get_agent(agent_config_id: int,
HTTPException (Status Code=404): If the agent configuration is not found.
"""
- db_agent_config = db.session.query(AgentConfiguration).filter(AgentConfiguration.id == agent_config_id).first()
- if not db_agent_config:
+ if (
+ db_agent_config := db.session.query(AgentConfiguration)
+ .filter(AgentConfiguration.id == agent_config_id)
+ .first()
+ ):
+ return db_agent_config
+ else:
raise HTTPException(status_code=404, detail="Agent Configuration not found")
- return db_agent_config
@router.put("/update", response_model=AgentConfigurationOut)
@@ -133,10 +137,10 @@ def get_agent_configurations(agent_id: int,
dict: The parsed response containing agent configurations.
Raises:
- HTTPException (Status Code=404): If the agent or agent configurations are not found.
+ HTTPException (Status Code=404): If the agent or agent configurations are not found or deleted.
"""
- agent = db.session.query(Agent).filter(Agent.id == agent_id).first()
+ agent = db.session.query(Agent).filter(Agent.id == agent_id, Agent.is_deleted == False).first()
if not agent:
raise HTTPException(status_code=404, detail="agent not found")
@@ -191,5 +195,5 @@ def get_agent_configurations(agent_id: int,
parsed_response["permission_type"] = value
elif key == "LTM_DB":
parsed_response["LTM_DB"] = value
-
+
return parsed_response
diff --git a/superagi/controllers/agent_execution.py b/superagi/controllers/agent_execution.py
index 54d574caf..646f426bf 100644
--- a/superagi/controllers/agent_execution.py
+++ b/superagi/controllers/agent_execution.py
@@ -73,8 +73,7 @@ def create_agent_execution(agent_execution: AgentExecutionIn,
HTTPException (Status Code=404): If the agent is not found.
"""
- agent = db.session.query(Agent).get(agent_execution.agent_id)
-
+ agent = db.session.query(Agent).filter(Agent.id == agent_execution.agent_id, Agent.is_deleted == False).first()
if not agent:
raise HTTPException(status_code=404, detail="Agent not found")
@@ -182,10 +181,14 @@ def get_agent_execution(agent_execution_id: int,
HTTPException (Status Code=404): If the agent execution is not found.
"""
- db_agent_execution = db.session.query(AgentExecution).filter(AgentExecution.id == agent_execution_id).first()
- if not db_agent_execution:
+ if (
+ db_agent_execution := db.session.query(AgentExecution)
+ .filter(AgentExecution.id == agent_execution_id)
+ .first()
+ ):
+ return db_agent_execution
+ else:
raise HTTPException(status_code=404, detail="Agent execution not found")
- return db_agent_execution
@router.put("/update/{agent_execution_id}", response_model=AgentExecutionOut)
@@ -202,11 +205,17 @@ def update_agent_execution(agent_execution_id: int,
raise HTTPException(status_code=404, detail="Agent Execution not found")
if agent_execution.agent_id:
- agent = db.session.query(Agent).get(agent_execution.agent_id)
- if not agent:
+ if agent := db.session.query(Agent).get(agent_execution.agent_id):
+ db_agent_execution.agent_id = agent.id
+ else:
raise HTTPException(status_code=404, detail="Agent not found")
- db_agent_execution.agent_id = agent.id
- if agent_execution.status != "CREATED" and agent_execution.status != "RUNNING" and agent_execution.status != "PAUSED" and agent_execution.status != "COMPLETED" and agent_execution.status != "TERMINATED":
+ if agent_execution.status not in [
+ "CREATED",
+ "RUNNING",
+ "PAUSED",
+ "COMPLETED",
+ "TERMINATED",
+ ]:
raise HTTPException(status_code=400, detail="Invalid Request")
db_agent_execution.status = agent_execution.status
diff --git a/superagi/helper/feed_parser.py b/superagi/helper/feed_parser.py
index c1e868b38..c05e217b5 100644
--- a/superagi/helper/feed_parser.py
+++ b/superagi/helper/feed_parser.py
@@ -45,7 +45,7 @@ def parse_feed(feed):
if feed.role == "system":
final_output = feed.feed
if "json-schema.org" in feed.feed:
- final_output = feed.feed.split("TOOLS")[0]
+ final_output = feed.feed.split("TOOLS:")[0]
return {"role": "system", "feed": final_output, "updated_at": feed.updated_at,
"time_difference": feed.time_difference}
diff --git a/superagi/helper/json_cleaner.py b/superagi/helper/json_cleaner.py
index b4085a3d3..d97b1ecf4 100644
--- a/superagi/helper/json_cleaner.py
+++ b/superagi/helper/json_cleaner.py
@@ -8,60 +8,20 @@
class JsonCleaner:
@classmethod
- def check_and_clean_json(cls, json_string: str):
+ def clean_boolean(cls, input_str: str = ""):
"""
- Checks if the given string is a valid json string.
- If not, tries to clean it up and return a valid json string.
+ Clean the boolean values in the given string.
Args:
- json_string (str): The json string to be checked and cleaned.
+ input_str (str): The string from which the json section is to be extracted.
Returns:
- str: The cleaned json string.
- """
- try:
- json_string = cls.remove_escape_sequences(json_string)
- json_string = cls.remove_trailing_newline_spaces(json_string)
- json_string = cls.clean_newline_characters(json_string)
-
- json5.loads(json_string)
- return json_string
- except (ValueError, json.JSONDecodeError) as e:
- # If the json is invalid, try to clean it up
- json_string = cls.extract_json_section(json_string)
- json_string = cls.preprocess_json_input(json_string)
- json_string = cls.add_quotes_to_property_names(json_string)
- json_string = cls.remove_escape_sequences(json_string)
- # call this post remove_escape_sequences
- json_string = cls.clean_newline_characters(json_string)
- json_string = cls.balance_braces(json_string)
- try:
- json5.loads(json_string)
- return json_string
- except (ValueError, json.JSONDecodeError) as e:
- logger.info(json_string)
- # If the json is still invalid, try to extract the json section
- json_string = cls.extract_json_section(json_string)
- return json_string
- return json_string
-
- @classmethod
- def preprocess_json_input(cls, input_str: str) -> str:
+ str: The extracted json section.
"""
- Preprocess the given json string.
- Replace single backslashes with double backslashes,
- while leaving already escaped ones intact.
-
- Args:
- input_str (str): The json string to be preprocessed.
+ input_str = re.sub(r':\s*false', ': False', input_str)
+ input_str = re.sub(r':\s*true', ': True', input_str)
+ return input_str
- Returns:
- str: The preprocessed json string.
- """
- corrected_str = re.sub(
- r'(? str:
- """
- Add quotes to property names in the given json string.
-
- Args:
- json_string (str): The json string to be processed.
-
- Returns:
- str: The json string with quotes added to property names.
- """
-
- def replace(match: re.Match) -> str:
- return f'{match.group(1)}"{match.group(2)}":'
-
- json_string = re.sub(r'([,{])\n*\s*(\b\w+\b):', replace, json_string)
-
- return json_string
-
@classmethod
def balance_braces(cls, json_string: str) -> str:
"""
diff --git a/superagi/jobs/agent_executor.py b/superagi/jobs/agent_executor.py
index 68c893cc0..b321361bf 100644
--- a/superagi/jobs/agent_executor.py
+++ b/superagi/jobs/agent_executor.py
@@ -257,7 +257,7 @@ def execute_next_action(self, agent_execution_id):
return
if "retry" in response and response["retry"]:
- superagi.worker.execute_agent.apply_async((agent_execution_id, datetime.now()), countdown=10)
+ superagi.worker.execute_agent.apply_async((agent_execution_id, datetime.now()), countdown=15)
session.close()
return
@@ -343,7 +343,7 @@ def handle_wait_for_permission(self, agent_execution, spawned_agent, session):
if agent_execution_permission.status == "PENDING":
raise ValueError("Permission is still pending")
if agent_execution_permission.status == "APPROVED":
- result = spawned_agent.handle_tool_response(agent_execution_permission.assistant_reply).get("result")
+ result = spawned_agent.handle_tool_response(session, agent_execution_permission.assistant_reply).get("result")
else:
result = f"User denied the permission to run the tool {agent_execution_permission.tool_name}" \
f"{' and has given the following feedback : ' + agent_execution_permission.user_feedback if agent_execution_permission.user_feedback else ''}"
diff --git a/superagi/lib/logger.py b/superagi/lib/logger.py
index 048c0c8a4..f600ff11c 100644
--- a/superagi/lib/logger.py
+++ b/superagi/lib/logger.py
@@ -52,27 +52,27 @@ def _make_custom_log_record(self, name, level, fn, lno, msg, args, exc_info, fun
def debug(self, message, *args):
self.logger.debug(message)
- if len(args) > 0:
+ if args:
self.logger.debug(*args)
def info(self, message, *args):
self.logger.info(message)
- if len(args) > 0:
+ if args:
self.logger.info(*args)
def warning(self, message, *args):
self.logger.warning(message)
- if len(args) > 0:
+ if args:
self.logger.warning(*args)
def error(self, message, *args):
self.logger.error(message)
- if len(args) > 0:
+ if args:
self.logger.error(*args)
def critical(self, message, *args):
self.logger.critical(message)
- if len(args) > 0:
+ if args:
self.logger.critical(*args)
diff --git a/superagi/llms/google_palm.py b/superagi/llms/google_palm.py
index b19b595b3..707a1579e 100644
--- a/superagi/llms/google_palm.py
+++ b/superagi/llms/google_palm.py
@@ -75,7 +75,7 @@ def chat_completion(self, messages, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT
return {"response": completion, "content": completion.result}
except Exception as exception:
logger.info("Google palm Exception:", exception)
- return {"error": exception}
+ return {"error": "ERROR_GOOGLE_PALM", "message": "Google palm exception"}
def verify_access_key(self):
"""
diff --git a/superagi/llms/llm_model_factory.py b/superagi/llms/llm_model_factory.py
index 65d8bd41a..b13f46067 100644
--- a/superagi/llms/llm_model_factory.py
+++ b/superagi/llms/llm_model_factory.py
@@ -18,6 +18,8 @@ def get_model(self, model, **kwargs):
factory = ModelFactory()
factory.register_format("gpt-4", lambda **kwargs: OpenAi(model="gpt-4", **kwargs))
+factory.register_format("gpt-4-32k", lambda **kwargs: OpenAi(model="gpt-4-32k", **kwargs))
+factory.register_format("gpt-3.5-turbo-16k", lambda **kwargs: OpenAi(model="gpt-3.5-turbo-16k", **kwargs))
factory.register_format("gpt-3.5-turbo", lambda **kwargs: OpenAi(model="gpt-3.5-turbo", **kwargs))
factory.register_format("google-palm-bison-001", lambda **kwargs: GooglePalm(model='models/chat-bison-001', **kwargs))
diff --git a/superagi/llms/openai.py b/superagi/llms/openai.py
index 1d44cc6dc..a25c0c847 100644
--- a/superagi/llms/openai.py
+++ b/superagi/llms/openai.py
@@ -1,4 +1,6 @@
import openai
+from openai import APIError, InvalidRequestError
+from openai.error import RateLimitError, AuthenticationError
from superagi.config.config import get_config
from superagi.lib.logger import logger
@@ -73,9 +75,18 @@ def chat_completion(self, messages, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT
)
content = response.choices[0].message["content"]
return {"response": response, "content": content}
+ except AuthenticationError as auth_error:
+ logger.info("OpenAi AuthenticationError:", auth_error)
+ return {"error": "ERROR_AUTHENTICATION", "message": "Authentication error please check the api keys.."}
+ except RateLimitError as api_error:
+ logger.info("OpenAi RateLimitError:", api_error)
+ return {"error": "ERROR_RATE_LIMIT", "message": "Openai rate limit exceeded.."}
+ except InvalidRequestError as invalid_request_error:
+ logger.info("OpenAi InvalidRequestError:", invalid_request_error)
+ return {"error": "ERROR_INVALID_REQUEST", "message": "Openai invalid request error.."}
except Exception as exception:
logger.info("OpenAi Exception:", exception)
- return {"error": exception}
+ return {"error": "ERROR_OPENAI", "message": "Open ai exception"}
def verify_access_key(self):
"""
diff --git a/superagi/models/agent.py b/superagi/models/agent.py
index 9ef14fdab..cca1d6e30 100644
--- a/superagi/models/agent.py
+++ b/superagi/models/agent.py
@@ -2,7 +2,7 @@
import json
-from sqlalchemy import Column, Integer, String
+from sqlalchemy import Column, Integer, String, Boolean
import superagi.models
from superagi.models.agent_config import AgentConfiguration
@@ -25,6 +25,7 @@ class Agent(DBBaseModel):
project_id (int): The identifier of the associated project.
description (str): The description of the agent.
agent_workflow_id (int): The identifier of the associated agent workflow.
+ is_deleted (bool): The flag associated for agent deletion
"""
__tablename__ = 'agents'
@@ -34,7 +35,8 @@ class Agent(DBBaseModel):
project_id = Column(Integer)
description = Column(String)
agent_workflow_id = Column(Integer)
-
+ is_deleted = Column(Boolean, default = False)
+
def __repr__(self):
"""
Returns a string representation of the Agent object.
@@ -44,8 +46,9 @@ def __repr__(self):
"""
return f"Agent(id={self.id}, name='{self.name}', project_id={self.project_id}, " \
- f"description='{self.description}', agent_workflow_id={self.agent_workflow_id})"
-
+ f"description='{self.description}', agent_workflow_id={self.agent_workflow_id}," \
+ f"is_deleted='{self.is_deleted}')"
+
@classmethod
def fetch_configuration(cls, session, agent_id: int):
"""
@@ -79,7 +82,8 @@ def fetch_configuration(cls, session, agent_id: int):
"permission_type": None,
"LTM_DB": None,
"memory_window": None,
- "max_iterations": None
+ "max_iterations": None,
+ "is_deleted": agent.is_deleted,
}
if not agent_configurations:
return parsed_config
@@ -105,7 +109,7 @@ def eval_agent_config(cls, key, value):
return value
elif key in ["project_id", "memory_window", "max_iterations", "iteration_interval"]:
return int(value)
- elif key == "goal" or key == "constraints" or key == "instruction":
+ elif key in ["goal", "constraints", "instruction", "is_deleted"]:
return eval(value)
elif key == "tools":
return [int(x) for x in json.loads(value)]
@@ -197,9 +201,12 @@ def create_agent_with_template_id(cls, db, project_id, agent_template):
configs = db.session.query(AgentTemplateConfig).filter(
AgentTemplateConfig.agent_template_id == agent_template.id).all()
- agent_configurations = []
- for config in configs:
- agent_configurations.append(AgentConfiguration(agent_id=db_agent.id, key=config.key, value=config.value))
+ agent_configurations = [
+ AgentConfiguration(
+ agent_id=db_agent.id, key=config.key, value=config.value
+ )
+ for config in configs
+ ]
db.session.add_all(agent_configurations)
db.session.commit()
db.session.flush()
@@ -229,9 +236,10 @@ def create_agent_with_marketplace_template_id(cls, db, project_id, agent_templat
db.session.flush() # Flush pending changes to generate the agent's ID
db.session.commit()
- agent_configurations = []
- for key, value in agent_template["configs"].items():
- agent_configurations.append(AgentConfiguration(agent_id=db_agent.id, key=key, value=value["value"]))
+ agent_configurations = [
+ AgentConfiguration(agent_id=db_agent.id, key=key, value=value["value"])
+ for key, value in agent_template["configs"].items()
+ ]
db.session.add_all(agent_configurations)
db.session.commit()
db.session.flush()
diff --git a/superagi/tools/base_tool.py b/superagi/tools/base_tool.py
index e066c8046..5c072832f 100644
--- a/superagi/tools/base_tool.py
+++ b/superagi/tools/base_tool.py
@@ -94,7 +94,7 @@ def _execute(self, *args: Any, **kwargs: Any):
@property
def max_token_limit(self):
- return get_config("MAX_TOOL_TOKEN_LIMIT", 600)
+ return int(get_config("MAX_TOOL_TOKEN_LIMIT", 600))
def _parse_input(
self,
diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py
index 107589d56..9c1a001a9 100644
--- a/superagi/tools/code/write_code.py
+++ b/superagi/tools/code/write_code.py
@@ -99,7 +99,7 @@ def _execute(self, code_description: str) -> str:
# Get README contents and save
split_result = result["content"].split("```")
- if len(split_result) > 0:
+ if split_result:
readme = split_result[0]
save_readme_result = self.resource_manager.write_file("README.md", readme)
if save_readme_result.startswith("Error"):
diff --git a/superagi/tools/email/README.md b/superagi/tools/email/README.md
new file mode 100644
index 000000000..bb360e296
--- /dev/null
+++ b/superagi/tools/email/README.md
@@ -0,0 +1,91 @@
+
+
+# SuperAGI Email Tool
+
+The robust SuperAGI Email Tool lets users send and read emails while providing a foundation for other fascinating use cases.
+
+## 💡 Features
+
+1.**Read Emails:** With SuperAGI's Email Tool, you can effortlessly manage your inbox and ensure that you never overlook a critical detail.
+
+2. **Send Emails:** SuperAGI's Email Tool uses its comprehensive language model capabilities to create personalised, context-aware emails, sparing you effort and time.
+
+3. **Save Emails to Drafts Folder:** By allowing SuperAGI to develop email draughts that you can examine and modify before sending, you'll gain greater control and make sure your messages are tailored to your tastes.
+
+4. **Send Emails with Attachments:** Send attachments in emails with ease to enrich and expand the scope of your message.
+
+5. **Custom Email Signature:** Create a unique signature for each email you send to add a touch of customization and automation.
+
+6. **Auto-Reply and Answer Questions:** Allow SuperAGI to read, analyse, and respond to incoming emails with precise answers to streamline your email responses.
+
+## ⚙️ Installation
+
+### 🛠 **Setting Up of SuperAGI**
+Set up the SuperAGI by following the instructions given (https://github.com/TransformerOptimus/SuperAGI/blob/main/README.MD)
+
+### 🔧 **Add Email configuration settings SuperAGI's Dashboard**
+
+![Config_Page](https://github.com/Phoenix2809/SuperAGI/assets/133874957/6abe8f84-370e-4512-8374-e7eebe5f836d)
+
+Add the following configuration in the Email Toolkit Page:
+
+1. _Email address and password:_
+ - Set 'EMAIL_ADDRESS' to sender's email address
+ - Set 'EMAIL_PASSWORD' to your Password. If using Gmail, use App Password (Follow the steps given below to obtain your app password.)
+
+2. _Provider-specific settings:_
+ - If not using Gmail, modify 'EMAIL_SMTP_HOST', 'EMAIL_SMTP_PORT', AND 'EMAIL_IMAP_HOST' according to your email service provider.
+
+3. _Sending and Drafts:_
+ - You can set the EMAIL_DRAFT_MODE to "FALSE" if you'd like your email to be directly sent and "TRUE" if you'd like to save your emails in Draft.
+ - If you're setting Draft Mode to True, Make sure to add the draft folder for your email service provider to prevent it from being sent.
+
+4. _Optional Settings:_
+ - Change the 'EMAIL_SIGNATURE' to your personalize signature.
+
+
+## Obtain your App Password
+
+To obtain App password for your Gmail Account follow the steps:
+
+- Navigate to the link (https://myaccount.google.com/apppasswords)
+
+![app_password](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/ec1e6222-e5d4-4b88-a69c-1fd5774ae0ea)
+
+- To get the App Password ensure that you have set up 2-Step Verification for your email address.
+
+- Generate the password by creating a custom app
+
+![password](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/32219756-8715-4f5a-bb1c-0b2cae4e73a3)
+
+- Copy the password generated and use it for 'EMAIL_PASSWORD'
+
+- Also make sure IMAP Access is enabled for your Gmail Address (Settings > See all settings > Forwarding and POP/IMAP > Enable IMAP)
+
+![imap_enable](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/50ef3e0c-c2ff-4848-aba7-8a6bd4a800ab)
+
+## Running SuperAGI Email Tool
+
+1. **Read an email**
+
+By default SuperAGI's email tool reads last 10 emails from your inbox, to change the limit you can modify the default limit in read_email.py
+
+2. **Send an email**
+
+To send an email to a particular receiver, mention the receiver's ID in your goal. Email will be stored in drafts if in case receiver's email address is not mentioned.
+
+![send_email](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/c4dc52b9-ab68-4db3-b1f9-3431c00710c4)
+
+3. **Send an email with attachment**
+
+SuperAGI can send Emails with Attachments if you have uploaded the file in the Resource Manager, or if your file is in the Input or the Output of your SuperAGI Workspace.
+
+![attachment](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/de112910-a623-469d-a0db-99063fb8572e)
+```
diff --git a/superagi/tools/google_serp_search/google_serp_search.py b/superagi/tools/google_serp_search/google_serp_search.py
index f780d2d78..e3b0cb438 100644
--- a/superagi/tools/google_serp_search/google_serp_search.py
+++ b/superagi/tools/google_serp_search/google_serp_search.py
@@ -51,7 +51,7 @@ def _execute(self, query: str) -> tuple:
serp_api = GoogleSerpApiWrap(api_key)
response = serp_api.search_run(query)
summary = self.summarise_result(query, response["snippets"])
- if len(response["links"]) > 0:
+ if response["links"]:
return summary + "\n\nLinks:\n" + "\n".join("- " + link for link in response["links"][:3])
return summary
diff --git a/superagi/tools/jira/edit_issue.py b/superagi/tools/jira/edit_issue.py
index d5119dc1f..b8c2e4162 100644
--- a/superagi/tools/jira/edit_issue.py
+++ b/superagi/tools/jira/edit_issue.py
@@ -44,7 +44,7 @@ def _execute(self, key: str, fields: dict):
"""
jira = self.build_jira_instance()
issues = jira.search_issues('key=')
- if len(issues) > 0:
+ if issues:
issues[0].update(fields=fields)
return f"Issue '{issues[0].key}' created successfully!"
return f"Issue not found!"
diff --git a/superagi/tools/twitter/README.md b/superagi/tools/twitter/README.md
new file mode 100644
index 000000000..23d065c39
--- /dev/null
+++ b/superagi/tools/twitter/README.md
@@ -0,0 +1,62 @@
+