feat(infra): 開發環境分離 + BuildKit cache 修復 + circuit breaker 優化
Some checks failed
CD Pipeline / build-and-deploy (push) Successful in 6m52s
E2E Health Check / e2e-health (push) Successful in 17s
CD Pipeline (Dev) / build-and-deploy-dev (push) Failing after 9s

1. k8s/awoooi-dev/: 新建 dev namespace (01-05 配置)
   - Namespace + ResourceQuota (cpu 2/4, mem 4Gi/8Gi)
   - ConfigMap: ENVIRONMENT=dev, LOG_LEVEL=DEBUG, SHADOW_MODE=false
   - Deployment: 1 replica, NodePort 32344, image dev-latest
   - RBAC: awoooi-executor-dev ServiceAccount

2. .gitea/workflows/cd-dev.yaml: dev branch CD pipeline
   - 觸發: dev branch push
   - Build: --no-cache (防 cache poisoning)
   - Tag: dev-{sha} / dev-latest
   - Deploy: awoooi-dev namespace, health check 32344
   - Telegram: [DEV] 前綴通知

3. apps/api/Dockerfile: ARG CACHE_BUST=none (防 BuildKit cache 毒化)
   - deps 層 (pip install) 仍可 cache
   - src/ 和 models.json 層每次重建

4. .gitea/workflows/cd.yaml: 正式環境 API build 加入 CACHE_BUST=git_sha
   - 確保 models.json 等配置變更正確進入 image

5. apps/api/src/services/nvidia_provider.py: timeout 不計入 circuit breaker
   - TimeoutException → 只 log,不 record_failure()
   - 只有硬性錯誤 (auth/rate limit/exception) 才斷路

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-01 16:22:21 +08:00
parent c9c60c3a61
commit 9913f5dc6d
10 changed files with 424 additions and 5 deletions

View File

@@ -787,14 +787,16 @@ class NvidiaProvider:
return text, True, total_tokens, cost_usd
except httpx.TimeoutException as e:
self._circuit_breaker.record_failure()
# 2026-04-01 ogt: timeout 不計入 circuit breaker
# Nemo free tier 偶爾慢是正常的,下次請求仍應優先嘗試
# 只有硬性錯誤 (auth/rate limit) 才應斷路
NVIDIA_REQUESTS_TOTAL.labels(status="timeout", tool_name="chat").inc()
logger.warning("nvidia_chat_timeout", error=str(e))
return f"Timeout: {e}", False, 0, 0.0
except httpx.HTTPStatusError as e:
# 2026-03-31 ogt: 記錄完整響應體以診斷 400 錯誤
self._circuit_breaker.record_failure()
self._circuit_breaker.record_failure() # 硬性錯誤才斷路
NVIDIA_REQUESTS_TOTAL.labels(status="error", tool_name="chat").inc()
response_text = e.response.text if e.response else "No response body"
logger.warning(