* wip

* refactor wip; finished v1 revamped instant stream response

* mark agent as stopped

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* exec on stream as background task
This commit is contained in:
Marko Kraemer 2025-04-05 03:40:06 -07:00 committed by GitHub
parent 9edad0073b
commit 1bf0bf0f7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 92 additions and 397 deletions

View File

@ -5,7 +5,7 @@ from agent.tools.files_tool import FilesTool
from agent.tools.terminal_tool import TerminalTool
from agent.tools.wait_tool import WaitTool
# from agent.tools.search_tool import CodeSearchTool
from typing import AsyncGenerator, Optional, Union, Dict, Any
from typing import Optional
from agent.test_prompt import get_system_prompt
from agentpress.response_processor import ProcessorConfig
from dotenv import load_dotenv

View File

@ -1,94 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adam Cohen Hillel | Creative Developer</title>
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#experience">Magic</a></li>
<li><a href="#contact">Let's Create</a></li>
</ul>
</nav>
</header>
<main>
<section id="hero">
<div class="hero-content">
<h1>Adam Cohen Hillel</h1>
<p class="tagline">Digital Alchemist & Code Artist</p>
<p class="sub-tagline">Turning caffeine into code & dreams into pixels</p>
</div>
</section>
<section id="about">
<div class="container">
<h2>The Plot Twist</h2>
<div class="about-content">
<div class="about-text">
<p>👋 Yo! I'm Adam part code wizard, part digital troublemaker. When I'm not pushing pixels around or making browsers do backflips, I'm probably dreaming up the next big thing that'll make your users go "Holy sh*t!"</p>
<p>I don't just build websites I craft digital experiences that punch mediocrity in the face. Think of me as your technical creative director who actually knows how to code. Wild, right?</p>
</div>
</div>
</div>
</section>
<section id="experience">
<div class="container">
<h2>The Magic Happens Here</h2>
<div class="experience-grid">
<div class="experience-card">
<i class="fas fa-wand-magic-sparkles"></i>
<h3>Creative Coding</h3>
<p>Making pixels dance and interfaces sing. Your website won't just exist it'll perform.</p>
</div>
<div class="experience-card">
<i class="fas fa-brain"></i>
<h3>Digital Innovation</h3>
<p>Breaking conventions and building tomorrow's web experiences today. Because normal is boring.</p>
</div>
<div class="experience-card">
<i class="fas fa-rocket"></i>
<h3>Performance Art</h3>
<p>Crafting lightning-fast, butter-smooth experiences that'll make your users' jaws drop.</p>
</div>
</div>
</div>
</section>
<section id="contact">
<div class="container">
<h2>Let's Create Something Legendary</h2>
<div class="contact-content">
<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>
</div>
<div class="contact-form">
<form>
<input type="text" placeholder="Your Name (Required for Spellcasting)" required>
<input type="email" placeholder="Your Email (Promise Not to Spam)" required>
<textarea placeholder="Tell me about your wildest digital dreams..." required></textarea>
<button type="submit">Send It to the Moon 🚀</button>
</form>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<p>© 2023 Adam Cohen Hillel. Made with ☕️ and good vibes.</p>
</div>
</footer>
</body>
</html>

View File

@ -1,272 +0,0 @@
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #ff0099;
--secondary-color: #00ff99;
--accent-color: #6e00ff;
--dark: #121212;
--light: #f8f9fa;
}
body {
font-family: 'Poppins', sans-serif;
line-height: 1.6;
color: var(--dark);
background-color: var(--dark);
overflow-x: hidden;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* Header and Navigation */
header {
background: rgba(18, 18, 18, 0.95);
backdrop-filter: blur(10px);
box-shadow: 0 4px 30px rgba(255, 0, 153, 0.2);
position: fixed;
width: 100%;
top: 0;
z-index: 1000;
}
nav ul {
display: flex;
justify-content: center;
list-style: none;
padding: 20px 0;
}
nav ul li {
margin: 0 20px;
position: relative;
}
nav ul li a {
text-decoration: none;
color: var(--light);
font-weight: 500;
transition: all 0.3s ease;
padding: 8px 16px;
border-radius: 20px;
text-transform: uppercase;
letter-spacing: 1px;
}
nav ul li a:hover {
color: var(--dark);
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
box-shadow: 0 0 20px rgba(255, 0, 153, 0.4);
}
/* Hero Section */
#hero {
height: 100vh;
background: linear-gradient(135deg, var(--dark) 0%, #1a1a1a 100%);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
position: relative;
overflow: hidden;
}
#hero::before {
content: '';
position: absolute;
width: 200%;
height: 200%;
background: radial-gradient(circle, var(--primary-color) 1%, transparent 10%);
background-size: 50px 50px;
animation: moveBackground 20s linear infinite;
opacity: 0.1;
}
.hero-content {
position: relative;
z-index: 2;
}
.hero-content h1 {
font-size: 5rem;
margin-bottom: 20px;
color: var(--light);
text-shadow: 0 0 20px var(--primary-color);
animation: glitch 5s infinite;
}
.tagline {
font-size: 2rem;
color: var(--secondary-color);
margin-bottom: 15px;
text-transform: uppercase;
letter-spacing: 3px;
}
.sub-tagline {
font-size: 1.2rem;
color: var(--primary-color);
opacity: 0.8;
}
/* About Section */
#about {
background-color: var(--dark);
color: var(--light);
}
.about-content {
background: rgba(255, 255, 255, 0.05);
border: 2px solid var(--primary-color);
border-radius: 20px;
padding: 40px;
box-shadow: 0 0 30px rgba(255, 0, 153, 0.2);
}
.about-text p {
font-size: 1.2rem;
margin-bottom: 20px;
line-height: 1.8;
}
/* Experience Section */
.experience-card {
background: rgba(255, 255, 255, 0.05);
border: 2px solid var(--secondary-color);
padding: 40px;
border-radius: 20px;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.experience-card:hover {
transform: translateY(-15px) scale(1.05);
box-shadow: 0 0 30px rgba(0, 255, 153, 0.3);
}
.experience-card i {
font-size: 3.5rem;
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
color: transparent;
margin-bottom: 20px;
}
.experience-card h3 {
color: var(--light);
margin-bottom: 15px;
}
.experience-card p {
color: rgba(255, 255, 255, 0.7);
}
/* Contact Section */
.contact-content {
background: rgba(255, 255, 255, 0.05);
border: 2px solid var(--accent-color);
padding: 40px;
border-radius: 20px;
}
.social-link {
font-size: 2.5rem;
color: var(--light);
margin: 0 20px;
transition: all 0.3s ease;
}
.social-link:hover {
color: var(--primary-color);
transform: translateY(-5px) scale(1.2);
}
.contact-form input,
.contact-form textarea {
background: rgba(255, 255, 255, 0.05);
border: 2px solid var(--primary-color);
color: var(--light);
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
transition: all 0.3s ease;
}
.contact-form input:focus,
.contact-form textarea:focus {
border-color: var(--secondary-color);
box-shadow: 0 0 20px rgba(0, 255, 153, 0.2);
}
.contact-form button {
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
color: var(--dark);
font-weight: bold;
padding: 15px 40px;
border: none;
border-radius: 25px;
cursor: pointer;
font-size: 1.1rem;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 2px;
}
.contact-form button:hover {
transform: translateY(-5px);
box-shadow: 0 0 30px rgba(255, 0, 153, 0.4);
}
/* Footer */
footer {
background: var(--dark);
color: var(--light);
text-align: center;
padding: 30px 0;
border-top: 4px solid var(--primary-color);
}
/* Animations */
@keyframes glitch {
0% { text-shadow: 0 0 20px var(--primary-color); }
25% { text-shadow: -2px 0 var(--primary-color), 2px 0 var(--secondary-color); }
50% { text-shadow: 2px 0 var(--secondary-color), -2px 0 var(--primary-color); }
75% { text-shadow: 0 0 20px var(--secondary-color); }
100% { text-shadow: 0 0 20px var(--primary-color); }
}
@keyframes moveBackground {
0% { transform: translate(0, 0); }
100% { transform: translate(-50%, -50%); }
}
/* Responsive Design */
@media (max-width: 768px) {
.hero-content h1 {
font-size: 3rem;
}
.tagline {
font-size: 1.5rem;
}
nav ul {
flex-direction: column;
align-items: center;
}
nav ul li {
margin: 10px 0;
}
.experience-grid {
grid-template-columns: 1fr;
}
}

View File

@ -9,7 +9,6 @@ This module handles processing of LLM responses including:
"""
import json
import logging
import asyncio
import re
import uuid
@ -28,7 +27,8 @@ ToolExecutionStrategy = Literal["sequential", "parallel"]
@dataclass
class ProcessorConfig:
"""Configuration for response processing and tool execution.
"""
Configuration for response processing and tool execution.
This class controls how the LLM's responses are processed, including how tool calls
are detected, executed, and their results handled.
@ -41,11 +41,10 @@ class ProcessorConfig:
tool_execution_strategy: How to execute multiple tools ("sequential" or "parallel")
xml_adding_strategy: How to add XML tool results to the conversation
"""
# Tool detection
xml_tool_calling: bool = True
native_tool_calling: bool = False
# Tool execution
execute_tools: bool = True
execute_on_stream: bool = False
tool_execution_strategy: ToolExecutionStrategy = "sequential"
@ -98,6 +97,9 @@ class ResponseProcessor:
# For tracking tool results during streaming to add later
tool_results_buffer = []
# For tracking pending tool executions
pending_tool_executions = []
logger.info(f"Starting to process streaming response for thread {thread_id}")
logger.info(f"Config: XML={config.xml_tool_calling}, Native={config.native_tool_calling}, "
f"Execute on stream={config.execute_on_stream}, Execution strategy={config.tool_execution_strategy}")
@ -131,19 +133,18 @@ class ResponseProcessor:
# Parse and extract the tool call
tool_call = self._parse_xml_tool_call(xml_chunk)
if tool_call:
# Execute tool if needed, but store results for later
# Execute tool if needed, but in background
if config.execute_tools and config.execute_on_stream:
result = await self._execute_tool(tool_call)
# Start tool execution as a background task
execution_task = asyncio.create_task(self._execute_tool(tool_call))
# Store result to add after assistant message
tool_results_buffer.append((tool_call, result))
# Store the task for later retrieval
pending_tool_executions.append({
"task": execution_task,
"tool_call": tool_call
})
# Yield tool execution result for client display
yield {
"type": "tool_result",
"name": tool_call["name"],
"result": str(result)
}
# Immediately continue processing more chunks
# Process native tool calls
if config.native_tool_calling and delta and hasattr(delta, 'tool_calls') and delta.tool_calls:
@ -190,17 +191,73 @@ class ResponseProcessor:
"arguments": json.loads(current_tool['function']['arguments']),
"id": current_tool['id']
}
result = await self._execute_tool(tool_call_data)
# Store result to add after assistant message
tool_results_buffer.append((tool_call_data, result))
# Start tool execution as a background task
execution_task = asyncio.create_task(self._execute_tool(tool_call_data))
# Yield tool execution result for client display
yield {
"type": "tool_result",
"name": tool_call_data["name"],
"result": str(result)
}
# Store the task for later retrieval
pending_tool_executions.append({
"task": execution_task,
"tool_call": tool_call_data
})
# Immediately continue processing more chunks
# Check for completed tool executions
completed_executions = []
for i, execution in enumerate(pending_tool_executions):
if execution["task"].done():
try:
# Get the result
result = execution["task"].result()
tool_call = execution["tool_call"]
# Store result for later database updates
tool_results_buffer.append((tool_call, result))
# Yield tool execution result for client display
yield {
"type": "tool_result",
"name": tool_call["name"],
"result": str(result)
}
# Mark for removal
completed_executions.append(i)
except Exception as e:
logger.error(f"Error getting tool execution result: {str(e)}")
# Remove completed executions from pending list (in reverse to maintain indices)
for i in sorted(completed_executions, reverse=True):
pending_tool_executions.pop(i)
# After streaming completes, wait for any remaining tool executions
if pending_tool_executions:
logger.info(f"Waiting for {len(pending_tool_executions)} pending tool executions to complete")
# Wait for all pending tasks to complete
pending_tasks = [execution["task"] for execution in pending_tool_executions]
done, _ = await asyncio.wait(pending_tasks)
# Process results
for execution in pending_tool_executions:
try:
if execution["task"].done():
result = execution["task"].result()
tool_call = execution["tool_call"]
# Store result for later
tool_results_buffer.append((tool_call, result))
# Yield tool execution result
yield {
"type": "tool_result",
"name": tool_call["name"],
"result": str(result)
}
except Exception as e:
logger.error(f"Error processing remaining tool execution: {str(e)}")
# After streaming completes, process any remaining content and tool calls
if accumulated_content:

View File

@ -10,19 +10,14 @@ This module provides comprehensive conversation management, including:
"""
import json
import logging
import asyncio
import uuid
import re
from typing import List, Dict, Any, Optional, Type, Union, AsyncGenerator, Tuple, Callable, Literal
from services.llm import make_llm_api_call
from agentpress.tool import Tool, ToolResult
from agentpress.tool_registry import ToolRegistry
from agentpress.response_processor import (
ResponseProcessor,
ProcessorConfig,
XmlAddingStrategy,
ToolExecutionStrategy
ProcessorConfig
)
from services.supabase import DBConnection
from utils.logger import logger
@ -318,6 +313,15 @@ class ThreadManager:
logger.debug(f"Processor config: XML={processor_config.xml_tool_calling}, Native={processor_config.native_tool_calling}, "
f"Execute tools={processor_config.execute_tools}, Strategy={processor_config.tool_execution_strategy}")
# Check if native_tool_calling is enabled and throw an error if it is
if processor_config.native_tool_calling:
error_message = "Native tool calling is not supported in this version"
logger.error(error_message)
return {
"status": "error",
"message": error_message
}
# 4. Prepare tools for LLM call
openapi_tool_schemas = None
if processor_config.native_tool_calling: