diff --git a/cmd/cognitive-worker/main.go b/cmd/cognitive-worker/main.go index 6ecc6e6..ae53a85 100644 --- a/cmd/cognitive-worker/main.go +++ b/cmd/cognitive-worker/main.go @@ -97,7 +97,8 @@ func main() { if gpuErr != nil { log.Printf("optional GPU provider disabled: %v", gpuErr) } else if gpuConfigured { - gpuAdapter, adapterErr := models.NewOpenAICompatibleLocalAdapterWithAPIKey(gpuURL, gpuKey, nil) + gpuTimeout := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second) + gpuAdapter, adapterErr := models.NewOpenAICompatibleLocalAdapterWithAPIKeyAndTimeout(gpuURL, gpuKey, nil, gpuTimeout) if adapterErr != nil { log.Printf("optional GPU provider disabled: %v", adapterErr) } else { diff --git a/cmd/cognitive-worker/main_test.go b/cmd/cognitive-worker/main_test.go index 4ccce5a..eb61dea 100644 --- a/cmd/cognitive-worker/main_test.go +++ b/cmd/cognitive-worker/main_test.go @@ -4,6 +4,7 @@ import ( "os" "path/filepath" "testing" + "time" ) func TestLifeboatProviderIsOptionalAndLocalAdapterBacked(t *testing.T) { @@ -19,6 +20,21 @@ func TestLifeboatProviderIsOptionalAndLocalAdapterBacked(t *testing.T) { } } +func TestGPUProviderTimeoutUsesBoundedConfigurableDefault(t *testing.T) { + t.Setenv("LOCAL_GPU_PROVIDER_TIMEOUT", "") + if got := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second); got != 60*time.Second { + t.Fatalf("default GPU timeout=%s", got) + } + t.Setenv("LOCAL_GPU_PROVIDER_TIMEOUT", "31s") + if got := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second); got != 31*time.Second { + t.Fatalf("configured GPU timeout=%s", got) + } + t.Setenv("LOCAL_GPU_PROVIDER_TIMEOUT", "invalid") + if got := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second); got != 60*time.Second { + t.Fatalf("invalid GPU timeout=%s", got) + } +} + func TestLoadLocalCPUProviderAPIKeyUsesProtectedFileReference(t *testing.T) { t.Setenv("LOCAL_CPU_PROVIDER_API_KEY", "") _ = os.Unsetenv("LOCAL_CPU_PROVIDER_API_KEY") diff --git a/deploy/docker-compose.prod.yml b/deploy/docker-compose.prod.yml index 038df91..a425a94 100644 --- a/deploy/docker-compose.prod.yml +++ b/deploy/docker-compose.prod.yml @@ -254,6 +254,7 @@ services: LOCAL_MEDIUM_PROVIDER_URL: ${LOCAL_MEDIUM_PROVIDER_URL:-http://llm-local-api:8080/v1} LOCAL_MEDIUM_PROVIDER_API_KEY_FILE: ${LOCAL_MEDIUM_PROVIDER_API_KEY_FILE:-} LOCAL_MEDIUM_PROVIDER_CREDENTIAL_REF: ${LOCAL_MEDIUM_PROVIDER_CREDENTIAL_REF:-credential://domovoy/local-medium/provider-api-key/v1} + LOCAL_GPU_PROVIDER_TIMEOUT: ${LOCAL_GPU_PROVIDER_TIMEOUT:-60s} LOCAL_LIFEBOAT_PROVIDER_URL: http://cognitive-lifeboat:8086/v1 COGNITIVE_OWNER_PRINCIPAL_ID: domovoy-creator-principal COGNITIVE_ALLOW_CANARY: "true" diff --git a/dev/ACTIVE_TASK.json b/dev/ACTIVE_TASK.json index 9854bd8..0daa400 100644 --- a/dev/ACTIVE_TASK.json +++ b/dev/ACTIVE_TASK.json @@ -1,5 +1,5 @@ { - "task_file": "dev/tasks/DEV-000267-refresh-gpu-and-ui-gap-evidence.json", - "task_id": "DEV-000267", + "task_file": "dev/tasks/DEV-000268-configurable-gpu-qualification-timeout.json", + "task_id": "DEV-000268", "task_revision": 1 } diff --git a/dev/tasks/DEV-000268-configurable-gpu-qualification-timeout.json b/dev/tasks/DEV-000268-configurable-gpu-qualification-timeout.json new file mode 100644 index 0000000..7883169 --- /dev/null +++ b/dev/tasks/DEV-000268-configurable-gpu-qualification-timeout.json @@ -0,0 +1,23 @@ +{ + "task_id": "DEV-000268", + "task_revision": 1, + "architecture_revision": "ADR-0018", + "base_repo_revision": "3ca7ffa", + "risk_tier": "E", + "intent": "Make the GPU cognitive provider invocation deadline configurable and bounded so large local models are not rejected by an accidental ten-second cold-start limit.", + "requirement_ids": ["model fleet/failover", "quality separated from reachability", "hardware-neutral routing"], + "allowed_change_scope": {"modules": ["cognitive-worker", "deployment-governance"], "paths": ["cmd/cognitive-worker/**", "deploy/docker-compose.prod.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000268-configurable-gpu-qualification-timeout.json", "docs/evidence/**", "docs/PROJECT_STATUS.md"]}, + "forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "LLM-local/**"]}, + "allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0}, + "contracts": {"consume": ["existing OpenAI-compatible local provider adapter", "existing qualification route"], "may_change": ["deployment timeout configuration only"]}, + "semantic_owner": "cognitive-worker invokes provider; capability remains qualification and promotion owner", + "canonical_state": ["existing qualification observations"], + "state_transitions": ["provider timeout window changes; no qualification or activation transition is inferred"], + "side_effects_and_privileges": ["bounded outbound GPU provider request through existing credential boundary"], + "persistence_and_migrations": ["none"], + "failure_retry_restart": ["timeout remains an unrated/unavailable observation; CPU/LIFEBOAT fallback remains authoritative"], + "assumptions": ["60 seconds is a bounded cold-start window and qualification models still run in parallel", "no model is promoted automatically"], + "acceptance_scenarios": ["GPU adapter uses configured bounded timeout", "default is 60 seconds", "qualification remains unrated on timeout", "routing does not infer intelligence from GPU"], + "required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "docker compose config", "git diff --check", "production health smoke", "one Domovoy-owned GPU qualification probe"], + "terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"] +}