basic workflow running
This commit is contained in:
parent
1711abf100
commit
811ed72615
19 changed files with 372 additions and 271 deletions
3
app.py
3
app.py
|
|
@ -28,7 +28,8 @@ from modules.auth import (
|
||||||
|
|
||||||
# Import models - import generically for INITIALIZATION, even if dummy!
|
# Import models - import generically for INITIALIZATION, even if dummy!
|
||||||
import modules.gateway_model as gateway_model
|
import modules.gateway_model as gateway_model
|
||||||
import modules.lucydom_interface as lucydom_model
|
|
||||||
|
#from modules.lucydom_interface import get_lucydom_interface as dom_interface
|
||||||
|
|
||||||
|
|
||||||
def init_logging():
|
def init_logging():
|
||||||
|
|
|
||||||
|
|
@ -52,3 +52,4 @@ Agent_Webcrawler_MAX_SEARCH_RESULTS = 5
|
||||||
# Agent Coder configuration
|
# Agent Coder configuration
|
||||||
Agent_Coder_INSTALL_TIMEOUT = 180
|
Agent_Coder_INSTALL_TIMEOUT = 180
|
||||||
Agent_Coder_EXECUTION_TIMEOUT = 60
|
Agent_Coder_EXECUTION_TIMEOUT = 60
|
||||||
|
Agent_Coder_EXECUTION_RETRY = 5
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ class ChatService:
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"OpenAI Connector initialized with model: {self.model_name}")
|
logger.info(f"OpenAI Connector initialized with model: {self.model_name}")
|
||||||
|
|
||||||
async def call_api(self, messages: List[Dict[str, Any]], temperature: float = None, max_tokens: int = None) -> str:
|
async def call_api(self, messages: List[Dict[str, Any]], temperature: float = None, max_tokens: int = None) -> str:
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,25 @@ from datetime import datetime
|
||||||
from typing import Dict, Any, List, Optional, Union
|
from typing import Dict, Any, List, Optional, Union
|
||||||
|
|
||||||
# Required imports
|
# Required imports
|
||||||
from connectors.connector_aichat_openai import ChatService
|
|
||||||
from modules.chat_registry import get_agent_registry
|
from modules.chat_registry import get_agent_registry
|
||||||
from modules.lucydom_interface import get_lucydom_interface, GLOBAL_SETTINGS
|
from modules.lucydom_interface import get_lucydom_interface as dom_interface
|
||||||
from modules.chat_content_extraction import get_document_contents
|
from modules.chat_content_extraction import get_document_contents
|
||||||
|
|
||||||
# Configure logger
|
# Configure logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Global settings for the workflow management
|
||||||
|
GLOBAL_WorkflowLabels = {
|
||||||
|
"system_name": "AI Assistant", # Default system name for logs
|
||||||
|
"workflow_status_messages": {
|
||||||
|
"init": "Workflow initialized",
|
||||||
|
"running": "Running workflow",
|
||||||
|
"waiting": "Waiting for input",
|
||||||
|
"completed": "Workflow completed",
|
||||||
|
"error": "Error in workflow"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ChatManager:
|
class ChatManager:
|
||||||
"""
|
"""
|
||||||
Manages the processing of chat requests, agent execution, and
|
Manages the processing of chat requests, agent execution, and
|
||||||
|
|
@ -38,13 +49,9 @@ class ChatManager:
|
||||||
"""
|
"""
|
||||||
self.mandate_id = mandate_id
|
self.mandate_id = mandate_id
|
||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
self.ai_service = ChatService()
|
self.mydom = dom_interface(mandate_id, user_id)
|
||||||
self.lucy_interface = get_lucydom_interface(mandate_id, user_id)
|
|
||||||
self.agent_registry = get_agent_registry()
|
self.agent_registry = get_agent_registry()
|
||||||
self.agent_registry.set_ai_service(self.ai_service)
|
self.agent_registry.set_mydom(self.mydom)
|
||||||
|
|
||||||
# Set AI service in lucy interface for language support
|
|
||||||
self.lucy_interface.set_ai_service(self.ai_service)
|
|
||||||
|
|
||||||
### Chat Management
|
### Chat Management
|
||||||
|
|
||||||
|
|
@ -73,9 +80,9 @@ class ChatManager:
|
||||||
obj_workplan = project_manager_response.get("obj_workplan", [])
|
obj_workplan = project_manager_response.get("obj_workplan", [])
|
||||||
obj_user_response = project_manager_response.get("obj_user_response", "")
|
obj_user_response = project_manager_response.get("obj_user_response", "")
|
||||||
|
|
||||||
# Get detected language and set it in the lucy interface
|
# Get detected language and set it in the mydom interface
|
||||||
user_language = project_manager_response.get("user_language", "en")
|
user_language = project_manager_response.get("user_language", "en")
|
||||||
self.lucy_interface.set_user_language(user_language)
|
self.mydom.set_user_language(user_language)
|
||||||
|
|
||||||
# 4. Save the response as a message in the workflow and add log entries
|
# 4. Save the response as a message in the workflow and add log entries
|
||||||
response_message = {
|
response_message = {
|
||||||
|
|
@ -203,7 +210,7 @@ JSON_OUTPUT = {{
|
||||||
}}
|
}}
|
||||||
# Multiple agent tasks can be added here and should build logically on each other
|
# Multiple agent tasks can be added here and should build logically on each other
|
||||||
],
|
],
|
||||||
"obj_user_response": "Information to the user about how his request will be solved.",
|
"obj_user_response": "Information to the user about how his request will be solved, in the language of the user's request.",
|
||||||
"user_language": "en" # Language code (e.g., en, de, fr, es) based on the user's request
|
"user_language": "en" # Language code (e.g., en, de, fr, es) based on the user's request
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
@ -227,9 +234,9 @@ JSON_OUTPUT = {{
|
||||||
4. If you use label for an existing file
|
4. If you use label for an existing file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Call the AI service through lucy_interface for language support
|
# Call the AI service through mydom for language support
|
||||||
logger.debug(f"Planning prompt: {prompt}")
|
logger.debug(f"Planning prompt: {prompt}")
|
||||||
project_manager_output = await self.lucy_interface.call_ai([
|
project_manager_output = await self.mydom.call_ai([
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are an experienced project manager who analyzes user requests and creates work plans. You pay very careful attention to ensure that all document dependencies are correct and that no non-existent documents are defined as inputs. The output follows strictly the specified format."
|
"content": "You are an experienced project manager who analyzes user requests and creates work plans. You pay very careful attention to ensure that all document dependencies are correct and that no non-existent documents are defined as inputs. The output follows strictly the specified format."
|
||||||
|
|
@ -318,8 +325,8 @@ JSON_OUTPUT = {{
|
||||||
matching_documents.append(doc_ref)
|
matching_documents.append(doc_ref)
|
||||||
break
|
break
|
||||||
|
|
||||||
# Use the lucy_interface for language-aware AI calls
|
# Use the mydom for language-aware AI calls
|
||||||
final_prompt = await self.lucy_interface.call_ai([
|
final_prompt = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a project manager, who delivers results to a user."},
|
{"role": "system", "content": "You are a project manager, who delivers results to a user."},
|
||||||
{"role": "user", "content": f"""
|
{"role": "user", "content": f"""
|
||||||
Give the final short feedback to the user with reference to the initial statement (obj_user_response). Provide a list of delivered files (files_delivered). If in the list of delivered files (files_delivered) some files from the original list (files_promised) are not available, then just give a comment on this, otherwise task is completed.
|
Give the final short feedback to the user with reference to the initial statement (obj_user_response). Provide a list of delivered files (files_delivered). If in the list of delivered files (files_delivered) some files from the original list (files_promised) are not available, then just give a comment on this, otherwise task is completed.
|
||||||
|
|
@ -359,7 +366,7 @@ JSON_OUTPUT = {{
|
||||||
"""
|
"""
|
||||||
current_time = datetime.now().isoformat()
|
current_time = datetime.now().isoformat()
|
||||||
|
|
||||||
if workflow_id is None or not self.lucy_interface.get_workflow(workflow_id):
|
if workflow_id is None or not self.mydom.get_workflow(workflow_id):
|
||||||
# Create new workflow
|
# Create new workflow
|
||||||
new_workflow_id = str(uuid.uuid4()) if workflow_id is None else workflow_id
|
new_workflow_id = str(uuid.uuid4()) if workflow_id is None else workflow_id
|
||||||
workflow = {
|
workflow = {
|
||||||
|
|
@ -390,13 +397,13 @@ JSON_OUTPUT = {{
|
||||||
"last_activity": workflow["last_activity"],
|
"last_activity": workflow["last_activity"],
|
||||||
"message_ids": workflow["message_ids"] # Include message_ids
|
"message_ids": workflow["message_ids"] # Include message_ids
|
||||||
}
|
}
|
||||||
self.lucy_interface.create_workflow(workflow_db)
|
self.mydom.create_workflow(workflow_db)
|
||||||
|
|
||||||
self.log_add(workflow, GLOBAL_SETTINGS["workflow_status_messages"]["init"], level="info", progress=0)
|
self.log_add(workflow, GLOBAL_WorkflowLabels["workflow_status_messages"]["init"], level="info", progress=0)
|
||||||
return workflow
|
return workflow
|
||||||
else:
|
else:
|
||||||
# Load existing workflow
|
# Load existing workflow
|
||||||
workflow = self.lucy_interface.load_workflow_state(workflow_id)
|
workflow = self.mydom.load_workflow_state(workflow_id)
|
||||||
|
|
||||||
# Ensure message_ids exists
|
# Ensure message_ids exists
|
||||||
if "message_ids" not in workflow:
|
if "message_ids" not in workflow:
|
||||||
|
|
@ -404,7 +411,7 @@ JSON_OUTPUT = {{
|
||||||
workflow["message_ids"] = [msg["id"] for msg in workflow.get("messages", [])]
|
workflow["message_ids"] = [msg["id"] for msg in workflow.get("messages", [])]
|
||||||
|
|
||||||
# Update in database
|
# Update in database
|
||||||
self.lucy_interface.update_workflow(workflow_id, {"message_ids": workflow["message_ids"]})
|
self.mydom.update_workflow(workflow_id, {"message_ids": workflow["message_ids"]})
|
||||||
|
|
||||||
# Update status and increment round counter
|
# Update status and increment round counter
|
||||||
workflow["status"] = "running"
|
workflow["status"] = "running"
|
||||||
|
|
@ -422,9 +429,9 @@ JSON_OUTPUT = {{
|
||||||
"last_activity": workflow["last_activity"],
|
"last_activity": workflow["last_activity"],
|
||||||
"current_round": workflow["current_round"]
|
"current_round": workflow["current_round"]
|
||||||
}
|
}
|
||||||
self.lucy_interface.update_workflow(workflow_id, workflow_update)
|
self.mydom.update_workflow(workflow_id, workflow_update)
|
||||||
|
|
||||||
self.log_add(workflow, GLOBAL_SETTINGS["workflow_status_messages"]["running"], level="info", progress=0)
|
self.log_add(workflow, GLOBAL_WorkflowLabels["workflow_status_messages"]["running"], level="info", progress=0)
|
||||||
return workflow
|
return workflow
|
||||||
|
|
||||||
def workflow_finish(self, workflow: Dict[str, Any]) -> Dict[str, Any]:
|
def workflow_finish(self, workflow: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
|
@ -448,9 +455,9 @@ JSON_OUTPUT = {{
|
||||||
workflow["last_activity"] = workflow_update["last_activity"]
|
workflow["last_activity"] = workflow_update["last_activity"]
|
||||||
|
|
||||||
# Save workflow state to database - only relevant fields, not the messages list
|
# Save workflow state to database - only relevant fields, not the messages list
|
||||||
self.lucy_interface.update_workflow(workflow["id"], workflow_update)
|
self.mydom.update_workflow(workflow["id"], workflow_update)
|
||||||
|
|
||||||
self.log_add(workflow, GLOBAL_SETTINGS["workflow_status_messages"]["completed"], level="info", progress=100)
|
self.log_add(workflow, GLOBAL_WorkflowLabels["workflow_status_messages"]["completed"], level="info", progress=100)
|
||||||
return workflow
|
return workflow
|
||||||
|
|
||||||
async def workflow_summarize(self, workflow: Dict[str, Any], message_user: Dict[str, Any]) -> str:
|
async def workflow_summarize(self, workflow: Dict[str, Any], message_user: Dict[str, Any]) -> str:
|
||||||
|
|
@ -583,8 +590,8 @@ JSON_OUTPUT = {{
|
||||||
# Extract and provide only the relevant information as requested.
|
# Extract and provide only the relevant information as requested.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Call the AI service through lucy_interface for language support
|
# Call the AI service through mydom for language support
|
||||||
processed_data = await self.lucy_interface.call_ai([
|
processed_data = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a document processing assistant. Extract only the relevant information as requested."},
|
{"role": "system", "content": "You are a document processing assistant. Extract only the relevant information as requested."},
|
||||||
{"role": "user", "content": ai_prompt}
|
{"role": "user", "content": ai_prompt}
|
||||||
])
|
])
|
||||||
|
|
@ -660,7 +667,7 @@ JSON_OUTPUT = {{
|
||||||
"workflow_round": workflow.get("current_round", 1),
|
"workflow_round": workflow.get("current_round", 1),
|
||||||
"agent_type": agent_name,
|
"agent_type": agent_name,
|
||||||
"timestamp": datetime.now().isoformat(),
|
"timestamp": datetime.now().isoformat(),
|
||||||
"language": self.lucy_interface.user_language # Pass language to agent
|
"language": self.mydom.user_language # Pass language to agent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -668,7 +675,7 @@ JSON_OUTPUT = {{
|
||||||
try:
|
try:
|
||||||
# Process the task using the agent's standardized interface
|
# Process the task using the agent's standardized interface
|
||||||
logger.debug("TASK: "+self.parse_json2text(agent_task))
|
logger.debug("TASK: "+self.parse_json2text(agent_task))
|
||||||
logger.debug(f"Agent '{agent_name}' AI service available: {agent.ai_service is not None}")
|
logger.debug(f"Agent '{agent_name}' AI service available: {agent.mydom is not None}")
|
||||||
|
|
||||||
agent_results = await agent.process_task(agent_task)
|
agent_results = await agent.process_task(agent_task)
|
||||||
|
|
||||||
|
|
@ -753,7 +760,7 @@ JSON_OUTPUT = {{
|
||||||
file_content = content
|
file_content = content
|
||||||
|
|
||||||
# Save file to database
|
# Save file to database
|
||||||
file_meta = self.lucy_interface.save_uploaded_file(file_content, label)
|
file_meta = self.mydom.save_uploaded_file(file_content, label)
|
||||||
|
|
||||||
if file_meta and "id" in file_meta:
|
if file_meta and "id" in file_meta:
|
||||||
file_id = file_meta["id"]
|
file_id = file_meta["id"]
|
||||||
|
|
@ -827,14 +834,14 @@ JSON_OUTPUT = {{
|
||||||
workflow["last_activity"] = current_time
|
workflow["last_activity"] = current_time
|
||||||
|
|
||||||
# Save to database - first the message itself
|
# Save to database - first the message itself
|
||||||
self.lucy_interface.create_workflow_message(message)
|
self.mydom.create_workflow_message(message)
|
||||||
|
|
||||||
# Then save the workflow with updated references
|
# Then save the workflow with updated references
|
||||||
workflow_update = {
|
workflow_update = {
|
||||||
"last_activity": current_time,
|
"last_activity": current_time,
|
||||||
"message_ids": workflow["message_ids"] # Update the message_ids field
|
"message_ids": workflow["message_ids"] # Update the message_ids field
|
||||||
}
|
}
|
||||||
self.lucy_interface.update_workflow(workflow["id"], workflow_update)
|
self.mydom.update_workflow(workflow["id"], workflow_update)
|
||||||
|
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
@ -853,8 +860,8 @@ JSON_OUTPUT = {{
|
||||||
content = message.get("content", "")
|
content = message.get("content", "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use the lucy_interface for language-aware AI calls
|
# Use the mydom for language-aware AI calls
|
||||||
content_summary = await self.lucy_interface.call_ai([
|
content_summary = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You are a chat message summarizer. Create a very concise summary (2-3 sentences, maximum 300 characters)"},
|
{"role": "system", "content": f"You are a chat message summarizer. Create a very concise summary (2-3 sentences, maximum 300 characters)"},
|
||||||
{"role": "user", "content": content}
|
{"role": "user", "content": content}
|
||||||
])
|
])
|
||||||
|
|
@ -890,8 +897,8 @@ JSON_OUTPUT = {{
|
||||||
is_text = content.get("metadata", {}).get("is_text", False)
|
is_text = content.get("metadata", {}).get("is_text", False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use the lucy_interface for language-aware AI calls
|
# Use the mydom for language-aware AI calls
|
||||||
summary = await self.lucy_interface.call_ai([
|
summary = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a content summarizer. Create very concise summary (1-2 sentences, maximum 200 characters) about this file."},
|
{"role": "system", "content": "You are a content summarizer. Create very concise summary (1-2 sentences, maximum 200 characters) about this file."},
|
||||||
{"role": "user", "content": f"Summarize this {content_type} content briefly:\n\n{data}"}
|
{"role": "user", "content": f"Summarize this {content_type} content briefly:\n\n{data}"}
|
||||||
])
|
])
|
||||||
|
|
@ -921,7 +928,7 @@ JSON_OUTPUT = {{
|
||||||
for file_id in file_ids:
|
for file_id in file_ids:
|
||||||
try:
|
try:
|
||||||
# Check if the file exists
|
# Check if the file exists
|
||||||
file = self.lucy_interface.get_file(file_id)
|
file = self.mydom.get_file(file_id)
|
||||||
if not file:
|
if not file:
|
||||||
logger.warning(f"File with ID {file_id} not found")
|
logger.warning(f"File with ID {file_id} not found")
|
||||||
continue
|
continue
|
||||||
|
|
@ -932,7 +939,7 @@ JSON_OUTPUT = {{
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Load file content
|
# Load file content
|
||||||
file_content = self.lucy_interface.get_file_data(file_id)
|
file_content = self.mydom.get_file_data(file_id)
|
||||||
if file_content is None:
|
if file_content is None:
|
||||||
logger.warning(f"No content found for file with ID {file_id}")
|
logger.warning(f"No content found for file with ID {file_id}")
|
||||||
continue
|
continue
|
||||||
|
|
@ -1047,7 +1054,7 @@ JSON_OUTPUT = {{
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
# Save file in the database
|
# Save file in the database
|
||||||
file_meta = self.lucy_interface.save_uploaded_file(data, name)
|
file_meta = self.mydom.save_uploaded_file(data, name)
|
||||||
if file_meta and "id" in file_meta:
|
if file_meta and "id" in file_meta:
|
||||||
# Update the Document with the File-ID
|
# Update the Document with the File-ID
|
||||||
document["file_id"] = file_meta["id"]
|
document["file_id"] = file_meta["id"]
|
||||||
|
|
@ -1123,7 +1130,7 @@ JSON_OUTPUT = {{
|
||||||
workflow_status = workflow.get("status", "running")
|
workflow_status = workflow.get("status", "running")
|
||||||
|
|
||||||
# Set agent_name from global settings
|
# Set agent_name from global settings
|
||||||
agent_name = GLOBAL_SETTINGS.get("system_name", "AI Assistant")
|
agent_name = GLOBAL_WorkflowLabels.get("system_name", "AI Assistant")
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
log_entry = {
|
log_entry = {
|
||||||
|
|
@ -1144,7 +1151,7 @@ JSON_OUTPUT = {{
|
||||||
workflow["logs"].append(log_entry)
|
workflow["logs"].append(log_entry)
|
||||||
|
|
||||||
# Save in database
|
# Save in database
|
||||||
self.lucy_interface.create_workflow_log(log_entry)
|
self.mydom.create_workflow_log(log_entry)
|
||||||
|
|
||||||
# Also log in logger
|
# Also log in logger
|
||||||
if level == "info":
|
if level == "info":
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ class AgentAnalyst(AgentBase):
|
||||||
# Set default visualization settings
|
# Set default visualization settings
|
||||||
plt.style.use('seaborn-v0_8-whitegrid')
|
plt.style.use('seaborn-v0_8-whitegrid')
|
||||||
|
|
||||||
def set_dependencies(self, ai_service=None):
|
def set_dependencies(self, mydom=None):
|
||||||
"""Set external dependencies for the agent."""
|
"""Set external dependencies for the agent."""
|
||||||
self.ai_service = ai_service
|
self.mydom = mydom
|
||||||
|
|
||||||
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -56,7 +56,7 @@ class AgentAnalyst(AgentBase):
|
||||||
output_specs = task.get("output_specifications", [])
|
output_specs = task.get("output_specifications", [])
|
||||||
|
|
||||||
# Check AI service
|
# Check AI service
|
||||||
if not self.ai_service:
|
if not self.mydom:
|
||||||
return {
|
return {
|
||||||
"feedback": "The Analyst agent requires an AI service to function.",
|
"feedback": "The Analyst agent requires an AI service to function.",
|
||||||
"documents": []
|
"documents": []
|
||||||
|
|
@ -105,9 +105,9 @@ class AgentAnalyst(AgentBase):
|
||||||
documents.append(document)
|
documents.append(document)
|
||||||
|
|
||||||
# Generate feedback
|
# Generate feedback
|
||||||
feedback = f"Analysis complete. Created {len(documents)} documents based on your requirements."
|
feedback = f"{analysis_plan.get('analysis_approach')}"
|
||||||
if analysis_plan.get("key_insights"):
|
if analysis_plan.get("key_insights"):
|
||||||
feedback += f"\n\nKey insights: {analysis_plan.get('key_insights')}"
|
feedback += f"\n\n{analysis_plan.get('key_insights')}"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"feedback": feedback,
|
"feedback": feedback,
|
||||||
|
|
@ -251,12 +251,11 @@ class AgentAnalyst(AgentBase):
|
||||||
|
|
||||||
Only return valid JSON. No preamble or explanations.
|
Only return valid JSON. No preamble or explanations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = await self.ai_service.call_api([
|
response = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a data analysis expert. Respond with valid JSON only."},
|
{"role": "system", "content": "You are a data analysis expert. Respond with valid JSON only."},
|
||||||
{"role": "user", "content": analysis_prompt}
|
{"role": "user", "content": analysis_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
# Extract JSON from response
|
# Extract JSON from response
|
||||||
json_start = response.find('{')
|
json_start = response.find('{')
|
||||||
|
|
@ -375,10 +374,10 @@ class AgentAnalyst(AgentBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get visualization code from AI
|
# Get visualization code from AI
|
||||||
viz_code = await self.ai_service.call_api([
|
viz_code = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a data visualization expert. Provide only executable Python code."},
|
{"role": "system", "content": "You are a data visualization expert. Provide only executable Python code."},
|
||||||
{"role": "user", "content": viz_prompt}
|
{"role": "user", "content": viz_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
# Clean code
|
# Clean code
|
||||||
viz_code = viz_code.replace("```python", "").replace("```", "").strip()
|
viz_code = viz_code.replace("```python", "").replace("```", "").strip()
|
||||||
|
|
@ -504,10 +503,10 @@ class AgentAnalyst(AgentBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get data processing code from AI
|
# Get data processing code from AI
|
||||||
data_code = await self.ai_service.call_api([
|
data_code = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a data processing expert. Provide only executable Python code."},
|
{"role": "system", "content": "You are a data processing expert. Provide only executable Python code."},
|
||||||
{"role": "user", "content": data_prompt}
|
{"role": "user", "content": data_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
# Clean code
|
# Clean code
|
||||||
data_code = data_code.replace("```python", "").replace("```", "").strip()
|
data_code = data_code.replace("```python", "").replace("```", "").strip()
|
||||||
|
|
@ -630,10 +629,10 @@ class AgentAnalyst(AgentBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get document content from AI
|
# Get document content from AI
|
||||||
document_content = await self.ai_service.call_api([
|
document_content = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You are a data analysis expert creating a {format_type} document."},
|
{"role": "system", "content": f"You are a data analysis expert creating a {format_type} document."},
|
||||||
{"role": "user", "content": analysis_prompt}
|
{"role": "user", "content": analysis_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
# Clean HTML or Markdown if needed
|
# Clean HTML or Markdown if needed
|
||||||
if format_type in ["md", "markdown"] and not document_content.strip().startswith("#"):
|
if format_type in ["md", "markdown"] and not document_content.strip().startswith("#"):
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,13 @@ class AgentCoder(AgentBase):
|
||||||
]
|
]
|
||||||
|
|
||||||
# Executor settings
|
# Executor settings
|
||||||
self.executor_timeout = APP_CONFIG.get("Agent_Coder_EXECUTION_TIMEOUT") # seconds
|
self.executor_timeout = int(APP_CONFIG.get("Agent_Coder_EXECUTION_TIMEOUT")) # seconds
|
||||||
|
self.execution_retry_limit = int(APP_CONFIG.get("Agent_Coder_EXECUTION_RETRY")) # max retries
|
||||||
self.temp_dir = None
|
self.temp_dir = None
|
||||||
|
|
||||||
def set_dependencies(self, ai_service=None):
|
def set_dependencies(self, mydom=None):
|
||||||
"""Set external dependencies for the agent."""
|
"""Set external dependencies for the agent."""
|
||||||
self.ai_service = ai_service
|
self.mydom = mydom
|
||||||
|
|
||||||
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,7 +59,7 @@ class AgentCoder(AgentBase):
|
||||||
output_specs = task.get("output_specifications", [])
|
output_specs = task.get("output_specifications", [])
|
||||||
|
|
||||||
# Check if AI service is available
|
# Check if AI service is available
|
||||||
if not self.ai_service:
|
if not self.mydom:
|
||||||
logger.error("No AI service configured for the Coder agent")
|
logger.error("No AI service configured for the Coder agent")
|
||||||
return {
|
return {
|
||||||
"feedback": "The Coder agent is not properly configured.",
|
"feedback": "The Coder agent is not properly configured.",
|
||||||
|
|
@ -121,13 +122,13 @@ class AgentCoder(AgentBase):
|
||||||
"documents": quick_completion.get("documents", [])
|
"documents": quick_completion.get("documents", [])
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Code to generate, quick check responded: {quick_completion.get("prompt", "(no answer)")}")
|
logger.debug(f"Code to generate, quick check responded: {quick_completion.get('prompt', '(no answer)')}")
|
||||||
|
|
||||||
# If quick completion not possible, continue with code generation and execution
|
# If quick completion not possible, continue with code generation and execution
|
||||||
logger.info("Generating code to solve the task")
|
logger.info("Generating code to solve the task")
|
||||||
|
|
||||||
# 4. Generate code using AI
|
# 4. Generate code using AI
|
||||||
code, requirements = await self._generate_code(prompt, document_data)
|
code, requirements = await self._generate_code(prompt)
|
||||||
|
|
||||||
if not code:
|
if not code:
|
||||||
return {
|
return {
|
||||||
|
|
@ -136,21 +137,69 @@ class AgentCoder(AgentBase):
|
||||||
}
|
}
|
||||||
|
|
||||||
# 5. Replace the placeholder with actual input_files data
|
# 5. Replace the placeholder with actual input_files data
|
||||||
document_data_json = json.dumps(document_data)
|
document_data_json = repr(document_data)
|
||||||
code_with_data = code.replace("input_files = \"=== JSONLOAD ===\"", f"input_files = {document_data_json }")
|
code_with_data = code.replace("input_files = \"=== JSONLOAD ===\"", f"input_files = {document_data_json}")
|
||||||
|
|
||||||
# 6. Execute code and get results
|
# 6. Execute code with retry logic
|
||||||
execution_result = self._execute_code(code_with_data, requirements)
|
retry_count = 0
|
||||||
|
max_retries = self.execution_retry_limit
|
||||||
|
execution_history = []
|
||||||
|
|
||||||
|
while retry_count <= max_retries:
|
||||||
|
execution_result = self._execute_code(code_with_data, requirements)
|
||||||
|
execution_history.append({
|
||||||
|
"attempt": retry_count + 1,
|
||||||
|
"code": code_with_data,
|
||||||
|
"result": execution_result
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check if execution was successful
|
||||||
|
if execution_result.get("success", False):
|
||||||
|
logger.info(f"Code execution succeeded on attempt {retry_count + 1}")
|
||||||
|
break
|
||||||
|
|
||||||
|
# If we've reached max retries, exit the loop
|
||||||
|
if retry_count >= max_retries:
|
||||||
|
logger.info(f"Reached maximum retry limit ({max_retries}). Giving up.")
|
||||||
|
break
|
||||||
|
|
||||||
|
# Log the error and attempt to improve the code
|
||||||
|
error = execution_result.get("error", "Unknown error")
|
||||||
|
logger.info(f"Execution attempt {retry_count + 1} failed: {error}. Attempting to improve code.")
|
||||||
|
|
||||||
|
# Generate improved code based on error
|
||||||
|
improved_code, improved_requirements = await self._improve_code(
|
||||||
|
original_code=code_with_data,
|
||||||
|
error=error,
|
||||||
|
execution_result=execution_result,
|
||||||
|
attempt=retry_count + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if improved_code:
|
||||||
|
code_with_data = improved_code
|
||||||
|
requirements = improved_requirements
|
||||||
|
logger.info(f"Code improved for retry {retry_count + 2}")
|
||||||
|
else:
|
||||||
|
logger.warning("Failed to improve code, using original code for retry")
|
||||||
|
|
||||||
|
retry_count += 1
|
||||||
|
|
||||||
# 7. Process results and create output documents
|
# 7. Process results and create output documents
|
||||||
documents = []
|
documents = []
|
||||||
|
|
||||||
# Always add the code document
|
# Always add the final code document
|
||||||
documents.append({
|
documents.append({
|
||||||
"label": "generated_code.py",
|
"label": "generated_code.py",
|
||||||
"content": code_with_data
|
"content": code_with_data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Add execution history document
|
||||||
|
execution_history_str = json.dumps(execution_history, indent=2)
|
||||||
|
documents.append({
|
||||||
|
"label": "execution_history.json",
|
||||||
|
"content": execution_history_str
|
||||||
|
})
|
||||||
|
|
||||||
# Create documents based on execution results
|
# Create documents based on execution results
|
||||||
if execution_result.get("success", False):
|
if execution_result.get("success", False):
|
||||||
result_data = execution_result.get("result")
|
result_data = execution_result.get("result")
|
||||||
|
|
@ -179,7 +228,10 @@ class AgentCoder(AgentBase):
|
||||||
"content": execution_result.get("output", "")
|
"content": execution_result.get("output", "")
|
||||||
})
|
})
|
||||||
|
|
||||||
feedback = "Code executed successfully. Generated output files based on specifications."
|
if retry_count > 0:
|
||||||
|
feedback = f"Code executed successfully after {retry_count + 1} attempts. Generated output files based on specifications."
|
||||||
|
else:
|
||||||
|
feedback = "Code executed successfully. Generated output files based on specifications."
|
||||||
else:
|
else:
|
||||||
# Execution failed
|
# Execution failed
|
||||||
error = execution_result.get("error", "Unknown error")
|
error = execution_result.get("error", "Unknown error")
|
||||||
|
|
@ -187,13 +239,92 @@ class AgentCoder(AgentBase):
|
||||||
"label": "execution_error.txt",
|
"label": "execution_error.txt",
|
||||||
"content": f"Error executing code:\n\n{error}"
|
"content": f"Error executing code:\n\n{error}"
|
||||||
})
|
})
|
||||||
feedback = f"Error during code execution: {error}"
|
|
||||||
|
if retry_count > 0:
|
||||||
|
feedback = f"Error during code execution after {retry_count + 1} attempts: {error}"
|
||||||
|
else:
|
||||||
|
feedback = f"Error during code execution: {error}"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"feedback": feedback,
|
"feedback": feedback,
|
||||||
"documents": documents
|
"documents": documents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def _improve_code(self, original_code: str, error: str, execution_result: Dict[str, Any], attempt: int) -> Tuple[str, List[str]]:
|
||||||
|
"""
|
||||||
|
Improve code based on execution error.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
original_code: The code that failed to execute
|
||||||
|
error: The error message
|
||||||
|
execution_result: Complete execution result dictionary
|
||||||
|
attempt: Current attempt number
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (improved_code, requirements)
|
||||||
|
"""
|
||||||
|
# Create prompt for code improvement
|
||||||
|
improvement_prompt = f"""
|
||||||
|
Fix the following Python code that failed during execution. This is attempt {attempt} to fix the code.
|
||||||
|
|
||||||
|
ORIGINAL CODE:
|
||||||
|
{original_code}
|
||||||
|
|
||||||
|
ERROR MESSAGE:
|
||||||
|
{error}
|
||||||
|
|
||||||
|
STDOUT:
|
||||||
|
{execution_result.get('output', '')}
|
||||||
|
|
||||||
|
INSTRUCTIONS:
|
||||||
|
1. Fix all errors identified in the error message
|
||||||
|
2. Diagnose and fix any logical issues
|
||||||
|
3. Pay special attention to:
|
||||||
|
- Type conversions and data handling
|
||||||
|
- Error handling and edge cases
|
||||||
|
- Resource management (file handles, etc.)
|
||||||
|
- Syntax errors and typos
|
||||||
|
4. Keep the input_files handling logic intact
|
||||||
|
5. Maintain the same overall structure and purpose
|
||||||
|
|
||||||
|
OUTPUT:
|
||||||
|
- Your improved code MUST still define a 'result' variable as a dictionary
|
||||||
|
- Each output file should be a key in the result dictionary
|
||||||
|
- DO NOT remove the input_files assignment line structure
|
||||||
|
|
||||||
|
REQUIREMENTS:
|
||||||
|
Required packages should be specified as:
|
||||||
|
# REQUIREMENTS: library==version,library2>=version
|
||||||
|
- You may add/remove requirements as needed to fix the code
|
||||||
|
|
||||||
|
Return ONLY Python code without explanations or markdown.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Call AI service
|
||||||
|
messages = [
|
||||||
|
{"role": "system", "content": "You are an expert Python code debugger. Provide only fixed Python code without explanations or formatting."},
|
||||||
|
{"role": "user", "content": improvement_prompt}
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
improved_content = await self.mydom.call_ai(messages, temperature=0.2)
|
||||||
|
|
||||||
|
# Extract code and requirements
|
||||||
|
improved_code = self._clean_code(improved_content)
|
||||||
|
|
||||||
|
# Extract requirements
|
||||||
|
requirements = []
|
||||||
|
for line in improved_code.split('\n'):
|
||||||
|
if line.strip().startswith("# REQUIREMENTS:"):
|
||||||
|
req_str = line.replace("# REQUIREMENTS:", "").strip()
|
||||||
|
requirements = [r.strip() for r in req_str.split(',') if r.strip()]
|
||||||
|
break
|
||||||
|
|
||||||
|
return improved_code, requirements
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error improving code: {str(e)}")
|
||||||
|
return None, []
|
||||||
|
|
||||||
async def _check_quick_completion(self, prompt: str, content_extraction: List[Dict], output_specs: List[Dict]) -> Dict:
|
async def _check_quick_completion(self, prompt: str, content_extraction: List[Dict], output_specs: List[Dict]) -> Dict:
|
||||||
"""
|
"""
|
||||||
Check if the task can be completed without writing and executing code.
|
Check if the task can be completed without writing and executing code.
|
||||||
|
|
@ -245,7 +376,7 @@ Only return valid JSON. Your entire response must be parseable as JSON.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Call AI service
|
# Call AI service
|
||||||
logger.debug("Checking if task can be completed without code execution")
|
logger.debug(f"Checking if task can be completed without code execution: {check_prompt}")
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system", "content": "You are an AI assistant that determines if tasks require code execution. Reply with JSON only."},
|
{"role": "system", "content": "You are an AI assistant that determines if tasks require code execution. Reply with JSON only."},
|
||||||
{"role": "user", "content": check_prompt}
|
{"role": "user", "content": check_prompt}
|
||||||
|
|
@ -253,7 +384,7 @@ Only return valid JSON. Your entire response must be parseable as JSON.
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use a lower temperature for more deterministic response
|
# Use a lower temperature for more deterministic response
|
||||||
response = await self.ai_service.call_api(messages, temperature=0.1)
|
response = await self.mydom.call_ai(messages, produce_user_answer = True, temperature=0.1)
|
||||||
|
|
||||||
# Parse response as JSON
|
# Parse response as JSON
|
||||||
if response:
|
if response:
|
||||||
|
|
@ -279,7 +410,7 @@ Only return valid JSON. Your entire response must be parseable as JSON.
|
||||||
# Default to requiring code execution
|
# Default to requiring code execution
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _generate_code(self, prompt: str, input_files: List) -> Tuple[str, List[str]]:
|
async def _generate_code(self, prompt: str) -> Tuple[str, List[str]]:
|
||||||
"""
|
"""
|
||||||
Generate Python code from a prompt with the input_files placeholder.
|
Generate Python code from a prompt with the input_files placeholder.
|
||||||
|
|
||||||
|
|
@ -297,20 +428,19 @@ Generate Python code to solve the following task:
|
||||||
TASK:
|
TASK:
|
||||||
{prompt}
|
{prompt}
|
||||||
|
|
||||||
IMPORTANT:
|
INPUT FILES:
|
||||||
- All input files are provided in the 'input_files' variable as a list of [filename, data, is_base64].
|
- 'input_files' variable is provided as [[filename, data, is_base64], ...]
|
||||||
- The 'input_files' variable is already defined at the top of your code, DO NOT modify it.
|
- For text files (is_base64=False): use data directly as string
|
||||||
- For each file, you can access:
|
- For binary files (is_base64=True): use base64.b64decode(data)
|
||||||
- filename: The name of the file (e.g., "image.png")
|
|
||||||
- data: The content of the file (base64 encoded or plain text)
|
|
||||||
- is_base64: Boolean flag indicating if the data is base64 encoded
|
|
||||||
|
|
||||||
- To use a file's data:
|
CODE QUALITY:
|
||||||
- For text files (when is_base64=False): Use the data directly as a string
|
- Use explicit type conversions where needed (int/float/str)
|
||||||
- For binary files (when is_base64=True): Use base64.b64decode(data) to get bytes
|
- Implement feature detection, not version checks
|
||||||
|
- Handle errors gracefully with appropriate fallbacks
|
||||||
- Do not perform any additional base64 detection - rely on the is_base64 flag
|
- Follow latest API conventions for libraries
|
||||||
|
- Validate inputs before processing
|
||||||
|
|
||||||
|
OUTPUT:
|
||||||
- Your code MUST define a 'result' variable as a dictionary to store outputs.
|
- Your code MUST define a 'result' variable as a dictionary to store outputs.
|
||||||
- Each output file should be a key in the result dictionary.
|
- Each output file should be a key in the result dictionary.
|
||||||
- For example: result = {{"output.txt": "output text", "results.json": json_string}}
|
- For example: result = {{"output.txt": "output text", "results.json": json_string}}
|
||||||
|
|
@ -318,10 +448,13 @@ IMPORTANT:
|
||||||
Your code must start with:
|
Your code must start with:
|
||||||
input_files = "=== JSONLOAD ===" # DO NOT CHANGE THIS LINE
|
input_files = "=== JSONLOAD ===" # DO NOT CHANGE THIS LINE
|
||||||
|
|
||||||
|
REQUIREMENTS:
|
||||||
Required packages should be specified as:
|
Required packages should be specified as:
|
||||||
# REQUIREMENTS: package1,package2,package3
|
# REQUIREMENTS: library==version,library2>=version
|
||||||
|
- Specify exact versions for critical libraries
|
||||||
|
- Use constraint operators (==,>=,<=) as needed
|
||||||
|
|
||||||
Return ONLY Python code without explanations or markdown formatting.
|
Return ONLY Python code without explanations or markdown.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Call AI service
|
# Call AI service
|
||||||
|
|
@ -330,7 +463,7 @@ Return ONLY Python code without explanations or markdown formatting.
|
||||||
{"role": "user", "content": ai_prompt}
|
{"role": "user", "content": ai_prompt}
|
||||||
]
|
]
|
||||||
|
|
||||||
generated_content = await self.ai_service.call_api(messages, temperature=0.1)
|
generated_content = await self.mydom.call_ai(messages, temperature=0.1)
|
||||||
|
|
||||||
# Extract code and requirements
|
# Extract code and requirements
|
||||||
code = self._clean_code(generated_content)
|
code = self._clean_code(generated_content)
|
||||||
|
|
@ -372,20 +505,23 @@ Return ONLY Python code without explanations or markdown formatting.
|
||||||
|
|
||||||
# 2. Install requirements if provided
|
# 2. Install requirements if provided
|
||||||
if requirements:
|
if requirements:
|
||||||
logger.debug(f"Installing requirements: {requirements}")
|
logger.info(f"Installing requirements: {requirements}")
|
||||||
|
|
||||||
# Create requirements.txt
|
# Create requirements.txt
|
||||||
req_file = os.path.join(self.temp_dir, "requirements.txt")
|
req_file = os.path.join(self.temp_dir, "requirements.txt")
|
||||||
with open(req_file, "w") as f:
|
with open(req_file, "w") as f:
|
||||||
f.write("\n".join(requirements))
|
f.write("\n".join(requirements))
|
||||||
|
|
||||||
|
x="\n".join(requirements)
|
||||||
|
logger.info(f"Requirements file: {x}.")
|
||||||
|
|
||||||
# Install requirements
|
# Install requirements
|
||||||
try:
|
try:
|
||||||
pip_result = subprocess.run(
|
pip_result = subprocess.run(
|
||||||
[python_exe, "-m", "pip", "install", "-r", req_file],
|
[python_exe, "-m", "pip", "install", "-r", req_file],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=APP_CONFIG.get("Agent_Coder_INSTALL_TIMEOUT")
|
timeout=int(APP_CONFIG.get("Agent_Coder_INSTALL_TIMEOUT"))
|
||||||
)
|
)
|
||||||
if pip_result.returncode != 0:
|
if pip_result.returncode != 0:
|
||||||
logger.debug(f"Error installing requirements: {pip_result.stderr}")
|
logger.debug(f"Error installing requirements: {pip_result.stderr}")
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ class AgentDocumentation(AgentBase):
|
||||||
"knowledge_organization"
|
"knowledge_organization"
|
||||||
]
|
]
|
||||||
|
|
||||||
def set_dependencies(self, ai_service=None):
|
def set_dependencies(self, mydom=None):
|
||||||
"""Set external dependencies for the agent."""
|
"""Set external dependencies for the agent."""
|
||||||
self.ai_service = ai_service
|
self.mydom = mydom
|
||||||
|
|
||||||
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -48,7 +48,7 @@ class AgentDocumentation(AgentBase):
|
||||||
output_specs = task.get("output_specifications", [])
|
output_specs = task.get("output_specifications", [])
|
||||||
|
|
||||||
# Check AI service
|
# Check AI service
|
||||||
if not self.ai_service:
|
if not self.mydom:
|
||||||
return {
|
return {
|
||||||
"feedback": "The Documentation agent requires an AI service to function.",
|
"feedback": "The Documentation agent requires an AI service to function.",
|
||||||
"documents": []
|
"documents": []
|
||||||
|
|
@ -201,7 +201,7 @@ class AgentDocumentation(AgentBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = await self.ai_service.call_api([
|
response = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a documentation expert. Respond with valid JSON only."},
|
{"role": "system", "content": "You are a documentation expert. Respond with valid JSON only."},
|
||||||
{"role": "user", "content": analysis_prompt}
|
{"role": "user", "content": analysis_prompt}
|
||||||
])
|
])
|
||||||
|
|
@ -371,10 +371,10 @@ class AgentDocumentation(AgentBase):
|
||||||
The introduction should be professional and engaging, formatted according to {format_type} standards.
|
The introduction should be professional and engaging, formatted according to {format_type} standards.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
introduction = await self.ai_service.call_api([
|
introduction = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You are a documentation expert creating an introduction in {format_type} format."},
|
{"role": "system", "content": f"You are a documentation expert creating an introduction in {format_type} format."},
|
||||||
{"role": "user", "content": intro_prompt}
|
{"role": "user", "content": intro_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
# Step 2: Generate executive summary (if applicable)
|
# Step 2: Generate executive summary (if applicable)
|
||||||
if document_type in ["report", "whitepaper", "case study"]:
|
if document_type in ["report", "whitepaper", "case study"]:
|
||||||
|
|
@ -397,10 +397,10 @@ class AgentDocumentation(AgentBase):
|
||||||
Keep the summary focused and impactful, approximately 200-300 words.
|
Keep the summary focused and impactful, approximately 200-300 words.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
executive_summary = await self.ai_service.call_api([
|
executive_summary = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You are a documentation expert creating an executive summary in {format_type} format."},
|
{"role": "system", "content": f"You are a documentation expert creating an executive summary in {format_type} format."},
|
||||||
{"role": "user", "content": summary_prompt}
|
{"role": "user", "content": summary_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
else:
|
else:
|
||||||
executive_summary = ""
|
executive_summary = ""
|
||||||
|
|
||||||
|
|
@ -447,10 +447,10 @@ class AgentDocumentation(AgentBase):
|
||||||
Be thorough in your coverage of this section, providing substantive content.
|
Be thorough in your coverage of this section, providing substantive content.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
section_content = await self.ai_service.call_api([
|
section_content = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You are a documentation expert creating detailed content for the {section_title} section."},
|
{"role": "system", "content": f"You are a documentation expert creating detailed content for the {section_title} section."},
|
||||||
{"role": "user", "content": section_prompt}
|
{"role": "user", "content": section_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
sections.append(section_content)
|
sections.append(section_content)
|
||||||
|
|
||||||
|
|
@ -474,10 +474,10 @@ class AgentDocumentation(AgentBase):
|
||||||
The conclusion should be professional and impactful, formatted according to {format_type} standards.
|
The conclusion should be professional and impactful, formatted according to {format_type} standards.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
conclusion = await self.ai_service.call_api([
|
conclusion = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You are a documentation expert creating a conclusion in {format_type} format."},
|
{"role": "system", "content": f"You are a documentation expert creating a conclusion in {format_type} format."},
|
||||||
{"role": "user", "content": conclusion_prompt}
|
{"role": "user", "content": conclusion_prompt}
|
||||||
])
|
], produce_user_answer = True)
|
||||||
|
|
||||||
# Step 5: Assemble the complete document
|
# Step 5: Assemble the complete document
|
||||||
if format_type in ["md", "markdown"]:
|
if format_type in ["md", "markdown"]:
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ class AgentWebcrawler(AgentBase):
|
||||||
self.search_engine = APP_CONFIG.get("Agent_Webcrawler_SEARCH_ENGINE", "https://html.duckduckgo.com/html/?q=")
|
self.search_engine = APP_CONFIG.get("Agent_Webcrawler_SEARCH_ENGINE", "https://html.duckduckgo.com/html/?q=")
|
||||||
self.user_agent = APP_CONFIG.get("Agent_Webcrawler_USER_AGENT", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
self.user_agent = APP_CONFIG.get("Agent_Webcrawler_USER_AGENT", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||||
|
|
||||||
def set_dependencies(self, ai_service=None):
|
def set_dependencies(self, mydom=None):
|
||||||
"""Set external dependencies for the agent."""
|
"""Set external dependencies for the agent."""
|
||||||
self.ai_service = ai_service
|
self.mydom = mydom
|
||||||
|
|
||||||
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
async def process_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -63,7 +63,7 @@ class AgentWebcrawler(AgentBase):
|
||||||
output_specs = task.get("output_specifications", [])
|
output_specs = task.get("output_specifications", [])
|
||||||
|
|
||||||
# Check AI service
|
# Check AI service
|
||||||
if not self.ai_service:
|
if not self.mydom:
|
||||||
return {
|
return {
|
||||||
"feedback": "The Webcrawler agent requires an AI service to function effectively.",
|
"feedback": "The Webcrawler agent requires an AI service to function effectively.",
|
||||||
"documents": []
|
"documents": []
|
||||||
|
|
@ -134,7 +134,7 @@ class AgentWebcrawler(AgentBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get research plan from AI
|
# Get research plan from AI
|
||||||
response = await self.ai_service.call_api([
|
response = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You are a web research planning expert. Create precise research plans in JSON format only."},
|
{"role": "system", "content": "You are a web research planning expert. Create precise research plans in JSON format only."},
|
||||||
{"role": "user", "content": research_prompt}
|
{"role": "user", "content": research_prompt}
|
||||||
])
|
])
|
||||||
|
|
@ -295,8 +295,8 @@ class AgentWebcrawler(AgentBase):
|
||||||
Only include information actually found in the content. No fabrications or assumptions.
|
Only include information actually found in the content. No fabrications or assumptions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.ai_service:
|
if self.mydom:
|
||||||
summary = await self.ai_service.call_api([
|
summary = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": "You summarize web content accurately and concisely, focusing only on what is actually in the content."},
|
{"role": "system", "content": "You summarize web content accurately and concisely, focusing only on what is actually in the content."},
|
||||||
{"role": "user", "content": summary_prompt}
|
{"role": "user", "content": summary_prompt}
|
||||||
])
|
])
|
||||||
|
|
@ -449,7 +449,7 @@ class AgentWebcrawler(AgentBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Generate report with AI
|
# Generate report with AI
|
||||||
report_content = await self.ai_service.call_api([
|
report_content = await self.mydom.call_ai([
|
||||||
{"role": "system", "content": f"You create professional research reports in {template_format} format."},
|
{"role": "system", "content": f"You create professional research reports in {template_format} format."},
|
||||||
{"role": "user", "content": report_prompt}
|
{"role": "user", "content": report_prompt}
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,11 @@ class AgentBase:
|
||||||
self.name = "base-agent"
|
self.name = "base-agent"
|
||||||
self.description = "Basic agent functionality"
|
self.description = "Basic agent functionality"
|
||||||
self.capabilities = []
|
self.capabilities = []
|
||||||
self.ai_service = None
|
self.mydom = None
|
||||||
|
|
||||||
def set_dependencies(self, ai_service=None):
|
def set_dependencies(self, mydom=None):
|
||||||
"""Set external dependencies for the agent."""
|
"""Set external dependencies for the agent."""
|
||||||
self.ai_service = ai_service
|
self.mydom = mydom
|
||||||
|
|
||||||
def get_agent_info(self) -> Dict[str, Any]:
|
def get_agent_info(self) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -88,7 +88,7 @@ class AgentRegistry:
|
||||||
raise RuntimeError("Singleton instance already exists - use get_instance()")
|
raise RuntimeError("Singleton instance already exists - use get_instance()")
|
||||||
|
|
||||||
self.agents = {}
|
self.agents = {}
|
||||||
self.ai_service = None
|
self.mydom = None
|
||||||
self._load_agents()
|
self._load_agents()
|
||||||
|
|
||||||
def _load_agents(self):
|
def _load_agents(self):
|
||||||
|
|
@ -146,16 +146,16 @@ class AgentRegistry:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error loading agent from module {module_name}: {e}")
|
logger.error(f"Error loading agent from module {module_name}: {e}")
|
||||||
|
|
||||||
def set_ai_service(self, ai_service):
|
def set_mydom(self, mydom):
|
||||||
"""Set the AI service for all agents."""
|
"""Set the AI service for all agents."""
|
||||||
self.ai_service = ai_service
|
self.mydom = mydom
|
||||||
self.update_agent_dependencies()
|
self.update_agent_dependencies()
|
||||||
|
|
||||||
def update_agent_dependencies(self):
|
def update_agent_dependencies(self):
|
||||||
"""Update dependencies for all registered agents."""
|
"""Update dependencies for all registered agents."""
|
||||||
for agent_id, agent in self.agents.items():
|
for agent_id, agent in self.agents.items():
|
||||||
if hasattr(agent, 'set_dependencies'):
|
if hasattr(agent, 'set_dependencies'):
|
||||||
agent.set_dependencies(ai_service=self.ai_service)
|
agent.set_dependencies(mydom=self.mydom)
|
||||||
|
|
||||||
def register_agent(self, agent):
|
def register_agent(self, agent):
|
||||||
"""
|
"""
|
||||||
|
|
@ -167,7 +167,7 @@ class AgentRegistry:
|
||||||
agent_id = getattr(agent, 'name', "unknown_agent")
|
agent_id = getattr(agent, 'name', "unknown_agent")
|
||||||
# Initialize agent with dependencies
|
# Initialize agent with dependencies
|
||||||
if hasattr(agent, 'set_dependencies'):
|
if hasattr(agent, 'set_dependencies'):
|
||||||
agent.set_dependencies(ai_service=self.ai_service)
|
agent.set_dependencies(mydom=self.mydom)
|
||||||
self.agents[agent_id] = agent
|
self.agents[agent_id] = agent
|
||||||
logger.debug(f"Agent '{agent.name}' registered")
|
logger.debug(f"Agent '{agent.name}' registered")
|
||||||
|
|
||||||
|
|
@ -182,8 +182,8 @@ class AgentRegistry:
|
||||||
if agent_identifier in self.agents:
|
if agent_identifier in self.agents:
|
||||||
agent = self.agents[agent_identifier]
|
agent = self.agents[agent_identifier]
|
||||||
# Ensure the agent has the AI service
|
# Ensure the agent has the AI service
|
||||||
if hasattr(agent, 'set_dependencies') and self.ai_service:
|
if hasattr(agent, 'set_dependencies') and self.mydom:
|
||||||
agent.set_dependencies(ai_service=self.ai_service)
|
agent.set_dependencies(mydom=self.mydom)
|
||||||
return agent
|
return agent
|
||||||
logger.error(f"Agent with identifier '{agent_identifier}' not found")
|
logger.error(f"Agent with identifier '{agent_identifier}' not found")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Interface to LucyDOM database.
|
Interface to LucyDOM database and AI Connectors.
|
||||||
Uses the JSON connector for data access with added language support.
|
Uses the JSON connector for data access with added language support.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -12,9 +12,12 @@ from typing import Dict, Any, List, Optional, Union
|
||||||
import importlib
|
import importlib
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
# DYNAMIC PART: Connectors to the Interface
|
||||||
from connectors.connector_db_json import DatabaseConnector
|
from connectors.connector_db_json import DatabaseConnector
|
||||||
from modules.configuration import APP_CONFIG
|
from connectors.connector_aichat_openai import ChatService
|
||||||
|
|
||||||
|
# Basic Configurations
|
||||||
|
from modules.configuration import APP_CONFIG
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Custom exceptions for file handling
|
# Custom exceptions for file handling
|
||||||
|
|
@ -132,18 +135,12 @@ class LucyDOMInterface:
|
||||||
|
|
||||||
# Language support methods
|
# Language support methods
|
||||||
|
|
||||||
def set_ai_service(self, ai_service):
|
|
||||||
"""Set the AI service for API calls"""
|
|
||||||
self.ai_service = ai_service
|
|
||||||
|
|
||||||
def set_user_language(self, language_code: str):
|
def set_user_language(self, language_code: str):
|
||||||
"""Set the user's preferred language"""
|
"""Set the user's preferred language"""
|
||||||
self.user_language = language_code
|
self.user_language = language_code
|
||||||
logger.info(f"User language set to: {language_code}")
|
logger.info(f"User language set to: {language_code}")
|
||||||
|
|
||||||
async def call_ai(self, messages: List[Dict[str, str]],
|
async def call_ai(self, messages: List[Dict[str, str]], produce_user_answer: bool = False, temperature: float = None) -> str:
|
||||||
produce_user_answer: bool = False,
|
|
||||||
temperature: float = None) -> str:
|
|
||||||
"""
|
"""
|
||||||
Enhanced AI service call with language support
|
Enhanced AI service call with language support
|
||||||
|
|
||||||
|
|
@ -161,14 +158,15 @@ class LucyDOMInterface:
|
||||||
|
|
||||||
# Add language instruction for user-facing responses
|
# Add language instruction for user-facing responses
|
||||||
if produce_user_answer and self.user_language:
|
if produce_user_answer and self.user_language:
|
||||||
|
ltext= f"Please respond in '{self.user_language}' language."
|
||||||
if messages and messages[0]["role"] == "system":
|
if messages and messages[0]["role"] == "system":
|
||||||
if "language" not in messages[0]["content"].lower():
|
if "language" not in messages[0]["content"].lower():
|
||||||
messages[0]["content"] = f"Please respond in {self.user_language} language. {messages[0]['content']}"
|
messages[0]["content"] = f"{ltext} {messages[0]['content']}"
|
||||||
else:
|
else:
|
||||||
# Insert a system message with language instruction
|
# Insert a system message with language instruction
|
||||||
messages.insert(0, {
|
messages.insert(0, {
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": f"Please respond in {self.user_language} language."
|
"content": ltext
|
||||||
})
|
})
|
||||||
|
|
||||||
# Call the AI service
|
# Call the AI service
|
||||||
|
|
@ -636,7 +634,7 @@ class LucyDOMInterface:
|
||||||
self.create_file_data(db_file["id"], file_content)
|
self.create_file_data(db_file["id"], file_content)
|
||||||
|
|
||||||
# Debug: Export file to static folder
|
# Debug: Export file to static folder
|
||||||
if logger.isEnabledFor(logging.DEBUG): self._export_file_to_static(file_content, db_file["id"], file_name)
|
self._export_file_to_static(file_content, db_file["id"], file_name) # DEBUG TODO
|
||||||
|
|
||||||
# Debug: Verify database record was created
|
# Debug: Verify database record was created
|
||||||
if not db_file:
|
if not db_file:
|
||||||
|
|
@ -1174,7 +1172,7 @@ class LucyDOMInterface:
|
||||||
"message": log.get("message", ""),
|
"message": log.get("message", ""),
|
||||||
"type": log.get("type", "info"),
|
"type": log.get("type", "info"),
|
||||||
"timestamp": log.get("timestamp", self._get_current_timestamp()),
|
"timestamp": log.get("timestamp", self._get_current_timestamp()),
|
||||||
"agent_name": log.get("agent_name", GLOBAL_SETTINGS.get("system_name", "AI Assistant")),
|
"agent_name": log.get("agent_name", "(undefined)"),
|
||||||
"status": log.get("status", "running"),
|
"status": log.get("status", "running"),
|
||||||
"progress": log.get("progress", 50)
|
"progress": log.get("progress", 50)
|
||||||
}
|
}
|
||||||
|
|
@ -1250,17 +1248,6 @@ class LucyDOMInterface:
|
||||||
logger.error(f"Error loading workflow state: {str(e)}")
|
logger.error(f"Error loading workflow state: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Global settings for the LucyDOM interface
|
|
||||||
GLOBAL_SETTINGS = {
|
|
||||||
"system_name": "AI Assistant", # Default system name for logs
|
|
||||||
"workflow_status_messages": {
|
|
||||||
"init": "Workflow initialized",
|
|
||||||
"running": "Running workflow",
|
|
||||||
"waiting": "Waiting for input",
|
|
||||||
"completed": "Workflow completed",
|
|
||||||
"error": "Error in workflow"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Singleton factory for LucyDOMInterface instances per context
|
# Singleton factory for LucyDOMInterface instances per context
|
||||||
_lucydom_interfaces = {}
|
_lucydom_interfaces = {}
|
||||||
|
|
@ -1272,7 +1259,12 @@ def get_lucydom_interface(mandate_id: int = 0, user_id: int = 0) -> LucyDOMInter
|
||||||
"""
|
"""
|
||||||
context_key = f"{mandate_id}_{user_id}"
|
context_key = f"{mandate_id}_{user_id}"
|
||||||
if context_key not in _lucydom_interfaces:
|
if context_key not in _lucydom_interfaces:
|
||||||
_lucydom_interfaces[context_key] = LucyDOMInterface(mandate_id, user_id)
|
# Create new interface instance
|
||||||
|
interface = LucyDOMInterface(mandate_id, user_id)
|
||||||
|
# Initialize AI service
|
||||||
|
ai_service = ChatService()
|
||||||
|
interface.ai_service = ai_service # Directly set the attribute
|
||||||
|
_lucydom_interfaces[context_key] = interface
|
||||||
return _lucydom_interfaces[context_key]
|
return _lucydom_interfaces[context_key]
|
||||||
|
|
||||||
# Init
|
# Init
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ class FileData(BaseModel):
|
||||||
id: int = Field(description="Unique ID of the data object")
|
id: int = Field(description="Unique ID of the data object")
|
||||||
data: str = Field(description="Binary content of the file as base64 string")
|
data: str = Field(description="Binary content of the file as base64 string")
|
||||||
|
|
||||||
|
|
||||||
# Workflow model classes
|
# Workflow model classes
|
||||||
|
|
||||||
class DocumentContent(BaseModel):
|
class DocumentContent(BaseModel):
|
||||||
|
|
@ -99,7 +100,7 @@ class DataStats(BaseModel):
|
||||||
bytes_sent: Optional[int] = Field(None, description="Bytes sent")
|
bytes_sent: Optional[int] = Field(None, description="Bytes sent")
|
||||||
bytes_received: Optional[int] = Field(None, description="Bytes received")
|
bytes_received: Optional[int] = Field(None, description="Bytes received")
|
||||||
|
|
||||||
class Message(BaseModel):
|
class WorkflowMessage(BaseModel):
|
||||||
"""Message object in the workflow"""
|
"""Message object in the workflow"""
|
||||||
id: str = Field(description="Unique ID of the message")
|
id: str = Field(description="Unique ID of the message")
|
||||||
workflow_id: str = Field(description="Reference to the parent workflow")
|
workflow_id: str = Field(description="Reference to the parent workflow")
|
||||||
|
|
@ -116,6 +117,19 @@ class Message(BaseModel):
|
||||||
content: Optional[str] = Field(None, description="Text content of the message")
|
content: Optional[str] = Field(None, description="Text content of the message")
|
||||||
agent_name: Optional[str] = Field(None, description="Name of the agent used")
|
agent_name: Optional[str] = Field(None, description="Name of the agent used")
|
||||||
|
|
||||||
|
class WorkflowLog(BaseModel):
|
||||||
|
"""Log entry for a workflow"""
|
||||||
|
id: str = Field(description="Unique ID of the log entry")
|
||||||
|
workflow_id: str = Field(description="ID of the associated workflow")
|
||||||
|
message: str = Field(description="Log message content")
|
||||||
|
type: str = Field(description="Type of log ('info', 'warning', 'error')")
|
||||||
|
timestamp: str = Field(description="Timestamp of the log entry")
|
||||||
|
agent_name: str = Field(description="Name of the agent that created the log")
|
||||||
|
status: str = Field(description="Status of the workflow at log time")
|
||||||
|
progress: Optional[int] = Field(None, description="Progress value (0-100)")
|
||||||
|
mandate_id: Optional[int] = Field(None, description="ID of the mandate")
|
||||||
|
user_id: Optional[int] = Field(None, description="ID of the user")
|
||||||
|
|
||||||
class Workflow(BaseModel):
|
class Workflow(BaseModel):
|
||||||
"""Workflow object for multi-agent system"""
|
"""Workflow object for multi-agent system"""
|
||||||
id: str = Field(description="Unique ID of the workflow")
|
id: str = Field(description="Unique ID of the workflow")
|
||||||
|
|
@ -125,11 +139,13 @@ class Workflow(BaseModel):
|
||||||
status: str = Field(description="Status of the workflow ('running', 'completed')")
|
status: str = Field(description="Status of the workflow ('running', 'completed')")
|
||||||
started_at: str = Field(description="Start timestamp")
|
started_at: str = Field(description="Start timestamp")
|
||||||
last_activity: str = Field(description="Timestamp of the last activity")
|
last_activity: str = Field(description="Timestamp of the last activity")
|
||||||
|
data_stats: Optional[Dict[str, Any]] = Field(None, description="Total statistics")
|
||||||
|
current_round: int = Field(default=1, description="Current round/iteration of the workflow")
|
||||||
message_ids: List[str] = Field(default=[], description="List of message IDs in this workflow")
|
message_ids: List[str] = Field(default=[], description="List of message IDs in this workflow")
|
||||||
|
|
||||||
data_stats: Optional[Dict[str, Any]] = Field(None, description="Total statistics")
|
messages: List[WorkflowMessage] = Field(default=[], description="Message history (in-memory representation)")
|
||||||
messages: List[Message] = Field(default=[], description="Message history")
|
logs: List[WorkflowLog] = Field(default=[], description="Log entries (in-memory representation)")
|
||||||
logs: List[Dict[str, Any]] = Field(default=[], description="Log entries")
|
|
||||||
|
|
||||||
# Request models for the API
|
# Request models for the API
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@ FRONTEND:
|
||||||
PRIO1:
|
PRIO1:
|
||||||
|
|
||||||
|
|
||||||
|
CHECK: If pictures not displayed to check utf-8 encoding in the base64 string!!
|
||||||
|
|
||||||
|
STOP File export to static folder ("TODO)
|
||||||
|
|
||||||
add connector to myoutlook
|
add connector to myoutlook
|
||||||
|
|
||||||
todo an agent for "code writing and editing" connected to the codebase, working in loops over each document...
|
todo an agent for "code writing and editing" connected to the codebase, working in loops over each document...
|
||||||
|
|
@ -26,8 +30,6 @@ Split big files into content-parts
|
||||||
|
|
||||||
PRIO2:
|
PRIO2:
|
||||||
|
|
||||||
implement cleanup routines for files in lucydom_interface (File_Management_CLEANUP_INTERVAL): temp older than interval, all orphaned
|
|
||||||
|
|
||||||
Integrate NDA Text as modal form - Data governance agreement by login with checkbox
|
Integrate NDA Text as modal form - Data governance agreement by login with checkbox
|
||||||
|
|
||||||
frontend to react
|
frontend to react
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
|
|
||||||
This is a test text file for the ChatManager workflow.
|
|
||||||
It contains some information for testing document processing.
|
|
||||||
|
|
||||||
The ChatManager should be able to process this file
|
|
||||||
and extract relevant information from it.
|
|
||||||
|
|
||||||
This file serves as an example for text-based documents that can be
|
|
||||||
used in a chat workflow.
|
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 284 B |
|
|
@ -1,52 +0,0 @@
|
||||||
# REQUIREMENTS: Pillow
|
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
def calculate_image_pixels(image_path):
|
|
||||||
try:
|
|
||||||
with Image.open(image_path) as img:
|
|
||||||
width, height = img.size
|
|
||||||
total_pixels = width * height
|
|
||||||
return total_pixels
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error calculating image pixels: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def calculate_text_characters(text_path):
|
|
||||||
try:
|
|
||||||
with open(text_path, 'r', encoding='utf-8') as file:
|
|
||||||
text = file.read()
|
|
||||||
total_characters = len(text)
|
|
||||||
return total_characters
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error calculating text characters: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def main():
|
|
||||||
image_path = 'test_image'
|
|
||||||
text_path = 'test_document'
|
|
||||||
|
|
||||||
# Calculate total pixels in the image
|
|
||||||
total_pixels = calculate_image_pixels(image_path)
|
|
||||||
|
|
||||||
# Calculate total characters in the text document
|
|
||||||
total_characters = calculate_text_characters(text_path)
|
|
||||||
|
|
||||||
# Prepare the result dictionary
|
|
||||||
result = {
|
|
||||||
'total_pixels': total_pixels,
|
|
||||||
'total_characters': total_characters
|
|
||||||
}
|
|
||||||
|
|
||||||
# Write the result to a text file
|
|
||||||
try:
|
|
||||||
with open('result.txt', 'w') as result_file:
|
|
||||||
result_file.write(str(result))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error writing result to file: {e}")
|
|
||||||
|
|
||||||
# Output the result
|
|
||||||
print(result)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
Execution error:
|
|
||||||
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "C:\Users\pmots\AppData\Local\Temp\code_exec_itmq0xhw\code_9cc3911d.py", line 3, in <module>
|
|
||||||
from PIL import Image
|
|
||||||
ModuleNotFoundError: No module named 'PIL'
|
|
||||||
50
test2.py
50
test2.py
|
|
@ -1,50 +0,0 @@
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
def calculate_image_pixels(image_path):
|
|
||||||
try:
|
|
||||||
with Image.open(image_path) as img:
|
|
||||||
width, height = img.size
|
|
||||||
total_pixels = width * height
|
|
||||||
return total_pixels
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error calculating image pixels: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def calculate_text_characters(text_path):
|
|
||||||
try:
|
|
||||||
with open(text_path, 'r', encoding='utf-8') as file:
|
|
||||||
text = file.read()
|
|
||||||
total_characters = len(text)
|
|
||||||
return total_characters
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error calculating text characters: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def main():
|
|
||||||
image_path = 'test_image'
|
|
||||||
text_path = 'test_document'
|
|
||||||
|
|
||||||
# Calculate total pixels in the image
|
|
||||||
total_pixels = calculate_image_pixels(image_path)
|
|
||||||
|
|
||||||
# Calculate total characters in the text document
|
|
||||||
total_characters = calculate_text_characters(text_path)
|
|
||||||
|
|
||||||
# Prepare the result dictionary
|
|
||||||
result = {
|
|
||||||
'total_pixels': total_pixels,
|
|
||||||
'total_characters': total_characters
|
|
||||||
}
|
|
||||||
|
|
||||||
# Write the result to a text file
|
|
||||||
try:
|
|
||||||
with open('result.txt', 'w') as result_file:
|
|
||||||
result_file.write(str(result))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error writing result to file: {e}")
|
|
||||||
|
|
||||||
# Output the result
|
|
||||||
print(result)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -13,7 +13,7 @@ from datetime import datetime
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.INFO,
|
||||||
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
|
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
|
||||||
handlers=[logging.StreamHandler()]
|
handlers=[logging.StreamHandler()]
|
||||||
)
|
)
|
||||||
|
|
@ -112,7 +112,7 @@ async def run_chat_workflow(mandate_id: int, user_id: int, file_ids: List[int])
|
||||||
|
|
||||||
# Create user request
|
# Create user request
|
||||||
user_input = {
|
user_input = {
|
||||||
"prompt": "Bitte zähle mir zusammen wieviele Pixel das Bild hat und wieviele Zeichen der Text der Dokumente hat",
|
"prompt": "Bitte integriere den Text der Datei ins angefügte Bild",
|
||||||
"list_file_id": file_ids
|
"list_file_id": file_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
66
testcode.py
Normal file
66
testcode.py
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
input_files = [['test_document.txt', 'CiAgICBUaGlzIGlzIGEgdGVzdCB0ZXh0IGZpbGUgZm9yIHRoZSBDaGF0TWFuYWdlciB3b3JrZmxvdy4KICAgIEl0IGNvbnRhaW5zIHNvbWUgaW5mb3JtYXRpb24gZm9yIHRlc3RpbmcgZG9jdW1lbnQgcHJvY2Vzc2luZy4KICAgIAogICAgVGhlIENoYXRNYW5hZ2VyIHNob3VsZCBiZSBhYmxlIHRvIHByb2Nlc3MgdGhpcyBmaWxlCiAgICBhbmQgZXh0cmFjdCByZWxldmFudCBpbmZvcm1hdGlvbiBmcm9tIGl0LgogICAgCiAgICBUaGlzIGZpbGUgc2VydmVzIGFzIGFuIGV4YW1wbGUgZm9yIHRleHQtYmFzZWQgZG9jdW1lbnRzIHRoYXQgY2FuIGJlCiAgICB1c2VkIGluIGEgY2hhdCB3b3JrZmxvdy4KICAgIA==', True], ['test_document.txt', '\n This is a test text file for the ChatManager workflow.\n It contains some information for testing document processing.\n \n The ChatManager should be able to process this file\n and extract relevant information from it.\n \n This file serves as an example for text-based documents that can be\n used in a chat workflow.\n ', False], ['test_image.png', 'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAA40lEQVR4nO3QsQEAIAyAsOr/P+sLZU9mJs4btu66xKzCrMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArMCswKzArNn7il4Bx2GaB88AAAAASUVORK5CYII=', True], ['test_image.png', 'Image (100x100, PNG, RGB)', False]] # DO NOT CHANGE THIS LINE
|
||||||
|
|
||||||
|
# REQUIREMENTS: Pillow==10.0.0
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
import io
|
||||||
|
|
||||||
|
# Initialize result dictionary
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
# Helper function to decode base64 data
|
||||||
|
def decode_base64(data: str) -> bytes:
|
||||||
|
return base64.b64decode(data)
|
||||||
|
|
||||||
|
# Extract input files
|
||||||
|
text_data = None
|
||||||
|
image_data = None
|
||||||
|
|
||||||
|
for file_info in input_files:
|
||||||
|
filename, data, is_base64 = file_info
|
||||||
|
if filename == 'test_document.txt' and not is_base64:
|
||||||
|
text_data = data
|
||||||
|
elif filename == 'test_image.png' and is_base64:
|
||||||
|
image_data = decode_base64(data)
|
||||||
|
|
||||||
|
# Validate inputs
|
||||||
|
if text_data is None:
|
||||||
|
raise ValueError("Text data not found in input files.")
|
||||||
|
if image_data is None:
|
||||||
|
raise ValueError("Image data not found in input files.")
|
||||||
|
|
||||||
|
# Load image
|
||||||
|
image = Image.open(io.BytesIO(image_data))
|
||||||
|
|
||||||
|
# Prepare to draw on the image
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
|
# Use a basic font
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype("arial", 15)
|
||||||
|
except IOError:
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
|
||||||
|
# Define text position
|
||||||
|
text_position = (10, 10)
|
||||||
|
|
||||||
|
# Overlay text onto image
|
||||||
|
draw.text(text_position, text_data, font=font, fill=(255, 255, 255))
|
||||||
|
|
||||||
|
# Save the resulting image to a bytes buffer
|
||||||
|
output_buffer = io.BytesIO()
|
||||||
|
image.save(output_buffer, format='PNG')
|
||||||
|
output_buffer.seek(0)
|
||||||
|
|
||||||
|
#result['integrated_image.png'] = base64.b64encode(output_buffer.getvalue()).decode('utf-8')
|
||||||
|
# binary_data = base64.b64decode(pic_str)
|
||||||
|
binary_data=output_buffer.getvalue()
|
||||||
|
|
||||||
|
# Write the binary data to a file
|
||||||
|
with open("./static/test_output.png", "wb") as f:
|
||||||
|
f.write(binary_data)
|
||||||
|
|
||||||
|
print("OK")
|
||||||
|
# Output the result dictionary
|
||||||
|
result
|
||||||
Loading…
Reference in a new issue