feat: dashboard SSE streaming progress (#1007) #1586
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. | |
| # SPDX-License-Identifier: MIT | |
| # This workflow tests the GAIA OpenAI-compatible API server functionality | |
| # Tests include: Chat completions, streaming SSE, model listing, error handling | |
| name: API Server Tests | |
| on: | |
| workflow_call: | |
| push: | |
| branches: ["main"] | |
| paths: | |
| - 'src/gaia/api/**' | |
| - 'src/gaia/agents/base/**' | |
| - 'src/gaia/llm/**' | |
| - 'tests/test_api.py' | |
| - 'setup.py' | |
| - '.github/workflows/test_api.yml' | |
| pull_request: | |
| branches: ["main"] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| paths: | |
| - 'src/gaia/api/**' | |
| - 'src/gaia/agents/base/**' | |
| - 'src/gaia/llm/**' | |
| - 'tests/test_api.py' | |
| - 'setup.py' | |
| - '.github/workflows/test_api.yml' | |
| merge_group: | |
| workflow_dispatch: | |
| # Cancel in-progress runs when a new run is triggered | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| test-api: | |
| name: API Tests | |
| runs-on: ${{ contains(github.event.pull_request.labels.*.name, 'stx-test') && 'stx-test' || 'stx' }} | |
| timeout-minutes: 30 | |
| if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Python environment | |
| uses: ./.github/actions/setup-venv | |
| with: | |
| python-version: '3.12' | |
| install-package: '.[dev,api]' | |
| - name: Install additional test dependencies | |
| shell: powershell | |
| run: | | |
| uv pip install pytest pytest-timeout requests --python .venv\Scripts\python.exe | |
| - name: Install Lemonade Server | |
| uses: ./.github/actions/install-lemonade | |
| - name: Start Lemonade Server and Run Tests | |
| shell: powershell | |
| run: | | |
| # Set console to UTF-8 for Unicode support | |
| [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 | |
| [Console]::InputEncoding = [System.Text.Encoding]::UTF8 | |
| $OutputEncoding = [System.Text.Encoding]::UTF8 | |
| $env:PYTHONIOENCODING = "utf-8" | |
| $env:PYTHONUTF8 = "1" | |
| chcp 65001 | Out-Null | |
| try { | |
| Write-Host "Starting Lemonade server..." | |
| $serverJob = Start-Job -ScriptBlock { | |
| # Workaround for Issue #612: Disable Vulkan cooperative matrix optimization | |
| $env:GGML_VK_DISABLE_COOPMAT = "1" | |
| & lemonade-server serve --ctx-size 8192 --host localhost --port 13305 --no-tray 2>&1 | |
| } | |
| Write-Host "Started Lemonade server job with ID: $($serverJob.Id)" | |
| $env:LEMONADE_JOB_ID = $serverJob.Id | |
| # Wait for server to be ready | |
| Write-Host "Waiting for Lemonade server to start..." | |
| $maxWaitTime = 60 | |
| $waitTime = 0 | |
| $serverReady = $false | |
| while ($waitTime -lt $maxWaitTime -and -not $serverReady) { | |
| Start-Sleep -Seconds 2 | |
| $waitTime += 2 | |
| try { | |
| $response = Invoke-RestMethod -Uri "http://localhost:13305/api/v1/health" -Method GET -TimeoutSec 5 | |
| Write-Host "[OK] Lemonade server is ready" | |
| Write-Host "Health response: $($response | ConvertTo-Json -Compress)" | |
| $serverReady = $true | |
| } catch { | |
| Write-Host "Waiting... ($waitTime/$maxWaitTime seconds)" | |
| } | |
| } | |
| if (-not $serverReady) { | |
| Write-Host "[ERROR] Server health check failed after $maxWaitTime seconds" | |
| throw "Server failed to start" | |
| } | |
| # Pull required model for API tests | |
| Write-Host "`n=== Pulling Required Models ===" | |
| Write-Host "Pulling Qwen3-0.6B-GGUF..." | |
| try { | |
| $body = @{ model_name = "Qwen3-0.6B-GGUF" } | ConvertTo-Json | |
| $response = Invoke-RestMethod -Uri "http://localhost:13305/api/v1/pull" ` | |
| -Method POST -ContentType "application/json" -Body $body -TimeoutSec 600 | |
| Write-Host " [OK] Qwen3-0.6B-GGUF pull initiated" | |
| } catch { | |
| Write-Host " [WARN] Pull may have failed: $($_.Exception.Message)" | |
| } | |
| # Wait for model to be available | |
| Write-Host "Waiting 5 seconds for model files to sync..." | |
| Start-Sleep -Seconds 5 | |
| # Load the model into Lemonade (required for inference) | |
| Write-Host "`n=== Loading LLM Model ===" | |
| try { | |
| $loadRequest = @{ model_name = "Qwen3-0.6B-GGUF" } | ConvertTo-Json | |
| Write-Host "Loading model: Qwen3-0.6B-GGUF" | |
| $loadResponse = Invoke-RestMethod -Uri "http://localhost:13305/api/v1/load" ` | |
| -Method POST -Body $loadRequest -ContentType "application/json" -TimeoutSec 120 | |
| Write-Host "[OK] Model loaded successfully: $($loadResponse | ConvertTo-Json -Compress)" | |
| } catch { | |
| Write-Host "[ERROR] Model load failed: $($_.Exception.Message)" | |
| if ($_.ErrorDetails) { | |
| Write-Host "Error details: $($_.ErrorDetails.Message)" | |
| } | |
| } | |
| # Wait for llamacpp backend initialization | |
| Write-Host "Waiting 10 seconds for llamacpp backend initialization..." | |
| Start-Sleep -Seconds 10 | |
| # Verify models | |
| try { | |
| $models = Invoke-RestMethod -Uri "http://localhost:13305/api/v1/models" -Method GET | |
| Write-Host "`n[OK] Available models:" | |
| $models.data | ForEach-Object { Write-Host " - $($_.id)" } | |
| } catch { | |
| Write-Host "[WARN] Could not list models: $($_.Exception.Message)" | |
| } | |
| # Start API server using gaia CLI (runs on default port 8080) | |
| Write-Host "`n=== Starting GAIA API Server ===" | |
| $apiJob = Start-Job -ScriptBlock { | |
| Set-Location $using:PWD | |
| & .\.venv\Scripts\Activate.ps1 | |
| $env:AGENT_ROUTING_MODEL = "Qwen3-0.6B-GGUF" | |
| & gaia api start 2>&1 | |
| } | |
| Write-Host "Started API server job with ID: $($apiJob.Id)" | |
| $env:API_JOB_ID = $apiJob.Id | |
| # Wait for API server to start | |
| Write-Host "Waiting for API server to start..." | |
| $maxWaitTime = 30 | |
| $waitTime = 0 | |
| $apiReady = $false | |
| while ($waitTime -lt $maxWaitTime -and -not $apiReady) { | |
| Start-Sleep -Seconds 2 | |
| $waitTime += 2 | |
| try { | |
| $response = Invoke-RestMethod -Uri "http://localhost:8080/health" -Method GET -TimeoutSec 5 | |
| Write-Host "[OK] API server is running and healthy" | |
| $apiReady = $true | |
| } catch { | |
| Write-Host "Waiting for API server... ($waitTime/$maxWaitTime seconds)" | |
| } | |
| } | |
| if (-not $apiReady) { | |
| Write-Host "[ERROR] API server failed to start after $maxWaitTime seconds" | |
| throw "API server failed to start" | |
| } | |
| # Run tests in same session while servers are running | |
| Write-Host "`n=== Running API Integration Tests ===" | |
| $env:CI = "true" | |
| uv run pytest tests/test_api.py -v --tb=short --capture=no | |
| $testExitCode = $LASTEXITCODE | |
| Write-Host "`nTests completed with exit code: $testExitCode" | |
| if ($testExitCode -ne 0) { | |
| throw "Tests failed with exit code $testExitCode" | |
| } | |
| } finally { | |
| # Always cleanup servers | |
| Write-Host "`n=== Stopping Servers ===" | |
| if ($env:API_JOB_ID) { | |
| Write-Host "Stopping API server..." | |
| Stop-Job -Id $env:API_JOB_ID -ErrorAction SilentlyContinue | |
| Remove-Job -Id $env:API_JOB_ID -ErrorAction SilentlyContinue | |
| Write-Host "[OK] API server stopped" | |
| } | |
| if ($env:LEMONADE_JOB_ID) { | |
| Write-Host "Stopping Lemonade server..." | |
| Stop-Job -Id $env:LEMONADE_JOB_ID -ErrorAction SilentlyContinue | |
| Remove-Job -Id $env:LEMONADE_JOB_ID -ErrorAction SilentlyContinue | |
| Write-Host "[OK] Lemonade server stopped" | |
| } | |
| } | |
| - name: Upload API Server Logs | |
| if: failure() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: api-server-logs | |
| path: | | |
| gaia.api.log | |
| gaia_api.log | |
| *.log |