mirror of https://github.com/kortix-ai/suna.git
proper xml schema result adding as user message, xml schema example
This commit is contained in:
parent
320dabb99d
commit
53557718e4
|
@ -7,16 +7,11 @@ from tools.terminal_tool import TerminalTool
|
|||
import logging
|
||||
from typing import AsyncGenerator
|
||||
import sys
|
||||
from agentpress.xml_tool_parser import XMLToolParser
|
||||
from agentpress.xml_tool_executor import XMLToolExecutor
|
||||
from agentpress.xml_results_adder import XMLResultsAdder
|
||||
|
||||
async def run_agent(thread_id: str, max_iterations: int = 5):
|
||||
# Initialize managers and tools
|
||||
thread_manager = ThreadManager()
|
||||
state_manager = StateManager()
|
||||
|
||||
# Initialize tools
|
||||
thread_manager.add_tool(FilesTool)
|
||||
thread_manager.add_tool(TerminalTool)
|
||||
|
||||
|
@ -24,12 +19,10 @@ async def run_agent(thread_id: str, max_iterations: int = 5):
|
|||
pass
|
||||
|
||||
async def pre_iteration():
|
||||
# Update files state
|
||||
files_tool = FilesTool()
|
||||
await files_tool._init_workspace_state()
|
||||
|
||||
async def after_iteration():
|
||||
# Ask the user for a custom message or use the default
|
||||
custom_message = input("Enter a message to send (or press Enter to use 'Continue!!!' as message): ")
|
||||
|
||||
message_content = custom_message if custom_message else """
|
||||
|
@ -151,8 +144,8 @@ Current development environment workspace state:
|
|||
max_tokens=8096,
|
||||
tool_choice="auto",
|
||||
temporary_message=state_message,
|
||||
native_tool_calling=True,
|
||||
xml_tool_calling=False,
|
||||
native_tool_calling=False,
|
||||
xml_tool_calling=True,
|
||||
stream=True,
|
||||
execute_tools_on_stream=True,
|
||||
parallel_tool_execution=True
|
||||
|
|
|
@ -138,7 +138,11 @@ class FilesTool(Tool):
|
|||
{"param_name": "file_path", "node_type": "attribute", "path": "."},
|
||||
{"param_name": "file_contents", "node_type": "content", "path": "."}
|
||||
],
|
||||
description="Create a new file with the provided contents"
|
||||
example='''
|
||||
<create-file file_path="path/to/file">
|
||||
File contents go here
|
||||
</create-file>
|
||||
'''
|
||||
)
|
||||
async def create_file(self, file_path: str, file_contents: str) -> ToolResult:
|
||||
try:
|
||||
|
@ -177,7 +181,10 @@ class FilesTool(Tool):
|
|||
mappings=[
|
||||
{"param_name": "file_path", "node_type": "attribute", "path": "."}
|
||||
],
|
||||
description="Delete a file at the given path"
|
||||
example='''
|
||||
<delete-file file_path="path/to/file">
|
||||
</delete-file>
|
||||
'''
|
||||
)
|
||||
async def delete_file(self, file_path: str) -> ToolResult:
|
||||
try:
|
||||
|
@ -221,7 +228,12 @@ class FilesTool(Tool):
|
|||
{"param_name": "old_str", "node_type": "element", "path": "old_str"},
|
||||
{"param_name": "new_str", "node_type": "element", "path": "new_str"}
|
||||
],
|
||||
description="Replace text in a file"
|
||||
example='''
|
||||
<str-replace file_path="path/to/file">
|
||||
<old_str>text to replace</old_str>
|
||||
<new_str>replacement text</new_str>
|
||||
</str-replace>
|
||||
'''
|
||||
)
|
||||
async def str_replace(self, file_path: str, old_str: str, new_str: str) -> ToolResult:
|
||||
try:
|
||||
|
|
|
@ -46,7 +46,11 @@ class TerminalTool(Tool):
|
|||
mappings=[
|
||||
{"param_name": "command", "node_type": "content", "path": "."}
|
||||
],
|
||||
description="Execute a shell command in the workspace directory"
|
||||
example='''
|
||||
<execute-command>
|
||||
npm install package-name
|
||||
</execute-command>
|
||||
'''
|
||||
)
|
||||
async def execute_command(self, command: str) -> ToolResult:
|
||||
original_dir = os.getcwd()
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Modern Landing Page</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<nav class="nav">
|
||||
<div class="logo">Brand</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#home">Home</a></li>
|
||||
<li><a href="#features">Features</a></li>
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
</ul>
|
||||
<button class="mobile-nav-toggle" aria-label="Menu">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section id="home" class="hero">
|
||||
<div class="hero-content">
|
||||
<h1>Welcome to the Future</h1>
|
||||
<p>Transform your digital presence with our innovative solutions</p>
|
||||
<div class="cta-group">
|
||||
<button class="cta-button primary">Get Started</button>
|
||||
<button class="cta-button secondary">Learn More</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-shape"></div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="features">
|
||||
<h2>Our Features</h2>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🚀</div>
|
||||
<h3>Fast Performance</h3>
|
||||
<p>Lightning-quick load times and smooth interactions</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🎨</div>
|
||||
<h3>Beautiful Design</h3>
|
||||
<p>Stunning visuals that capture attention</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📱</div>
|
||||
<h3>Responsive</h3>
|
||||
<p>Perfect display on all devices</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="about" class="about">
|
||||
<div class="about-content">
|
||||
<h2>About Us</h2>
|
||||
<p>We're dedicated to creating exceptional digital experiences that drive results.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contact" class="contact">
|
||||
<h2>Get in Touch</h2>
|
||||
<form class="contact-form">
|
||||
<input type="text" placeholder="Name" required>
|
||||
<input type="email" placeholder="Email" required>
|
||||
<textarea placeholder="Message" required></textarea>
|
||||
<button type="submit">Send Message</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>© 2024 Brand. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,61 +0,0 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mobileNavToggle = document.querySelector('.mobile-nav-toggle');
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
const header = document.querySelector('.header');
|
||||
let lastScroll = 0;
|
||||
|
||||
mobileNavToggle.addEventListener('click', () => {
|
||||
navLinks.classList.toggle('active');
|
||||
const spans = mobileNavToggle.querySelectorAll('span');
|
||||
spans[0].style.transform = navLinks.classList.contains('active') ? 'rotate(45deg) translate(8px, 8px)' : '';
|
||||
spans[1].style.opacity = navLinks.classList.contains('active') ? '0' : '1';
|
||||
spans[2].style.transform = navLinks.classList.contains('active') ? 'rotate(-45deg) translate(7px, -7px)' : '';
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const currentScroll = window.pageYOffset;
|
||||
|
||||
if (currentScroll <= 0) {
|
||||
header.style.transform = 'translateY(0)';
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentScroll > lastScroll && !header.classList.contains('scroll-down')) {
|
||||
header.style.transform = 'translateY(-100%)';
|
||||
} else if (currentScroll < lastScroll && header.classList.contains('scroll-down')) {
|
||||
header.style.transform = 'translateY(0)';
|
||||
}
|
||||
|
||||
lastScroll = currentScroll;
|
||||
});
|
||||
|
||||
const observerOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px'
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.style.opacity = '1';
|
||||
entry.target.style.transform = 'translateY(0)';
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
document.querySelectorAll('section').forEach(section => {
|
||||
section.style.opacity = '0';
|
||||
section.style.transform = 'translateY(20px)';
|
||||
section.style.transition = 'opacity 0.6s ease-out, transform 0.6s ease-out';
|
||||
observer.observe(section);
|
||||
});
|
||||
|
||||
const form = document.querySelector('.contact-form');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData);
|
||||
console.log('Form submitted:', data);
|
||||
form.reset();
|
||||
});
|
||||
});
|
|
@ -1,343 +0,0 @@
|
|||
:root {
|
||||
--primary-color: #6366f1;
|
||||
--secondary-color: #4f46e5;
|
||||
--text-color: #18181b;
|
||||
--light-text: #71717a;
|
||||
--background: #ffffff;
|
||||
--glass-bg: rgba(255, 255, 255, 0.7);
|
||||
--glass-border: rgba(255, 255, 255, 0.3);
|
||||
--section-padding: 5rem 2rem;
|
||||
--gradient-1: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
|
||||
--gradient-2: linear-gradient(135deg, #c084fc 0%, #a855f7 100%);
|
||||
--shadow-1: 0 10px 30px -10px rgba(99, 102, 241, 0.2);
|
||||
--shadow-2: 0 20px 40px -15px rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
box-shadow: var(--shadow-1);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.mobile-nav-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background: var(--gradient-1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: var(--section-padding);
|
||||
}
|
||||
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 150%;
|
||||
height: 150%;
|
||||
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 60%);
|
||||
animation: rotate 20s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 800px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 3rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--glass-border);
|
||||
box-shadow: var(--shadow-2);
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3.5rem;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.25rem;
|
||||
color: var(--light-text);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.cta-group {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cta-button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s ease, height 0.6s ease;
|
||||
}
|
||||
|
||||
.cta-button:hover::before {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
}
|
||||
|
||||
.cta-button.primary {
|
||||
background: var(--gradient-1);
|
||||
color: white;
|
||||
box-shadow: var(--shadow-1);
|
||||
}
|
||||
|
||||
.cta-button.secondary {
|
||||
background: var(--glass-bg);
|
||||
color: var(--primary-color);
|
||||
border: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
|
||||
.features {
|
||||
padding: var(--section-padding);
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.features h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: var(--shadow-1);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
box-shadow: var(--shadow-2);
|
||||
background: var(--gradient-2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.about {
|
||||
padding: var(--section-padding);
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.about-content {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about h2 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.contact {
|
||||
padding: var(--section-padding);
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.contact h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.contact-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.contact-form input,
|
||||
.contact-form textarea {
|
||||
padding: 1rem;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-form input:focus,
|
||||
.contact-form textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.contact-form textarea {
|
||||
height: 150px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.contact-form button {
|
||||
padding: 1rem;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-form button:hover {
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nav-links {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--background);
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.nav-links.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mobile-nav-toggle {
|
||||
display: block;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.mobile-nav-toggle span {
|
||||
display: block;
|
||||
width: 25px;
|
||||
height: 3px;
|
||||
background: var(--text-color);
|
||||
margin: 5px 0;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from typing import List, Dict, Any, Optional, Type, Union, AsyncGenerator
|
||||
from agentpress.llm import make_llm_api_call
|
||||
from agentpress.tool import Tool, ToolResult
|
||||
from agentpress.tool_registry import ToolRegistry
|
||||
from agentpress.llm_response_processor import LLMResponseProcessor
|
||||
from agentpress.base_processors import ToolParserBase, ToolExecutorBase, ResultsAdderBase
|
||||
import uuid
|
||||
|
||||
from agentpress.xml_tool_parser import XMLToolParser
|
||||
from agentpress.xml_tool_executor import XMLToolExecutor
|
||||
|
|
|
@ -26,7 +26,7 @@ class XMLTagSchema:
|
|||
"""Schema for XML tool tags with improved node mapping"""
|
||||
tag_name: str # Root tag name (e.g. "str-replace")
|
||||
mappings: List[XMLNodeMapping] = field(default_factory=list)
|
||||
description: Optional[str] = None
|
||||
example: Optional[str] = None # Changed from description to example
|
||||
|
||||
def add_mapping(self, param_name: str, node_type: str = "element", path: str = ".") -> None:
|
||||
"""Add a new node mapping"""
|
||||
|
@ -97,7 +97,7 @@ def openapi_schema(schema: Dict[str, Any]):
|
|||
def xml_schema(
|
||||
tag_name: str,
|
||||
mappings: List[Dict[str, str]] = None,
|
||||
description: str = None
|
||||
example: str = None # Changed from description to example
|
||||
):
|
||||
"""
|
||||
Decorator for XML schema tools with improved node mapping.
|
||||
|
@ -108,7 +108,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)
|
||||
description: Optional description of the tool
|
||||
example: Optional example showing how to use the XML tag
|
||||
|
||||
Example:
|
||||
@xml_schema(
|
||||
|
@ -118,11 +118,16 @@ def xml_schema(
|
|||
{"param_name": "old_str", "node_type": "element", "path": "old_str"},
|
||||
{"param_name": "new_str", "node_type": "element", "path": "new_str"}
|
||||
],
|
||||
description="Replace text in a file"
|
||||
example='''
|
||||
<str-replace file_path="path/to/file">
|
||||
<old_str>text to replace</old_str>
|
||||
<new_str>replacement text</new_str>
|
||||
</str-replace>
|
||||
'''
|
||||
)
|
||||
"""
|
||||
def decorator(func):
|
||||
xml_schema = XMLTagSchema(tag_name=tag_name, description=description)
|
||||
xml_schema = XMLTagSchema(tag_name=tag_name, example=example)
|
||||
|
||||
# Add mappings
|
||||
if mappings:
|
||||
|
|
|
@ -129,3 +129,16 @@ class ToolRegistry:
|
|||
for tool_info in self.tools.values()
|
||||
if tool_info['schema'].schema_type == SchemaType.OPENAPI
|
||||
]
|
||||
|
||||
def get_xml_examples(self) -> Dict[str, str]:
|
||||
"""Get all XML tag examples from registered tools.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: Dictionary mapping tag names to their examples
|
||||
"""
|
||||
examples = {}
|
||||
for tool_info in self.xml_tools.values():
|
||||
schema = tool_info['schema']
|
||||
if schema.xml_schema and schema.xml_schema.example:
|
||||
examples[schema.xml_schema.tag_name] = schema.xml_schema.example
|
||||
return examples
|
||||
|
|
|
@ -11,68 +11,65 @@ class XMLResultsAdder(ResultsAdderBase):
|
|||
|
||||
def __init__(self, thread_manager):
|
||||
super().__init__(thread_manager)
|
||||
self.pending_tool_results = {}
|
||||
|
||||
def _format_xml_response(self, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None) -> str:
|
||||
"""Format the response content with XML tool results."""
|
||||
response_parts = []
|
||||
|
||||
# Add any non-XML content first
|
||||
non_xml_content = []
|
||||
lines = content.split('\n')
|
||||
for line in lines:
|
||||
if not (line.strip().startswith('<') and line.strip().endswith('>')):
|
||||
non_xml_content.append(line)
|
||||
if non_xml_content:
|
||||
response_parts.append('\n'.join(non_xml_content))
|
||||
|
||||
# Add XML blocks with their results
|
||||
if tool_calls:
|
||||
for tool_call in tool_calls:
|
||||
tool_id = tool_call['id']
|
||||
if tool_id in self.pending_tool_results:
|
||||
result = self.pending_tool_results[tool_id]
|
||||
response_parts.append(
|
||||
f"<tool-result id='{tool_id}'>\n"
|
||||
f"{result}\n"
|
||||
f"</tool-result>"
|
||||
)
|
||||
|
||||
return '\n\n'.join(response_parts)
|
||||
self.message_added = False
|
||||
|
||||
async def add_initial_response(self, thread_id: str, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None):
|
||||
"""Add initial response with XML formatting."""
|
||||
formatted_content = self._format_xml_response(content, tool_calls)
|
||||
"""Add initial response without modifications."""
|
||||
message = {
|
||||
"role": "assistant",
|
||||
"content": formatted_content
|
||||
"content": content
|
||||
}
|
||||
await self.add_message(thread_id, message)
|
||||
self.message_added = True
|
||||
|
||||
async def update_response(self, thread_id: str, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None):
|
||||
"""Update response with XML formatting."""
|
||||
"""Update response without modifications."""
|
||||
if not self.message_added:
|
||||
await self.add_initial_response(thread_id, content, tool_calls)
|
||||
return
|
||||
|
||||
formatted_content = self._format_xml_response(content, tool_calls)
|
||||
message = {
|
||||
"role": "assistant",
|
||||
"content": formatted_content
|
||||
"content": content
|
||||
}
|
||||
await self.update_message(thread_id, message)
|
||||
|
||||
async def add_tool_result(self, thread_id: str, result: Dict[str, Any]):
|
||||
"""Store tool result for inclusion in the XML response."""
|
||||
tool_call_id = result['tool_call_id']
|
||||
self.pending_tool_results[tool_call_id] = result['content']
|
||||
|
||||
# Update the message to include the new result
|
||||
messages = await self.list_messages(thread_id)
|
||||
for msg in reversed(messages):
|
||||
if msg['role'] == 'assistant':
|
||||
content = msg['content']
|
||||
tool_calls = msg.get('tool_calls', [])
|
||||
await self.update_response(thread_id, content, tool_calls)
|
||||
break
|
||||
"""Add tool result as a user message."""
|
||||
try:
|
||||
# Get the original tool call to find the root tag
|
||||
messages = await self.list_messages(thread_id)
|
||||
assistant_msg = next((msg for msg in reversed(messages)
|
||||
if msg['role'] == 'assistant'), None)
|
||||
|
||||
if assistant_msg:
|
||||
content = assistant_msg['content']
|
||||
# Find the opening XML tag for this tool call
|
||||
tool_start = content.find(f'<{result["name"]}')
|
||||
if tool_start >= 0:
|
||||
tag_end = content.find('>', tool_start)
|
||||
if tag_end >= 0:
|
||||
root_tag = content[tool_start:tag_end + 1]
|
||||
# Create a simple reference message as user role
|
||||
result_message = {
|
||||
"role": "user",
|
||||
"content": f"Result for {root_tag}\n{result['content']}"
|
||||
}
|
||||
await self.add_message(thread_id, result_message)
|
||||
return
|
||||
|
||||
# Fallback if we can't find the root tag
|
||||
result_message = {
|
||||
"role": "user",
|
||||
"content": f"Result for {result['name']}:\n{result['content']}"
|
||||
}
|
||||
await self.add_message(thread_id, result_message)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error adding tool result: {e}")
|
||||
# Ensure the result is still added even if there's an error
|
||||
result_message = {
|
||||
"role": "user",
|
||||
"content": f"Result for {result['name']}:\n{result['content']}"
|
||||
}
|
||||
await self.add_message(thread_id, result_message)
|
Loading…
Reference in New Issue