mirror of https://github.com/kortix-ai/suna.git
tool indexes & msgs in stream
This commit is contained in:
parent
1bf0bf0f7d
commit
d27b814c97
|
@ -0,0 +1,74 @@
|
||||||
|
class ParticleSystem {
|
||||||
|
constructor() {
|
||||||
|
this.particles = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
createParticle(x, y, color) {
|
||||||
|
return {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
color: color,
|
||||||
|
velocity: {
|
||||||
|
x: (Math.random() - 0.5) * 3,
|
||||||
|
y: (Math.random() - 0.5) * 3
|
||||||
|
},
|
||||||
|
size: Math.random() * 3 + 2,
|
||||||
|
life: 1,
|
||||||
|
decay: 0.02
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(x, y, count, color) {
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
this.particles.push(this.createParticle(x, y, color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(ctx) {
|
||||||
|
for (let i = this.particles.length - 1; i >= 0; i--) {
|
||||||
|
const p = this.particles[i];
|
||||||
|
|
||||||
|
p.x += p.velocity.x;
|
||||||
|
p.y += p.velocity.y;
|
||||||
|
p.life -= p.decay;
|
||||||
|
|
||||||
|
if (p.life <= 0) {
|
||||||
|
this.particles.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = `${p.color}${Math.floor(p.life * 255).toString(16).padStart(2, '0')}`;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Trail {
|
||||||
|
constructor(maxPoints = 10) {
|
||||||
|
this.points = [];
|
||||||
|
this.maxPoints = maxPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPoint(x, y) {
|
||||||
|
this.points.unshift({ x, y, alpha: 1 });
|
||||||
|
if (this.points.length > this.maxPoints) {
|
||||||
|
this.points.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx) {
|
||||||
|
ctx.save();
|
||||||
|
for (let i = this.points.length - 1; i >= 0; i--) {
|
||||||
|
const p = this.points[i];
|
||||||
|
p.alpha = i / this.points.length;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(p.x, p.y, 15 * p.alpha, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = `rgba(0, 255, 255, ${p.alpha * 0.3})`;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Sound effects manager
|
||||||
|
const Sounds = {
|
||||||
|
flap: new Audio('assets/sounds/flap.mp3'),
|
||||||
|
score: new Audio('assets/sounds/score.mp3'),
|
||||||
|
hit: new Audio('assets/sounds/hit.mp3'),
|
||||||
|
|
||||||
|
play: function(sound) {
|
||||||
|
this[sound].currentTime = 0;
|
||||||
|
this[sound].play().catch(err => console.log('Audio play failed:', err));
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Sprite loading and management
|
||||||
|
const Sprites = {
|
||||||
|
bird: new Image(),
|
||||||
|
pipeTop: new Image(),
|
||||||
|
pipeBottom: new Image(),
|
||||||
|
background: new Image(),
|
||||||
|
|
||||||
|
load: function() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let loadedImages = 0;
|
||||||
|
const totalImages = 4;
|
||||||
|
|
||||||
|
const checkLoaded = () => {
|
||||||
|
loadedImages++;
|
||||||
|
if (loadedImages === totalImages) resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.bird.onload = checkLoaded;
|
||||||
|
this.pipeTop.onload = checkLoaded;
|
||||||
|
this.pipeBottom.onload = checkLoaded;
|
||||||
|
this.background.onload = checkLoaded;
|
||||||
|
|
||||||
|
this.bird.src = 'assets/images/bird.png';
|
||||||
|
this.pipeTop.src = 'assets/images/pipe-top.png';
|
||||||
|
this.pipeBottom.src = 'assets/images/pipe-bottom.png';
|
||||||
|
this.background.src = 'assets/images/background.png';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background: #111;
|
||||||
|
font-family: 'Press Start 2P', cursive;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
border: 3px solid #0ff;
|
||||||
|
background: #070721;
|
||||||
|
box-shadow: 0 0 20px #0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#game-over {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
color: #0ff;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0 0 10px #0ff;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #0ff;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { box-shadow: 0 0 10px #0ff; }
|
||||||
|
50% { box-shadow: 0 0 20px #0ff, 0 0 30px #f0f; }
|
||||||
|
100% { box-shadow: 0 0 10px #0ff; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.score {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
color: #0ff;
|
||||||
|
text-shadow: 0 0 10px #0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#current-score { left: 20px; }
|
||||||
|
#high-score { right: 20px; }
|
|
@ -0,0 +1,193 @@
|
||||||
|
const canvas = document.getElementById('gameCanvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const gameOverDiv = document.getElementById('game-over');
|
||||||
|
|
||||||
|
// Game constants
|
||||||
|
const GRAVITY = 0.4;
|
||||||
|
const FLAP_SPEED = -7;
|
||||||
|
const PIPE_SPEED = 2;
|
||||||
|
const PIPE_GAP = 150;
|
||||||
|
|
||||||
|
// Initialize particle system and trail
|
||||||
|
const particles = new ParticleSystem();
|
||||||
|
const trail = new Trail();
|
||||||
|
|
||||||
|
// Game state
|
||||||
|
let bird = {
|
||||||
|
x: 50,
|
||||||
|
y: canvas.height / 2,
|
||||||
|
velocity: 0,
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
rotation: 0,
|
||||||
|
wingAngle: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let pipes = [];
|
||||||
|
let score = 0;
|
||||||
|
let gameRunning = false;
|
||||||
|
let frameCount = 0;
|
||||||
|
let highScore = localStorage.getItem('highScore') || 0;
|
||||||
|
|
||||||
|
// Neon colors for the bird
|
||||||
|
const birdColors = {
|
||||||
|
primary: '#0ff',
|
||||||
|
secondary: '#f0f',
|
||||||
|
glow: '#fff'
|
||||||
|
};
|
||||||
|
|
||||||
|
function drawNeonBird() {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(bird.x, bird.y);
|
||||||
|
ctx.rotate(bird.rotation * Math.PI / 180);
|
||||||
|
|
||||||
|
// Draw trail
|
||||||
|
trail.draw(ctx);
|
||||||
|
|
||||||
|
// Draw wing
|
||||||
|
bird.wingAngle = Math.sin(frameCount * 0.3) * 30;
|
||||||
|
|
||||||
|
// Bird body glow
|
||||||
|
ctx.shadowBlur = 20;
|
||||||
|
ctx.shadowColor = birdColors.primary;
|
||||||
|
|
||||||
|
// Main body
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(0, 0, bird.width/2, bird.height/2, 0, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = birdColors.primary;
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Wing
|
||||||
|
ctx.save();
|
||||||
|
ctx.rotate(bird.wingAngle * Math.PI / 180);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(0, 0, bird.width/3, bird.height/4, 0, 0, Math.PI);
|
||||||
|
ctx.fillStyle = birdColors.secondary;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// Eye
|
||||||
|
ctx.shadowBlur = 10;
|
||||||
|
ctx.shadowColor = birdColors.glow;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(bird.width/4, -bird.height/6, 3, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawNeonPipe(pipe, isTop) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.shadowBlur = 15;
|
||||||
|
ctx.shadowColor = '#0ff';
|
||||||
|
ctx.strokeStyle = '#0ff';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.fillStyle = 'rgba(0, 255, 255, 0.1)';
|
||||||
|
|
||||||
|
if (isTop) {
|
||||||
|
ctx.fillRect(pipe.x, pipe.y, pipe.width, pipe.height);
|
||||||
|
ctx.strokeRect(pipe.x, pipe.y, pipe.width, pipe.height);
|
||||||
|
} else {
|
||||||
|
ctx.fillRect(pipe.x, pipe.y, pipe.width, pipe.height);
|
||||||
|
ctx.strokeRect(pipe.x, pipe.y, pipe.width, pipe.height);
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the original drawPipes function
|
||||||
|
function drawPipes() {
|
||||||
|
pipes.forEach((pipe, index) => {
|
||||||
|
drawNeonPipe(pipe, index % 2 === 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the original handleClick function
|
||||||
|
function handleClick() {
|
||||||
|
if (!gameRunning) {
|
||||||
|
startGame();
|
||||||
|
} else {
|
||||||
|
bird.velocity = FLAP_SPEED;
|
||||||
|
bird.rotation = -45;
|
||||||
|
particles.emit(bird.x, bird.y, 5, birdColors.primary);
|
||||||
|
Sounds.play('flap');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the original updateGame function
|
||||||
|
function updateGame() {
|
||||||
|
if (!gameRunning) return;
|
||||||
|
|
||||||
|
// Update bird
|
||||||
|
bird.velocity += GRAVITY;
|
||||||
|
bird.y += bird.velocity;
|
||||||
|
|
||||||
|
// Update trail
|
||||||
|
trail.addPoint(bird.x, bird.y);
|
||||||
|
|
||||||
|
// Update bird rotation
|
||||||
|
if (bird.velocity > 0) {
|
||||||
|
bird.rotation += 4;
|
||||||
|
bird.rotation = Math.min(90, bird.rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate particles while moving
|
||||||
|
if (frameCount % 3 === 0) {
|
||||||
|
particles.emit(bird.x - 10, bird.y, 1, birdColors.secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rest of the updateGame function remains the same
|
||||||
|
[... existing updateGame code ...]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the original animate function
|
||||||
|
function animate() {
|
||||||
|
if (!gameRunning) return;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Draw background with a gradient
|
||||||
|
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
||||||
|
gradient.addColorStop(0, '#000');
|
||||||
|
gradient.addColorStop(1, '#111');
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Draw grid effect
|
||||||
|
drawGrid();
|
||||||
|
|
||||||
|
updateGame();
|
||||||
|
drawPipes();
|
||||||
|
particles.update(ctx);
|
||||||
|
drawNeonBird();
|
||||||
|
drawScore();
|
||||||
|
|
||||||
|
frameCount++;
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new grid effect
|
||||||
|
function drawGrid() {
|
||||||
|
ctx.strokeStyle = 'rgba(0, 255, 255, 0.1)';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
|
||||||
|
const gridSize = 30;
|
||||||
|
const offset = frameCount % gridSize;
|
||||||
|
|
||||||
|
for (let x = -offset; x < canvas.width; x += gridSize) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, canvas.height);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let y = -offset; y < canvas.height; y += gridSize) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(canvas.width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the game
|
||||||
|
initGame();
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Flappy Bird Clone</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="game-over">
|
||||||
|
Game Over!<br>
|
||||||
|
Score: <span id="final-score">0</span><br>
|
||||||
|
Click to restart
|
||||||
|
</div>
|
||||||
|
<canvas id="gameCanvas" width="320" height="480"></canvas>
|
||||||
|
<script src="assets/sounds.js"></script>
|
||||||
|
<script src="assets/sprites.js"></script>
|
||||||
|
<script src="game.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -100,6 +100,9 @@ class ResponseProcessor:
|
||||||
# For tracking pending tool executions
|
# For tracking pending tool executions
|
||||||
pending_tool_executions = []
|
pending_tool_executions = []
|
||||||
|
|
||||||
|
# Tool execution index counter
|
||||||
|
tool_execution_index = 0
|
||||||
|
|
||||||
logger.info(f"Starting to process streaming response for thread {thread_id}")
|
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}, "
|
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}")
|
f"Execute on stream={config.execute_on_stream}, Execution strategy={config.tool_execution_strategy}")
|
||||||
|
@ -135,15 +138,28 @@ class ResponseProcessor:
|
||||||
if tool_call:
|
if tool_call:
|
||||||
# Execute tool if needed, but in background
|
# Execute tool if needed, but in background
|
||||||
if config.execute_tools and config.execute_on_stream:
|
if config.execute_tools and config.execute_on_stream:
|
||||||
|
# Yield tool execution start message
|
||||||
|
yield {
|
||||||
|
"type": "tool_status",
|
||||||
|
"status": "started",
|
||||||
|
"name": tool_call["name"],
|
||||||
|
"message": f"Starting execution of {tool_call['name']}",
|
||||||
|
"tool_index": tool_execution_index
|
||||||
|
}
|
||||||
|
|
||||||
# Start tool execution as a background task
|
# Start tool execution as a background task
|
||||||
execution_task = asyncio.create_task(self._execute_tool(tool_call))
|
execution_task = asyncio.create_task(self._execute_tool(tool_call))
|
||||||
|
|
||||||
# Store the task for later retrieval
|
# Store the task for later retrieval
|
||||||
pending_tool_executions.append({
|
pending_tool_executions.append({
|
||||||
"task": execution_task,
|
"task": execution_task,
|
||||||
"tool_call": tool_call
|
"tool_call": tool_call,
|
||||||
|
"tool_index": tool_execution_index
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Increment the tool execution index
|
||||||
|
tool_execution_index += 1
|
||||||
|
|
||||||
# Immediately continue processing more chunks
|
# Immediately continue processing more chunks
|
||||||
|
|
||||||
# Process native tool calls
|
# Process native tool calls
|
||||||
|
@ -192,15 +208,28 @@ class ResponseProcessor:
|
||||||
"id": current_tool['id']
|
"id": current_tool['id']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Yield tool execution start message
|
||||||
|
yield {
|
||||||
|
"type": "tool_status",
|
||||||
|
"status": "started",
|
||||||
|
"name": tool_call_data["name"],
|
||||||
|
"message": f"Starting execution of {tool_call_data['name']}",
|
||||||
|
"tool_index": tool_execution_index
|
||||||
|
}
|
||||||
|
|
||||||
# Start tool execution as a background task
|
# Start tool execution as a background task
|
||||||
execution_task = asyncio.create_task(self._execute_tool(tool_call_data))
|
execution_task = asyncio.create_task(self._execute_tool(tool_call_data))
|
||||||
|
|
||||||
# Store the task for later retrieval
|
# Store the task for later retrieval
|
||||||
pending_tool_executions.append({
|
pending_tool_executions.append({
|
||||||
"task": execution_task,
|
"task": execution_task,
|
||||||
"tool_call": tool_call_data
|
"tool_call": tool_call_data,
|
||||||
|
"tool_index": tool_execution_index
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Increment the tool execution index
|
||||||
|
tool_execution_index += 1
|
||||||
|
|
||||||
# Immediately continue processing more chunks
|
# Immediately continue processing more chunks
|
||||||
|
|
||||||
# Check for completed tool executions
|
# Check for completed tool executions
|
||||||
|
@ -215,11 +244,24 @@ class ResponseProcessor:
|
||||||
# Store result for later database updates
|
# Store result for later database updates
|
||||||
tool_results_buffer.append((tool_call, result))
|
tool_results_buffer.append((tool_call, result))
|
||||||
|
|
||||||
|
# Get the tool index
|
||||||
|
tool_index = execution.get("tool_index", -1)
|
||||||
|
|
||||||
|
# Yield tool status message first
|
||||||
|
yield {
|
||||||
|
"type": "tool_status",
|
||||||
|
"status": "completed" if result.success else "failed",
|
||||||
|
"name": tool_call["name"],
|
||||||
|
"message": f"Tool {tool_call['name']} {'completed successfully' if result.success else 'failed'}",
|
||||||
|
"tool_index": tool_index
|
||||||
|
}
|
||||||
|
|
||||||
# Yield tool execution result for client display
|
# Yield tool execution result for client display
|
||||||
yield {
|
yield {
|
||||||
"type": "tool_result",
|
"type": "tool_result",
|
||||||
"name": tool_call["name"],
|
"name": tool_call["name"],
|
||||||
"result": str(result)
|
"result": str(result),
|
||||||
|
"tool_index": tool_index
|
||||||
}
|
}
|
||||||
|
|
||||||
# Mark for removal
|
# Mark for removal
|
||||||
|
@ -250,14 +292,37 @@ class ResponseProcessor:
|
||||||
# Store result for later
|
# Store result for later
|
||||||
tool_results_buffer.append((tool_call, result))
|
tool_results_buffer.append((tool_call, result))
|
||||||
|
|
||||||
|
# Get the tool index
|
||||||
|
tool_index = execution.get("tool_index", -1)
|
||||||
|
|
||||||
|
# Yield tool status message first
|
||||||
|
yield {
|
||||||
|
"type": "tool_status",
|
||||||
|
"status": "completed" if result.success else "failed",
|
||||||
|
"name": tool_call["name"],
|
||||||
|
"message": f"Tool {tool_call['name']} {'completed successfully' if result.success else 'failed'}",
|
||||||
|
"tool_index": tool_index
|
||||||
|
}
|
||||||
|
|
||||||
# Yield tool execution result
|
# Yield tool execution result
|
||||||
yield {
|
yield {
|
||||||
"type": "tool_result",
|
"type": "tool_result",
|
||||||
"name": tool_call["name"],
|
"name": tool_call["name"],
|
||||||
"result": str(result)
|
"result": str(result),
|
||||||
|
"tool_index": tool_index
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing remaining tool execution: {str(e)}")
|
logger.error(f"Error processing remaining tool execution: {str(e)}")
|
||||||
|
# Yield error status for the tool
|
||||||
|
if "tool_call" in execution:
|
||||||
|
tool_index = execution.get("tool_index", -1)
|
||||||
|
yield {
|
||||||
|
"type": "tool_status",
|
||||||
|
"status": "error",
|
||||||
|
"name": execution["tool_call"].get("name", "unknown"),
|
||||||
|
"message": f"Error processing tool result: {str(e)}",
|
||||||
|
"tool_index": tool_index
|
||||||
|
}
|
||||||
|
|
||||||
# After streaming completes, process any remaining content and tool calls
|
# After streaming completes, process any remaining content and tool calls
|
||||||
if accumulated_content:
|
if accumulated_content:
|
||||||
|
|
Loading…
Reference in New Issue