mirror of https://github.com/kortix-ai/suna.git
only 1 agent run wip
This commit is contained in:
parent
296a68e8d1
commit
12c37c663b
|
@ -84,6 +84,31 @@ async def restore_running_agent_runs():
|
|||
"completed_at": datetime.now(timezone.utc).isoformat()
|
||||
}).eq("id", run['id']).execute()
|
||||
|
||||
async def check_for_active_project_agent_run(client, project_id: str):
|
||||
"""
|
||||
Check if there is an active agent run for any thread in the given project.
|
||||
|
||||
Args:
|
||||
client: The Supabase client
|
||||
project_id: The project ID to check
|
||||
|
||||
Raises:
|
||||
HTTPException: If an agent run is already active for the project
|
||||
"""
|
||||
# Get all threads from this project
|
||||
project_threads = await client.table('threads').select('thread_id').eq('project_id', project_id).execute()
|
||||
project_thread_ids = [t['thread_id'] for t in project_threads.data]
|
||||
|
||||
# Check if there are any active agent runs for any thread in this project
|
||||
if project_thread_ids:
|
||||
active_runs = await client.table('agent_runs').select('id').in_('thread_id', project_thread_ids).eq('status', 'running').execute()
|
||||
|
||||
if active_runs.data and len(active_runs.data) > 0:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Another agent is already running for this project. Please wait for it to complete or stop it before starting a new one."
|
||||
)
|
||||
|
||||
@router.post("/thread/{thread_id}/agent/start")
|
||||
async def start_agent(thread_id: str, user_id: str = Depends(get_current_user_id)):
|
||||
"""Start an agent for a specific thread in the background."""
|
||||
|
@ -94,6 +119,16 @@ async def start_agent(thread_id: str, user_id: str = Depends(get_current_user_id
|
|||
# Verify user has access to this thread
|
||||
await verify_thread_access(client, thread_id, user_id)
|
||||
|
||||
# Get the project_id for this thread
|
||||
thread_result = await client.table('threads').select('project_id').eq('thread_id', thread_id).execute()
|
||||
if not thread_result.data:
|
||||
raise HTTPException(status_code=404, detail="Thread not found")
|
||||
|
||||
project_id = thread_result.data[0]['project_id']
|
||||
|
||||
# Check if there is already an active agent run for this project
|
||||
await check_for_active_project_agent_run(client, project_id)
|
||||
|
||||
# Create a new agent run
|
||||
agent_run = await client.table('agent_runs').insert({
|
||||
"thread_id": thread_id,
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# Brand Identity Business Card
|
||||
|
||||
This is a modern, interactive business card design implemented with HTML, CSS, and JavaScript.
|
||||
|
||||
## Features
|
||||
|
||||
- Interactive flip animation to show front and back of the card
|
||||
- Color theme customization with predefined color options
|
||||
- Responsive design that works on different screen sizes
|
||||
- Modern, clean aesthetic with attention to typography and spacing
|
||||
- Social media icons and contact information
|
||||
|
||||
## Usage
|
||||
|
||||
1. Open `index.html` in any modern web browser
|
||||
2. Click the "Flip Card" button to see both sides of the business card
|
||||
3. Use the color dots below to change the card's color theme
|
||||
4. You can also double-click on the card to flip it
|
||||
|
||||
## Customization
|
||||
|
||||
To customize this business card for your own use:
|
||||
|
||||
1. Edit the HTML to change the name, title, and contact information
|
||||
2. Modify the logo and branding elements in the CSS
|
||||
3. Add or remove social media icons as needed
|
||||
4. Adjust the color palette by changing the color option values
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- HTML5
|
||||
- CSS3 (with variables, flexbox, and transitions)
|
||||
- JavaScript
|
||||
- Font Awesome for icons
|
||||
- Google Fonts (Montserrat and Playfair Display)
|
|
@ -3,55 +3,52 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Brand Identity Business Card</title>
|
||||
<title>Web Development Demo</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Playfair+Display:wght@400;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="card-container">
|
||||
<div class="card front">
|
||||
<div class="logo">
|
||||
<div class="logo-symbol">A</div>
|
||||
<div class="logo-text">ACME</div>
|
||||
</div>
|
||||
<div class="tagline">Creative Solutions</div>
|
||||
<div class="pattern"></div>
|
||||
</div>
|
||||
<div class="card back">
|
||||
<div class="info">
|
||||
<h2 class="name">ALEX JOHNSON</h2>
|
||||
<p class="title">Creative Director</p>
|
||||
<div class="divider"></div>
|
||||
<div class="contact">
|
||||
<p><i class="fas fa-phone"></i> (555) 123-4567</p>
|
||||
<p><i class="fas fa-envelope"></i> alex@acmecreative.com</p>
|
||||
<p><i class="fas fa-map-marker-alt"></i> 123 Design Street, Creative City</p>
|
||||
<p><i class="fas fa-globe"></i> www.acmecreative.com</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social-icons">
|
||||
<i class="fab fa-linkedin"></i>
|
||||
<i class="fab fa-twitter"></i>
|
||||
<i class="fab fa-instagram"></i>
|
||||
<i class="fab fa-behance"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<header>
|
||||
<h1>Welcome to My Web App</h1>
|
||||
<p>This is a simple demonstration of web development capabilities.</p>
|
||||
</header>
|
||||
|
||||
<div class="controls">
|
||||
<button id="flip-btn">Flip Card</button>
|
||||
<div class="color-options">
|
||||
<div class="color-option" data-color="#1a237e" style="background-color: #1a237e;"></div>
|
||||
<div class="color-option" data-color="#006064" style="background-color: #006064;"></div>
|
||||
<div class="color-option" data-color="#4a148c" style="background-color: #4a148c;"></div>
|
||||
<div class="color-option" data-color="#880e4f" style="background-color: #880e4f;"></div>
|
||||
<div class="color-option" data-color="#bf360c" style="background-color: #bf360c;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main>
|
||||
<section class="card">
|
||||
<h2>Interactive Demo</h2>
|
||||
<p>Click the button below to see some magic happen!</p>
|
||||
<button id="changeColorBtn">Change Colors</button>
|
||||
<div id="colorDisplay" class="color-box"></div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Counter Demo</h2>
|
||||
<p>A simple counter implementation:</p>
|
||||
<div class="counter-container">
|
||||
<button id="decrementBtn">-</button>
|
||||
<span id="counterValue">0</span>
|
||||
<button id="incrementBtn">+</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>To-Do List</h2>
|
||||
<p>Add and manage your tasks:</p>
|
||||
<div class="todo-container">
|
||||
<div class="todo-input-container">
|
||||
<input type="text" id="todoInput" placeholder="Enter a new task...">
|
||||
<button id="addTodoBtn">Add</button>
|
||||
</div>
|
||||
<ul id="todoList" class="todo-list"></ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Created as a demonstration of web development capabilities</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,26 +1,158 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const cardContainer = document.querySelector('.card-container');
|
||||
const flipBtn = document.getElementById('flip-btn');
|
||||
const colorOptions = document.querySelectorAll('.color-option');
|
||||
// Color changing functionality
|
||||
const changeColorBtn = document.getElementById('changeColorBtn');
|
||||
const colorDisplay = document.getElementById('colorDisplay');
|
||||
|
||||
// Set initial primary color
|
||||
document.documentElement.style.setProperty('--primary-color', '#1a237e');
|
||||
|
||||
// Flip card functionality
|
||||
flipBtn.addEventListener('click', function() {
|
||||
cardContainer.classList.toggle('flipped');
|
||||
changeColorBtn.addEventListener('click', function() {
|
||||
// Generate a random color
|
||||
const randomColor = getRandomColor();
|
||||
colorDisplay.style.backgroundColor = randomColor;
|
||||
colorDisplay.textContent = randomColor;
|
||||
colorDisplay.style.color = getContrastColor(randomColor);
|
||||
colorDisplay.style.display = 'flex';
|
||||
colorDisplay.style.justifyContent = 'center';
|
||||
colorDisplay.style.alignItems = 'center';
|
||||
colorDisplay.style.fontWeight = 'bold';
|
||||
});
|
||||
|
||||
// Color change functionality
|
||||
colorOptions.forEach(option => {
|
||||
option.addEventListener('click', function() {
|
||||
const color = this.getAttribute('data-color');
|
||||
document.documentElement.style.setProperty('--primary-color', color);
|
||||
// Counter functionality
|
||||
const decrementBtn = document.getElementById('decrementBtn');
|
||||
const incrementBtn = document.getElementById('incrementBtn');
|
||||
const counterValue = document.getElementById('counterValue');
|
||||
|
||||
let count = 0;
|
||||
|
||||
decrementBtn.addEventListener('click', function() {
|
||||
count--;
|
||||
updateCounter();
|
||||
});
|
||||
|
||||
incrementBtn.addEventListener('click', function() {
|
||||
count++;
|
||||
updateCounter();
|
||||
});
|
||||
|
||||
function updateCounter() {
|
||||
counterValue.textContent = count;
|
||||
// Add some visual feedback
|
||||
counterValue.style.color = count < 0 ? 'red' : count > 0 ? 'green' : '#333';
|
||||
}
|
||||
|
||||
// Todo List functionality
|
||||
const todoInput = document.getElementById('todoInput');
|
||||
const addTodoBtn = document.getElementById('addTodoBtn');
|
||||
const todoList = document.getElementById('todoList');
|
||||
|
||||
// Load todos from localStorage
|
||||
let todos = JSON.parse(localStorage.getItem('todos')) || [];
|
||||
|
||||
// Render existing todos
|
||||
renderTodos();
|
||||
|
||||
addTodoBtn.addEventListener('click', addTodo);
|
||||
todoInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
addTodo();
|
||||
}
|
||||
});
|
||||
|
||||
function addTodo() {
|
||||
const todoText = todoInput.value.trim();
|
||||
if (todoText) {
|
||||
const todo = {
|
||||
id: Date.now(),
|
||||
text: todoText,
|
||||
completed: false
|
||||
};
|
||||
|
||||
todos.push(todo);
|
||||
saveTodos();
|
||||
renderTodos();
|
||||
todoInput.value = '';
|
||||
todoInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTodo(id) {
|
||||
todos = todos.map(todo => {
|
||||
if (todo.id === id) {
|
||||
todo.completed = !todo.completed;
|
||||
}
|
||||
return todo;
|
||||
});
|
||||
});
|
||||
saveTodos();
|
||||
renderTodos();
|
||||
}
|
||||
|
||||
// Double click to flip card
|
||||
cardContainer.addEventListener('dblclick', function() {
|
||||
this.classList.toggle('flipped');
|
||||
});
|
||||
function deleteTodo(id) {
|
||||
todos = todos.filter(todo => todo.id !== id);
|
||||
saveTodos();
|
||||
renderTodos();
|
||||
}
|
||||
|
||||
function renderTodos() {
|
||||
todoList.innerHTML = '';
|
||||
|
||||
if (todos.length === 0) {
|
||||
const emptyMessage = document.createElement('li');
|
||||
emptyMessage.textContent = 'No tasks yet. Add one above!';
|
||||
emptyMessage.style.textAlign = 'center';
|
||||
emptyMessage.style.padding = '10px';
|
||||
emptyMessage.style.color = '#888';
|
||||
todoList.appendChild(emptyMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
todos.forEach(todo => {
|
||||
const todoItem = document.createElement('li');
|
||||
todoItem.className = `todo-item ${todo.completed ? 'completed' : ''}`;
|
||||
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.checked = todo.completed;
|
||||
checkbox.addEventListener('change', () => toggleTodo(todo.id));
|
||||
|
||||
const todoText = document.createElement('span');
|
||||
todoText.className = 'todo-text';
|
||||
todoText.textContent = todo.text;
|
||||
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'delete-btn';
|
||||
deleteBtn.textContent = 'Delete';
|
||||
deleteBtn.addEventListener('click', () => deleteTodo(todo.id));
|
||||
|
||||
todoItem.appendChild(checkbox);
|
||||
todoItem.appendChild(todoText);
|
||||
todoItem.appendChild(deleteBtn);
|
||||
|
||||
todoList.appendChild(todoItem);
|
||||
});
|
||||
}
|
||||
|
||||
function saveTodos() {
|
||||
localStorage.setItem('todos', JSON.stringify(todos));
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function getRandomColor() {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
function getContrastColor(hexColor) {
|
||||
// Convert hex to RGB
|
||||
const r = parseInt(hexColor.substr(1, 2), 16);
|
||||
const g = parseInt(hexColor.substr(3, 2), 16);
|
||||
const b = parseInt(hexColor.substr(5, 2), 16);
|
||||
|
||||
// Calculate luminance
|
||||
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
||||
|
||||
// Return black or white based on luminance
|
||||
return luminance > 0.5 ? '#000000' : '#FFFFFF';
|
||||
}
|
||||
});
|
|
@ -5,226 +5,170 @@
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
width: 350px;
|
||||
height: 200px;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-style: preserve-3d;
|
||||
transition: transform 0.8s;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card.front {
|
||||
background-color: var(--primary-color, #1a237e);
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.card.back {
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
transform: rotateY(180deg);
|
||||
backface-visibility: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-container.flipped .card.front {
|
||||
transform: rotateY(180deg);
|
||||
header {
|
||||
background: linear-gradient(135deg, #6e8efb, #a777e3);
|
||||
color: white;
|
||||
padding: 30px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-container.flipped .card.back {
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
header h1 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logo-symbol {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
main {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: white;
|
||||
color: var(--primary-color, #1a237e);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: 24px;
|
||||
font-family: 'Playfair Display', serif;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 1px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.pattern {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 150px;
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, transparent 25%, rgba(255, 255, 255, 0.1) 25%,
|
||||
rgba(255, 255, 255, 0.1) 50%, transparent 50%,
|
||||
transparent 75%, rgba(255, 255, 255, 0.1) 75%);
|
||||
background-size: 20px 20px;
|
||||
border-radius: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color, #1a237e);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
.card h2 {
|
||||
margin-bottom: 15px;
|
||||
color: #6e8efb;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 40px;
|
||||
height: 3px;
|
||||
background-color: var(--primary-color, #1a237e);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.contact {
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contact p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.contact i {
|
||||
color: var(--primary-color, #1a237e);
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
justify-content: flex-end;
|
||||
padding: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.social-icons i {
|
||||
color: var(--primary-color, #1a237e);
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.social-icons i:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#flip-btn {
|
||||
background-color: var(--primary-color, #1a237e);
|
||||
button {
|
||||
background-color: #6e8efb;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
#flip-btn:hover {
|
||||
background-color: #0d1642;
|
||||
button:hover {
|
||||
background-color: #5d7ce0;
|
||||
}
|
||||
|
||||
.color-options {
|
||||
.color-box {
|
||||
height: 100px;
|
||||
background-color: #f0f0f0;
|
||||
margin-top: 15px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.5s;
|
||||
}
|
||||
|
||||
.counter-container {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.color-option {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
#counterValue {
|
||||
font-size: 24px;
|
||||
margin: 0 20px;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.color-option:hover {
|
||||
transform: scale(1.1);
|
||||
/* Todo List Styles */
|
||||
.todo-container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.card-container {
|
||||
width: 300px;
|
||||
height: 180px;
|
||||
.todo-input-container {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#todoInput {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px 0 0 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#addTodoBtn {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.todo-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.todo-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
animation: fadeIn 0.3s ease-in;
|
||||
}
|
||||
|
||||
.todo-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.todo-text {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.todo-item.completed .todo-text {
|
||||
text-decoration: line-through;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background-color: #ff6b6b;
|
||||
margin-left: 10px;
|
||||
padding: 4px 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background-color: #ff5252;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
color: #777;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.logo-symbol {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
font-size: 20px;
|
||||
header {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 20px;
|
||||
.card {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
|
@ -180,7 +180,7 @@ class LLMResponseProcessor:
|
|||
if 'tool_calls' in response:
|
||||
logger.debug(f"Found {len(response['tool_calls'])} tool calls in response")
|
||||
if execute_tools:
|
||||
await self._execute_tool_calls(response)
|
||||
await self._process_tool_call(response)
|
||||
else:
|
||||
logger.info("Tool execution disabled, skipping tool calls")
|
||||
else:
|
||||
|
@ -190,7 +190,7 @@ class LLMResponseProcessor:
|
|||
logger.error(f"Error processing response: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
async def _execute_tool_calls(self, response: Dict[str, Any]) -> None:
|
||||
async def _process_tool_call(self, response: Dict[str, Any]) -> None:
|
||||
"""Execute tool calls from the response."""
|
||||
logger.info(f"Executing tool calls for thread {self.thread_id}")
|
||||
try:
|
||||
|
|
|
@ -110,7 +110,7 @@ class XMLResultsAdder(ResultsAdderBase):
|
|||
# Fallback if we can't find the root tag
|
||||
result_message = {
|
||||
"role": "user",
|
||||
"content": f"<tool_result>>Result for {result['name']}:\n{result['content']}</<tool_result>>"
|
||||
"content": f"<tool_result>Result for {result['name']}:\n{result['content']}</tool_result>"
|
||||
}
|
||||
await self.add_message(thread_id, result_message)
|
||||
|
||||
|
|
|
@ -268,15 +268,26 @@ class ThreadManager:
|
|||
) -> Union[Dict[str, Any], AsyncGenerator]:
|
||||
"""Run a conversation thread with specified parameters."""
|
||||
logger.info(f"Starting thread execution for thread {thread_id}")
|
||||
logger.debug(f"Parameters: model={model_name}, temperature={temperature}, stream={stream}")
|
||||
logger.debug(f"Parameters: model={model_name}, temperature={temperature}, max_tokens={max_tokens}, "
|
||||
f"tool_choice={tool_choice}, native_tool_calling={native_tool_calling}, "
|
||||
f"xml_tool_calling={xml_tool_calling}, execute_tools={execute_tools}, stream={stream}, "
|
||||
f"execute_tools_on_stream={execute_tools_on_stream}, "
|
||||
f"parallel_tool_execution={parallel_tool_execution}")
|
||||
|
||||
try:
|
||||
# Validate tool calling configuration
|
||||
# 1. Get messages from thread
|
||||
messages = await self.get_messages(thread_id)
|
||||
|
||||
# 2. Prepare messages for LLM + add temporary message if it exists
|
||||
prepared_messages = [system_message] + messages
|
||||
if temporary_message:
|
||||
prepared_messages.append(temporary_message)
|
||||
logger.debug("Added temporary message to prepared messages")
|
||||
|
||||
if native_tool_calling and xml_tool_calling:
|
||||
logger.error("Invalid configuration: Cannot use both native and XML tool calling")
|
||||
raise ValueError("Cannot use both native LLM tool calling and XML tool calling simultaneously")
|
||||
|
||||
# Initialize tool components if any tool calling is enabled
|
||||
if native_tool_calling or xml_tool_calling:
|
||||
logger.debug("Initializing tool components")
|
||||
if tool_parser is None:
|
||||
|
@ -291,12 +302,6 @@ class ThreadManager:
|
|||
results_adder = XMLResultsAdder(self) if xml_tool_calling else StandardResultsAdder(self)
|
||||
logger.debug(f"Using {results_adder.__class__.__name__} for results adding")
|
||||
|
||||
messages = await self.get_messages(thread_id)
|
||||
prepared_messages = [system_message] + messages
|
||||
if temporary_message:
|
||||
prepared_messages.append(temporary_message)
|
||||
logger.debug("Added temporary message to prepared messages")
|
||||
|
||||
openapi_tool_schemas = None
|
||||
if native_tool_calling:
|
||||
openapi_tool_schemas = self.tool_registry.get_openapi_schemas()
|
||||
|
@ -309,6 +314,17 @@ class ThreadManager:
|
|||
available_functions = {}
|
||||
logger.debug("No tool calling enabled")
|
||||
|
||||
logger.info("Making LLM API call")
|
||||
llm_response = await self._run_thread_completion(
|
||||
messages=prepared_messages,
|
||||
model_name=model_name,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
tools=openapi_tool_schemas,
|
||||
tool_choice=tool_choice if native_tool_calling else None,
|
||||
stream=stream
|
||||
)
|
||||
|
||||
response_processor = LLMResponseProcessor(
|
||||
thread_id=thread_id,
|
||||
available_functions=available_functions,
|
||||
|
@ -321,17 +337,6 @@ class ThreadManager:
|
|||
results_adder=results_adder
|
||||
)
|
||||
|
||||
logger.info("Making LLM API call")
|
||||
llm_response = await self._run_thread_completion(
|
||||
messages=prepared_messages,
|
||||
model_name=model_name,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
tools=openapi_tool_schemas,
|
||||
tool_choice=tool_choice if native_tool_calling else None,
|
||||
stream=stream
|
||||
)
|
||||
|
||||
if stream:
|
||||
logger.info("Processing streaming response")
|
||||
return response_processor.process_stream(
|
||||
|
@ -339,15 +344,13 @@ class ThreadManager:
|
|||
execute_tools=execute_tools,
|
||||
execute_tools_on_stream=execute_tools_on_stream
|
||||
)
|
||||
|
||||
logger.info("Processing non-streaming response")
|
||||
await response_processor.process_response(
|
||||
response=llm_response,
|
||||
execute_tools=execute_tools
|
||||
)
|
||||
|
||||
logger.info("Thread execution completed successfully")
|
||||
return llm_response
|
||||
else:
|
||||
logger.info("Processing non-streaming response")
|
||||
await response_processor.process_response(
|
||||
response=llm_response,
|
||||
execute_tools=execute_tools
|
||||
)
|
||||
return llm_response
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in run_thread: {str(e)}", exc_info=True)
|
||||
|
@ -382,4 +385,4 @@ class ThreadManager:
|
|||
return response
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to make LLM API call: {str(e)}", exc_info=True)
|
||||
raise
|
||||
raise
|
Loading…
Reference in New Issue