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
|
||||
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"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}")
|
||||
|
@ -135,15 +138,28 @@ class ResponseProcessor:
|
|||
if tool_call:
|
||||
# Execute tool if needed, but in background
|
||||
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
|
||||
execution_task = asyncio.create_task(self._execute_tool(tool_call))
|
||||
|
||||
# Store the task for later retrieval
|
||||
pending_tool_executions.append({
|
||||
"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
|
||||
|
||||
# Process native tool calls
|
||||
|
@ -192,15 +208,28 @@ class ResponseProcessor:
|
|||
"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
|
||||
execution_task = asyncio.create_task(self._execute_tool(tool_call_data))
|
||||
|
||||
# Store the task for later retrieval
|
||||
pending_tool_executions.append({
|
||||
"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
|
||||
|
||||
# Check for completed tool executions
|
||||
|
@ -215,11 +244,24 @@ class ResponseProcessor:
|
|||
# Store result for later database updates
|
||||
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 {
|
||||
"type": "tool_result",
|
||||
"name": tool_call["name"],
|
||||
"result": str(result)
|
||||
"result": str(result),
|
||||
"tool_index": tool_index
|
||||
}
|
||||
|
||||
# Mark for removal
|
||||
|
@ -250,14 +292,37 @@ class ResponseProcessor:
|
|||
# Store result for later
|
||||
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 {
|
||||
"type": "tool_result",
|
||||
"name": tool_call["name"],
|
||||
"result": str(result)
|
||||
"result": str(result),
|
||||
"tool_index": tool_index
|
||||
}
|
||||
except Exception as 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
|
||||
if accumulated_content:
|
||||
|
|
Loading…
Reference in New Issue