mirror of https://github.com/kortix-ai/suna.git
fix
This commit is contained in:
parent
f8fc799726
commit
304fa1978c
|
@ -4,14 +4,13 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Modern Landing Page</title>
|
||||
<link rel="stylesheet" href="reset.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header container">
|
||||
<div class="header-logo">Brand</div>
|
||||
<header>
|
||||
<nav>
|
||||
<ul class="nav-links">
|
||||
<div class="logo">Brand</div>
|
||||
<ul>
|
||||
<li><a href="#home">Home</a></li>
|
||||
<li><a href="#features">Features</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
|
@ -20,56 +19,54 @@
|
|||
</header>
|
||||
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="container hero-content">
|
||||
<h1 class="hero-title">Transform Your Digital Experience</h1>
|
||||
<p class="hero-subtitle">Discover innovative solutions that drive your business forward</p>
|
||||
<a href="#features" class="cta-button">Get Started</a>
|
||||
<section id="home" class="hero">
|
||||
<div class="hero-content">
|
||||
<h1>Transform Your Ideas into Reality</h1>
|
||||
<p>Discover innovative solutions that drive your business forward.</p>
|
||||
<a href="#contact" class="cta-button">Get Started</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="features">
|
||||
<div class="container">
|
||||
<div class="features-content">
|
||||
<h2>Our Key Features</h2>
|
||||
<div class="feature-grid">
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🚀</div>
|
||||
<h3>Fast Performance</h3>
|
||||
<p>Optimized solutions for maximum efficiency</p>
|
||||
<h3>Innovative Design</h3>
|
||||
<p>Cutting-edge solutions tailored to your needs.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🔒</div>
|
||||
<h3>Secure Platform</h3>
|
||||
<p>Advanced security measures to protect your data</p>
|
||||
<h3>Scalable Technology</h3>
|
||||
<p>Grow your business with flexible platforms.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">📊</div>
|
||||
<h3>Smart Analytics</h3>
|
||||
<p>Comprehensive insights for strategic decisions</p>
|
||||
<h3>Expert Support</h3>
|
||||
<p>24/7 professional assistance for your projects.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contact" class="contact">
|
||||
<div class="contact-content">
|
||||
<h2>Ready to Get Started?</h2>
|
||||
<p>Connect with our team and unlock your potential.</p>
|
||||
<a href="#" class="cta-button">Contact Us</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-logo">Brand</div>
|
||||
<div class="footer-links">
|
||||
<a href="#home">Home</a>
|
||||
<a href="#features">Features</a>
|
||||
<a href="#contact">Contact</a>
|
||||
</div>
|
||||
<div class="footer-social">
|
||||
<a href="#">Twitter</a>
|
||||
<a href="#">LinkedIn</a>
|
||||
<a href="#">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-copyright">
|
||||
© 2023 Brand. All rights reserved.
|
||||
<footer>
|
||||
<div class="footer-content">
|
||||
<p>© 2023 Brand. All rights reserved.</p>
|
||||
<div class="social-links">
|
||||
<a href="#">Twitter</a>
|
||||
<a href="#">LinkedIn</a>
|
||||
<a href="#">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html{scroll-behavior:smooth}body{line-height:1.6;font-family:'-apple-system','BlinkMacSystemFont','Segoe UI','Roboto','Oxygen','Ubuntu','Cantarell','Open Sans','Helvetica Neue',sans-serif}img{max-width:100%;height:auto}a{text-decoration:none;color:inherit}
|
|
@ -0,0 +1,48 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const ctaButtons = document.querySelectorAll('.cta-button');
|
||||
const navLinks = document.querySelectorAll('nav ul li a');
|
||||
|
||||
ctaButtons.forEach(ctaButton => {
|
||||
ctaButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
ctaButton.classList.add('clicked');
|
||||
setTimeout(() => {
|
||||
ctaButton.classList.remove('clicked');
|
||||
}, 300);
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.classList.add('modal');
|
||||
modal.innerHTML = `
|
||||
<div class="modal-content">
|
||||
<h2>Thank You!</h2>
|
||||
<p>We'll get back to you soon.</p>
|
||||
<button class="close-modal">Close</button>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
modal.querySelector('.close-modal').addEventListener('click', () => {
|
||||
document.body.removeChild(modal);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const targetId = link.getAttribute('href').substring(1);
|
||||
const targetSection = document.getElementById(targetId) || document.querySelector('main');
|
||||
targetSection.scrollIntoView({ behavior: 'smooth' });
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const features = document.querySelectorAll('.feature');
|
||||
features.forEach(feature => {
|
||||
const rect = feature.getBoundingClientRect();
|
||||
if (rect.top < window.innerHeight * 0.8) {
|
||||
feature.classList.add('visible');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1 +1,227 @@
|
|||
:root{--primary-color:#3498db;--secondary-color:#2ecc71;--text-color:#333;--background-color:#f4f4f4;--accent-color:#e74c3c}body{scroll-behavior:smooth;font-smooth:always;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background-color:var(--background-color);color:var(--text-color)}.container{max-width:1200px;margin:0 auto;padding:0 15px}.header{display:flex;justify-content:space-between;align-items:center;padding:20px 0;background-color:white;box-shadow:0 2px 4px rgba(0,0,0,0.1)}.header-logo{font-size:24px;font-weight:bold;color:var(--primary-color)}.nav-links{display:flex;list-style:none;gap:20px}.nav-links a{color:var(--text-color);transition:color 0.3s ease}.nav-links a:hover{color:var(--primary-color)}.hero{display:flex;align-items:center;min-height:70vh;background:linear-gradient(135deg,var(--primary-color),var(--secondary-color));color:white;text-align:center}.hero-content{max-width:800px;margin:0 auto}.hero-title{font-size:48px;margin-bottom:20px;font-weight:bold}.hero-subtitle{font-size:24px;margin-bottom:30px}.cta-button{display:inline-block;background-color:white;color:var(--primary-color);padding:12px 30px;border-radius:5px;font-weight:bold;transition:transform 0.3s ease}.cta-button:hover{transform:scale(1.05)}.footer{background-color:#333;color:white;text-align:center;padding:20px 0}.features{padding:80px 0;background-color:white}.feature-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:30px}.feature{text-align:center;padding:30px;border-radius:10px;transition:all 0.3s ease;box-shadow:0 4px 6px rgba(0,0,0,0.1);background-color:white}.feature:hover{transform:translateY(-10px);box-shadow:0 8px 15px rgba(0,0,0,0.15);background-color:var(--background-color)}.feature-icon{font-size:48px;margin-bottom:20px}.feature h3{margin-bottom:15px;font-size:20px}.feature p{color:var(--text-color)}.footer{background-color:#1a1a1a;color:white;padding:40px 0}.footer-content{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.footer-logo{font-size:24px;font-weight:bold}.footer-links{display:flex;gap:20px}.footer-links a{color:white;transition:color 0.3s ease}.footer-links a:hover{color:var(--primary-color)}.footer-social{display:flex;gap:15px}.footer-social a{color:white;transition:color 0.3s ease}.footer-social a:hover{color:var(--secondary-color)}.footer-copyright{text-align:center;padding-top:20px;border-top:1px solid rgba(255,255,255,0.1)}@media(max-width:768px){.header{flex-direction:column;text-align:center}.header-logo{margin-bottom:15px}.nav-links{flex-direction:column;align-items:center}.hero{text-align:center}.hero-title{font-size:36px}.hero-subtitle{font-size:18px}.feature-grid{grid-template-columns:1fr}.features{padding:40px 0}.footer-content{flex-direction:column;text-align:center}.footer-links,.footer-social{margin-top:20px;flex-direction:column;align-items:center}}
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--secondary-color: #2ecc71;
|
||||
--text-color: #333;
|
||||
--background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 800px;
|
||||
padding: 2rem;
|
||||
animation: fadeInUp 1s ease-out;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 2rem;
|
||||
color: rgba(255,255,255,0.9);
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
background-color: white;
|
||||
color: var(--primary-color);
|
||||
padding: 0.75rem 1.5rem;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s ease;
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background-color: #f4f4f4;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 8px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
color: white;
|
||||
margin: 0 0.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
nav {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
margin-top: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-button.clicked {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
margin-top: 1rem;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.feature {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.6s ease;
|
||||
}
|
||||
|
||||
.feature.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
|
@ -3,6 +3,8 @@ from typing import Dict, Any, AsyncGenerator, Callable
|
|||
from agentpress.tool_parser import ToolParser
|
||||
from agentpress.tool_executor import ToolExecutor
|
||||
import asyncio
|
||||
import os
|
||||
import json
|
||||
|
||||
class LLMResponseProcessor:
|
||||
"""
|
||||
|
@ -60,80 +62,120 @@ class LLMResponseProcessor:
|
|||
) -> AsyncGenerator:
|
||||
"""
|
||||
Process streaming LLM response and handle tool execution.
|
||||
|
||||
Yields chunks immediately as they arrive, while handling tool execution
|
||||
and message management in the background.
|
||||
Yields chunks immediately while managing message state and tool execution efficiently.
|
||||
"""
|
||||
pending_tool_calls = []
|
||||
background_tasks = set()
|
||||
last_update_time = 0
|
||||
UPDATE_INTERVAL = 0.5 # Minimum seconds between message updates
|
||||
tool_results = [] # Track tool results
|
||||
|
||||
async def handle_message_management(chunk):
|
||||
# Accumulate content
|
||||
if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
|
||||
self.content_buffer += chunk.choices[0].delta.content
|
||||
|
||||
# Parse and accumulate tool calls
|
||||
parsed_message, is_complete = await self.tool_parser.parse_stream(
|
||||
chunk,
|
||||
self.tool_calls_buffer
|
||||
)
|
||||
if parsed_message and 'tool_calls' in parsed_message:
|
||||
self.tool_calls_accumulated = parsed_message['tool_calls']
|
||||
try:
|
||||
nonlocal last_update_time, tool_results
|
||||
current_time = asyncio.get_event_loop().time()
|
||||
|
||||
# Handle message management and tool execution
|
||||
if chunk.choices[0].finish_reason or (self.content_buffer and self.tool_calls_accumulated):
|
||||
message = {
|
||||
"role": "assistant",
|
||||
"content": self.content_buffer
|
||||
}
|
||||
if self.tool_calls_accumulated:
|
||||
message["tool_calls"] = self.tool_calls_accumulated
|
||||
# Accumulate content
|
||||
if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
|
||||
self.content_buffer += chunk.choices[0].delta.content
|
||||
|
||||
# Parse tool calls only if present in chunk
|
||||
if hasattr(chunk.choices[0].delta, 'tool_calls'):
|
||||
parsed_message, is_complete = await self.tool_parser.parse_stream(
|
||||
chunk,
|
||||
self.tool_calls_buffer
|
||||
)
|
||||
if parsed_message and 'tool_calls' in parsed_message:
|
||||
self.tool_calls_accumulated = parsed_message['tool_calls']
|
||||
|
||||
if not self.message_added:
|
||||
await self.add_message(self.thread_id, message)
|
||||
self.message_added = True
|
||||
else:
|
||||
await self.update_message(self.thread_id, message)
|
||||
|
||||
# Handle tool execution
|
||||
# Handle tool execution and results
|
||||
if execute_tools and self.tool_calls_accumulated:
|
||||
new_tool_calls = [
|
||||
tool_call for tool_call in self.tool_calls_accumulated
|
||||
if tool_call['id'] not in self.processed_tool_calls
|
||||
]
|
||||
|
||||
if new_tool_calls:
|
||||
if immediate_execution:
|
||||
results = await self.tool_executor.execute_tool_calls(
|
||||
tool_calls=new_tool_calls,
|
||||
available_functions=self.available_functions,
|
||||
thread_id=self.thread_id,
|
||||
executed_tool_calls=self.processed_tool_calls
|
||||
)
|
||||
for result in results:
|
||||
await self.add_message(self.thread_id, result)
|
||||
self.processed_tool_calls.add(result['tool_call_id'])
|
||||
else:
|
||||
pending_tool_calls.extend(new_tool_calls)
|
||||
if new_tool_calls and immediate_execution:
|
||||
results = await self.tool_executor.execute_tool_calls(
|
||||
tool_calls=new_tool_calls,
|
||||
available_functions=self.available_functions,
|
||||
thread_id=self.thread_id,
|
||||
executed_tool_calls=self.processed_tool_calls
|
||||
)
|
||||
tool_results.extend(results)
|
||||
for result in results:
|
||||
self.processed_tool_calls.add(result['tool_call_id'])
|
||||
elif new_tool_calls:
|
||||
pending_tool_calls.extend(new_tool_calls)
|
||||
|
||||
# Handle end of stream
|
||||
if chunk.choices[0].finish_reason:
|
||||
if not immediate_execution and pending_tool_calls:
|
||||
results = await self.tool_executor.execute_tool_calls(
|
||||
tool_calls=pending_tool_calls,
|
||||
available_functions=self.available_functions,
|
||||
thread_id=self.thread_id,
|
||||
executed_tool_calls=self.processed_tool_calls
|
||||
)
|
||||
for result in results:
|
||||
await self.add_message(self.thread_id, result)
|
||||
self.processed_tool_calls.add(result['tool_call_id'])
|
||||
pending_tool_calls.clear()
|
||||
# Update message state with rate limiting
|
||||
should_update = (
|
||||
chunk.choices[0].finish_reason or
|
||||
(current_time - last_update_time) >= UPDATE_INTERVAL
|
||||
)
|
||||
|
||||
async for chunk in response_stream:
|
||||
# Start background task for message management and tool execution
|
||||
asyncio.create_task(handle_message_management(chunk))
|
||||
# Immediately yield the chunk
|
||||
yield chunk
|
||||
if should_update:
|
||||
# First, add tool results if any
|
||||
for result in tool_results:
|
||||
if not any(msg.get('tool_call_id') == result['tool_call_id']
|
||||
for msg in await self.list_messages(self.thread_id)):
|
||||
await self.add_message(self.thread_id, result)
|
||||
tool_results = [] # Clear processed results
|
||||
|
||||
# Then add/update assistant message
|
||||
message = {
|
||||
"role": "assistant",
|
||||
"content": self.content_buffer
|
||||
}
|
||||
if self.tool_calls_accumulated:
|
||||
message["tool_calls"] = self.tool_calls_accumulated
|
||||
|
||||
if not self.message_added:
|
||||
await self.add_message(self.thread_id, message)
|
||||
self.message_added = True
|
||||
else:
|
||||
await self.update_message(self.thread_id, message)
|
||||
last_update_time = current_time
|
||||
|
||||
# Handle stream completion
|
||||
if chunk.choices[0].finish_reason:
|
||||
if not immediate_execution and pending_tool_calls:
|
||||
results = await self.tool_executor.execute_tool_calls(
|
||||
tool_calls=pending_tool_calls,
|
||||
available_functions=self.available_functions,
|
||||
thread_id=self.thread_id,
|
||||
executed_tool_calls=self.processed_tool_calls
|
||||
)
|
||||
for result in results:
|
||||
await self.add_message(self.thread_id, result)
|
||||
self.processed_tool_calls.add(result['tool_call_id'])
|
||||
pending_tool_calls.clear()
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in background task: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
async for chunk in response_stream:
|
||||
# Create and track background task
|
||||
task = asyncio.create_task(handle_message_management(chunk))
|
||||
background_tasks.add(task)
|
||||
task.add_done_callback(background_tasks.discard)
|
||||
|
||||
# Immediately yield the chunk
|
||||
yield chunk
|
||||
|
||||
# Wait for all background tasks to complete
|
||||
if background_tasks:
|
||||
await asyncio.gather(*background_tasks, return_exceptions=True)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in stream processing: {e}")
|
||||
# Clean up any remaining background tasks
|
||||
for task in background_tasks:
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
raise
|
||||
|
||||
async def process_response(
|
||||
self,
|
||||
|
@ -172,4 +214,14 @@ class LLMResponseProcessor:
|
|||
await self.add_message(self.thread_id, {
|
||||
"role": "assistant",
|
||||
"content": response_content or ""
|
||||
})
|
||||
})
|
||||
|
||||
async def list_messages(self, thread_id: str) -> list:
|
||||
"""Helper method to get current messages in thread"""
|
||||
thread_path = os.path.join(self.threads_dir, f"{thread_id}.json")
|
||||
try:
|
||||
with open(thread_path, 'r') as f:
|
||||
thread_data = json.load(f)
|
||||
return thread_data.get("messages", [])
|
||||
except Exception:
|
||||
return []
|
Loading…
Reference in New Issue