Skip to content

Commit

Permalink
cleanup run_thread params
Browse files Browse the repository at this point in the history
  • Loading branch information
markokraemer committed Nov 18, 2024
1 parent 8923b86 commit 320dabb
Show file tree
Hide file tree
Showing 14 changed files with 1,150 additions and 631 deletions.
137 changes: 137 additions & 0 deletions agentpress/base_processors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import logging
from typing import Dict, Any, Callable, List, Optional, Set
from abc import ABC, abstractmethod

# --- Tool Parser Base ---

class ToolParserBase(ABC):
"""Abstract base class defining the interface for parsing tool calls from LLM responses.
This class provides the foundational interface for parsing both complete and streaming
responses from Language Models, specifically focusing on tool call extraction and processing.
Attributes:
None
Methods:
parse_response: Processes complete LLM responses
parse_stream: Handles streaming response chunks
"""

@abstractmethod
async def parse_response(self, response: Any) -> Dict[str, Any]:
"""Parse a complete LLM response and extract tool calls.
Args:
response (Any): The complete response from the LLM
Returns:
Dict[str, Any]: A dictionary containing:
- role: The message role (usually 'assistant')
- content: The text content of the response
- tool_calls: List of extracted tool calls (if present)
"""
pass

@abstractmethod
async def parse_stream(self, response_chunk: Any, tool_calls_buffer: Dict[int, Dict]) -> tuple[Optional[Dict[str, Any]], bool]:
"""Parse a streaming response chunk and manage tool call accumulation.
Args:
response_chunk (Any): A single chunk from the streaming response
tool_calls_buffer (Dict[int, Dict]): Buffer storing incomplete tool calls
Returns:
tuple[Optional[Dict[str, Any]], bool]: A tuple containing:
- The parsed message if complete tool calls are found (or None)
- Boolean indicating if the stream is complete
"""
pass

# --- Tool Executor Base ---

class ToolExecutorBase(ABC):
"""Abstract base class defining the interface for tool execution strategies.
This class provides the foundation for implementing different tool execution
approaches, supporting both parallel and sequential execution patterns.
Attributes:
None
Methods:
execute_tool_calls: Main entry point for tool execution
_execute_parallel: Handles parallel tool execution
_execute_sequential: Handles sequential tool execution
"""

@abstractmethod
async def execute_tool_calls(
self,
tool_calls: List[Dict[str, Any]],
available_functions: Dict[str, Callable],
thread_id: str,
executed_tool_calls: Optional[Set[str]] = None
) -> List[Dict[str, Any]]:
"""Execute a list of tool calls and return their results.
Args:
tool_calls: List of tool calls to execute
available_functions: Dictionary of available tool functions
thread_id: ID of the current conversation thread
executed_tool_calls: Set of already executed tool call IDs
Returns:
List[Dict[str, Any]]: List of tool execution results
"""
pass

@abstractmethod
async def _execute_parallel(
self,
tool_calls: List[Dict[str, Any]],
available_functions: Dict[str, Callable],
thread_id: str,
executed_tool_calls: Set[str]
) -> List[Dict[str, Any]]:
"""Execute tool calls in parallel."""
pass

@abstractmethod
async def _execute_sequential(
self,
tool_calls: List[Dict[str, Any]],
available_functions: Dict[str, Callable],
thread_id: str,
executed_tool_calls: Set[str]
) -> List[Dict[str, Any]]:
"""Execute tool calls sequentially."""
pass

# --- Results Adder Base ---

class ResultsAdderBase(ABC):
"""Abstract base class for handling tool results and message processing."""

def __init__(self, thread_manager):
"""Initialize with a ThreadManager instance.
Args:
thread_manager: The ThreadManager instance to use for message operations
"""
self.add_message = thread_manager.add_message
self.update_message = thread_manager._update_message
self.list_messages = thread_manager.list_messages
self.message_added = False

@abstractmethod
async def add_initial_response(self, thread_id: str, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None):
pass

@abstractmethod
async def update_response(self, thread_id: str, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None):
pass

@abstractmethod
async def add_tool_result(self, thread_id: str, result: Dict[str, Any]):
pass
25 changes: 9 additions & 16 deletions agentpress/examples/example_agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async def run_agent(thread_id: str, max_iterations: int = 5):
thread_manager = ThreadManager()
state_manager = StateManager()

# Initialize tools with XML schema support
# Initialize tools
thread_manager.add_tool(FilesTool)
thread_manager.add_tool(TerminalTool)

Expand Down Expand Up @@ -60,7 +60,9 @@ async def finalizer():
# file contents here
# </create-file>

# <str-replace file_path="path/to/file" old_str="old_str" new_str="new_str">
# <str-replace file_path="path/to/file">
# <old_str>text to replace</old_str>
# <new_str>replacement text</new_str>
# </str-replace>

# <delete-file file_path="path/to/file">
Expand All @@ -71,7 +73,7 @@ async def finalizer():
"content": """
You are a world-class web developer who can create, edit, and delete files, and execute terminal commands. You write clean, well-structured code. Keep iterating on existing files, continue working on this existing codebase - do not omit previous progress; instead, keep iterating.
FORMAT:
RESPONSE FORMAT:
Use XML tags to specify file operations:
<create-file file_path="path/to/file">
Expand Down Expand Up @@ -141,11 +143,6 @@ async def finalizer():

model_name = "anthropic/claude-3-5-sonnet-latest"

registry = thread_manager.tool_registry
tool_parser = XMLToolParser(tool_registry=registry)
tool_executor = XMLToolExecutor(parallel=True, tool_registry=registry)
results_adder = XMLResultsAdder(thread_manager)

response = await thread_manager.run_thread(
thread_id=thread_id,
system_message=system_message,
Expand All @@ -154,15 +151,11 @@ async def finalizer():
max_tokens=8096,
tool_choice="auto",
temporary_message=state_message,
use_tools=True,
native_tool_calling=False,
execute_tools=True,
native_tool_calling=True,
xml_tool_calling=False,
stream=True,
immediate_tool_execution=True,
parallel_tool_execution=True,
tool_parser=tool_parser,
tool_executor=tool_executor,
results_adder=results_adder
execute_tools_on_stream=True,
parallel_tool_execution=True
)

if isinstance(response, AsyncGenerator):
Expand Down
85 changes: 85 additions & 0 deletions agentpress/examples/example_agent/workspace/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!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>&copy; 2024 Brand. All rights reserved.</p>
</footer>

<script src="script.js"></script>
</body>
</html>
61 changes: 61 additions & 0 deletions agentpress/examples/example_agent/workspace/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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();
});
});
Loading

0 comments on commit 320dabb

Please sign in to comment.