fix duplicate agent run

This commit is contained in:
marko-kraemer 2025-04-06 17:45:02 +01:00
parent 7ee68b4282
commit 3c0634f6bc
14 changed files with 736 additions and 220 deletions

View File

@ -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})")

View File

@ -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

View File

@ -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():
'''

View File

@ -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())

View File

@ -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

View File

@ -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())

View File

@ -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>&copy; 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>

View File

@ -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}")

View File

@ -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

View File

@ -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(

View File

@ -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}")