mirror of https://github.com/kortix-ai/suna.git
auto kill other active agent runs on start of new
This commit is contained in:
parent
12c37c663b
commit
6f9db5ba88
|
@ -87,13 +87,14 @@ async def restore_running_agent_runs():
|
|||
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.
|
||||
If found, returns the ID of the active run, otherwise returns None.
|
||||
|
||||
Args:
|
||||
client: The Supabase client
|
||||
project_id: The project ID to check
|
||||
|
||||
Raises:
|
||||
HTTPException: If an agent run is already active for the project
|
||||
Returns:
|
||||
str or None: The ID of the active agent run if found, None otherwise
|
||||
"""
|
||||
# Get all threads from this project
|
||||
project_threads = await client.table('threads').select('thread_id').eq('project_id', project_id).execute()
|
||||
|
@ -104,10 +105,9 @@ async def check_for_active_project_agent_run(client, project_id: str):
|
|||
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."
|
||||
)
|
||||
return active_runs.data[0]['id']
|
||||
|
||||
return None
|
||||
|
||||
@router.post("/thread/{thread_id}/agent/start")
|
||||
async def start_agent(thread_id: str, user_id: str = Depends(get_current_user_id)):
|
||||
|
@ -127,7 +127,12 @@ async def start_agent(thread_id: str, user_id: str = Depends(get_current_user_id
|
|||
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)
|
||||
active_run_id = await check_for_active_project_agent_run(client, project_id)
|
||||
|
||||
# If there's an active run, stop it first
|
||||
if active_run_id:
|
||||
logger.info(f"Stopping existing agent run {active_run_id} before starting new one")
|
||||
await stop_agent_run(active_run_id)
|
||||
|
||||
# Create a new agent run
|
||||
agent_run = await client.table('agent_runs').insert({
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const galleryContainer = document.getElementById('galleryContainer');
|
||||
const galleryImages = document.getElementById('galleryImages');
|
||||
const prevBtn = document.getElementById('galleryPrev');
|
||||
const nextBtn = document.getElementById('galleryNext');
|
||||
const imageUpload = document.getElementById('imageUpload');
|
||||
const uploadBtn = document.getElementById('uploadImageBtn');
|
||||
|
||||
// Sample images for demo
|
||||
const demoImages = [
|
||||
'https://source.unsplash.com/random/300x200?nature,1',
|
||||
'https://source.unsplash.com/random/300x200?city,1',
|
||||
'https://source.unsplash.com/random/300x200?technology,1',
|
||||
'https://source.unsplash.com/random/300x200?animals,1'
|
||||
];
|
||||
|
||||
// Load images from localStorage or use demo images
|
||||
let images = JSON.parse(localStorage.getItem('galleryImages')) || demoImages;
|
||||
let currentIndex = 0;
|
||||
|
||||
// Initialize gallery
|
||||
renderGallery();
|
||||
|
||||
// Event listeners
|
||||
prevBtn.addEventListener('click', showPreviousImage);
|
||||
nextBtn.addEventListener('click', showNextImage);
|
||||
uploadBtn.addEventListener('click', handleImageUpload);
|
||||
|
||||
function renderGallery() {
|
||||
if (images.length === 0) {
|
||||
galleryImages.innerHTML = '<p class="gallery-empty">No images in gallery. Add some!</p>';
|
||||
prevBtn.disabled = true;
|
||||
nextBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
showImage(currentIndex);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
function showImage(index) {
|
||||
galleryImages.innerHTML = '';
|
||||
const img = document.createElement('img');
|
||||
img.src = images[index];
|
||||
img.alt = `Gallery image ${index + 1}`;
|
||||
img.className = 'gallery-img';
|
||||
|
||||
const counter = document.createElement('div');
|
||||
counter.className = 'gallery-counter';
|
||||
counter.textContent = `${index + 1} / ${images.length}`;
|
||||
|
||||
galleryImages.appendChild(img);
|
||||
galleryImages.appendChild(counter);
|
||||
}
|
||||
|
||||
function showPreviousImage() {
|
||||
currentIndex = (currentIndex - 1 + images.length) % images.length;
|
||||
showImage(currentIndex);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
function showNextImage() {
|
||||
currentIndex = (currentIndex + 1) % images.length;
|
||||
showImage(currentIndex);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
function updateButtons() {
|
||||
prevBtn.disabled = images.length <= 1;
|
||||
nextBtn.disabled = images.length <= 1;
|
||||
}
|
||||
|
||||
function handleImageUpload() {
|
||||
const imageUrl = imageUpload.value.trim();
|
||||
if (imageUrl) {
|
||||
// Simple validation - check if it looks like a URL
|
||||
if (imageUrl.startsWith('http') && (imageUrl.endsWith('.jpg') ||
|
||||
imageUrl.endsWith('.jpeg') || imageUrl.endsWith('.png') ||
|
||||
imageUrl.endsWith('.gif') || imageUrl.includes('unsplash'))) {
|
||||
|
||||
addImage(imageUrl);
|
||||
imageUpload.value = '';
|
||||
} else {
|
||||
alert('Please enter a valid image URL (ending with .jpg, .jpeg, .png, or .gif)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addImage(url) {
|
||||
images.push(url);
|
||||
saveImages();
|
||||
currentIndex = images.length - 1; // Show the newly added image
|
||||
renderGallery();
|
||||
}
|
||||
|
||||
function saveImages() {
|
||||
localStorage.setItem('galleryImages', JSON.stringify(images));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,315 @@
|
|||
// Test Runner Script
|
||||
console.log("Test Runner initialized");
|
||||
|
||||
// Function to run all tests automatically
|
||||
function autoRunTests() {
|
||||
console.log("Auto-running tests...");
|
||||
|
||||
// Tests to run in sequence
|
||||
const tests = [
|
||||
testColorChanger,
|
||||
testCounter,
|
||||
testTodoList,
|
||||
testWeatherWidget,
|
||||
testGallery
|
||||
];
|
||||
|
||||
// Run tests sequentially
|
||||
runTestSequence(tests, 0);
|
||||
}
|
||||
|
||||
// Run tests in sequence
|
||||
function runTestSequence(tests, index) {
|
||||
if (index >= tests.length) {
|
||||
console.log("All tests completed!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Running test ${index + 1}/${tests.length}...`);
|
||||
|
||||
// Run current test
|
||||
const currentTest = tests[index];
|
||||
currentTest(() => {
|
||||
// When test completes, run next test
|
||||
runTestSequence(tests, index + 1);
|
||||
});
|
||||
}
|
||||
|
||||
// Test Color Changer
|
||||
function testColorChanger(callback) {
|
||||
console.log("Testing Color Changer...");
|
||||
|
||||
const changeColorBtn = document.getElementById('changeColorBtn');
|
||||
const colorDisplay = document.getElementById('colorDisplay');
|
||||
|
||||
if (!changeColorBtn || !colorDisplay) {
|
||||
console.error("Color changer elements not found!");
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get initial state
|
||||
const initialColor = colorDisplay.style.backgroundColor;
|
||||
console.log(`Initial color: ${initialColor || 'none'}`);
|
||||
|
||||
// Click button
|
||||
changeColorBtn.click();
|
||||
|
||||
// Check result
|
||||
setTimeout(() => {
|
||||
const newColor = colorDisplay.style.backgroundColor;
|
||||
console.log(`New color: ${newColor}`);
|
||||
|
||||
if (newColor && newColor !== initialColor) {
|
||||
console.log("✅ Color changer test passed!");
|
||||
} else {
|
||||
console.error("❌ Color changer test failed!");
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Test Counter
|
||||
function testCounter(callback) {
|
||||
console.log("Testing Counter...");
|
||||
|
||||
const decrementBtn = document.getElementById('decrementBtn');
|
||||
const incrementBtn = document.getElementById('incrementBtn');
|
||||
const counterValue = document.getElementById('counterValue');
|
||||
|
||||
if (!decrementBtn || !incrementBtn || !counterValue) {
|
||||
console.error("Counter elements not found!");
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get initial value
|
||||
const initialValue = parseInt(counterValue.textContent);
|
||||
console.log(`Initial counter value: ${initialValue}`);
|
||||
|
||||
// Test increment
|
||||
incrementBtn.click();
|
||||
|
||||
setTimeout(() => {
|
||||
const afterIncrement = parseInt(counterValue.textContent);
|
||||
console.log(`After increment: ${afterIncrement}`);
|
||||
|
||||
if (afterIncrement === initialValue + 1) {
|
||||
console.log("✅ Increment test passed!");
|
||||
} else {
|
||||
console.error("❌ Increment test failed!");
|
||||
}
|
||||
|
||||
// Test decrement
|
||||
decrementBtn.click();
|
||||
decrementBtn.click();
|
||||
|
||||
setTimeout(() => {
|
||||
const afterDecrement = parseInt(counterValue.textContent);
|
||||
console.log(`After decrement: ${afterDecrement}`);
|
||||
|
||||
if (afterDecrement === initialValue - 1) {
|
||||
console.log("✅ Decrement test passed!");
|
||||
} else {
|
||||
console.error("❌ Decrement test failed!");
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
}, 500);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Test Todo List
|
||||
function testTodoList(callback) {
|
||||
console.log("Testing Todo List...");
|
||||
|
||||
const todoInput = document.getElementById('todoInput');
|
||||
const addTodoBtn = document.getElementById('addTodoBtn');
|
||||
const todoList = document.getElementById('todoList');
|
||||
|
||||
if (!todoInput || !addTodoBtn || !todoList) {
|
||||
console.error("Todo list elements not found!");
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get initial todo count
|
||||
const initialCount = todoList.querySelectorAll('.todo-item').length;
|
||||
console.log(`Initial todo count: ${initialCount}`);
|
||||
|
||||
// Add a new todo
|
||||
const testTodoText = `Test Todo ${Date.now()}`;
|
||||
todoInput.value = testTodoText;
|
||||
addTodoBtn.click();
|
||||
|
||||
setTimeout(() => {
|
||||
// Check if todo was added
|
||||
const newCount = todoList.querySelectorAll('.todo-item').length;
|
||||
console.log(`New todo count: ${newCount}`);
|
||||
|
||||
if (newCount === initialCount + 1) {
|
||||
console.log("✅ Add todo test passed!");
|
||||
|
||||
// Get the last todo item
|
||||
const lastTodo = todoList.querySelector('.todo-item:last-child');
|
||||
const checkbox = lastTodo.querySelector('input[type="checkbox"]');
|
||||
const deleteBtn = lastTodo.querySelector('.delete-btn');
|
||||
|
||||
// Test toggle functionality
|
||||
checkbox.click();
|
||||
|
||||
setTimeout(() => {
|
||||
if (lastTodo.classList.contains('completed')) {
|
||||
console.log("✅ Toggle todo test passed!");
|
||||
} else {
|
||||
console.error("❌ Toggle todo test failed!");
|
||||
}
|
||||
|
||||
// Test delete functionality
|
||||
deleteBtn.click();
|
||||
|
||||
setTimeout(() => {
|
||||
const finalCount = todoList.querySelectorAll('.todo-item').length;
|
||||
console.log(`Final todo count: ${finalCount}`);
|
||||
|
||||
if (finalCount === initialCount) {
|
||||
console.log("✅ Delete todo test passed!");
|
||||
} else {
|
||||
console.error("❌ Delete todo test failed!");
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
}, 500);
|
||||
}, 500);
|
||||
} else {
|
||||
console.error("❌ Add todo test failed!");
|
||||
if (callback) callback();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Test Weather Widget
|
||||
function testWeatherWidget(callback) {
|
||||
console.log("Testing Weather Widget...");
|
||||
|
||||
const weatherCity = document.getElementById('weatherCity');
|
||||
const weatherForm = document.getElementById('weatherForm');
|
||||
const weatherDisplay = document.getElementById('weatherDisplay');
|
||||
|
||||
if (!weatherCity || !weatherForm || !weatherDisplay) {
|
||||
console.error("Weather widget elements not found!");
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if initial weather is displayed
|
||||
const initialWeatherInfo = weatherDisplay.querySelector('.weather-info');
|
||||
|
||||
if (initialWeatherInfo) {
|
||||
console.log("✅ Initial weather display test passed!");
|
||||
} else {
|
||||
console.log("❓ No initial weather displayed, will test form submission");
|
||||
}
|
||||
|
||||
// Test form submission
|
||||
weatherCity.value = "Tokyo";
|
||||
|
||||
// Create and dispatch submit event
|
||||
const submitEvent = new Event('submit');
|
||||
weatherForm.dispatchEvent(submitEvent);
|
||||
|
||||
console.log("Weather form submitted with city: Tokyo");
|
||||
|
||||
// Wait for API response
|
||||
setTimeout(() => {
|
||||
const weatherInfo = weatherDisplay.querySelector('.weather-info');
|
||||
|
||||
if (weatherInfo) {
|
||||
const cityName = weatherInfo.querySelector('h3');
|
||||
const temperature = weatherInfo.querySelector('.temperature');
|
||||
|
||||
if (cityName && temperature) {
|
||||
console.log("✅ Weather API test passed!");
|
||||
console.log(`City: ${cityName.textContent}, Temperature: ${temperature.textContent}`);
|
||||
} else {
|
||||
console.error("❌ Weather display structure test failed!");
|
||||
}
|
||||
} else {
|
||||
console.error("❌ Weather API test failed!");
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// Test Gallery
|
||||
function testGallery(callback) {
|
||||
console.log("Testing Image Gallery...");
|
||||
|
||||
const galleryImages = document.getElementById('galleryImages');
|
||||
const prevBtn = document.getElementById('galleryPrev');
|
||||
const nextBtn = document.getElementById('galleryNext');
|
||||
const imageUpload = document.getElementById('imageUpload');
|
||||
const uploadBtn = document.getElementById('uploadImageBtn');
|
||||
|
||||
if (!galleryImages || !prevBtn || !nextBtn || !imageUpload || !uploadBtn) {
|
||||
console.error("Gallery elements not found!");
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if initial gallery is displayed
|
||||
const initialImage = galleryImages.querySelector('.gallery-img');
|
||||
|
||||
if (initialImage) {
|
||||
console.log("✅ Initial gallery display test passed!");
|
||||
|
||||
// Test navigation
|
||||
const initialCounter = galleryImages.querySelector('.gallery-counter').textContent;
|
||||
console.log(`Initial image: ${initialCounter}`);
|
||||
|
||||
// Test next button
|
||||
nextBtn.click();
|
||||
|
||||
setTimeout(() => {
|
||||
const newCounter = galleryImages.querySelector('.gallery-counter').textContent;
|
||||
console.log(`After next: ${newCounter}`);
|
||||
|
||||
if (newCounter !== initialCounter) {
|
||||
console.log("✅ Gallery navigation test passed!");
|
||||
} else {
|
||||
console.error("❌ Gallery navigation test failed!");
|
||||
}
|
||||
|
||||
// Test image upload
|
||||
const testImageUrl = "https://source.unsplash.com/random/300x200?test";
|
||||
imageUpload.value = testImageUrl;
|
||||
uploadBtn.click();
|
||||
|
||||
setTimeout(() => {
|
||||
const finalCounter = galleryImages.querySelector('.gallery-counter').textContent;
|
||||
console.log(`After upload: ${finalCounter}`);
|
||||
|
||||
if (finalCounter.includes(testImageUrl)) {
|
||||
console.log("✅ Image upload test passed!");
|
||||
} else {
|
||||
console.log("⚠️ Image upload test inconclusive - can't verify image URL");
|
||||
}
|
||||
|
||||
if (callback) callback();
|
||||
}, 500);
|
||||
}, 500);
|
||||
} else {
|
||||
console.log("⚠️ No initial gallery image displayed");
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
|
||||
// Start tests when page is fully loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log("DOM loaded, waiting for all resources...");
|
||||
|
||||
// Wait a bit longer to ensure all scripts are initialized
|
||||
setTimeout(autoRunTests, 1000);
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const weatherWidget = document.getElementById('weatherWidget');
|
||||
const weatherCity = document.getElementById('weatherCity');
|
||||
const weatherForm = document.getElementById('weatherForm');
|
||||
const weatherDisplay = document.getElementById('weatherDisplay');
|
||||
|
||||
weatherForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const city = weatherCity.value.trim();
|
||||
if (city) {
|
||||
fetchWeather(city);
|
||||
}
|
||||
});
|
||||
|
||||
async function fetchWeather(city) {
|
||||
try {
|
||||
weatherDisplay.innerHTML = '<p>Loading weather data...</p>';
|
||||
|
||||
// Using OpenWeatherMap API with a free tier (you would need to replace with your own API key in production)
|
||||
const apiKey = 'demo'; // Using demo mode for example purposes
|
||||
const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('City not found or API limit reached');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
displayWeather(data);
|
||||
} catch (error) {
|
||||
weatherDisplay.innerHTML = `<p class="weather-error">Error: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
function displayWeather(data) {
|
||||
// For demo purposes, we'll show a simplified version since the API key is demo
|
||||
weatherDisplay.innerHTML = `
|
||||
<div class="weather-info">
|
||||
<h3>${data.name}, ${data.sys.country}</h3>
|
||||
<div class="weather-main">
|
||||
<span class="temperature">${Math.round(data.main.temp)}°C</span>
|
||||
<span class="description">${data.weather[0].description}</span>
|
||||
</div>
|
||||
<div class="weather-details">
|
||||
<p>Feels like: ${Math.round(data.main.feels_like)}°C</p>
|
||||
<p>Humidity: ${data.main.humidity}%</p>
|
||||
<p>Wind: ${data.wind.speed} m/s</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Initialize with a default city
|
||||
fetchWeather('London');
|
||||
});
|
Loading…
Reference in New Issue