diff --git a/agentpress/examples/example_agent/agent.py b/agentpress/examples/example_agent/agent.py
index 87fc07a..1779b5d 100644
--- a/agentpress/examples/example_agent/agent.py
+++ b/agentpress/examples/example_agent/agent.py
@@ -7,16 +7,11 @@
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 init():
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 @@ async def finalizer():
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
diff --git a/agentpress/examples/example_agent/tools/files_tool.py b/agentpress/examples/example_agent/tools/files_tool.py
index 485d52f..c27159b 100644
--- a/agentpress/examples/example_agent/tools/files_tool.py
+++ b/agentpress/examples/example_agent/tools/files_tool.py
@@ -138,7 +138,11 @@ async def _update_workspace_state(self):
{"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='''
+
+ File contents go here
+
+ '''
)
async def create_file(self, file_path: str, file_contents: str) -> ToolResult:
try:
@@ -177,7 +181,10 @@ async def create_file(self, file_path: str, file_contents: str) -> ToolResult:
mappings=[
{"param_name": "file_path", "node_type": "attribute", "path": "."}
],
- description="Delete a file at the given path"
+ example='''
+
+
+ '''
)
async def delete_file(self, file_path: str) -> ToolResult:
try:
@@ -221,7 +228,12 @@ async def delete_file(self, file_path: str) -> ToolResult:
{"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='''
+
+ text to replace
+ replacement text
+
+ '''
)
async def str_replace(self, file_path: str, old_str: str, new_str: str) -> ToolResult:
try:
diff --git a/agentpress/examples/example_agent/tools/terminal_tool.py b/agentpress/examples/example_agent/tools/terminal_tool.py
index f5ad66a..5bd7eb7 100644
--- a/agentpress/examples/example_agent/tools/terminal_tool.py
+++ b/agentpress/examples/example_agent/tools/terminal_tool.py
@@ -46,7 +46,11 @@ async def _update_command_history(self, command: str, output: str, success: bool
mappings=[
{"param_name": "command", "node_type": "content", "path": "."}
],
- description="Execute a shell command in the workspace directory"
+ example='''
+
+ npm install package-name
+
+ '''
)
async def execute_command(self, command: str) -> ToolResult:
original_dir = os.getcwd()
diff --git a/agentpress/examples/example_agent/workspace/index.html b/agentpress/examples/example_agent/workspace/index.html
deleted file mode 100644
index abf96d6..0000000
--- a/agentpress/examples/example_agent/workspace/index.html
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
- Modern Landing Page
-
-
-
-
-
-
-
-
-
Welcome to the Future
-
Transform your digital presence with our innovative solutions
-
-
-
-
-
-
-
-
-
- Our Features
-
-
-
🚀
-
Fast Performance
-
Lightning-quick load times and smooth interactions
-
-
-
🎨
-
Beautiful Design
-
Stunning visuals that capture attention
-
-
-
📱
-
Responsive
-
Perfect display on all devices
-
-
-
-
-
-
-
About Us
-
We're dedicated to creating exceptional digital experiences that drive results.
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/agentpress/examples/example_agent/workspace/script.js b/agentpress/examples/example_agent/workspace/script.js
deleted file mode 100644
index fdc5510..0000000
--- a/agentpress/examples/example_agent/workspace/script.js
+++ /dev/null
@@ -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();
- });
-});
\ No newline at end of file
diff --git a/agentpress/examples/example_agent/workspace/styles.css b/agentpress/examples/example_agent/workspace/styles.css
deleted file mode 100644
index 9e05405..0000000
--- a/agentpress/examples/example_agent/workspace/styles.css
+++ /dev/null
@@ -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;
- }
-}
\ No newline at end of file
diff --git a/agentpress/thread_manager.py b/agentpress/thread_manager.py
index 51c82ce..17c568f 100644
--- a/agentpress/thread_manager.py
+++ b/agentpress/thread_manager.py
@@ -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
diff --git a/agentpress/tool.py b/agentpress/tool.py
index bde9ac5..6c8dccb 100644
--- a/agentpress/tool.py
+++ b/agentpress/tool.py
@@ -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 decorator(func):
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='''
+
+ text to replace
+ replacement text
+
+ '''
)
"""
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:
diff --git a/agentpress/tool_registry.py b/agentpress/tool_registry.py
index d3b079d..0e9ce88 100644
--- a/agentpress/tool_registry.py
+++ b/agentpress/tool_registry.py
@@ -129,3 +129,16 @@ def get_openapi_schemas(self) -> List[Dict[str, Any]]:
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
diff --git a/agentpress/xml_results_adder.py b/agentpress/xml_results_adder.py
index 47aa519..af7fae3 100644
--- a/agentpress/xml_results_adder.py
+++ b/agentpress/xml_results_adder.py
@@ -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"\n"
- f"{result}\n"
- f""
- )
-
- 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
\ No newline at end of file
+ """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)
\ No newline at end of file