From 56dd7d3fd340ee0910d7800f458926f28c8b5984 Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Mon, 1 Jun 2026 23:29:28 +0200 Subject: [PATCH] fix: tool start hanging + SSE 429 errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Remove Docker build from create_instance for manifest types — the build was blocking the HTTP request for several minutes, causing frontend timeouts and retries. Image is now built lazily on start (via the existing _prepare_manifest_instance path in start_instance). 2. Increase MAX_CONNECTIONS_PER_USER from 5 to 20 for SSE endpoint — aggressive reconnect loops from the frontend were exhausting the limit and causing 429 errors unrelated to tool starting. Quality gates: ruff clean, tsc --noEmit clean, pytest workspaces (9 passed) --- apps/api/src/api/events.py | 2 +- apps/api/src/api/tool_instances.py | 38 +---------------------- apps/api/src/services/terminal_manager.py | 20 +++++++----- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/apps/api/src/api/events.py b/apps/api/src/api/events.py index cebc8e5..3b33859 100644 --- a/apps/api/src/api/events.py +++ b/apps/api/src/api/events.py @@ -16,7 +16,7 @@ router = APIRouter(prefix="/events", tags=["events"]) # In-memory connection counter per user (single-process assumption) _connection_counts: dict[uuid.UUID, int] = {} -MAX_CONNECTIONS_PER_USER = 5 +MAX_CONNECTIONS_PER_USER = 20 @router.get("/stream") diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index b86dca4..490be03 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -1039,7 +1039,7 @@ services: write_compose_file(instance_dir, compose_content) elif tool_type.definition_type == "manifest": - # Manifest-based: build image and generate compose + # Manifest-based: generate compose only; image built lazily on start from src.models.tool_definition_manifest import ToolDefinitionManifest manifest_def = await session.get( @@ -1061,44 +1061,8 @@ services: deep_merge(dict(base_def.manifest), manifest) ) - # Determine home directory for path expansion - _home_dir = get_manifest_home_dir(manifest) - image_tag = compute_image_tag(tool_type.name, manifest) - # Build image during creation so start is fast - dockerfile = compile_dockerfile(manifest) - entrypoint = compile_entrypoint(manifest) - build_ctx = { - "Dockerfile": dockerfile, - ".headquarter/entrypoint.sh": entrypoint, - } - - returncode, stdout, stderr = await asyncio.to_thread( - build_image, - instance_dir=instance_dir, - dockerfile=dockerfile, - tag=image_tag, - build_context=build_ctx, - ) - - if returncode != 0: - logger.error( - "Failed to build image for manifest instance %s: %s", - instance_name, - stderr, - ) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to build Docker image: {stderr[:500]}", - ) - - logger.info( - "Built manifest image %s for instance %s", - image_tag, - instance_name, - ) - variables = { "IMAGE_TAG": image_tag, "INSTANCE_NAME": instance_name.lower(), diff --git a/apps/api/src/services/terminal_manager.py b/apps/api/src/services/terminal_manager.py index c767161..b9605db 100644 --- a/apps/api/src/services/terminal_manager.py +++ b/apps/api/src/services/terminal_manager.py @@ -91,14 +91,18 @@ class TerminalManager: """ try: async with SessionLocal() as db_session: - stmt = pg_insert(TerminalSessionModel).values( - id=uuid.UUID(session_id), - instance_id=instance_id, - name=name, - status="active", - created_at=datetime.now(timezone.utc), - last_activity_at=datetime.now(timezone.utc), - ).on_conflict_do_nothing(index_elements=["id"]) + stmt = ( + pg_insert(TerminalSessionModel) + .values( + id=uuid.UUID(session_id), + instance_id=instance_id, + name=name, + status="active", + created_at=datetime.now(timezone.utc), + last_activity_at=datetime.now(timezone.utc), + ) + .on_conflict_do_nothing(index_elements=["id"]) + ) await db_session.execute(stmt) await db_session.commit() logger.debug(