라이브코치 소스팩 새로고침 보강
This commit is contained in:
parent
84599bbaa2
commit
1e9f293fda
5 changed files with 182 additions and 8 deletions
|
|
@ -18,11 +18,12 @@ import hashlib
|
|||
from functools import lru_cache
|
||||
from typing import TYPE_CHECKING, Any, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from ..config import settings
|
||||
from ..engine_client import EngineClient, EngineError, EngineMessage, GenerateRequest, GenerateResponse
|
||||
from ..paths import repo_root, repo_path
|
||||
from ..session_read_model import StageLabel, stage_label_or_none
|
||||
from . import guardrail
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -77,12 +78,17 @@ class LiveCoachEvent(BaseModel):
|
|||
event_id: str
|
||||
session_id: str
|
||||
turn_seq: int
|
||||
stage: str
|
||||
stage: StageLabel | None = None
|
||||
created_at: str
|
||||
learner_text_excerpt: Optional[str] = None
|
||||
client_reply_excerpt: Optional[str] = None
|
||||
suggestion: LiveCoachSuggestion
|
||||
|
||||
@field_validator("stage", mode="before")
|
||||
@classmethod
|
||||
def _normalize_stage(cls, value: object) -> StageLabel | None:
|
||||
return stage_label_or_none(value)
|
||||
|
||||
|
||||
class LiveCoachInput(BaseModel):
|
||||
"""라이브 코칭 입력. raw text는 서비스 내부에서 마스킹 후 프롬프트에 쓴다."""
|
||||
|
|
@ -126,6 +132,8 @@ _ALLOWED_KB_KINDS = {
|
|||
"ko_context",
|
||||
"microskill",
|
||||
}
|
||||
LOCAL_SOURCE_PACK_CACHE_KEY = "repo:data/kb/live_coaching_workbook_0615.json+data/kb/live_coaching_sources/*.json"
|
||||
LOCAL_SOURCE_PACK_CACHE_LIFETIME = "api-process"
|
||||
|
||||
|
||||
def _configured_model(value: str | None) -> str | None:
|
||||
|
|
@ -135,6 +143,13 @@ def _configured_model(value: str | None) -> str | None:
|
|||
|
||||
@lru_cache(maxsize=1)
|
||||
def _local_source_entries() -> tuple[tuple[str, dict[str, Any]], ...]:
|
||||
"""Load repo-managed live-coach source packs.
|
||||
|
||||
Cache ownership: live turn generation keeps this process-local snapshot to
|
||||
avoid per-turn file IO. Admin/CLI source-pack sync is the invalidation
|
||||
boundary because that path explicitly compares repo files with kb.document.
|
||||
"""
|
||||
|
||||
entries: list[tuple[str, dict[str, Any]]] = []
|
||||
paths = [_WORKBOOK_PATH]
|
||||
if _SOURCE_DIR.exists():
|
||||
|
|
@ -164,6 +179,12 @@ def _local_source_entries() -> tuple[tuple[str, dict[str, Any]], ...]:
|
|||
)
|
||||
|
||||
|
||||
def clear_local_source_pack_cache() -> None:
|
||||
"""Invalidate the process-local source pack snapshot for admin/CLI refresh."""
|
||||
|
||||
_local_source_entries.cache_clear()
|
||||
|
||||
|
||||
def _local_source_payloads() -> list[dict[str, Any]]:
|
||||
return [payload for _, payload in _local_source_entries()]
|
||||
|
||||
|
|
@ -673,5 +694,6 @@ __all__ = [
|
|||
"LiveCoachInput",
|
||||
"LiveCoachSource",
|
||||
"LiveCoachSuggestion",
|
||||
"clear_local_source_pack_cache",
|
||||
"generate_live_coaching",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -40,9 +40,11 @@ class RepoSourcePackSyncResult:
|
|||
items: list[RepoSourcePackSyncItem]
|
||||
|
||||
|
||||
def build_repo_source_pack_manifest() -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
def build_repo_source_pack_manifest(*, refresh: bool = False) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
"""Return repo-managed source rows and RAG index payloads without DB access."""
|
||||
|
||||
if refresh:
|
||||
live_coach.clear_local_source_pack_cache()
|
||||
return live_coach.build_rag_source_rows(), live_coach.build_rag_index_payloads()
|
||||
|
||||
|
||||
|
|
@ -92,7 +94,12 @@ async def _upsert_source_rows(conn: Any, source_rows: list[dict[str, Any]]) -> N
|
|||
)
|
||||
|
||||
|
||||
async def sync_repo_source_packs(conn: Any, *, apply: bool = False) -> RepoSourcePackSyncResult:
|
||||
async def sync_repo_source_packs(
|
||||
conn: Any,
|
||||
*,
|
||||
apply: bool = False,
|
||||
refresh: bool = False,
|
||||
) -> RepoSourcePackSyncResult:
|
||||
"""Compare or apply repo-managed source packs against kb.document.
|
||||
|
||||
Dry-run mode still reads the active DB document row so it can report whether
|
||||
|
|
@ -100,7 +107,7 @@ async def sync_repo_source_packs(conn: Any, *, apply: bool = False) -> RepoSourc
|
|||
happen only when apply=True.
|
||||
"""
|
||||
|
||||
source_rows, index_payloads = build_repo_source_pack_manifest()
|
||||
source_rows, index_payloads = build_repo_source_pack_manifest(refresh=refresh)
|
||||
source_row_by_id = {row["source_id"]: row for row in source_rows}
|
||||
if apply:
|
||||
await _upsert_source_rows(conn, source_rows)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue