mirror of https://github.com/kortix-ai/suna.git
fix duplicate agent run
This commit is contained in:
parent
7ee68b4282
commit
3c0634f6bc
|
@ -512,6 +512,9 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
agent_gen = run_agent(thread_id, stream=True,
|
||||
thread_manager=thread_manager)
|
||||
|
||||
# Collect all responses to save to database
|
||||
all_responses = []
|
||||
|
||||
async for response in agent_gen:
|
||||
# Check if stop signal received
|
||||
if stop_signal_received:
|
||||
|
@ -521,12 +524,13 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
# Store response in memory
|
||||
if agent_run_id in active_agent_runs:
|
||||
active_agent_runs[agent_run_id].append(response)
|
||||
all_responses.append(response)
|
||||
total_responses += 1
|
||||
|
||||
# Periodically log progress for long-running agents
|
||||
if total_responses % 100 == 0:
|
||||
duration = (datetime.now(timezone.utc) - start_time).total_seconds()
|
||||
logger.info(f"Agent run {agent_run_id} still running after {duration:.1f}s, processed {total_responses} responses (instance: {instance_id})")
|
||||
# # Periodically log progress for long-running agents
|
||||
# if total_responses % 100 == 0:
|
||||
# duration = (datetime.now(timezone.utc) - start_time).total_seconds()
|
||||
# logger.info(f"Agent run {agent_run_id} still running after {duration:.1f}s, processed {total_responses} responses (instance: {instance_id})")
|
||||
|
||||
# Signal all done if we weren't stopped
|
||||
if not stop_signal_received:
|
||||
|
@ -535,11 +539,13 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
|
||||
# Add completion message to the stream
|
||||
if agent_run_id in active_agent_runs:
|
||||
active_agent_runs[agent_run_id].append({
|
||||
completion_message = {
|
||||
"type": "status",
|
||||
"status": "completed",
|
||||
"message": "Agent run completed successfully"
|
||||
})
|
||||
}
|
||||
active_agent_runs[agent_run_id].append(completion_message)
|
||||
all_responses.append(completion_message)
|
||||
|
||||
# Update the agent run status in the database
|
||||
completion_timestamp = datetime.now(timezone.utc).isoformat()
|
||||
|
@ -549,7 +555,8 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
try:
|
||||
update_result = await client.table('agent_runs').update({
|
||||
"status": "completed",
|
||||
"completed_at": completion_timestamp
|
||||
"completed_at": completion_timestamp,
|
||||
"responses": json.dumps(all_responses)
|
||||
}).eq("id", agent_run_id).execute()
|
||||
|
||||
if hasattr(update_result, 'data') and update_result.data:
|
||||
|
@ -597,11 +604,16 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
|
||||
# Add error message to the stream
|
||||
if agent_run_id in active_agent_runs:
|
||||
active_agent_runs[agent_run_id].append({
|
||||
error_response = {
|
||||
"type": "status",
|
||||
"status": "error",
|
||||
"message": error_message
|
||||
})
|
||||
}
|
||||
active_agent_runs[agent_run_id].append(error_response)
|
||||
if 'all_responses' in locals():
|
||||
all_responses.append(error_response)
|
||||
else:
|
||||
all_responses = [error_response] # Initialize if not exists
|
||||
|
||||
# Update the agent run with the error - with retry
|
||||
completion_timestamp = datetime.now(timezone.utc).isoformat()
|
||||
|
@ -611,7 +623,8 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
update_result = await client.table('agent_runs').update({
|
||||
"status": "failed",
|
||||
"error": f"{error_message}\n{traceback_str}",
|
||||
"completed_at": completion_timestamp
|
||||
"completed_at": completion_timestamp,
|
||||
"responses": json.dumps(all_responses if 'all_responses' in locals() else [])
|
||||
}).eq("id", agent_run_id).execute()
|
||||
|
||||
if hasattr(update_result, 'data') and update_result.data:
|
||||
|
@ -667,6 +680,4 @@ async def run_agent_background(agent_run_id: str, thread_id: str, instance_id: s
|
|||
except Exception as e:
|
||||
logger.warning(f"Error deleting active run key: {str(e)}")
|
||||
|
||||
logger.info(f"Agent run background task fully completed for: {agent_run_id} (instance: {instance_id})")
|
||||
|
||||
|
||||
logger.info(f"Agent run background task fully completed for: {agent_run_id} (instance: {instance_id})")
|
|
@ -0,0 +1,100 @@
|
|||
'''
|
||||
Agent prompt configuration with instructions for XML-based tool usage.
|
||||
This defines the system prompt that instructs the agent how to properly use the XML tool syntax.
|
||||
'''
|
||||
|
||||
SYSTEM_PROMPT = '''
|
||||
You are an AI agent specialized in helping users with coding tasks and web development projects.
|
||||
|
||||
# XML-BASED TOOLS GUIDE
|
||||
|
||||
You have access to several tools to help with files, searching code, and executing commands. These tools must be used with specific XML syntax.
|
||||
|
||||
## File Operations
|
||||
|
||||
### Reading Files
|
||||
To read a file, use the `read_file` tag:
|
||||
```
|
||||
<read_file path='File path here' start_line='1' end_line='20' read_entire='false'>
|
||||
</read_file>
|
||||
```
|
||||
|
||||
### Writing Files
|
||||
To create or overwrite a file, use the `write_to_file` tag:
|
||||
```
|
||||
<write_to_file path='File path here'>
|
||||
Your file content here
|
||||
</write_to_file>
|
||||
```
|
||||
|
||||
### Replacing Content in Files
|
||||
To modify parts of a file, use the `replace_in_file` tag with search/replace blocks:
|
||||
```
|
||||
<replace_in_file path='File path here'>
|
||||
<<<<<<< SEARCH
|
||||
[exact content to find]
|
||||
=======
|
||||
[new content to replace with]
|
||||
>>>>>>> REPLACE
|
||||
</replace_in_file>
|
||||
```
|
||||
|
||||
### Deleting Files
|
||||
To delete a file, use the `delete_file` tag:
|
||||
```
|
||||
<delete_file path='path/to/file'>
|
||||
</delete_file>
|
||||
```
|
||||
|
||||
## Searching and Code Navigation
|
||||
|
||||
### Listing Directory Contents
|
||||
To list the contents of a directory, use the `list_dir` tag:
|
||||
```
|
||||
<list_dir relative_workspace_path='src/'>
|
||||
</list_dir>
|
||||
```
|
||||
|
||||
### Searching for Text Pattern
|
||||
To search for text patterns in files, use the `grep_search` tag:
|
||||
```
|
||||
<grep_search query='function' include_pattern='*.js' case_sensitive='false'>
|
||||
</grep_search>
|
||||
```
|
||||
|
||||
### Finding Files
|
||||
To search for files by name, use the `file_search` tag:
|
||||
```
|
||||
<file_search query='component'>
|
||||
</file_search>
|
||||
```
|
||||
|
||||
## Terminal Operations
|
||||
|
||||
### Executing Commands
|
||||
To run a command in the terminal, use the `execute_command` tag:
|
||||
```
|
||||
<execute_command>
|
||||
<command>npm install react</command>
|
||||
<requires_approval>true</requires_approval>
|
||||
</execute_command>
|
||||
```
|
||||
|
||||
# USING TOOLS
|
||||
|
||||
1. **Choose the right tool** for each task based on what you're trying to accomplish.
|
||||
2. **Use the exact XML syntax** shown in the examples above.
|
||||
3. **Provide all required parameters** for each tool.
|
||||
4. **Process the results** returned by the tools to inform your next actions.
|
||||
5. **Use appropriate tools based on the extent of changes** needed.
|
||||
|
||||
When executing commands that might modify the system (installing packages, removing files, etc.), always set `requires_approval` to true.
|
||||
|
||||
Always provide clear explanations to the user about what actions you're taking and why.
|
||||
'''
|
||||
|
||||
def get_system_prompt():
|
||||
'''
|
||||
Returns the system prompt with XML tool usage instructions.
|
||||
'''
|
||||
return SYSTEM_PROMPT
|
|
@ -1,97 +1,79 @@
|
|||
'''
|
||||
Agent prompt configuration with instructions for XML-based tool usage.
|
||||
This defines the system prompt that instructs the agent how to properly use the XML tool syntax.
|
||||
'''
|
||||
SYSTEM_PROMPT = """
|
||||
You are a powerful general purpose AI assistant capable of helping users with a wide range of tasks. As a versatile assistant, you combine deep knowledge across many domains with helpful problem-solving skills to deliver high-quality responses. You excel at understanding user needs, providing accurate information, and offering creative solutions to various challenges.
|
||||
|
||||
SYSTEM_PROMPT = '''
|
||||
You are an AI agent specialized in helping users with coding tasks and web development projects.
|
||||
You are capable of:
|
||||
1. Information gathering, fact-checking, and documentation
|
||||
2. Data processing, analysis, and visualization
|
||||
3. Writing multi-chapter articles and in-depth research reports
|
||||
4. Creating websites, applications, and tools
|
||||
5. Using programming to solve various problems beyond development
|
||||
6. Various tasks that can be accomplished using computers and the internet
|
||||
|
||||
# XML-BASED TOOLS GUIDE
|
||||
The tasks you handle may include answering questions, performing research, drafting content, explaining complex concepts, or helping with specific technical requirements. As a professional assistant, you'll approach each request with expertise and clarity.
|
||||
|
||||
You have access to several tools to help with files, searching code, and executing commands. These tools must be used with specific XML syntax.
|
||||
Your main goal is to follow the USER's instructions at each message, delivering helpful, accurate, and clear responses tailored to their needs.
|
||||
FOLLOW THE USER'S QUESTIONS, INSTRUCTIONS AND REQUESTS AT ALL TIMES.
|
||||
|
||||
## File Operations
|
||||
Remember:
|
||||
1. ALWAYS follow the exact response format shown above
|
||||
2. When using str_replace, only include the minimal changes needed
|
||||
3. When using full_file_rewrite, include ALL necessary code
|
||||
4. Use appropriate tools based on the extent of changes
|
||||
5. Focus on providing accurate, helpful information
|
||||
6. Consider context and user needs in your responses
|
||||
7. Handle ambiguity gracefully by asking clarifying questions when needed
|
||||
8. ISSUE ONLY ONE SINGLE XML TOOL CALL AT A TIME - complete one action before proceeding to the next
|
||||
|
||||
### Reading Files
|
||||
To read a file, use the `read_file` tag:
|
||||
```
|
||||
<read_file path='File path here' start_line='1' end_line='20' read_entire='false'>
|
||||
</read_file>
|
||||
```
|
||||
<available_tools>
|
||||
You have access to these tools through XML-based tool calling:
|
||||
- create_file: Create new files with specified content
|
||||
- delete_file: Remove existing files
|
||||
- str_replace: Replace specific text in files
|
||||
- full_file_rewrite: Completely rewrite an existing file with new content
|
||||
- terminal_tool: Execute shell commands in the workspace directory
|
||||
- message_notify_user: Send a message to user without requiring a response. Use for acknowledging receipt of messages, providing progress updates, reporting task completion, or explaining changes in approach
|
||||
- message_ask_user: Ask user a question and wait for response. Use for requesting clarification, asking for confirmation, or gathering additional information
|
||||
- idle: A special tool to indicate you have completed all tasks and are entering idle state
|
||||
</available_tools>
|
||||
|
||||
### Writing Files
|
||||
To create or overwrite a file, use the `write_to_file` tag:
|
||||
```
|
||||
<write_to_file path='File path here'>
|
||||
Your file content here
|
||||
</write_to_file>
|
||||
```
|
||||
<response_format>
|
||||
RESPONSE FORMAT – STRICTLY Output XML tags for tool calling
|
||||
|
||||
### Replacing Content in Files
|
||||
To modify parts of a file, use the `replace_in_file` tag with search/replace blocks:
|
||||
```
|
||||
<replace_in_file path='File path here'>
|
||||
<<<<<<< SEARCH
|
||||
[exact content to find]
|
||||
=======
|
||||
[new content to replace with]
|
||||
>>>>>>> REPLACE
|
||||
</replace_in_file>
|
||||
```
|
||||
You must only use ONE tool call at a time. Wait for each action to complete before proceeding to the next one.
|
||||
|
||||
### Deleting Files
|
||||
To delete a file, use the `delete_file` tag:
|
||||
```
|
||||
<delete_file path='path/to/file'>
|
||||
</delete_file>
|
||||
```
|
||||
<create-file file_path="path/to/file">
|
||||
file contents here
|
||||
</create-file>
|
||||
|
||||
## Searching and Code Navigation
|
||||
<str-replace file_path="path/to/file">
|
||||
<old_str>text to replace</old_str>
|
||||
<new_str>replacement text</new_str>
|
||||
</str-replace>
|
||||
|
||||
### Listing Directory Contents
|
||||
To list the contents of a directory, use the `list_dir` tag:
|
||||
```
|
||||
<list_dir relative_workspace_path='src/'>
|
||||
</list_dir>
|
||||
```
|
||||
<full-file-rewrite file_path="path/to/file">
|
||||
New file contents go here, replacing all existing content
|
||||
</full-file-rewrite>
|
||||
|
||||
### Searching for Text Pattern
|
||||
To search for text patterns in files, use the `grep_search` tag:
|
||||
```
|
||||
<grep_search query='function' include_pattern='*.js' case_sensitive='false'>
|
||||
</grep_search>
|
||||
```
|
||||
<delete-file file_path="path/to/file">
|
||||
</delete-file>
|
||||
|
||||
### Finding Files
|
||||
To search for files by name, use the `file_search` tag:
|
||||
```
|
||||
<file_search query='component'>
|
||||
</file_search>
|
||||
```
|
||||
<execute-command>
|
||||
command here
|
||||
</execute-command>
|
||||
|
||||
## Terminal Operations
|
||||
<message-notify-user>
|
||||
Message text to display to user
|
||||
</message-notify-user>
|
||||
|
||||
### Executing Commands
|
||||
To run a command in the terminal, use the `execute_command` tag:
|
||||
```
|
||||
<execute_command>
|
||||
<command>npm install react</command>
|
||||
<requires_approval>true</requires_approval>
|
||||
</execute_command>
|
||||
```
|
||||
<message-ask-user>
|
||||
Question text to present to user
|
||||
</message-ask-user>
|
||||
|
||||
# USING TOOLS
|
||||
<idle></idle>
|
||||
|
||||
1. **Choose the right tool** for each task based on what you're trying to accomplish.
|
||||
2. **Use the exact XML syntax** shown in the examples above.
|
||||
3. **Provide all required parameters** for each tool.
|
||||
4. **Process the results** returned by the tools to inform your next actions.
|
||||
5. **Use appropriate tools based on the extent of changes** needed.
|
||||
</response_format>
|
||||
|
||||
When executing commands that might modify the system (installing packages, removing files, etc.), always set `requires_approval` to true.
|
||||
|
||||
Always provide clear explanations to the user about what actions you're taking and why.
|
||||
'''
|
||||
"""
|
||||
|
||||
def get_system_prompt():
|
||||
'''
|
||||
|
|
|
@ -7,7 +7,7 @@ from agent.tools.terminal_tool import TerminalTool
|
|||
from agent.tools.wait_tool import WaitTool
|
||||
# from agent.tools.search_tool import CodeSearchTool
|
||||
from typing import Optional
|
||||
from agent.test_prompt import get_system_prompt
|
||||
from agent.prompt import get_system_prompt
|
||||
from agentpress.response_processor import ProcessorConfig
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
@ -155,4 +155,4 @@ if __name__ == "__main__":
|
|||
load_dotenv() # Ensure environment variables are loaded
|
||||
|
||||
# Run the test function
|
||||
asyncio.run(test_agent())
|
||||
asyncio.run(test_agent())
|
|
@ -1,59 +0,0 @@
|
|||
|
||||
SYSTEM_PROMPT = """
|
||||
You are a powerful AI web development partner helping users create modern web applications. As an expert web development partner, you combine deep technical expertise with best practices to deliver high-quality, scalable web solutions. You excel at modern web development, creating responsive user interfaces, crafting polished visuals with CSS, and implementing robust functionality with JavaScript.
|
||||
|
||||
The task will require modifying or creating web applications, implementing new features, optimizing performance, or simply answering technical questions. As a professional web development assistant, you'll approach each request with the expertise of a senior web developer.
|
||||
|
||||
Your main goal is to follow the USER's instructions at each message, delivering high-quality code solutions, thoughtful architectural advice, and clear technical explanations in a non-technical friendly way.
|
||||
FOLLOW THE USER'S QUESTIONS, INSTRUCTIONS AND REQUESTS AT ALL TIMES.
|
||||
|
||||
Remember:
|
||||
1. ALWAYS follow the exact response format shown above
|
||||
2. Use CDN links for external libraries (if needed)
|
||||
3. When using str_replace, only include the minimal changes needed
|
||||
4. When using full_file_rewrite, include ALL necessary code
|
||||
5. Use appropriate tools based on the extent of changes
|
||||
6. Focus on creating maintainable and scalable web applications
|
||||
7. Implement proper error handling and edge cases
|
||||
|
||||
<available_tools>
|
||||
You have access to these tools:
|
||||
- create_file: Create new files with specified content
|
||||
- delete_file: Remove existing files
|
||||
- str_replace: Replace specific text in files
|
||||
- full_file_rewrite: Completely rewrite an existing file with new content
|
||||
- terminal_tool: Execute shell commands in the workspace directory
|
||||
</available_tools>
|
||||
|
||||
<response_format>
|
||||
RESPONSE FORMAT – STRICTLY Output XML tags
|
||||
|
||||
<create-file file_path="path/to/file">
|
||||
file contents here
|
||||
</create-file>
|
||||
|
||||
<str-replace file_path="path/to/file">
|
||||
<old_str>text to replace</old_str>
|
||||
<new_str>replacement text</new_str>
|
||||
</str-replace>
|
||||
|
||||
<full-file-rewrite file_path="path/to/file">
|
||||
New file contents go here, replacing all existing content
|
||||
</full-file-rewrite>
|
||||
|
||||
<delete-file file_path="path/to/file">
|
||||
</delete-file>
|
||||
|
||||
<execute-command>
|
||||
command here
|
||||
</execute-command>
|
||||
|
||||
</response_format>
|
||||
|
||||
"""
|
||||
|
||||
def get_system_prompt():
|
||||
'''
|
||||
Returns the system prompt with XML tool usage instructions.
|
||||
'''
|
||||
return SYSTEM_PROMPT
|
|
@ -0,0 +1,203 @@
|
|||
import os
|
||||
from typing import List, Optional, Union
|
||||
from agentpress.tool import Tool, ToolResult, openapi_schema, xml_schema
|
||||
|
||||
class MessageTool(Tool):
|
||||
"""Tool for user communication and interaction.
|
||||
|
||||
This tool provides methods for notifying users and asking questions, with support for
|
||||
attachments and user takeover suggestions.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@openapi_schema({
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "message_notify_user",
|
||||
"description": "Send a message to user without requiring a response. Use for acknowledging receipt of messages, providing progress updates, reporting task completion, or explaining changes in approach.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": "Message text to display to user"
|
||||
},
|
||||
"attachments": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"items": {"type": "string"}, "type": "array"}
|
||||
],
|
||||
"description": "(Optional) List of attachments to show to user, can be file paths or URLs"
|
||||
}
|
||||
},
|
||||
"required": ["text"]
|
||||
}
|
||||
}
|
||||
})
|
||||
@xml_schema(
|
||||
tag_name="message-notify-user",
|
||||
mappings=[
|
||||
{"param_name": "text", "node_type": "content", "path": "."},
|
||||
{"param_name": "attachments", "node_type": "attribute", "path": ".", "required": False}
|
||||
],
|
||||
example='''
|
||||
<message-notify-user attachments="path/to/file1.txt,path/to/file2.pdf,https://example.com/doc.pdf">
|
||||
Task completed successfully!
|
||||
</message-notify-user>
|
||||
'''
|
||||
)
|
||||
async def message_notify_user(self, text: str, attachments: Optional[Union[str, List[str]]] = None) -> ToolResult:
|
||||
"""Send a notification message to the user without requiring a response.
|
||||
|
||||
Args:
|
||||
text: The message to display to the user
|
||||
attachments: Optional file paths or URLs to attach to the message
|
||||
|
||||
Returns:
|
||||
ToolResult indicating success or failure of the notification
|
||||
"""
|
||||
try:
|
||||
# Convert single attachment to list for consistent handling
|
||||
if attachments and isinstance(attachments, str):
|
||||
attachments = [attachments]
|
||||
|
||||
# Format the response message
|
||||
response_text = f"NOTIFICATION: {text}"
|
||||
|
||||
# Add attachments information if present
|
||||
if attachments:
|
||||
attachment_list = "\n- ".join(attachments)
|
||||
response_text += f"\n\nAttachments:\n- {attachment_list}"
|
||||
|
||||
return self.success_response(response_text)
|
||||
except Exception as e:
|
||||
return self.fail_response(f"Error sending notification: {str(e)}")
|
||||
|
||||
@openapi_schema({
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "message_ask_user",
|
||||
"description": "Ask user a question and wait for response. Use for requesting clarification, asking for confirmation, or gathering additional information.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": "Question text to present to user"
|
||||
},
|
||||
"attachments": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"items": {"type": "string"}, "type": "array"}
|
||||
],
|
||||
"description": "(Optional) List of question-related files or reference materials"
|
||||
},
|
||||
"suggest_user_takeover": {
|
||||
"type": "string",
|
||||
"enum": ["none", "browser"],
|
||||
"description": "(Optional) Suggested operation for user takeover"
|
||||
}
|
||||
},
|
||||
"required": ["text"]
|
||||
}
|
||||
}
|
||||
})
|
||||
@xml_schema(
|
||||
tag_name="message-ask-user",
|
||||
mappings=[
|
||||
{"param_name": "text", "node_type": "content", "path": "."},
|
||||
{"param_name": "attachments", "node_type": "attribute", "path": ".", "required": False},
|
||||
{"param_name": "suggest_user_takeover", "node_type": "attribute", "path": ".", "required": False}
|
||||
],
|
||||
example='''
|
||||
<message-ask-user attachments="path/to/file1.txt,path/to/file2.pdf" suggest_user_takeover="browser">
|
||||
Would you like to continue with this approach?
|
||||
</message-ask-user>
|
||||
'''
|
||||
)
|
||||
async def message_ask_user(self, text: str, attachments: Optional[Union[str, List[str]]] = None,
|
||||
suggest_user_takeover: str = "none") -> ToolResult:
|
||||
"""Ask the user a question and wait for a response.
|
||||
|
||||
Args:
|
||||
text: The question to present to the user
|
||||
attachments: Optional file paths or URLs to attach to the question
|
||||
suggest_user_takeover: Optional suggestion for user takeover (none, browser)
|
||||
|
||||
Returns:
|
||||
ToolResult indicating the question was successfully sent
|
||||
"""
|
||||
try:
|
||||
# Convert single attachment to list for consistent handling
|
||||
if attachments and isinstance(attachments, str):
|
||||
attachments = [attachments]
|
||||
|
||||
# Format the question message
|
||||
response_text = f"QUESTION: {text}"
|
||||
|
||||
# Add attachments information if present
|
||||
if attachments:
|
||||
attachment_list = "\n- ".join(attachments)
|
||||
response_text += f"\n\nAttachments:\n- {attachment_list}"
|
||||
|
||||
# Add user takeover suggestion if not "none"
|
||||
if suggest_user_takeover and suggest_user_takeover != "none":
|
||||
response_text += f"\n\nSuggested takeover: {suggest_user_takeover}"
|
||||
|
||||
return self.success_response(response_text, requires_response=True)
|
||||
except Exception as e:
|
||||
return self.fail_response(f"Error asking user: {str(e)}")
|
||||
|
||||
@openapi_schema({
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "idle",
|
||||
"description": "A special tool to indicate you have completed all tasks and are about to enter idle state.",
|
||||
"parameters": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
})
|
||||
@xml_schema(
|
||||
tag_name="idle",
|
||||
mappings=[],
|
||||
example='''
|
||||
<idle></idle>
|
||||
'''
|
||||
)
|
||||
async def idle(self) -> ToolResult:
|
||||
"""Indicate that the agent has completed all tasks and is entering idle state.
|
||||
|
||||
Returns:
|
||||
ToolResult indicating successful transition to idle state
|
||||
"""
|
||||
try:
|
||||
return self.success_response("Entering idle state")
|
||||
except Exception as e:
|
||||
return self.fail_response(f"Error entering idle state: {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
|
||||
async def test_message_tool():
|
||||
message_tool = MessageTool()
|
||||
|
||||
# Test notification
|
||||
notify_result = await message_tool.message_notify_user(
|
||||
"Processing has completed successfully!",
|
||||
attachments=["results.txt", "output.log"]
|
||||
)
|
||||
print("Notification result:", notify_result)
|
||||
|
||||
# Test question
|
||||
ask_result = await message_tool.message_ask_user(
|
||||
"Would you like to proceed with the next phase?",
|
||||
attachments="summary.pdf",
|
||||
suggest_user_takeover="browser"
|
||||
)
|
||||
print("Question result:", ask_result)
|
||||
|
||||
asyncio.run(test_message_tool())
|
|
@ -0,0 +1,331 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>My Portfolio</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<h1 class="logo">My Portfolio</h1>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="#home">Home</a></li>
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#skills">Skills</a></li>
|
||||
<li><a href="#projects">Projects</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="hamburger">
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="home" class="hero">
|
||||
<div class="container">
|
||||
<div class="hero-content">
|
||||
<h1>Hello, I'm <span class="highlight">Your Name</span></h1>
|
||||
<p class="tagline">Web Developer & Designer</p>
|
||||
<div class="cta-buttons">
|
||||
<a href="#projects" class="btn primary-btn">View My Work</a>
|
||||
<a href="#contact" class="btn secondary-btn">Contact Me</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="about" class="about">
|
||||
<div class="container">
|
||||
<h2 class="section-title">About Me</h2>
|
||||
<div class="about-content">
|
||||
<div class="about-image">
|
||||
<div class="image-placeholder">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="about-text">
|
||||
<p>Hello! I'm a passionate web developer with a keen eye for design and a love for creating seamless user experiences. With a background in [Your Background], I bring a unique perspective to every project I work on.</p>
|
||||
<p>I enjoy solving complex problems and turning ideas into reality through clean and efficient code. When I'm not coding, you can find me [Your Hobbies/Interests].</p>
|
||||
<div class="about-details">
|
||||
<div class="detail">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>Education: [Your Education]</span>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<i class="fas fa-briefcase"></i>
|
||||
<span>Experience: [Years] years</span>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<span>Location: [Your Location]</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="btn primary-btn">Download Resume</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="skills" class="skills">
|
||||
<div class="container">
|
||||
<h2 class="section-title">My Skills</h2>
|
||||
<div class="skills-content">
|
||||
<div class="skill-category">
|
||||
<h3>Frontend Development</h3>
|
||||
<div class="skills-grid">
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fab fa-html5"></i></div>
|
||||
<h4>HTML5</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 90%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fab fa-css3-alt"></i></div>
|
||||
<h4>CSS3</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 85%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fab fa-js"></i></div>
|
||||
<h4>JavaScript</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 80%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fab fa-react"></i></div>
|
||||
<h4>React</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 75%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="skill-category">
|
||||
<h3>Other Skills</h3>
|
||||
<div class="skills-grid">
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fas fa-database"></i></div>
|
||||
<h4>SQL</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 70%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fab fa-git-alt"></i></div>
|
||||
<h4>Git</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 85%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fas fa-mobile-alt"></i></div>
|
||||
<h4>Responsive Design</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 90%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-item">
|
||||
<div class="skill-icon"><i class="fas fa-paint-brush"></i></div>
|
||||
<h4>UI/UX</h4>
|
||||
<div class="skill-bar">
|
||||
<div class="skill-level" style="width: 75%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="projects" class="projects">
|
||||
<div class="container">
|
||||
<h2 class="section-title">My Projects</h2>
|
||||
<div class="project-filters">
|
||||
<button class="filter-btn active" data-filter="all">All</button>
|
||||
<button class="filter-btn" data-filter="web">Web Development</button>
|
||||
<button class="filter-btn" data-filter="design">Design</button>
|
||||
<button class="filter-btn" data-filter="other">Other</button>
|
||||
</div>
|
||||
<div class="projects-grid">
|
||||
<div class="project-card" data-category="web">
|
||||
<div class="project-image">
|
||||
<div class="image-placeholder">
|
||||
<i class="fas fa-laptop-code"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-info">
|
||||
<h3>Project Title 1</h3>
|
||||
<p>A brief description of the project and your role in it. Explain the technologies used and the problems solved.</p>
|
||||
<div class="project-tags">
|
||||
<span>HTML</span>
|
||||
<span>CSS</span>
|
||||
<span>JavaScript</span>
|
||||
</div>
|
||||
<div class="project-links">
|
||||
<a href="#" class="btn small-btn"><i class="fas fa-eye"></i> Live Demo</a>
|
||||
<a href="#" class="btn small-btn"><i class="fab fa-github"></i> Source Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card" data-category="design">
|
||||
<div class="project-image">
|
||||
<div class="image-placeholder">
|
||||
<i class="fas fa-paint-brush"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-info">
|
||||
<h3>Project Title 2</h3>
|
||||
<p>A brief description of the project and your role in it. Explain the technologies used and the problems solved.</p>
|
||||
<div class="project-tags">
|
||||
<span>Figma</span>
|
||||
<span>UI/UX</span>
|
||||
<span>Prototyping</span>
|
||||
</div>
|
||||
<div class="project-links">
|
||||
<a href="#" class="btn small-btn"><i class="fas fa-eye"></i> Live Demo</a>
|
||||
<a href="#" class="btn small-btn"><i class="fab fa-github"></i> Source Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card" data-category="web">
|
||||
<div class="project-image">
|
||||
<div class="image-placeholder">
|
||||
<i class="fas fa-mobile-alt"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-info">
|
||||
<h3>Project Title 3</h3>
|
||||
<p>A brief description of the project and your role in it. Explain the technologies used and the problems solved.</p>
|
||||
<div class="project-tags">
|
||||
<span>React</span>
|
||||
<span>Node.js</span>
|
||||
<span>MongoDB</span>
|
||||
</div>
|
||||
<div class="project-links">
|
||||
<a href="#" class="btn small-btn"><i class="fas fa-eye"></i> Live Demo</a>
|
||||
<a href="#" class="btn small-btn"><i class="fab fa-github"></i> Source Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card" data-category="other">
|
||||
<div class="project-image">
|
||||
<div class="image-placeholder">
|
||||
<i class="fas fa-cogs"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-info">
|
||||
<h3>Project Title 4</h3>
|
||||
<p>A brief description of the project and your role in it. Explain the technologies used and the problems solved.</p>
|
||||
<div class="project-tags">
|
||||
<span>Python</span>
|
||||
<span>Data Analysis</span>
|
||||
<span>Visualization</span>
|
||||
</div>
|
||||
<div class="project-links">
|
||||
<a href="#" class="btn small-btn"><i class="fas fa-eye"></i> Live Demo</a>
|
||||
<a href="#" class="btn small-btn"><i class="fab fa-github"></i> Source Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contact" class="contact">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Get In Touch</h2>
|
||||
<div class="contact-content">
|
||||
<div class="contact-info">
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h3>Email</h3>
|
||||
<p>your.email@example.com</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<i class="fas fa-phone"></i>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h3>Phone</h3>
|
||||
<p>+1 (123) 456-7890</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h3>Location</h3>
|
||||
<p>City, Country</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social-links">
|
||||
<a href="#" class="social-link"><i class="fab fa-linkedin"></i></a>
|
||||
<a href="#" class="social-link"><i class="fab fa-github"></i></a>
|
||||
<a href="#" class="social-link"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#" class="social-link"><i class="fab fa-instagram"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-form">
|
||||
<form id="contactForm">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subject">Subject</label>
|
||||
<input type="text" id="subject" name="subject" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="message">Message</label>
|
||||
<textarea id="message" name="message" rows="5" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn primary-btn">Send Message</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© 2023 My Portfolio. All Rights Reserved.</p>
|
||||
<div class="footer-links">
|
||||
<a href="#home">Home</a>
|
||||
<a href="#about">About</a>
|
||||
<a href="#skills">Skills</a>
|
||||
<a href="#projects">Projects</a>
|
||||
<a href="#contact">Contact</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div class="scroll-to-top">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -768,7 +768,7 @@ class ResponseProcessor:
|
|||
continue
|
||||
|
||||
# Validate required parameters
|
||||
missing = [mapping.param_name for mapping in schema.mappings if mapping.param_name not in params]
|
||||
missing = [mapping.param_name for mapping in schema.mappings if mapping.required and mapping.param_name not in params]
|
||||
if missing:
|
||||
logger.error(f"Missing required parameters: {missing}")
|
||||
logger.error(f"Current params: {params}")
|
||||
|
|
|
@ -202,19 +202,7 @@ class ThreadManager:
|
|||
openapi_tool_schemas = self.tool_registry.get_openapi_schemas()
|
||||
logger.debug(f"Retrieved {len(openapi_tool_schemas) if openapi_tool_schemas else 0} OpenAPI tool schemas")
|
||||
|
||||
# 5. Track this agent run in the database
|
||||
run_id = str(uuid.uuid4())
|
||||
client = await self.db.client
|
||||
run_data = {
|
||||
'id': run_id,
|
||||
'thread_id': thread_id,
|
||||
'status': 'running',
|
||||
'started_at': 'now()',
|
||||
}
|
||||
await client.table('agent_runs').insert(run_data).execute()
|
||||
logger.debug(f"Created agent run record with ID: {run_id}")
|
||||
|
||||
# 6. Make LLM API call
|
||||
# 5. Make LLM API call - removed agent run tracking
|
||||
logger.info("Making LLM API call")
|
||||
try:
|
||||
llm_response = await make_llm_api_call(
|
||||
|
@ -228,17 +216,10 @@ class ThreadManager:
|
|||
)
|
||||
logger.debug("Successfully received LLM API response")
|
||||
except Exception as e:
|
||||
# Update agent_run status to error
|
||||
await client.table('agent_runs').update({
|
||||
'status': 'error',
|
||||
'error': str(e),
|
||||
'completed_at': 'now()'
|
||||
}).eq('id', run_id).execute()
|
||||
|
||||
logger.error(f"Failed to make LLM API call: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
# 7. Process LLM response using the ResponseProcessor
|
||||
# 6. Process LLM response using the ResponseProcessor
|
||||
if stream:
|
||||
logger.info("Processing streaming response")
|
||||
response_generator = self.response_processor.process_streaming_response(
|
||||
|
@ -247,32 +228,8 @@ class ThreadManager:
|
|||
config=processor_config
|
||||
)
|
||||
|
||||
# Wrap the generator to update the agent_run when complete
|
||||
async def wrapped_generator():
|
||||
responses = []
|
||||
try:
|
||||
async for chunk in response_generator:
|
||||
responses.append(chunk)
|
||||
yield chunk
|
||||
|
||||
# Update agent_run to completed when done
|
||||
await client.table('agent_runs').update({
|
||||
'status': 'completed',
|
||||
'responses': json.dumps(responses),
|
||||
'completed_at': 'now()'
|
||||
}).eq('id', run_id).execute()
|
||||
logger.debug(f"Updated agent run {run_id} to completed status")
|
||||
except Exception as e:
|
||||
# Update agent_run to error
|
||||
await client.table('agent_runs').update({
|
||||
'status': 'error',
|
||||
'error': str(e),
|
||||
'completed_at': 'now()'
|
||||
}).eq('id', run_id).execute()
|
||||
logger.error(f"Error in streaming response: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
return wrapped_generator()
|
||||
# Return the generator directly without agent run updates
|
||||
return response_generator
|
||||
else:
|
||||
logger.info("Processing non-streaming response")
|
||||
try:
|
||||
|
@ -281,23 +238,8 @@ class ThreadManager:
|
|||
thread_id=thread_id,
|
||||
config=processor_config
|
||||
)
|
||||
|
||||
# Update agent_run to completed
|
||||
await client.table('agent_runs').update({
|
||||
'status': 'completed',
|
||||
'responses': json.dumps([response]),
|
||||
'completed_at': 'now()'
|
||||
}).eq('id', run_id).execute()
|
||||
logger.debug(f"Updated agent run {run_id} to completed status")
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
# Update agent_run to error
|
||||
await client.table('agent_runs').update({
|
||||
'status': 'error',
|
||||
'error': str(e),
|
||||
'completed_at': 'now()'
|
||||
}).eq('id', run_id).execute()
|
||||
logger.error(f"Error in non-streaming response: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
|
|
@ -29,10 +29,12 @@ class XMLNodeMapping:
|
|||
param_name (str): Name of the function parameter
|
||||
node_type (str): Type of node ("element", "attribute", or "content")
|
||||
path (str): XPath-like path to the node ("." means root element)
|
||||
required (bool): Whether the parameter is required (defaults to True)
|
||||
"""
|
||||
param_name: str
|
||||
node_type: str = "element"
|
||||
path: str = "."
|
||||
required: bool = True
|
||||
|
||||
@dataclass
|
||||
class XMLTagSchema:
|
||||
|
@ -50,20 +52,22 @@ class XMLTagSchema:
|
|||
mappings: List[XMLNodeMapping] = field(default_factory=list)
|
||||
example: Optional[str] = None
|
||||
|
||||
def add_mapping(self, param_name: str, node_type: str = "element", path: str = ".") -> None:
|
||||
def add_mapping(self, param_name: str, node_type: str = "element", path: str = ".", required: bool = True) -> None:
|
||||
"""Add a new node mapping to the schema.
|
||||
|
||||
Args:
|
||||
param_name: Name of the function parameter
|
||||
node_type: Type of node ("element", "attribute", or "content")
|
||||
path: XPath-like path to the node
|
||||
required: Whether the parameter is required
|
||||
"""
|
||||
self.mappings.append(XMLNodeMapping(
|
||||
param_name=param_name,
|
||||
node_type=node_type,
|
||||
path=path
|
||||
path=path,
|
||||
required=required
|
||||
))
|
||||
logger.debug(f"Added XML mapping for parameter '{param_name}' with type '{node_type}' at path '{path}'")
|
||||
logger.debug(f"Added XML mapping for parameter '{param_name}' with type '{node_type}' at path '{path}', required={required}")
|
||||
|
||||
@dataclass
|
||||
class ToolSchema:
|
||||
|
@ -173,7 +177,7 @@ def openapi_schema(schema: Dict[str, Any]):
|
|||
|
||||
def xml_schema(
|
||||
tag_name: str,
|
||||
mappings: List[Dict[str, str]] = None,
|
||||
mappings: List[Dict[str, Any]] = None,
|
||||
example: str = None
|
||||
):
|
||||
"""
|
||||
|
@ -185,6 +189,7 @@ def xml_schema(
|
|||
- param_name: Name of the function parameter
|
||||
- node_type: "element", "attribute", or "content"
|
||||
- path: Path to the node (default "." for root)
|
||||
- required: Whether the parameter is required (default True)
|
||||
example: Optional example showing how to use the XML tag
|
||||
|
||||
Example:
|
||||
|
@ -213,7 +218,8 @@ def xml_schema(
|
|||
xml_schema.add_mapping(
|
||||
param_name=mapping["param_name"],
|
||||
node_type=mapping.get("node_type", "element"),
|
||||
path=mapping.get("path", ".")
|
||||
path=mapping.get("path", "."),
|
||||
required=mapping.get("required", True)
|
||||
)
|
||||
|
||||
return _add_schema(func, ToolSchema(
|
||||
|
|
|
@ -210,7 +210,7 @@ async def make_llm_api_call(
|
|||
for attempt in range(MAX_RETRIES):
|
||||
try:
|
||||
logger.debug(f"Attempt {attempt + 1}/{MAX_RETRIES}")
|
||||
logger.debug(f"API request parameters: {json.dumps(params, indent=2)}")
|
||||
# logger.debug(f"API request parameters: {json.dumps(params, indent=2)}")
|
||||
|
||||
response = await litellm.acompletion(**params)
|
||||
logger.info(f"Successfully received API response from {model_name}")
|
||||
|
|
Loading…
Reference in New Issue