Files
awoooi/apps/api/src/repositories/interfaces.py
Your Name b4055c5915
Some checks failed
Code Review / ai-code-review (push) Successful in 57s
run-migration / migrate (push) Failing after 44s
feat(embedding): ADR-110 升級 bge-m3:latest 1024 維向量
GCP-A (34.143.170.20) 無 nomic-embed-text,改用 bge-m3:latest(專用
多語言 embedding 模型),產生 1024 維向量。

變更:
- embedding_service.py: 加入 bge-m3:latest=1024 維到 MODEL_DIMENSIONS,
  預設模型改為 bge-m3:latest,更新文件說明
- playbook_embedding_repository.py + interfaces.py: 更新維度說明
- migrations/embedding_bge_m3_1024.sql: pgvector schema 遷移
  rag_chunks + playbook_embeddings vector(768) → vector(1024)
- scripts/reembed_bge_m3.py: 遷移後重新嵌入現有資料的 script

遷移步驟:
  1. 執行 embedding_bge_m3_1024.sql(清空現有 768 維向量,變更維度)
  2. 執行 python scripts/reembed_bge_m3.py 重新嵌入

2026-05-04 ogt + Claude Sonnet 4.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 11:18:20 +08:00

680 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Repository Interfaces - Protocol 定義
======================================
Phase 16 R3: Repository 層 Protocol 介面
設計原則:
- 使用 Python Protocol 實現 DI
- Service 層只依賴 Protocol不依賴具體實作
- 便於測試 (可注入 Mock Repository)
版本: v1.0
建立: 2026-03-26 (台北時區)
建立者: Claude Code (Phase 16 架構重構)
"""
from datetime import datetime
from typing import Protocol, runtime_checkable
from uuid import UUID
from src.models.approval import ApprovalRequest, ApprovalRequestCreate, ApprovalStatus
from src.models.incident import Incident
from src.models.knowledge import (
EntryStatus,
EntryType,
KnowledgeEntry,
KnowledgeEntryCreate,
)
from src.models.playbook import (
Playbook,
PlaybookStatus,
SymptomPattern,
)
@runtime_checkable
class IApprovalRepository(Protocol):
"""
Approval Repository Protocol
職責: ApprovalRecord CRUD 操作
實作: ApprovalDBRepository (PostgreSQL)
"""
async def create(self, data: dict) -> ApprovalRequest:
"""
建立新的 Approval
Phase 22 P2: 簽名修正為 dict (與實作一致)
2026-03-31 Claude Code (首席架構師 P2 修復)
Args:
data: ApprovalRecord 建立資料 dict
Returns:
ApprovalRequest
"""
...
async def get_by_id(self, approval_id: UUID) -> ApprovalRequest | None:
"""根據 ID 取得 Approval"""
...
async def get_pending(self) -> list[ApprovalRequest]:
"""取得所有待審核的 Approval"""
...
async def update_status(
self,
approval_id: UUID,
status: ApprovalStatus,
actor: str | None = None,
) -> ApprovalRequest | None:
"""更新 Approval 狀態"""
...
async def add_signature(
self,
approval_id: UUID,
signer: str,
decision: str,
comment: str | None = None,
) -> ApprovalRequest | None:
"""新增簽核"""
...
async def find_by_fingerprint(
self,
fingerprint: str,
) -> ApprovalRequest | None:
"""
根據指紋查找 Approval (告警收斂用)
Phase 22 P2: 補齊缺失 Protocol 方法
2026-03-31 Claude Code (首席架構師 P2 修復)
"""
...
async def increment_hit_count(
self,
approval_id: UUID,
) -> ApprovalRequest | None:
"""
增加 hit_count (告警收斂用)
Phase 22 P2: 補齊缺失 Protocol 方法
2026-03-31 Claude Code (首席架構師 P2 修復)
"""
...
@runtime_checkable
class IIncidentRepository(Protocol):
"""
Incident Repository Protocol
職責: IncidentRecord CRUD 操作
實作: IncidentDBRepository (PostgreSQL)
"""
async def create(self, incident: Incident) -> Incident:
"""建立新的 Incident"""
...
async def get_by_id(self, incident_id: str) -> Incident | None:
"""根據 ID 取得 Incident"""
...
async def get_active(self) -> list[Incident]:
"""取得所有活躍的 Incident"""
...
async def update(self, incident: Incident) -> Incident | None:
"""更新 Incident"""
...
async def upsert(self, incident: Incident) -> bool:
"""Upsert Incident (存在則更新,不存在則建立)"""
...
@runtime_checkable
class ITimelineRepository(Protocol):
"""
Timeline Repository Protocol
職責: TimelineEvent CRUD 操作
實作: TimelineDBRepository (PostgreSQL)
"""
async def add_event(
self,
approval_id: UUID,
event_type: str,
actor: str,
details: dict | None = None,
) -> bool:
"""新增 Timeline 事件"""
...
async def get_events(
self,
approval_id: UUID,
limit: int = 100,
) -> list[dict]:
"""取得 Approval 的 Timeline 事件"""
...
async def get_recent_events(
self,
limit: int = 50,
hours: int = 24,
) -> list[dict]:
"""取得最近的 Timeline 事件"""
...
@runtime_checkable
class IMetricsRepository(Protocol):
"""
Metrics Repository Protocol
職責: Metrics 相關 DB 查詢 (AI Success Rate)
實作: MetricsDBRepository (PostgreSQL)
版本: v1.0
建立: 2026-03-26 (台北時區)
建立者: Claude Code (Phase 17 技術債修復)
"""
async def get_ai_success_rate(
self,
hours: int = 24,
) -> tuple[float, int, int]:
"""
計算 AI 提案成功執行率
Args:
hours: 統計時間範圍 (小時)
Returns:
(success_rate_percent, executed_count, total_count)
"""
...
async def get_ai_success_trend(
self,
hours: int = 24,
points: int = 10,
) -> list[float]:
"""
取得 AI 成功率趨勢 (Sparkline 用)
Args:
hours: 統計時間範圍 (小時)
points: 趨勢點數量
Returns:
list[float]: 每小時成功率列表 (由舊到新)
"""
...
@runtime_checkable
class IKnowledgeRepository(Protocol):
"""
Knowledge Repository Protocol
職責: KnowledgeEntry CRUD 操作 (PostgreSQL)
實作: KnowledgeDBRepository
建立: 2026-04-02 (台北時區)
建立者: Claude Code (Knowledge Base Phase 1)
"""
async def create(self, data: KnowledgeEntryCreate) -> KnowledgeEntry:
"""建立知識條目"""
...
async def get_by_id(self, entry_id: str) -> KnowledgeEntry | None:
"""根據 ID 取得知識條目"""
...
async def update(self, entry_id: str, data: dict) -> KnowledgeEntry | None:
"""更新知識條目"""
...
async def delete(self, entry_id: str) -> bool:
"""軟刪除知識條目 (status → archived)"""
...
async def list_entries(
self,
category: str | None = None,
entry_type: EntryType | None = None,
status: EntryStatus | None = None,
tags: list[str] | None = None,
q: str | None = None,
limit: int = 20,
offset: int = 0,
) -> tuple[list[KnowledgeEntry], int]:
"""列出知識條目 (支援篩選)"""
...
async def get_categories(self) -> list[tuple[str, int]]:
"""取得分類統計 [(category, count)]"""
...
async def search(self, query: str, limit: int = 20) -> list[KnowledgeEntry]:
"""關鍵字搜尋 (title + content + tags)"""
...
async def increment_view_count(self, entry_id: str) -> bool:
"""view_count +1"""
...
async def save_embedding(self, entry_id: str, embedding: list[float]) -> bool:
"""儲存向量 embedding (1024 維, pgvector, bge-m3:latest)"""
...
async def semantic_search(
self,
query_embedding: list[float],
limit: int = 10,
threshold: float = 0.5,
) -> list[tuple["KnowledgeEntry", float]]:
"""語意搜尋 — cosine similarity, 回傳 (entry, score) 降序"""
...
async def list_unembedded_entries(
self,
) -> list[tuple[str, str, str]]:
"""列出尚未產生 embedding 的條目 [(id, title, content)]"""
...
@runtime_checkable
class IPlaybookRepository(Protocol):
"""
Playbook Repository Protocol
職責: Playbook CRUD 操作 (PostgreSQL + Redis 雙層)
實作: PlaybookRepository
版本: v1.0
建立: 2026-03-26 (台北時區)
建立者: Claude Code (#7 Playbook 萃取)
"""
async def create(self, playbook: Playbook) -> Playbook:
"""建立新的 Playbook"""
...
async def get_by_id(self, playbook_id: str) -> Playbook | None:
"""根據 ID 取得 Playbook"""
...
async def update(self, playbook: Playbook) -> Playbook | None:
"""更新 Playbook"""
...
async def delete(self, playbook_id: str) -> bool:
"""刪除 Playbook (軟刪除 → DEPRECATED)"""
...
async def list_playbooks(
self,
status: PlaybookStatus | None = None,
tags: list[str] | None = None,
limit: int = 20,
offset: int = 0,
) -> tuple[list[Playbook], int]:
"""
列出 Playbooks
Returns:
(items, total_count)
"""
...
async def find_by_symptoms(
self,
symptoms: SymptomPattern,
top_k: int = 5,
min_similarity: float = 0.5,
) -> list[tuple[Playbook, float]]:
"""
根據症狀模式找相似 Playbook
Returns:
list[(Playbook, similarity_score)]
"""
...
async def update_stats(
self,
playbook_id: str,
success: bool,
) -> bool:
"""更新執行統計"""
...
async def find_by_source_incident(
self,
incident_id: str,
) -> list[Playbook]:
"""
根據來源 Incident ID 找 Playbook
2026-03-30 Claude Code: Learning Service 信心度調整用
尋找 source_incident_ids 包含此 incident_id 的 Playbooks
"""
...
async def adjust_confidence(
self,
playbook_id: str,
delta: float,
reason: str,
) -> Playbook | None:
"""
調整 Playbook 信心度
2026-03-30 Claude Code: Learning Service 信心度調整用
Args:
playbook_id: Playbook ID
delta: 調整量 (+/- 0.0~1.0)
reason: 調整原因 (審計用)
Returns:
更新後的 Playbook或 None (如果不存在)
"""
...
@runtime_checkable
class ILearningRepository(Protocol):
"""
Learning Repository Protocol
職責: 學習數據持久化 (Redis)
實作: LearningRepository
版本: v1.0
建立: 2026-03-29 (台北時區)
建立者: Claude Code (Phase D-G P0 修正)
設計原則:
- Service 層不直接存取 Redis
- 透過 Repository 進行資料存取
- 符合 leWOOOgo 積木化原則
"""
async def record_repair(
self,
anomaly_key: str,
repair_action: str,
success: bool,
root_cause: str | None = None,
fix_description: str | None = None,
execution_time_seconds: float | None = None,
) -> bool:
"""記錄修復結果"""
...
async def get_repair_stats(
self,
anomaly_key: str,
repair_action: str,
) -> dict:
"""取得修復統計 (成功率、執行次數)"""
...
async def get_all_repair_stats(
self,
anomaly_key: str,
) -> dict[str, dict]:
"""取得所有修復動作的統計"""
...
async def get_repair_history(
self,
anomaly_key: str,
repair_action: str,
limit: int = 20,
) -> list[dict]:
"""取得修復歷史記錄"""
...
async def get_learning_summary(
self,
anomaly_key: str,
) -> dict:
"""取得學習摘要"""
...
@runtime_checkable
class IEmbeddingCacheRepository(Protocol):
"""
Embedding Cache Repository Protocol
職責: Playbook 向量快取 (Redis)
實作: EmbeddingCacheRepository
2026-03-27 ogt: 模組化改造 (P1 違規修復)
"""
async def store(
self,
playbook_id: str,
embedding: list[float],
metadata: dict | None = None,
) -> bool:
"""儲存 Playbook 向量"""
...
async def get(self, playbook_id: str) -> list[float] | None:
"""取得 Playbook 向量"""
...
async def get_all(self) -> dict[str, list[float]]:
"""取得所有 Playbook 向量"""
...
async def remove(self, playbook_id: str) -> bool:
"""移除 Playbook 向量"""
...
@runtime_checkable
class IK8sRepository(Protocol):
"""
K8s Repository Protocol
職責: K8s API 操作 (Pods/Deployments/Events)
實作: K8sRepository (kubernetes_asyncio)
版本: v1.0
建立: 2026-03-30 (台北時區)
建立者: Claude Code (首席架構師 P1 改進)
設計原則:
- Service 層不直接呼叫 kubernetes_asyncio
- 透過 Repository 進行 K8s 操作
- 符合 leWOOOgo 積木化原則
"""
async def list_pods(
self,
namespace: str = "awoooi",
) -> list[dict]:
"""
列出 Namespace 下的 Pods
Returns:
list[{name, phase, ready, restarts, age}]
"""
...
async def list_deployments(
self,
namespace: str = "awoooi",
) -> list[dict]:
"""
列出 Namespace 下的 Deployments
Returns:
list[{name, ready_replicas, replicas, available}]
"""
...
async def get_pod_status_summary(
self,
namespace: str = "awoooi",
) -> dict:
"""
取得 Pod 狀態摘要
Returns:
{total, running, pending, failed, problem_pods: [...]}
"""
...
async def is_available(self) -> bool:
"""
檢查 K8s API 是否可用
Returns:
True 如果 K8s 連線正常
"""
...
# =============================================================================
# Phase 18: Failure Auto-Repair Loop Protocols
# =============================================================================
# 2026-03-31 Claude Code (統帥批准)
# =============================================================================
@runtime_checkable
class IFailureWatcher(Protocol):
"""
Failure Watcher Protocol
職責: 監聽失敗事件並觸發修復流程
實作: FailureWatcherService
版本: v1.0
建立: 2026-03-31 (台北時區)
建立者: Claude Code (Phase 18 失敗自動修復)
"""
async def process_failure(
self,
audit_log_id: str,
failure_data: dict,
) -> dict:
"""
處理單一失敗事件
Args:
audit_log_id: AuditLog ID
failure_data: 失敗詳情
Returns:
{
"repair_attempted": bool,
"repair_result": str | None,
"risk_level": str, # LOW/MEDIUM/CRITICAL
"next_action": str, # auto_repair/await_approval/escalate
}
"""
...
async def analyze_failure(
self,
error_message: str,
operation_type: str,
target_resource: str,
) -> dict:
"""
AI 分析失敗原因
Args:
error_message: 錯誤訊息
operation_type: 操作類型
target_resource: 目標資源
Returns:
{
"classification": str, # TIMEOUT/K8S_ERROR/NETWORK_ERROR/PERMISSION_DENIED
"root_cause": str,
"suggested_repair": str,
"risk_level": str,
"confidence": float,
}
"""
...
async def execute_auto_repair(
self,
audit_log_id: str,
repair_strategy: str,
failure_data: dict | None = None,
) -> tuple[bool, str]:
"""
執行自動修復 (僅限 LOW 風險)
Phase 18.3: K8s Executor 整合
2026-03-31 Claude Code (統帥批准)
Args:
audit_log_id: 原始失敗的 AuditLog ID
repair_strategy: 修復策略
failure_data: 失敗詳情 (含 target_resource, namespace)
Returns:
(success, result_message)
"""
...
@runtime_checkable
class ITrustRepository(Protocol):
"""
Trust Repository Protocol
職責: TrustRecord 持久化 (PostgreSQL)
實作: TrustRepository
ADR-088: TrustScoreManager 持久化層
2026-04-17 ogt + Claude Sonnet 4.6(亞太): Phase 4 信任持久化
"""
async def upsert(
self,
action_pattern: str,
score: int,
total_approvals: int,
total_rejections: int,
last_approval_by: str | None = None,
last_approval_at: datetime | None = None,
last_rejection_by: str | None = None,
last_rejection_at: datetime | None = None,
) -> bool:
"""INSERT or UPDATE trust record (upsert by action_pattern)"""
...
async def load_all(self) -> list[dict]:
"""
載入所有 trust records 供啟動 warm-up
Returns:
list[{action_pattern, score, total_approvals, total_rejections,
last_approval_by, last_approval_at, last_rejection_by, last_rejection_at}]
"""
...