From 3dfddcac6ff2b9101162308258834541bc174ea8 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Wed, 15 Jul 2026 21:31:30 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A0=84=20=EC=A0=80=EC=9E=A5=EC=86=8C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81=EA=B3=BC=20SSOT=20?= =?UTF-8?q?=EC=A0=95=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + README.md | 4 +- apps/api/app/auth_sessions.py | 274 +- apps/api/app/config.py | 17 + apps/api/app/engine_client.py | 4 +- apps/api/app/persona_repository.py | 228 +- apps/api/app/routes/admin.py | 200 +- apps/api/app/routes/auth.py | 2 +- apps/api/app/routes/kb.py | 94 +- apps/api/app/routes/personas.py | 39 +- apps/api/app/routes/sessions.py | 318 +- apps/api/app/routes/users.py | 89 +- apps/api/app/routes/voice.py | 317 +- apps/api/app/runtime_schema.py | 126 + apps/api/app/services/dataset_export.py | 184 +- apps/api/app/services/evaluation_contract.py | 25 + apps/api/app/services/evaluator.py | 173 +- apps/api/app/services/guardrail.py | 3 + apps/api/app/services/live_coach.py | 46 +- apps/api/app/services/llm_audit.py | 47 + apps/api/app/services/memory.py | 2 +- apps/api/app/services/notifications.py | 179 +- apps/api/app/services/orchestrator.py | 33 +- apps/api/app/services/persona.py | 5 +- apps/api/app/services/phase3_kpi_export.py | 2 +- apps/api/app/services/session_metrics.py | 31 +- apps/api/app/services/tabular_ingest.py | 138 + apps/api/app/session_persistence.py | 497 ++-- apps/api/app/session_read_model.py | 90 +- apps/api/app/store.py | 5 +- apps/api/app/taxonomy.py | 56 +- apps/api/app/test_client_reply_quality.py | 22 + apps/api/app/test_dataset_export.py | 104 +- apps/api/app/test_evaluation_persistence.py | 10 +- apps/api/app/test_live_coach_privacy.py | 54 +- apps/api/app/test_rbac_idor.py | 7 +- apps/api/app/test_runtime_policy.py | 186 +- apps/api/app/test_runtime_schema_ssot.py | 57 + apps/api/app/test_session_progress.py | 80 + apps/api/app/test_session_time_and_goals.py | 240 ++ apps/api/app/test_session_turn_persistence.py | 562 +++- apps/api/app/test_tabular_ingest.py | 99 + apps/api/app/test_voice_ws.py | 672 +++-- apps/api/app/turn_runtime.py | 57 +- apps/api/engine_gateway/gateway.py | 5 +- apps/api/engine_gateway/test_gateway_model.py | 4 +- apps/api/requirements.txt | 2 + apps/web/e2e/README.md | 12 + apps/web/e2e/admin.spec.ts | 263 +- apps/web/e2e/auth-visual.spec.ts | 117 + apps/web/e2e/avatar-expression.spec.ts | 77 +- apps/web/e2e/layout-visual-gate.spec.ts | 360 ++- apps/web/e2e/learner.spec.ts | 13 +- apps/web/e2e/public-admin-visual.spec.ts | 223 ++ apps/web/e2e/readiness.spec.ts | 12 +- apps/web/e2e/session-mvp.spec.ts | 9 +- apps/web/e2e/session-persistence.spec.ts | 23 +- apps/web/e2e/session-review.spec.ts | 105 +- apps/web/e2e/teacher.spec.ts | 118 +- apps/web/knip.json | 19 + apps/web/package-lock.json | 1410 +++++++-- apps/web/package.json | 13 +- apps/web/playwright.config.ts | 3 + .../background-dark-corner.png | Bin 0 -> 218000 bytes .../background-dark-sidebar.png | Bin 0 -> 1761725 bytes .../background-light-corner.png | Bin 0 -> 146490 bytes .../background-light-sidebar.png | Bin 0 -> 1168196 bytes .../card-flowing-ribbon-light.png | Bin 0 -> 99718 bytes .../learner-dashboard/card-flowing-ribbon.png | Bin 0 -> 111468 bytes .../card-folded-ribbon-light.png | Bin 0 -> 110707 bytes .../learner-dashboard/card-folded-ribbon.png | Bin 0 -> 134200 bytes .../card-leaf-sprig-light.png | Bin 0 -> 99413 bytes .../learner-dashboard/card-leaf-sprig.png | Bin 0 -> 104739 bytes .../card-orbital-ring-light.png | Bin 0 -> 122470 bytes .../learner-dashboard/card-orbital-ring.png | Bin 0 -> 138311 bytes .../card-paired-leaves-light.png | Bin 0 -> 106542 bytes .../learner-dashboard/card-paired-leaves.png | Bin 0 -> 119290 bytes apps/web/scripts/check-design-ssot.mjs | 207 ++ apps/web/scripts/run-e2e.mjs | 56 + apps/web/src/App.tsx | 139 +- apps/web/src/components/auth/AuthShell.tsx | 31 + apps/web/src/components/auth/auth-shell.css | 85 + .../src/components/avatar/ClientAvatar.tsx | 37 +- apps/web/src/components/avatar/RasterBust.tsx | 32 +- apps/web/src/components/avatar/persona.ts | 6 - apps/web/src/components/shell/AppShell.tsx | 15 +- apps/web/src/components/shell/Sidebar.tsx | 1 + apps/web/src/components/shell/Topbar.tsx | 16 +- apps/web/src/components/shell/shell.css | 426 ++- apps/web/src/components/ui/Card.tsx | 13 +- apps/web/src/components/ui/EmptyState.tsx | 26 + apps/web/src/components/ui/Panel.tsx | 13 +- apps/web/src/components/ui/SectionHead.tsx | 51 - apps/web/src/components/ui/StatLine.tsx | 36 - apps/web/src/components/ui/Surface.tsx | 52 + apps/web/src/components/ui/index.ts | 10 +- apps/web/src/components/ui/ui.css | 67 +- apps/web/src/lib/api.gen.ts | 175 +- apps/web/src/lib/api.ts | 33 +- apps/web/src/lib/format.ts | 56 + apps/web/src/lib/personaViewModel.ts | 141 +- apps/web/src/lib/runtimeDiagnostics.ts | 9 + apps/web/src/lib/theme.ts | 15 + apps/web/src/lib/useTheme.ts | 17 + apps/web/src/main.tsx | 1 + apps/web/src/pages/Admin.tsx | 1861 +++++++----- apps/web/src/pages/AdminAi.tsx | 504 ++++ apps/web/src/pages/AvatarPreview.tsx | 32 +- apps/web/src/pages/LearnerHome.tsx | 1164 +++++--- apps/web/src/pages/Login.tsx | 5 +- apps/web/src/pages/Onboarding.tsx | 8 +- apps/web/src/pages/PendingApproval.tsx | 8 +- apps/web/src/pages/PersonaStudio.tsx | 2548 ++++++++++++----- apps/web/src/pages/Professor.tsx | 1087 ++++--- apps/web/src/pages/Session.tsx | 1151 ++++---- apps/web/src/pages/SessionReview.tsx | 969 ++++--- apps/web/src/pages/Settings.tsx | 77 +- apps/web/src/pages/admin/admin-ai.css | 766 +++++ apps/web/src/pages/admin/admin-console.css | 396 ++- apps/web/src/pages/admin/dataNormalization.ts | 428 +++ apps/web/src/pages/learner-home.css | 619 ++++ apps/web/src/pages/learner-home/model.ts | 222 ++ apps/web/src/pages/login/LoginPanel.tsx | 17 +- apps/web/src/pages/login/login.css | 110 +- apps/web/src/pages/onboarding.css | 3 +- apps/web/src/pages/pending-approval.css | 6 +- apps/web/src/pages/persona-studio.css | 430 +++ apps/web/src/pages/persona-studio/model.ts | 552 ++++ apps/web/src/pages/session-review/model.ts | 168 ++ .../pages/session-review/session-review.css | 412 +-- apps/web/src/pages/session/session.css | 624 +++- apps/web/src/pages/session/voiceCapture.ts | 466 +++ apps/web/src/pages/settings/settings.css | 220 +- apps/web/src/pages/settings/useTheme.ts | 36 - apps/web/src/styles/tokens.css | 158 +- .../dbt_crisis_commitment.json | 67 + .../gestalt_session_moves.json | 53 + .../parent_intake_moves.json | 53 + docs/DESIGN_CONCEPT.md | 32 + docs/HANDOFF.md | 8 +- docs/README.md | 9 +- docs/TODO.md | 33 +- docs/design/darkmode-2026-07-14/README.md | 33 + .../design/darkmode-2026-07-14/darkmode-a.png | Bin 0 -> 1465834 bytes .../design/darkmode-2026-07-14/darkmode-b.png | Bin 0 -> 1457379 bytes .../design/darkmode-2026-07-14/darkmode-c.png | Bin 0 -> 1396758 bytes .../darkmode-2026-07-14/implemented-learn.png | Bin 0 -> 255064 bytes .../implemented-session.png | Bin 0 -> 488998 bytes .../review-redesigned-2026-07-15.png | Bin 0 -> 658534 bytes docs/dev_dashboard.html | 53 +- docs/guides/architecture.md | 54 +- docs/guides/collaboration-for-researchers.md | 185 ++ docs/guides/local-development.md | 4 +- docs/guides/source-docs-and-gaps.md | 4 +- docs/guides/testing.md | 61 +- docs/ops/backlog-2026-06-26.md | 2 +- docs/ops/handoff-avatar-seoyeon-2026-06-27.md | 32 +- .../ops/hanshin-meeting-actions-2026-07-13.md | 70 + .../persona-dashboard-layout-guideline.md | 12 +- docs/ops/pilot-usage-guide-draft.md | 69 + docs/ops/refactor-governance-2026-07-15.md | 95 + docs/ops/server-migration-runbook-draft.md | 191 ++ ...ession-progress-gauge-design-2026-07-14.md | 48 + .../ops/tagged-data-utilization-2026-07-15.md | 51 + docs/taxonomy.md | 48 +- infra/db/init/02_schema.sql | 4 + infra/db/init/04_audit_eval_rls.sql | 23 + infra/db/init/05_runtime_auth.sql | 7 + infra/db/init/06_session_evaluation.sql | 33 + scripts/export-recursive-dataset.py | 92 +- scripts/load-test-sessions.py | 175 ++ scripts/start-public-runtime.ps1 | 7 + scripts/watch-public-runtime.ps1 | 42 +- 173 files changed, 19679 insertions(+), 6952 deletions(-) create mode 100644 apps/api/app/runtime_schema.py create mode 100644 apps/api/app/services/evaluation_contract.py create mode 100644 apps/api/app/services/llm_audit.py create mode 100644 apps/api/app/services/tabular_ingest.py create mode 100644 apps/api/app/test_runtime_schema_ssot.py create mode 100644 apps/api/app/test_session_progress.py create mode 100644 apps/api/app/test_session_time_and_goals.py create mode 100644 apps/api/app/test_tabular_ingest.py create mode 100644 apps/web/e2e/auth-visual.spec.ts create mode 100644 apps/web/e2e/public-admin-visual.spec.ts create mode 100644 apps/web/knip.json create mode 100644 apps/web/public/design-elements/learner-dashboard/background-dark-corner.png create mode 100644 apps/web/public/design-elements/learner-dashboard/background-dark-sidebar.png create mode 100644 apps/web/public/design-elements/learner-dashboard/background-light-corner.png create mode 100644 apps/web/public/design-elements/learner-dashboard/background-light-sidebar.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-flowing-ribbon-light.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-flowing-ribbon.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-folded-ribbon-light.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-folded-ribbon.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-leaf-sprig-light.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-leaf-sprig.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-orbital-ring-light.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-orbital-ring.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-paired-leaves-light.png create mode 100644 apps/web/public/design-elements/learner-dashboard/card-paired-leaves.png create mode 100644 apps/web/scripts/check-design-ssot.mjs create mode 100644 apps/web/scripts/run-e2e.mjs create mode 100644 apps/web/src/components/auth/AuthShell.tsx create mode 100644 apps/web/src/components/auth/auth-shell.css create mode 100644 apps/web/src/components/ui/EmptyState.tsx delete mode 100644 apps/web/src/components/ui/SectionHead.tsx delete mode 100644 apps/web/src/components/ui/StatLine.tsx create mode 100644 apps/web/src/components/ui/Surface.tsx create mode 100644 apps/web/src/lib/runtimeDiagnostics.ts create mode 100644 apps/web/src/lib/useTheme.ts create mode 100644 apps/web/src/pages/AdminAi.tsx create mode 100644 apps/web/src/pages/admin/admin-ai.css create mode 100644 apps/web/src/pages/admin/dataNormalization.ts create mode 100644 apps/web/src/pages/learner-home/model.ts create mode 100644 apps/web/src/pages/persona-studio/model.ts create mode 100644 apps/web/src/pages/session-review/model.ts create mode 100644 apps/web/src/pages/session/voiceCapture.ts delete mode 100644 apps/web/src/pages/settings/useTheme.ts create mode 100644 data/kb/live_coaching_sources/dbt_crisis_commitment.json create mode 100644 data/kb/live_coaching_sources/gestalt_session_moves.json create mode 100644 data/kb/live_coaching_sources/parent_intake_moves.json create mode 100644 docs/design/darkmode-2026-07-14/README.md create mode 100644 docs/design/darkmode-2026-07-14/darkmode-a.png create mode 100644 docs/design/darkmode-2026-07-14/darkmode-b.png create mode 100644 docs/design/darkmode-2026-07-14/darkmode-c.png create mode 100644 docs/design/darkmode-2026-07-14/implemented-learn.png create mode 100644 docs/design/darkmode-2026-07-14/implemented-session.png create mode 100644 docs/design/darkmode-2026-07-14/review-redesigned-2026-07-15.png create mode 100644 docs/guides/collaboration-for-researchers.md create mode 100644 docs/ops/hanshin-meeting-actions-2026-07-13.md create mode 100644 docs/ops/pilot-usage-guide-draft.md create mode 100644 docs/ops/refactor-governance-2026-07-15.md create mode 100644 docs/ops/server-migration-runbook-draft.md create mode 100644 docs/ops/session-progress-gauge-design-2026-07-14.md create mode 100644 docs/ops/tagged-data-utilization-2026-07-15.md create mode 100644 scripts/load-test-sessions.py diff --git a/.gitignore b/.gitignore index 57449e9..7db8ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,8 @@ apps/api/uploads/ .idea/ .vscode/ postgres-data/ +.wrangler/ +public-runtime-watchdog.failcount # 임시 실행/로그 (P1 빌드) *.out diff --git a/README.md b/README.md index 957cf90..5c26d92 100644 --- a/README.md +++ b/README.md @@ -95,8 +95,8 @@ uvicorn engine_gateway.gateway:app --host 0.0.0.0 --port 9099 ```powershell # 백엔드 -cd apps\api; python -m pytest app/ -q # 백엔드 기준선 178 pass -python -m pytest engine_gateway/ -q # 현재 23 pass +cd apps\api; python -m pytest app/ -q # 백엔드 기준선 400 pass +python -m pytest engine_gateway/ -q # 현재 29 pass # 프론트엔드 cd apps\web; npm run typecheck # tsc -b npm run build # tsc -b && vite build diff --git a/apps/api/app/auth_sessions.py b/apps/api/app/auth_sessions.py index 8fd31b5..76575d8 100644 --- a/apps/api/app/auth_sessions.py +++ b/apps/api/app/auth_sessions.py @@ -20,6 +20,7 @@ from .auth_types import AccountStatus, RoleName from .config import settings from .db import get_pool from .runtime_policy import require_runtime_fallback_allowed, runtime_fallback_allowed +from .runtime_schema import runtime_schema_bootstrap_required from .services import notifications logger = logging.getLogger(__name__) @@ -191,7 +192,11 @@ def user_id_from_email(email: str) -> str: def user_id_from_external_id(external_id: str) -> str: - return str(uuid.uuid5(uuid.NAMESPACE_URL, f"vignette:user-external:{external_id.strip().lower()}")) + return str( + uuid.uuid5( + uuid.NAMESPACE_URL, f"vignette:user-external:{external_id.strip().lower()}" + ) + ) def _normalize_email(email: str) -> str: @@ -344,10 +349,11 @@ async def _runtime_tables_ready(conn) -> bool: 'persona_code', 'persona_display_name', 'persona_difficulty', - 'prev_rapport_credit' + 'prev_rapport_credit', + 'session_goals' ) GROUP BY table_schema, table_name - HAVING count(*) = 5 + HAVING count(*) = 6 ) AS has_session_columns, EXISTS ( SELECT 1 FROM information_schema.columns @@ -517,13 +523,9 @@ async def ensure_runtime_tables() -> None: """Ensure DB-backed auth/user runtime tables exist when a pool is available.""" pool = get_pool() async with pool.acquire() as conn: - if await _runtime_tables_ready(conn): + ready = await _runtime_tables_ready(conn) + if not runtime_schema_bootstrap_required("auth/user", ready=ready): return - if settings.environment != "dev": - raise RuntimeError( - "runtime DB schema is incomplete; run owner migration/init and " - "scripts/check-deploy-preflight.py before starting the API" - ) await conn.execute( """ DO $$ @@ -904,7 +906,8 @@ async def ensure_runtime_tables() -> None: ADD COLUMN IF NOT EXISTS persona_code TEXT, ADD COLUMN IF NOT EXISTS persona_display_name TEXT, ADD COLUMN IF NOT EXISTS persona_difficulty TEXT, - ADD COLUMN IF NOT EXISTS prev_rapport_credit REAL NOT NULL DEFAULT 0.0 + ADD COLUMN IF NOT EXISTS prev_rapport_credit REAL NOT NULL DEFAULT 0.0, + ADD COLUMN IF NOT EXISTS session_goals JSONB NOT NULL DEFAULT '[]'::jsonb """ ) await conn.execute( @@ -1054,6 +1057,10 @@ async def ensure_runtime_tables() -> None: ) """ ) + if not await _runtime_tables_ready(conn): + raise RuntimeError( + "auth/user development schema bootstrap did not satisfy readiness" + ) def _managed_user_from_row(row) -> ManagedUser: @@ -1092,15 +1099,23 @@ def _memory_upsert_managed_user(data: ManagedUserMemoryInput) -> ManagedUser: raise InactiveUserError("user is inactive") if data.reactivate: _inactive_emails.discard(normalized_email) - uid = data.user_id or _email_index.get(normalized_email) or user_id_from_email(normalized_email) + uid = ( + data.user_id + or _email_index.get(normalized_email) + or user_id_from_email(normalized_email) + ) current = _users.get(uid) user = ManagedUser( user_id=uid, email=normalized_email, - display_name=(data.display_name.strip() if data.display_name else "") or normalized_email, + display_name=(data.display_name.strip() if data.display_name else "") + or normalized_email, role=data.role, - admin_access=data.admin_access if data.admin_access is not None else (current.admin_access if current else False), - account_status=data.account_status or (current.account_status if current else "approved"), + admin_access=data.admin_access + if data.admin_access is not None + else (current.admin_access if current else False), + account_status=data.account_status + or (current.account_status if current else "approved"), cohort_ids=( list(data.cohort_ids) if data.cohort_ids is not None @@ -1126,20 +1141,28 @@ def _memory_upsert_managed_user(data: ManagedUserMemoryInput) -> ManagedUser: if data.grade_level is not None else (current.grade_level if current else "") ), - phone=data.phone.strip() if data.phone is not None else (current.phone if current else ""), + phone=data.phone.strip() + if data.phone is not None + else (current.phone if current else ""), contact_address=( data.contact_address.strip() if data.contact_address is not None else (current.contact_address if current else "") ), - nickname=data.nickname.strip() if data.nickname is not None else (current.nickname if current else ""), + nickname=data.nickname.strip() + if data.nickname is not None + else (current.nickname if current else ""), self_introduction=( data.self_introduction.strip() if data.self_introduction is not None else (current.self_introduction if current else "") ), - avatar_url=data.avatar_url.strip() if data.avatar_url is not None else (current.avatar_url if current else ""), - consent_at=data.consent_at if data.consent_at is not None else (current.consent_at if current else None), + avatar_url=data.avatar_url.strip() + if data.avatar_url is not None + else (current.avatar_url if current else ""), + consent_at=data.consent_at + if data.consent_at is not None + else (current.consent_at if current else None), profile_completed_at=( data.profile_completed_at if data.profile_completed_at is not None @@ -1173,6 +1196,25 @@ def _memory_upsert_managed_user(data: ManagedUserMemoryInput) -> ManagedUser: return user +def _sync_managed_user_sessions(user: ManagedUser) -> None: + """관리 사용자 변경을 현재 로그인 세션의 권한 스냅샷에 반영한다.""" + for session in _sessions.values(): + if session.user_id != user.user_id: + continue + session.display_name = user.display_name + session.role = user.role + session.admin_access = has_admin_access( + user.email, + user.role, + user.admin_access, + ) + session.super_admin = is_super_admin_email(user.email) + session.account_status = user.account_status + session.cohort_ids = list(user.cohort_ids) + session.consent_at = user.consent_at + session.profile_completed_at = user.profile_completed_at + + async def upsert_managed_user(data: ManagedUserUpsertInput) -> ManagedUser: normalized_email = _normalize_email(data.email) normalized_external_id = _normalize_external_id(data.external_id, normalized_email) @@ -1183,11 +1225,11 @@ async def upsert_managed_user(data: ManagedUserUpsertInput) -> ManagedUser: reactivate=data.reactivate, ) try: - pool = get_pool() - async with pool.acquire() as conn: - if data.user_id is None and normalized_external_id != manual_external_id: - row = await conn.fetchrow( - """ + pool = get_pool() + async with pool.acquire() as conn: + if data.user_id is None and normalized_external_id != manual_external_id: + row = await conn.fetchrow( + """ UPDATE app.app_user SET external_id = $1, display_name = COALESCE(NULLIF(display_name, ''), $3), @@ -1234,19 +1276,25 @@ async def upsert_managed_user(data: ManagedUserUpsertInput) -> ManagedUser: created_at, last_seen_at """, - normalized_external_id, - normalized_email, - (data.display_name.strip() if data.display_name else normalized_email), - _cohort_value(data.cohort_ids), - data.affiliation or DEFAULT_AFFILIATION, - manual_external_id, - ) - if row is not None: - user = _managed_user_from_row(row) - _memory_upsert_managed_user(ManagedUserMemoryInput.from_user(user, reactivate=True)) - return user - row = await conn.fetchrow( - """ + normalized_external_id, + normalized_email, + ( + data.display_name.strip() + if data.display_name + else normalized_email + ), + _cohort_value(data.cohort_ids), + data.affiliation or DEFAULT_AFFILIATION, + manual_external_id, + ) + if row is not None: + user = _managed_user_from_row(row) + _memory_upsert_managed_user( + ManagedUserMemoryInput.from_user(user, reactivate=True) + ) + return user + row = await conn.fetchrow( + """ INSERT INTO app.app_user ( external_id, email, @@ -1306,35 +1354,44 @@ async def upsert_managed_user(data: ManagedUserUpsertInput) -> ManagedUser: created_at, last_seen_at """, - normalized_external_id, - normalized_email, - (data.display_name.strip() if data.display_name else normalized_email), - _db_role(data.role), - _cohort_value(data.cohort_ids), - data.affiliation or DEFAULT_AFFILIATION, - data.reactivate, - desired_account_status, - data.admin_access, - ) - if row is None: - _inactive_emails.add(normalized_email) - raise InactiveUserError("user is inactive") - user = _managed_user_from_row(row) - _memory_upsert_managed_user(ManagedUserMemoryInput.from_user(user, reactivate=True)) - return user + normalized_external_id, + normalized_email, + (data.display_name.strip() if data.display_name else normalized_email), + _db_role(data.role), + _cohort_value(data.cohort_ids), + data.affiliation or DEFAULT_AFFILIATION, + data.reactivate, + desired_account_status, + data.admin_access, + ) + if row is None: + _inactive_emails.add(normalized_email) + raise InactiveUserError("user is inactive") + user = _managed_user_from_row(row) + _memory_upsert_managed_user( + ManagedUserMemoryInput.from_user(user, reactivate=True) + ) + return user except InactiveUserError: raise except Exception: require_runtime_fallback_allowed("managed user") - current = _users.get(data.user_id or "") or _users.get(_email_index.get(normalized_email, "")) + current = _users.get(data.user_id or "") or _users.get( + _email_index.get(normalized_email, "") + ) fallback_uid = data.user_id or ( - current.user_id if current is not None else user_id_from_external_id(normalized_external_id) + current.user_id + if current is not None + else user_id_from_external_id(normalized_external_id) ) fallback_account_status = desired_account_status if current is not None: if current.account_status == "suspended": fallback_account_status = "suspended" - elif current.account_status == "approved" and desired_account_status == "pending": + elif ( + current.account_status == "approved" + and desired_account_status == "pending" + ): fallback_account_status = "approved" return _memory_upsert_managed_user( ManagedUserMemoryInput( @@ -1561,33 +1618,28 @@ async def update_managed_user( patch.department.strip() if patch.department is not None else None, patch.grade_level.strip() if patch.grade_level is not None else None, patch.phone.strip() if patch.phone is not None else None, - patch.contact_address.strip() if patch.contact_address is not None else None, + patch.contact_address.strip() + if patch.contact_address is not None + else None, patch.nickname.strip() if patch.nickname is not None else None, - patch.self_introduction.strip() if patch.self_introduction is not None else None, + patch.self_introduction.strip() + if patch.self_introduction is not None + else None, patch.avatar_url.strip() if patch.avatar_url is not None else None, patch.complete_onboarding, - patch.terms_version.strip() if patch.terms_version is not None else None, - patch.privacy_version.strip() if patch.privacy_version is not None else None, + patch.terms_version.strip() + if patch.terms_version is not None + else None, + patch.privacy_version.strip() + if patch.privacy_version is not None + else None, patch.account_status, patch.admin_access, ) if row is not None: next_user = _managed_user_from_row(row) _memory_upsert_managed_user(ManagedUserMemoryInput.from_user(next_user)) - for session in _sessions.values(): - if session.user_id == user_id: - session.display_name = next_user.display_name - session.role = next_user.role - session.admin_access = has_admin_access( - next_user.email, - next_user.role, - next_user.admin_access, - ) - session.super_admin = is_super_admin_email(next_user.email) - session.account_status = next_user.account_status - session.cohort_ids = list(next_user.cohort_ids) - session.consent_at = next_user.consent_at - session.profile_completed_at = next_user.profile_completed_at + _sync_managed_user_sessions(next_user) return next_user except Exception: require_runtime_fallback_allowed("managed user update") @@ -1601,32 +1653,58 @@ async def update_managed_user( next_user = ManagedUser( user_id=current.user_id, email=current.email, - display_name=patch.display_name.strip() if patch.display_name is not None else current.display_name, + display_name=patch.display_name.strip() + if patch.display_name is not None + else current.display_name, role=patch.role if patch.role is not None else current.role, - admin_access=patch.admin_access if patch.admin_access is not None else current.admin_access, - account_status=patch.account_status if patch.account_status is not None else current.account_status, - cohort_ids=list(patch.cohort_ids) if patch.cohort_ids is not None else current.cohort_ids, - affiliation=patch.affiliation.strip() if patch.affiliation is not None else current.affiliation, - legal_name=patch.legal_name.strip() if patch.legal_name is not None else current.legal_name, - department=patch.department.strip() if patch.department is not None else current.department, - grade_level=patch.grade_level.strip() if patch.grade_level is not None else current.grade_level, + admin_access=patch.admin_access + if patch.admin_access is not None + else current.admin_access, + account_status=patch.account_status + if patch.account_status is not None + else current.account_status, + cohort_ids=list(patch.cohort_ids) + if patch.cohort_ids is not None + else current.cohort_ids, + affiliation=patch.affiliation.strip() + if patch.affiliation is not None + else current.affiliation, + legal_name=patch.legal_name.strip() + if patch.legal_name is not None + else current.legal_name, + department=patch.department.strip() + if patch.department is not None + else current.department, + grade_level=patch.grade_level.strip() + if patch.grade_level is not None + else current.grade_level, phone=patch.phone.strip() if patch.phone is not None else current.phone, contact_address=( patch.contact_address.strip() if patch.contact_address is not None else current.contact_address ), - nickname=patch.nickname.strip() if patch.nickname is not None else current.nickname, + nickname=patch.nickname.strip() + if patch.nickname is not None + else current.nickname, self_introduction=( patch.self_introduction.strip() if patch.self_introduction is not None else current.self_introduction ), - avatar_url=patch.avatar_url.strip() if patch.avatar_url is not None else current.avatar_url, + avatar_url=patch.avatar_url.strip() + if patch.avatar_url is not None + else current.avatar_url, consent_at=current.consent_at, - profile_completed_at=time.time() if patch.complete_onboarding else current.profile_completed_at, - terms_agreed_at=time.time() if patch.complete_onboarding else current.terms_agreed_at, - privacy_agreed_at=time.time() if patch.complete_onboarding else current.privacy_agreed_at, + profile_completed_at=time.time() + if patch.complete_onboarding + else current.profile_completed_at, + terms_agreed_at=time.time() + if patch.complete_onboarding + else current.terms_agreed_at, + privacy_agreed_at=time.time() + if patch.complete_onboarding + else current.privacy_agreed_at, terms_version=( patch.terms_version.strip() if patch.complete_onboarding and patch.terms_version is not None @@ -1641,21 +1719,7 @@ async def update_managed_user( last_seen_at=time.time(), ) _users[user_id] = next_user - - for session in _sessions.values(): - if session.user_id == user_id: - session.display_name = next_user.display_name - session.role = next_user.role - session.admin_access = has_admin_access( - next_user.email, - next_user.role, - next_user.admin_access, - ) - session.super_admin = is_super_admin_email(next_user.email) - session.account_status = next_user.account_status - session.cohort_ids = list(next_user.cohort_ids) - session.consent_at = next_user.consent_at - session.profile_completed_at = next_user.profile_completed_at + _sync_managed_user_sessions(next_user) return next_user @@ -1865,7 +1929,9 @@ async def create_session( email=normalized_email, display_name=managed.display_name, role=managed.role, - admin_access=has_admin_access(managed.email, managed.role, managed.admin_access), + admin_access=has_admin_access( + managed.email, managed.role, managed.admin_access + ), super_admin=is_super_admin_email(managed.email), account_status=managed.account_status, cohort_ids=list(managed.cohort_ids), @@ -1976,7 +2042,9 @@ async def get_session(raw_sid: str | None) -> SessionUser | None: managed.last_seen_at = time.time() user.display_name = managed.display_name user.role = managed.role - user.admin_access = has_admin_access(managed.email, managed.role, managed.admin_access) + user.admin_access = has_admin_access( + managed.email, managed.role, managed.admin_access + ) user.super_admin = is_super_admin_email(managed.email) user.account_status = managed.account_status user.cohort_ids = list(managed.cohort_ids) diff --git a/apps/api/app/config.py b/apps/api/app/config.py index 554bb0b..ef3adf7 100644 --- a/apps/api/app/config.py +++ b/apps/api/app/config.py @@ -274,6 +274,23 @@ class Settings(BaseSettings): validation_alias="ALLOW_SEED_PERSONA_FALLBACK", ) + # ── 회기 시간 제한 (2026-07-13 한신대 회의: 시간 기반 종료 전환) ── + # 임상 근거: 한 회기에 4단계가 모두 이뤄지지 않는 게 정상 — 종료는 단계 완수가 아니라 + # 시간이 결정한다. 목표 달성 후에도 시간 내에는 계속 진행할 수 있다. + session_duration_minutes: int = Field( + default=60, + validation_alias="SESSION_DURATION_MINUTES", + ) + session_warning_minutes: int = Field( + default=10, + validation_alias="SESSION_WARNING_MINUTES", + ) + # 시간 만료 후에도 마무리 인사를 나눌 수 있는 유예. 유예까지 지나면 새 턴을 거부한다. + session_overtime_grace_minutes: int = Field( + default=10, + validation_alias="SESSION_OVERTIME_GRACE_MINUTES", + ) + # ── SSE 스트리밍 ───────────────────────────────────── sse_heartbeat_seconds: int = 30 # Cloudflare 100초 timeout 회피 (R2) diff --git a/apps/api/app/engine_client.py b/apps/api/app/engine_client.py index 8e8e503..1f6d59f 100644 --- a/apps/api/app/engine_client.py +++ b/apps/api/app/engine_client.py @@ -22,8 +22,8 @@ import httpx from .config import settings from .contracts.engine_gateway import ( - AIRole, - EngineMessage, + AIRole as AIRole, + EngineMessage as EngineMessage, EngineGatewaySseLineDecoder, EngineGatewaySsePacket, GenerateRequest, diff --git a/apps/api/app/persona_repository.py b/apps/api/app/persona_repository.py index c6ffa18..8fc0df8 100644 --- a/apps/api/app/persona_repository.py +++ b/apps/api/app/persona_repository.py @@ -117,7 +117,9 @@ def load_file_personas(persona_dir: Path = REPO_PERSONA_DIR) -> list[PersonaCard def built_in_personas() -> list[PersonaCard]: """Return deterministic built-in catalog cards from code seeds plus repo JSON cards.""" - cards: dict[str, PersonaCard] = {card.code.upper(): card for card in SEED_PERSONAS.values()} + cards: dict[str, PersonaCard] = { + card.code.upper(): card for card in SEED_PERSONAS.values() + } for card in load_file_personas(): cards.setdefault(card.code.upper(), card) return [cards[code] for code in sorted(cards)] @@ -222,7 +224,14 @@ def seed_fallback_persona(code: str) -> CatalogPersona | None: normalized = code.strip().upper() card = get_seed_persona(normalized) if card is None: - card = next((entry for entry in load_file_personas() if entry.code.upper() == normalized), None) + card = next( + ( + entry + for entry in load_file_personas() + if entry.code.upper() == normalized + ), + None, + ) if card is None: return None return CatalogPersona( @@ -448,6 +457,83 @@ async def get_persona_draft_record( return persona_draft_record_from_row(row) if row is not None else None +async def _insert_persona_card( + conn: Any, + *, + persona_id: str, + card: PersonaCard, + version: int, + next_status: str, + author_id: str, + returning_columns: str, +) -> Any: + """Insert one immutable persona version from the canonical PersonaCard shape.""" + + return await conn.fetchrow( + f""" + INSERT INTO app.persona_card ( + persona_id, code, version, status, display_name, difficulty, + theory_target, demographics, presenting, history, big5, + resistance, speech_style, affect_baseline, ccd, + dsm5_dimensional, source_provenance, is_synthetic, triggers, + created_by + ) + VALUES ( + $1::uuid, $2, $3, $4, $5, $6, + $7::text[], $8::jsonb, $9::jsonb, $10::jsonb, $11::jsonb, + $12::jsonb, $13::jsonb, $14::jsonb, $15::jsonb, + $16::jsonb, $17, $18, $19::jsonb, $20::uuid + ) + RETURNING {returning_columns} + """, + persona_id, + card.code, + version, + next_status, + card.display_name, + card.difficulty, + card.theory_target, + card.demographics, + card.presenting, + card.history, + card.big5, + card.resistance, + card.speech_style, + card.affect_baseline, + card.ccd, + card.dsm5_dimensional, + card.source_provenance, + card.is_synthetic, + card.triggers, + author_id, + ) + + +async def _record_persona_create_audit( + conn: Any, + *, + author_id: str, + action: str, + persona_id: str, + next_status: str, + card: PersonaCard, + version: int, +) -> None: + await conn.execute( + """ + INSERT INTO audit.audit_log ( + actor_uid, action, target_kind, target_id, detail + ) + VALUES ($1::uuid, $2, $3, $4, $5::jsonb) + """, + author_id, + action, + "persona_card", + persona_id, + {"next_status": next_status, "code": card.code, "version": version}, + ) + + async def create_persona_draft( *, card: PersonaCard, @@ -470,60 +556,23 @@ async def create_persona_draft( """, card.code, ) - row = await conn.fetchrow( - f""" - INSERT INTO app.persona_card ( - persona_id, code, version, status, display_name, difficulty, - theory_target, demographics, presenting, history, big5, - resistance, speech_style, affect_baseline, ccd, - dsm5_dimensional, source_provenance, is_synthetic, triggers, - created_by - ) - VALUES ( - $1::uuid, $2, $3, $4, $5, $6, - $7::text[], $8::jsonb, $9::jsonb, $10::jsonb, $11::jsonb, - $12::jsonb, $13::jsonb, $14::jsonb, $15::jsonb, - $16::jsonb, $17, $18, $19::jsonb, $20::uuid - ) - RETURNING {_REVIEW_COLUMNS} - """, - persona_id, - card.code, - int(version or 1), - next_status, - card.display_name, - card.difficulty, - card.theory_target, - card.demographics, - card.presenting, - card.history, - card.big5, - card.resistance, - card.speech_style, - card.affect_baseline, - card.ccd, - card.dsm5_dimensional, - card.source_provenance, - card.is_synthetic, - card.triggers, - author_id, + row = await _insert_persona_card( + conn, + persona_id=persona_id, + card=card, + version=int(version or 1), + next_status=next_status, + author_id=author_id, + returning_columns=_REVIEW_COLUMNS, ) - await conn.execute( - """ - INSERT INTO audit.audit_log ( - actor_uid, action, target_kind, target_id, detail - ) - VALUES ($1::uuid, $2, $3, $4, $5::jsonb) - """, - author_id, - "persona_draft_create", - "persona_card", - persona_id, - { - "next_status": next_status, - "code": card.code, - "version": int(row["version"]), - }, + await _record_persona_create_audit( + conn, + author_id=author_id, + action="persona_draft_create", + persona_id=persona_id, + next_status=next_status, + card=card, + version=int(row["version"]), ) return persona_review_item_from_row(row) @@ -577,60 +626,23 @@ async def create_persona_revision_from_existing( """, card.code, ) - row = await conn.fetchrow( - f""" - INSERT INTO app.persona_card ( - persona_id, code, version, status, display_name, difficulty, - theory_target, demographics, presenting, history, big5, - resistance, speech_style, affect_baseline, ccd, - dsm5_dimensional, source_provenance, is_synthetic, triggers, - created_by - ) - VALUES ( - $1::uuid, $2, $3, $4, $5, $6, - $7::text[], $8::jsonb, $9::jsonb, $10::jsonb, $11::jsonb, - $12::jsonb, $13::jsonb, $14::jsonb, $15::jsonb, - $16::jsonb, $17, $18, $19::jsonb, $20::uuid - ) - RETURNING {_CARD_COLUMNS}, created_at, approved_at - """, - persona_id, - card.code, - int(version or 1), - next_status, - card.display_name, - card.difficulty, - card.theory_target, - card.demographics, - card.presenting, - card.history, - card.big5, - card.resistance, - card.speech_style, - card.affect_baseline, - card.ccd, - card.dsm5_dimensional, - card.source_provenance, - card.is_synthetic, - card.triggers, - author_id, + row = await _insert_persona_card( + conn, + persona_id=persona_id, + card=card, + version=int(version or 1), + next_status=next_status, + author_id=author_id, + returning_columns=f"{_CARD_COLUMNS}, created_at, approved_at", ) - await conn.execute( - """ - INSERT INTO audit.audit_log ( - actor_uid, action, target_kind, target_id, detail - ) - VALUES ($1::uuid, $2, $3, $4, $5::jsonb) - """, - author_id, - "persona_revision_create", - "persona_card", - persona_id, - { - "next_status": next_status, - "code": card.code, - "version": int(row["version"]), - }, + await _record_persona_create_audit( + conn, + author_id=author_id, + action="persona_revision_create", + persona_id=persona_id, + next_status=next_status, + card=card, + version=int(row["version"]), ) return persona_draft_record_from_row(row) diff --git a/apps/api/app/routes/admin.py b/apps/api/app/routes/admin.py index 9f4780c..b416018 100644 --- a/apps/api/app/routes/admin.py +++ b/apps/api/app/routes/admin.py @@ -51,6 +51,70 @@ TicketPriority = Literal["low", "normal", "high", "urgent"] TicketStatus = Literal["open", "triaged", "in_progress", "resolved", "closed"] NotificationDeliveryStatus = Literal["queued", "sending", "sent", "failed", "skipped"] +METERED_CLIENT_TURN_FILTER_SQL = """ + speaker = 'client' + AND ( + llm_provider IS NOT NULL OR model IS NOT NULL + OR tokens_in IS NOT NULL OR tokens_out IS NOT NULL + OR cost_usd IS NOT NULL + ) +""" + +USAGE_AGGREGATE_COLUMNS_SQL = """ + COUNT(*) AS turns, + COALESCE(SUM(tokens_in), 0)::bigint AS tokens_in, + COALESCE(SUM(tokens_out), 0)::bigint AS tokens_out, + COALESCE(SUM(cost_usd), 0)::numeric AS cost_usd +""" + +SUPPORT_TICKET_DETAIL_FROM_SQL = """ + SELECT + t.id, + t.reporter_id, + t.reporter_email, + t.reporter_name, + t.reporter_role, + t.category, + t.priority, + t.status, + t.subject, + t.body, + t.source_path, + t.fingerprint, + t.parent_ticket_id, + t.assigned_group, + t.resolution_note, + t.created_at, + t.updated_at, + t.resolved_at, + COALESCE(dup.duplicate_count, 0) AS duplicate_count, + dup.parent_candidate_id AS duplicate_parent_candidate_id, + COALESCE(child.child_ticket_count, 0) AS child_ticket_count, + COALESCE(ev.event_count, 0) AS event_count, + ev.last_event_at + FROM app.support_ticket AS t + LEFT JOIN LATERAL ( + SELECT + count(*) FILTER (WHERE id <> t.id)::int AS duplicate_count, + (array_agg(id::text ORDER BY created_at ASC, id ASC))[1] AS parent_candidate_id + FROM app.support_ticket + WHERE fingerprint <> '' + AND fingerprint = t.fingerprint + ) AS dup ON TRUE + LEFT JOIN LATERAL ( + SELECT count(*)::int AS child_ticket_count + FROM app.support_ticket + WHERE parent_ticket_id = t.id + ) AS child ON TRUE + LEFT JOIN LATERAL ( + SELECT count(*)::int AS event_count, max(created_at) AS last_event_at + FROM audit.audit_log + WHERE action = 'support_ticket_update' + AND target_kind = 'support_ticket' + AND target_id = t.id::text + ) AS ev ON TRUE +""" + class AdminServiceHealth(BaseModel): key: str @@ -450,17 +514,10 @@ async def _runtime_health_metrics(*, db_ok: bool) -> RuntimeHealthMetrics: async def _usage_from_database(window_days: int) -> AdminUsageResponse: async with acquire(role="admin") as conn: total_row = await conn.fetchrow( - """ + f""" SELECT COUNT(*) FILTER (WHERE speaker = 'client') AS total_turns, - COUNT(*) FILTER ( - WHERE speaker = 'client' - AND ( - llm_provider IS NOT NULL OR model IS NOT NULL - OR tokens_in IS NOT NULL OR tokens_out IS NOT NULL - OR cost_usd IS NOT NULL - ) - ) AS metered_turns, + COUNT(*) FILTER (WHERE {METERED_CLIENT_TURN_FILTER_SQL}) AS metered_turns, COALESCE(SUM(tokens_in) FILTER (WHERE speaker = 'client'), 0)::bigint AS tokens_in, COALESCE(SUM(tokens_out) FILTER (WHERE speaker = 'client'), 0)::bigint AS tokens_out, COALESCE(SUM(cost_usd) FILTER (WHERE speaker = 'client'), 0)::numeric AS cost_usd @@ -470,22 +527,14 @@ async def _usage_from_database(window_days: int) -> AdminUsageResponse: window_days, ) rows = await conn.fetch( - """ + f""" SELECT COALESCE(llm_provider, 'unknown') AS provider, COALESCE(model, 'unknown') AS model, - COUNT(*) AS turns, - COALESCE(SUM(tokens_in), 0)::bigint AS tokens_in, - COALESCE(SUM(tokens_out), 0)::bigint AS tokens_out, - COALESCE(SUM(cost_usd), 0)::numeric AS cost_usd + {USAGE_AGGREGATE_COLUMNS_SQL} FROM app.turns WHERE created_at >= now() - ($1::int * interval '1 day') - AND speaker = 'client' - AND ( - llm_provider IS NOT NULL OR model IS NOT NULL - OR tokens_in IS NOT NULL OR tokens_out IS NOT NULL - OR cost_usd IS NOT NULL - ) + AND {METERED_CLIENT_TURN_FILTER_SQL} GROUP BY 1, 2 ORDER BY cost_usd DESC, @@ -496,21 +545,13 @@ async def _usage_from_database(window_days: int) -> AdminUsageResponse: window_days, ) daily_rows = await conn.fetch( - """ + f""" SELECT to_char(date_trunc('day', created_at), 'YYYY-MM-DD') AS day, - COUNT(*) AS turns, - COALESCE(SUM(tokens_in), 0)::bigint AS tokens_in, - COALESCE(SUM(tokens_out), 0)::bigint AS tokens_out, - COALESCE(SUM(cost_usd), 0)::numeric AS cost_usd + {USAGE_AGGREGATE_COLUMNS_SQL} FROM app.turns WHERE created_at >= now() - ($1::int * interval '1 day') - AND speaker = 'client' - AND ( - llm_provider IS NOT NULL OR model IS NOT NULL - OR tokens_in IS NOT NULL OR tokens_out IS NOT NULL - OR cost_usd IS NOT NULL - ) + AND {METERED_CLIENT_TURN_FILTER_SQL} GROUP BY 1 ORDER BY 1 """, @@ -761,52 +802,8 @@ async def _tickets_from_database( search_filter = search.strip().lower() async with acquire(role="admin") as conn: rows = await conn.fetch( - """ - SELECT - t.id, - t.reporter_id, - t.reporter_email, - t.reporter_name, - t.reporter_role, - t.category, - t.priority, - t.status, - t.subject, - t.body, - t.source_path, - t.fingerprint, - t.parent_ticket_id, - t.assigned_group, - t.resolution_note, - t.created_at, - t.updated_at, - t.resolved_at, - COALESCE(dup.duplicate_count, 0) AS duplicate_count, - dup.parent_candidate_id AS duplicate_parent_candidate_id, - COALESCE(child.child_ticket_count, 0) AS child_ticket_count, - COALESCE(ev.event_count, 0) AS event_count, - ev.last_event_at - FROM app.support_ticket AS t - LEFT JOIN LATERAL ( - SELECT - count(*) FILTER (WHERE id <> t.id)::int AS duplicate_count, - (array_agg(id::text ORDER BY created_at ASC, id ASC))[1] AS parent_candidate_id - FROM app.support_ticket - WHERE fingerprint <> '' - AND fingerprint = t.fingerprint - ) AS dup ON TRUE - LEFT JOIN LATERAL ( - SELECT count(*)::int AS child_ticket_count - FROM app.support_ticket - WHERE parent_ticket_id = t.id - ) AS child ON TRUE - LEFT JOIN LATERAL ( - SELECT count(*)::int AS event_count, max(created_at) AS last_event_at - FROM audit.audit_log - WHERE action = 'support_ticket_update' - AND target_kind = 'support_ticket' - AND target_id = t.id::text - ) AS ev ON TRUE + SUPPORT_TICKET_DETAIL_FROM_SQL + + """ WHERE ($1::text IS NULL OR status = $1) AND ($2::text IS NULL OR category = $2) AND ($3::text IS NULL OR priority = $3) @@ -1736,54 +1733,7 @@ async def patch_ticket( detail=detail, ) row = await conn.fetchrow( - """ - SELECT - t.id, - t.reporter_id, - t.reporter_email, - t.reporter_name, - t.reporter_role, - t.category, - t.priority, - t.status, - t.subject, - t.body, - t.source_path, - t.fingerprint, - t.parent_ticket_id, - t.assigned_group, - t.resolution_note, - t.created_at, - t.updated_at, - t.resolved_at, - COALESCE(dup.duplicate_count, 0) AS duplicate_count, - dup.parent_candidate_id AS duplicate_parent_candidate_id, - COALESCE(child.child_ticket_count, 0) AS child_ticket_count, - COALESCE(ev.event_count, 0) AS event_count, - ev.last_event_at - FROM app.support_ticket AS t - LEFT JOIN LATERAL ( - SELECT - count(*) FILTER (WHERE id <> t.id)::int AS duplicate_count, - (array_agg(id::text ORDER BY created_at ASC, id ASC))[1] AS parent_candidate_id - FROM app.support_ticket - WHERE fingerprint <> '' - AND fingerprint = t.fingerprint - ) AS dup ON TRUE - LEFT JOIN LATERAL ( - SELECT count(*)::int AS child_ticket_count - FROM app.support_ticket - WHERE parent_ticket_id = t.id - ) AS child ON TRUE - LEFT JOIN LATERAL ( - SELECT count(*)::int AS event_count, max(created_at) AS last_event_at - FROM audit.audit_log - WHERE action = 'support_ticket_update' - AND target_kind = 'support_ticket' - AND target_id = t.id::text - ) AS ev ON TRUE - WHERE t.id = $1::uuid - """, + SUPPORT_TICKET_DETAIL_FROM_SQL + " WHERE t.id = $1::uuid", ticket_id, ) except Exception as exc: diff --git a/apps/api/app/routes/auth.py b/apps/api/app/routes/auth.py index 9228893..296f3e6 100644 --- a/apps/api/app/routes/auth.py +++ b/apps/api/app/routes/auth.py @@ -967,7 +967,7 @@ async def callback( cohort_ids=cohort_ids, external_id=external_id, ) - except InactiveUserError as exc: + except InactiveUserError: _log_oauth_callback_failure( request, "inactive_user", diff --git a/apps/api/app/routes/kb.py b/apps/api/app/routes/kb.py index c30ba67..7fe91d2 100644 --- a/apps/api/app/routes/kb.py +++ b/apps/api/app/routes/kb.py @@ -33,7 +33,7 @@ RoleLiteral = Literal["client", "counselor", "evaluator"] class KBSearchRequest(BaseModel): query: str = Field(..., min_length=1) # PII 마스킹된 질의(마스킹은 가드레일 책임) - role: RoleLiteral = "evaluator" # 검색 주체(정보비대칭 정책 선택) + role: RoleLiteral = "evaluator" # 검색 주체(정보비대칭 정책 선택) k: int = Field(default=5, ge=1, le=50) rerank: bool = True # 정책 화이트리스트를 *좁히는* 추가 필터만 허용(넓히지 못함 — 정보비대칭 보존) @@ -51,9 +51,9 @@ class ChunkOut(BaseModel): kb_kind: str heading_path: Optional[str] = None context_prefix: Optional[str] = None - body: Optional[str] = None # expose_body=True(상담사/평가) 정책에서만 - behavior_cue: Optional[str] = None # 내담자 정책: 본문 비노출, 행동단서만(M6) - label_id: Optional[int] = None # 평가 정책에서만 + body: Optional[str] = None # expose_body=True(상담사/평가) 정책에서만 + behavior_cue: Optional[str] = None # 내담자 정책: 본문 비노출, 행동단서만(M6) + label_id: Optional[int] = None # 평가 정책에서만 meta: dict[str, Any] = Field(default_factory=dict) source_id: Optional[str] = None @@ -62,9 +62,9 @@ class KBSearchResponse(BaseModel): chunks: list[ChunkOut] policy: str top1_score: float - crag_pass: bool # top1 >= 임계(F-06: 미달 시 관찰 프레이밍) + crag_pass: bool # top1 >= 임계(F-06: 미달 시 관찰 프레이밍) latency_ms: int - degraded: bool = False # reranker/embed 폴백 투명성 + degraded: bool = False # reranker/embed 폴백 투명성 class MemoryRecallRequest(BaseModel): @@ -79,11 +79,11 @@ class IndexChunkIn(BaseModel): seq: int chunk_text: str = Field(..., min_length=1) heading_path: Optional[str] = None - context_prefix: Optional[str] = None # Contextual Retrieval 프리픽스(색인 대상) + context_prefix: Optional[str] = None # Contextual Retrieval 프리픽스(색인 대상) kb_kind: Optional[str] = None - visible_to: Optional[list[str]] = None # 미지정 시 {client,counselor,evaluator} + visible_to: Optional[list[str]] = None # 미지정 시 {client,counselor,evaluator} sensitivity: Optional[int] = Field(default=None, ge=0, le=3) - label_id: Optional[int] = None # taxonomy 정답 라벨 FK + label_id: Optional[int] = None # taxonomy 정답 라벨 FK meta: Optional[dict[str, Any]] = None token_count: Optional[int] = None @@ -99,8 +99,8 @@ class IndexRequestIn(BaseModel): class IndexResponse(BaseModel): doc_id: Optional[int] chunks_indexed: int - skipped_unchanged: bool # content_hash 동일 → 증분 스킵 - embedded: bool # 임베딩 적재 여부(모델 미가용 시 False) + skipped_unchanged: bool # content_hash 동일 → 증분 스킵 + embedded: bool # 임베딩 적재 여부(모델 미가용 시 False) degraded: bool = False @@ -211,16 +211,11 @@ async def search(body: KBSearchRequest) -> KBSearchResponse: ) except RuntimeError as e: # DB 풀 미초기화(lifespan 밖) — 시연/테스트 degraded - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}" + ) - return KBSearchResponse( - chunks=[_to_chunk_out(c) for c in result.chunks], - policy=result.policy_name, - top1_score=round(result.top1_score, 6), - crag_pass=result.top1_score >= rag.CRAG_TOP1_THRESHOLD, - latency_ms=result.latency_ms, - degraded=result.degraded, - ) + return _search_response(result) # ════════════════════════════════════════════════════════════════════════════ @@ -254,18 +249,15 @@ async def eval_grounding(body: KBSearchRequest) -> KBSearchResponse: except Exception: pass except rag.NotConfigured as e: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {e}" + ) except RuntimeError as e: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}" + ) - return KBSearchResponse( - chunks=[_to_chunk_out(c) for c in result.chunks], - policy=result.policy_name, - top1_score=round(result.top1_score, 6), - crag_pass=result.top1_score >= rag.CRAG_TOP1_THRESHOLD, - latency_ms=result.latency_ms, - degraded=result.degraded, - ) + return _search_response(result) # ════════════════════════════════════════════════════════════════════════════ @@ -287,9 +279,19 @@ async def persona_memory(body: MemoryRecallRequest) -> KBSearchResponse: k=body.k, ) except rag.NotConfigured as e: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {e}" + ) except RuntimeError as e: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}" + ) + + return _search_response(result) + + +def _search_response(result: rag.SearchResult) -> KBSearchResponse: + """Project every RAG policy result through the same browser-facing contract.""" return KBSearchResponse( chunks=[_to_chunk_out(c) for c in result.chunks], @@ -304,7 +306,9 @@ async def persona_memory(body: MemoryRecallRequest) -> KBSearchResponse: # ════════════════════════════════════════════════════════════════════════════ # 인덱싱 트리거 — 관리자 전용(content_hash 증분, 오프라인 배치) # ════════════════════════════════════════════════════════════════════════════ -@router.post("/index", response_model=IndexResponse, status_code=status.HTTP_202_ACCEPTED) +@router.post( + "/index", response_model=IndexResponse, status_code=status.HTTP_202_ACCEPTED +) async def index_document( body: IndexRequestIn, principal: Annotated[Principal, Depends(require_role(Role.ADMIN))], @@ -329,9 +333,13 @@ async def index_document( except rag.IndexPolicyViolation as e: raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)) from e except rag.NotConfigured as e: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {e}" + ) except RuntimeError as e: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}") + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {e}" + ) return IndexResponse( doc_id=result.doc_id, @@ -356,7 +364,9 @@ async def sync_live_coach_source_packs( evaluator RAG 검색에도 올린다. source row를 먼저 upsert한 뒤 content_hash 기반 증분 색인을 수행한다. 임베딩 모델 미가용 시 BM25-only degraded 색인으로 이어진다. """ - source_rows, index_payloads = source_pack_sync.build_repo_source_pack_manifest(refresh=True) + source_rows, index_payloads = source_pack_sync.build_repo_source_pack_manifest( + refresh=True + ) if not source_rows or not index_payloads: raise HTTPException( status.HTTP_422_UNPROCESSABLE_ENTITY, @@ -365,11 +375,17 @@ async def sync_live_coach_source_packs( try: async with acquire() as conn: - result = await source_pack_sync.sync_repo_source_packs(conn, apply=True, refresh=True) + result = await source_pack_sync.sync_repo_source_packs( + conn, apply=True, refresh=True + ) except rag.NotConfigured as exc: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {exc}") from exc + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"RAG not configured: {exc}" + ) from exc except RuntimeError as exc: - raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {exc}") from exc + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"DB not ready: {exc}" + ) from exc return LiveCoachSourcePackSyncResponse( sources_upserted=result.sources_upserted, diff --git a/apps/api/app/routes/personas.py b/apps/api/app/routes/personas.py index ac884f3..2315041 100644 --- a/apps/api/app/routes/personas.py +++ b/apps/api/app/routes/personas.py @@ -8,7 +8,7 @@ import re import uuid from typing import Annotated, Any, Literal -from fastapi import APIRouter, Depends, HTTPException, Response, status +from fastapi import APIRouter, Depends, File, Form, HTTPException, Response, UploadFile, status from ..db import acquire from ..deps import CurrentPrincipal, Principal, Role, require_role @@ -52,6 +52,7 @@ from ..persona_read_model import ( from ..engine_client import EngineError, EngineMessage, GenerateRequest, engine_client from ..services import rag from ..services.guardrail import mask_pii +from ..services.tabular_ingest import TabularIngestError, extract_tabular_text router = APIRouter(prefix="/personas", tags=["personas"]) TeacherOrAdmin = Annotated[Principal, Depends(require_role(Role.TEACHER, Role.ADMIN))] @@ -555,6 +556,42 @@ async def create_persona_source_route( return await _register_persona_source_document(request, principal) +@router.post( + "/sources/upload", + response_model=PersonaSourceDocumentResponse, + status_code=status.HTTP_201_CREATED, +) +async def upload_persona_source_route( + principal: TeacherOrAdmin, + file: UploadFile = File(...), + source_kind: PersonaSourceKind = Form("mixed_notes"), + title: str | None = Form(None), + source_note: str = Form(""), +) -> PersonaSourceDocumentResponse: + """자유 양식 엑셀/CSV 업로드 → 텍스트 변환 → 기존 source 등록 경로 재사용 (P4). + + 업로드 원본 바이트는 이 핸들러 메모리에서만 파싱하고 저장하지 않는다(원본 파기). + 파생 텍스트만 기존 마스킹·hash-only 증거·sanitized chunk 경로로 등록된다. + """ + _ensure_teacher_or_admin(principal) + data = await file.read() + try: + text = extract_tabular_text(filename=file.filename or "", data=data) + except TabularIngestError as exc: + raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc)) from exc + finally: + # 원본 바이트 참조를 즉시 놓는다 — 파생 텍스트 외에는 남기지 않는다. + del data + request = PersonaSourceDocumentRequest( + filename=(file.filename or "uploaded-table.xlsx")[:240], + source_kind=source_kind, + text=text, + title=(title or None), + source_note=source_note[:800], + ) + return await _register_persona_source_document(request, principal) + + @router.post("/drafts/generate", response_model=PersonaDraftGenerateResponse) async def generate_persona_draft_route( request: PersonaDraftGenerateRequest, diff --git a/apps/api/app/routes/sessions.py b/apps/api/app/routes/sessions.py index d447d5f..461db5e 100644 --- a/apps/api/app/routes/sessions.py +++ b/apps/api/app/routes/sessions.py @@ -12,11 +12,12 @@ import asyncio import json import logging import secrets +import time from datetime import datetime from typing import Literal, Optional from fastapi import APIRouter, HTTPException, Request, status -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from sse_starlette.sse import EventSourceResponse from .. import db, session_persistence, turn_runtime @@ -46,15 +47,17 @@ from ..session_read_model import ( MISSING_SESSION_EVALUATION_GRACE_SECONDS, ReviewCaseWorksheet, ReviewCaseWorksheetSaveRequest, - ReviewWorksheetItem, - ReviewWorksheetSection, + ReviewWorksheetItem as ReviewWorksheetItem, + ReviewWorksheetSection as ReviewWorksheetSection, SessionArchiveResponse, SessionDetailResponse, SessionReviewReadInput, SessionReviewResponse, + SessionProgress, SessionShareDeleteResponse, SessionShareResponse, StageLabel, + build_session_progress, build_session_review, dashboard_achievements as _dashboard_achievements, dashboard_feedback as _dashboard_feedback, @@ -68,7 +71,7 @@ from ..session_read_model import ( session_share_payload as _session_share_payload, stage_label as _stage_label, ) -from ..store import InProcSession, TurnRecord, store +from ..store import InProcSession, store router = APIRouter(prefix="/sessions", tags=["sessions"]) logger = logging.getLogger(__name__) @@ -82,6 +85,19 @@ EndStateValue = str | int | float | bool | None | dict[str, float] class SessionStartRequest(BaseModel): persona_code: str = Field(..., examples=["P1"]) theory_mode: TheoryMode = "humanistic" + # 이번 회기 목표 단계(2026-07-13 회의 P1). 회의 권장은 2개 수준이지만 + # 소유자 지시(2026-07-15)로 1~4개까지 자유 선택을 허용한다. + # 빈 리스트는 구계약 클라이언트 호환용 — 준비 페이지는 항상 1개 이상을 보낸다. + goal_stages: list[StageLabel] = Field(default_factory=list, max_length=4) + + @field_validator("goal_stages") + @classmethod + def _dedupe_goal_stages(cls, value: list[StageLabel]) -> list[StageLabel]: + seen: list[StageLabel] = [] + for stage in value: + if stage not in seen: + seen.append(stage) + return seen[:4] class SessionStartResponse(BaseModel): @@ -92,6 +108,11 @@ class SessionStartResponse(BaseModel): effective_openness: float recall_summary: Optional[str] = None degraded: bool = False + started_at: str = "" + goal_stages: list[StageLabel] = Field(default_factory=list) + # 시간 기반 회기 종료 계약(회의 P1): 프론트 타이머·10분 전 알람의 기준값. + duration_limit_seconds: int = 0 + warning_before_end_seconds: int = 0 class TurnRequest(BaseModel): @@ -132,6 +153,8 @@ class TurnResponse(BaseModel): crisis_resource: Optional[CrisisResourceResponse] = None conversation_stopped: bool = False output_error: Optional[str] = None + # P2 단계 누적 게이지·상세 수치 — 턴마다 갱신된 파생값. + progress: Optional[SessionProgress] = None class SessionEndResponse(BaseModel): @@ -146,6 +169,20 @@ _RECALL_CACHE: dict[str, memory.RecallContext] = {} _KB_CUES_CACHE: dict[str, list[str]] = {} _RAG_WARM_SEMAPHORE = asyncio.Semaphore(1) + +def cached_kb_cues(session_id: str) -> list[str]: + """Return a defensive copy of the session-scoped, process-lifetime KB cues.""" + + return list(_KB_CUES_CACHE.get(session_id) or []) + + +def invalidate_session_context_cache(session_id: str) -> None: + """Invalidate all derived turn context when a session reaches its terminal state.""" + + _RECALL_CACHE.pop(session_id, None) + _KB_CUES_CACHE.pop(session_id, None) + + # ──────────────────────────────────────────────────────────────────────────── # RAG 배선 헬퍼 — 내담자(CLIENT) 뷰. 임베더/KB/DB 풀 미가용 시 빈 값으로 graceful # degradation: 상담 루프를 절대 막지 않는다(라이브 루프 비차단이 계약). routes/kb.py가 @@ -213,7 +250,13 @@ async def _retrieve_live_coach_grounding( conn, query=query, k=4, - kinds=("theory", "technique", "supervisor_pattern", "microskill", "taxonomy"), + kinds=( + "theory", + "technique", + "supervisor_pattern", + "microskill", + "taxonomy", + ), ) try: await rag.log_retrieval( @@ -233,9 +276,16 @@ async def _retrieve_live_coach_grounding( if not body: continue meta = chunk.meta if isinstance(chunk.meta, dict) else {} - title = str(meta.get("source_title") or meta.get("title") or chunk.source_id or "Vignette KB").strip() + title = str( + meta.get("source_title") + or meta.get("title") + or chunk.source_id + or "Vignette KB" + ).strip() source_type = str(meta.get("source_type") or "").strip() - source_version = str(meta.get("source_version") or meta.get("version") or "").strip() + source_version = str( + meta.get("source_version") or meta.get("version") or "" + ).strip() citation = str(meta.get("citation") or "").strip() out.append( live_coach.LiveCoachGrounding( @@ -252,7 +302,9 @@ async def _retrieve_live_coach_grounding( return out -def _latest_turn_evaluation(sess: InProcSession, turn_seq: int | None) -> Optional[dict]: +def _latest_turn_evaluation( + sess: InProcSession, turn_seq: int | None +) -> Optional[dict]: """방금 상담자 발화에 붙은 fast-loop 평가를 찾는다.""" for turn in reversed(sess.turns): if turn.speaker != "counselor": @@ -322,7 +374,8 @@ async def _load_case_memory(case_id: str) -> dict: "end_state": dict(summary_row["end_state"] or {}), } return { - "case_digest": (case_row["case_digest"] if case_row is not None else None) or None, + "case_digest": (case_row["case_digest"] if case_row is not None else None) + or None, "prev_summary": prev_summary, "pinned_facts": [row["value"] for row in fact_rows if row["value"]], } @@ -361,7 +414,10 @@ async def _episodic_recall_snippets(case_id: str, query: str) -> list[str]: try: async with db.acquire(ai_view=rag.AIRole.CLIENT.value) as conn: result = await rag.retrieve_persona_memory( - conn, case_id=case_id, query=query, k=_RAG_RECALL_K, + conn, + case_id=case_id, + query=query, + k=_RAG_RECALL_K, ) return await _hydrate_episodic_text(conn, result) except Exception: @@ -412,14 +468,39 @@ async def ensure_recall_context(sess: InProcSession) -> memory.RecallContext: return recall +def session_time_over(sess: InProcSession) -> bool: + """시간 기반 회기 종료(회의 P1): 제한 + 마무리 유예까지 지난 세션인지 판정. + + 제한 시간(기본 60분) 도달 자체는 프론트가 정리 유도·자동 종료로 처리하고, + 서버는 유예(기본 +10분)까지 지난 뒤의 새 턴만 거부한다(마무리 인사 허용). + """ + if settings.session_duration_minutes <= 0: + return False + limit_seconds = ( + settings.session_duration_minutes + settings.session_overtime_grace_minutes + ) * 60 + return (time.time() - sess.created_at) > limit_seconds + + +def _ensure_turn_time_allowed(sess: InProcSession) -> None: + if session_time_over(sess): + raise HTTPException( + status.HTTP_409_CONFLICT, + detail="session_time_over", + ) + + async def _prepare_turn_context( *, session_id: str, sess: InProcSession, learner_text: str, ) -> orchestrator.TurnContext: + _ensure_turn_time_allowed(sess) recall = await ensure_recall_context(sess) - kb_cues = _KB_CUES_CACHE.get(session_id) or [] # 비차단: warm 전이면 빈 단서(graceful) + kb_cues = ( + _KB_CUES_CACHE.get(session_id) or [] + ) # 비차단: warm 전이면 빈 단서(graceful) ctx = orchestrator.prepare_turn( session_id=session_id, case_id=sess.case_id, @@ -446,7 +527,9 @@ async def _warm_rag_caches(session_id: str, case_id: str, card) -> None: """ async with _RAG_WARM_SEMAPHORE: try: - _RECALL_CACHE[session_id] = await _build_start_recall(case_id=case_id, card=card) + _RECALL_CACHE[session_id] = await _build_start_recall( + case_id=case_id, card=card + ) except Exception: pass try: @@ -460,7 +543,9 @@ def _ensure_learner(principal: Principal) -> Principal: return principal if principal.can_access_role(Role.LEARNER): return principal.with_role(Role.LEARNER) - raise HTTPException(status.HTTP_403_FORBIDDEN, detail="only learners can use sessions") + raise HTTPException( + status.HTTP_403_FORBIDDEN, detail="only learners can use sessions" + ) async def _ensure_practice_consent(principal: Principal) -> None: @@ -495,7 +580,9 @@ async def _load_session_or_404( if err == turn_runtime.SessionAccessError.NOT_FOUND: raise HTTPException(status.HTTP_404_NOT_FOUND, detail="session not found") if err == turn_runtime.SessionAccessError.FORBIDDEN: - raise HTTPException(status.HTTP_403_FORBIDDEN, detail="session does not belong to user") + raise HTTPException( + status.HTTP_403_FORBIDDEN, detail="session does not belong to user" + ) if err == turn_runtime.SessionAccessError.ENDED: raise HTTPException(status.HTTP_409_CONFLICT, detail="session already ended") assert sess is not None @@ -562,7 +649,9 @@ async def _load_review_session_or_404( supervisor = _review_supervisor_principal(principal) if supervisor is None: - raise HTTPException(status.HTTP_403_FORBIDDEN, detail="session review access denied") + raise HTTPException( + status.HTTP_403_FORBIDDEN, detail="session review access denied" + ) return ( await _load_supervisor_review_session_or_404( session_id, @@ -587,7 +676,9 @@ async def _end_persisted_session(sess: InProcSession, carry: memory.CarryOver) - def _should_schedule_session_digest_worker(carry: memory.CarryOver) -> bool: - return bool(settings.session_digest_worker_enabled and carry.compression_job is not None) + return bool( + settings.session_digest_worker_enabled and carry.compression_job is not None + ) async def _run_session_digest_worker_for_session(session_id: str) -> None: @@ -600,7 +691,9 @@ async def _run_session_digest_worker_for_session(session_id: str) -> None: try: db.get_pool() async with db.acquire(role="admin") as conn: - loaded = await session_digest_worker.load_session_digest_job(conn, session_id) + loaded = await session_digest_worker.load_session_digest_job( + conn, session_id + ) if loaded is None: return model = settings.session_digest_worker_model.strip() or None @@ -620,7 +713,9 @@ async def _run_session_digest_worker_for_session(session_id: str) -> None: learner_id=loaded.learner_id, ) except Exception: - logger.warning("session digest worker failed for session_id=%s", session_id, exc_info=True) + logger.warning( + "session digest worker failed for session_id=%s", session_id, exc_info=True + ) return @@ -653,7 +748,10 @@ def _public_share_url(request: Request, token: str) -> str: base = str(request.base_url).rstrip("/") return f"{base}/share/session/{token}" -async def _evaluate_stream_turn(ctx: orchestrator.TurnContext, final_reply: str) -> Optional[dict]: + +async def _evaluate_stream_turn( + ctx: orchestrator.TurnContext, final_reply: str +) -> Optional[dict]: """stream 경로 완료 후 fast-loop 평가를 계산한다. 실패는 턴 저장을 막지 않는다.""" if not final_reply: return None @@ -689,7 +787,9 @@ def _stream_result_from_done( state_after=ctx.state_after, evaluation=evaluation, crisis_kind=ctx.crisis.kind.value if ctx.crisis else "none", - crisis_resource=data.get("crisis_resource") if isinstance(data.get("crisis_resource"), dict) else None, + crisis_resource=data.get("crisis_resource") + if isinstance(data.get("crisis_resource"), dict) + else None, conversation_stopped=bool(data.get("conversation_stopped")), llm_provider=str(data.get("llm_provider") or "") or None, model=str(data.get("model") or "") or None, @@ -780,9 +880,13 @@ def _observe_session_evaluation_task(task: asyncio.Task[None], session_id: str) try: task.result() except asyncio.CancelledError: - logger.warning("session evaluation background task cancelled: session_id=%s", session_id) + logger.warning( + "session evaluation background task cancelled: session_id=%s", session_id + ) except Exception: - logger.exception("session evaluation background task crashed: session_id=%s", session_id) + logger.exception( + "session evaluation background task crashed: session_id=%s", session_id + ) def _schedule_session_evaluation(sess: InProcSession) -> asyncio.Task[None] | None: @@ -800,20 +904,26 @@ def _schedule_session_evaluation(sess: InProcSession) -> asyncio.Task[None] | No name=f"session-evaluation:{sess.session_id}", ) task.add_done_callback( - lambda done, session_id=sess.session_id: _observe_session_evaluation_task(done, session_id) + lambda done, session_id=sess.session_id: _observe_session_evaluation_task( + done, session_id + ) ) return task async def recover_missing_session_evaluations(*, limit: int | None = None) -> int: - recovery_limit = settings.session_evaluation_recovery_limit if limit is None else limit + recovery_limit = ( + settings.session_evaluation_recovery_limit if limit is None else limit + ) if recovery_limit <= 0: return 0 stale_after_seconds = ( - _session_evaluation_timeout_seconds() - + MISSING_SESSION_EVALUATION_GRACE_SECONDS + _session_evaluation_timeout_seconds() + MISSING_SESSION_EVALUATION_GRACE_SECONDS ) - candidates, durable = await session_persistence.list_sessions_missing_session_evaluation( + ( + candidates, + durable, + ) = await session_persistence.list_sessions_missing_session_evaluation( older_than_seconds=stale_after_seconds, limit=recovery_limit, ) @@ -892,9 +1002,7 @@ async def _load_learner_sessions( if not durable: require_runtime_fallback_allowed("session list") sessions = [ - sess - for sess in store.list() - if sess.learner_id == principal.user_id + sess for sess in store.list() if sess.learner_id == principal.user_id ] sessions.sort(key=lambda sess: sess.created_at, reverse=True) return sessions, durable @@ -917,7 +1025,9 @@ async def _review_ready_map( sessions: list[InProcSession], principal: Principal, ) -> dict[str, bool]: - results = await asyncio.gather(*[_review_ready(sess, principal) for sess in sessions]) + results = await asyncio.gather( + *[_review_ready(sess, principal) for sess in sessions] + ) return {sess.session_id: ready for sess, ready in zip(sessions, results)} @@ -970,7 +1080,9 @@ async def list_learner_sessions(principal: CurrentPrincipal) -> LearnerSessionsR sess, review_ready=await _review_ready(sess, principal), archived=archive_record is not None, - archived_at=_iso(float(archived_at)) if isinstance(archived_at, (int, float)) else None, + archived_at=_iso(float(archived_at)) + if isinstance(archived_at, (int, float)) + else None, ) ) @@ -1042,7 +1154,9 @@ async def archive_session( allow_ended=True, ) if not sess.ended: - raise HTTPException(status.HTTP_409_CONFLICT, detail="active sessions cannot be archived") + raise HTTPException( + status.HTTP_409_CONFLICT, detail="active sessions cannot be archived" + ) record, durable = await session_persistence.set_session_archived( session_id=sess.session_id, learner_id=principal.user_id, @@ -1053,7 +1167,9 @@ async def archive_session( sess, principal, archived=True, - archived_at=_iso(float(archived_at)) if isinstance(archived_at, (int, float)) else None, + archived_at=_iso(float(archived_at)) + if isinstance(archived_at, (int, float)) + else None, source="database" if durable else "runtime", ) @@ -1084,7 +1200,9 @@ async def restore_archived_session( ) -@router.post("", response_model=SessionStartResponse, status_code=status.HTTP_201_CREATED) +@router.post( + "", response_model=SessionStartResponse, status_code=status.HTTP_201_CREATED +) async def start_session( body: SessionStartRequest, principal: CurrentPrincipal, @@ -1102,14 +1220,18 @@ async def start_session( detail="persona catalog database unavailable", ) from exc if catalog_persona is None: - raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"unknown persona {body.persona_code}") + raise HTTPException( + status.HTTP_404_NOT_FOUND, detail=f"unknown persona {body.persona_code}" + ) card = catalog_persona.card case_context = await session_persistence.get_case_context( learner_id=principal.user_id, persona_id=catalog_persona.persona_id, ) - recall = await _build_seed_recall(case_id=case_context.case_id if case_context else None) + recall = await _build_seed_recall( + case_id=case_context.case_id if case_context else None + ) session_no = (case_context.last_session_no + 1) if case_context else 1 st = state_machine.init_state( params=card.openness_params(), @@ -1117,6 +1239,7 @@ async def start_session( ) carry_rapport = st.rapport_credit + goal_stages = [str(stage) for stage in body.goal_stages] sess = await session_persistence.create_session( learner_id=principal.user_id, card=card, @@ -1127,6 +1250,7 @@ async def start_session( persona_id=catalog_persona.persona_id, persona_version=catalog_persona.version, case_id=case_context.case_id if case_context else None, + goal_stages=goal_stages, ) degraded = catalog_persona.degraded or sess is None if sess is None: @@ -1138,6 +1262,7 @@ async def start_session( state=st, session_no=session_no, carry_rapport=carry_rapport, + goal_stages=goal_stages, ) else: store.put(sess) @@ -1155,6 +1280,10 @@ async def start_session( effective_openness=round(st.effective_openness, 4), recall_summary=recall.recall_summary, degraded=degraded, + started_at=_iso(sess.created_at) or "", + goal_stages=body.goal_stages, + duration_limit_seconds=settings.session_duration_minutes * 60, + warning_before_end_seconds=settings.session_warning_minutes * 60, ) @@ -1169,7 +1298,10 @@ async def get_session_review( principal, include_turn_evaluation=True, ) - evaluation_record, evaluation_durable = await session_persistence.load_session_evaluation( + ( + evaluation_record, + evaluation_durable, + ) = await session_persistence.load_session_evaluation( session_id, review_principal, ) @@ -1196,6 +1328,7 @@ async def get_session_review( ) ) + @router.post("/{session_id}/share", response_model=SessionShareResponse) async def create_session_share( session_id: str, @@ -1211,7 +1344,9 @@ async def create_session_share( include_turn_evaluation=True, ) if not sess.ended: - raise HTTPException(status.HTTP_409_CONFLICT, detail="session must be ended before sharing") + raise HTTPException( + status.HTTP_409_CONFLICT, detail="session must be ended before sharing" + ) review = await get_session_review(session_id, principal) token = secrets.token_urlsafe(32) @@ -1228,7 +1363,14 @@ async def create_session_share( detail="session share persistence unavailable", ) created_at = saved.get("created_at") - created_label = _iso(created_at if isinstance(created_at, (int, float)) else datetime.now().timestamp()) or "" + created_label = ( + _iso( + created_at + if isinstance(created_at, (int, float)) + else datetime.now().timestamp() + ) + or "" + ) return SessionShareResponse( shareUrl=_public_share_url(request, token), title=str(payload["title"]), @@ -1334,6 +1476,11 @@ async def submit_turn( crisis_resource=result.crisis_resource, conversation_stopped=result.conversation_stopped, output_error=result.output_error, + progress=build_session_progress( + result.state_after, + prev_rapport_credit=sess.prev_rapport_credit, + goal_stages=list(sess.goal_stages or []), + ), ) @@ -1345,17 +1492,28 @@ async def list_live_coach_history( """현재 회기에서 학습자에게 실제로 전달된 라이브 코칭 이력을 반환한다.""" principal = _ensure_learner(principal) await _load_session_or_404(session_id, principal) - events, durable = await session_persistence.list_live_coach_events(session_id, principal) - quota, quota_durable = await session_persistence.get_live_coach_quota(session_id, principal) - credit_events, credit_durable = await session_persistence.list_live_coach_credit_events( + events, durable = await session_persistence.list_live_coach_events( + session_id, principal + ) + quota, quota_durable = await session_persistence.get_live_coach_quota( + session_id, principal + ) + ( + credit_events, + credit_durable, + ) = await session_persistence.list_live_coach_credit_events( session_id, principal, ) return LiveCoachHistoryResponse( - source="database" if durable and quota_durable and credit_durable else "runtime", + source="database" + if durable and quota_durable and credit_durable + else "runtime", quota=live_coach.LiveCoachQuota(**quota), events=[live_coach.LiveCoachEvent(**event) for event in events], - credit_events=[live_coach.LiveCoachCreditEvent(**event) for event in credit_events], + credit_events=[ + live_coach.LiveCoachCreditEvent(**event) for event in credit_events + ], ) @@ -1382,6 +1540,16 @@ async def live_coach_turn( stage=stage, theory_mode=sess.theory_mode, ) + prior_events, _ = await session_persistence.list_live_coach_events( + session_id, principal + ) + prior_coach = [ + { + "title": str((event.get("suggestion") or {}).get("title") or ""), + "focus": str((event.get("suggestion") or {}).get("focus") or ""), + } + for event in prior_events[-2:] + ] item = live_coach.LiveCoachInput( session_id=sess.session_id, turn_seq=turn_seq, @@ -1394,6 +1562,8 @@ async def live_coach_turn( client_reply=body.client_reply, recent_turns=sess.recent_turns(k=8, visible_to=LEARNER_VISIBLE_AI_ROLE), evaluation=_latest_turn_evaluation(sess, body.turn_seq), + goal_stages=list(sess.goal_stages or []), + prior_coach=prior_coach, ) suggestion = await live_coach.generate_live_coaching( item, @@ -1416,8 +1586,13 @@ async def live_coach_turn( status_code=status.HTTP_409_CONFLICT, detail="live coach credit exhausted", ) from exc - quota_after, quota_durable = await session_persistence.get_live_coach_quota(session_id, principal) - credit_events, credit_durable = await session_persistence.list_live_coach_credit_events(session_id, principal) + quota_after, quota_durable = await session_persistence.get_live_coach_quota( + session_id, principal + ) + ( + credit_events, + credit_durable, + ) = await session_persistence.list_live_coach_credit_events(session_id, principal) turn_credit_events = [ live_coach.LiveCoachCreditEvent(**event) for event in credit_events @@ -1425,7 +1600,9 @@ async def live_coach_turn( ] return suggestion.model_copy( update={ - "persistence_source": "database" if coach_event_durable and quota_durable and credit_durable else "runtime", + "persistence_source": "database" + if coach_event_durable and quota_durable and credit_durable + else "runtime", "quota": live_coach.LiveCoachQuota(**quota_after), "credit_events": turn_credit_events[-2:], } @@ -1462,10 +1639,20 @@ async def stream_turn( final_reply += text yield {"event": "token", "data": text} elif ev.event == "done": - data = {**ev.data, "stage": _stage_label(ctx.state_after.stage)} + data = { + **ev.data, + "stage": _stage_label(ctx.state_after.stage), + "progress": build_session_progress( + ctx.state_after, + prev_rapport_credit=sess.prev_rapport_credit, + goal_stages=list(sess.goal_stages or []), + ).model_dump(), + } if not finalized_turn: evaluation = await _evaluate_stream_turn(ctx, final_reply) - result = _stream_result_from_done(ctx, final_reply, data, evaluation) + result = _stream_result_from_done( + ctx, final_reply, data, evaluation + ) await turn_runtime.finalize_completed_turn( sess, ctx, @@ -1473,7 +1660,10 @@ async def stream_turn( context_prefix="session", ) finalized_turn = True - yield {"event": "done", "data": json.dumps(data, ensure_ascii=False)} + yield { + "event": "done", + "data": json.dumps(data, ensure_ascii=False), + } elif ( ev.event == "safety" and bool(ev.data.get("conversation_stopped")) @@ -1484,14 +1674,18 @@ async def stream_turn( safety_data = { "session_id": ctx.session_id, "stage": _stage_label(ctx.state_after.stage), - "effective_openness": round(ctx.state_after.effective_openness, 4), + "effective_openness": round( + ctx.state_after.effective_openness, 4 + ), "turn_seq": ctx.state_after.turn_seq, "safety_flagged": True, "crisis_kind": ctx.crisis.kind.value, "crisis_resource": ev.data.get("crisis_resource"), "conversation_stopped": True, } - result = _stream_result_from_done(ctx, final_reply, safety_data, None) + result = _stream_result_from_done( + ctx, final_reply, safety_data, None + ) await turn_runtime.finalize_completed_turn( sess, ctx, @@ -1499,16 +1693,25 @@ async def stream_turn( context_prefix="session", ) finalized_turn = True - yield {"event": ev.event, "data": json.dumps(ev.data, ensure_ascii=False)} + yield { + "event": ev.event, + "data": json.dumps(ev.data, ensure_ascii=False), + } else: - yield {"event": ev.event, "data": json.dumps(ev.data, ensure_ascii=False)} + yield { + "event": ev.event, + "data": json.dumps(ev.data, ensure_ascii=False), + } now = asyncio.get_running_loop().time() if now - last_beat >= settings.sse_heartbeat_seconds: yield {"event": "ping", "data": "{}"} last_beat = now except Exception as exc: - yield {"event": "error", "data": json.dumps({"detail": str(exc)}, ensure_ascii=False)} + yield { + "event": "error", + "data": json.dumps({"detail": str(exc)}, ensure_ascii=False), + } return return EventSourceResponse(event_generator()) @@ -1536,8 +1739,7 @@ async def end_session( ) await _end_persisted_session(sess, carry) - _RECALL_CACHE.pop(session_id, None) - _KB_CUES_CACHE.pop(session_id, None) + invalidate_session_context_cache(session_id) if not was_ended: _schedule_session_evaluation(sess) diff --git a/apps/api/app/routes/users.py b/apps/api/app/routes/users.py index cd4fa26..e7c81eb 100644 --- a/apps/api/app/routes/users.py +++ b/apps/api/app/routes/users.py @@ -22,6 +22,7 @@ from ..config import settings from ..db import acquire, get_pool from ..deps import CurrentPrincipal, Role from ..runtime_policy import require_runtime_fallback_allowed +from ..services.phase3_kpi_contract import PREPOST_MEASURE_NAMES, PREPOST_TIMEPOINTS from ..services.support_tickets import support_ticket_fingerprint from ..services.voice import PRESET_RATE, PRESET_TO_OPENAI_VOICE @@ -37,13 +38,6 @@ AVATAR_CONTENT_TYPES = { } DEFAULT_PREPOST_PILOT_ID = "phase3-pilot-draft" DEFAULT_PREPOST_INSTRUMENT_VERSION = "pilot-prepost-scaffold-2026-06-28" -PREPOST_MEASURE_NAMES = ( - "self_efficacy", - "skill_proficiency", - "training_satisfaction", -) -PREPOST_TIMEPOINTS = ("pre", "post") - TERMS_BODY = """Vignette 서비스 이용약관 초안 1. 목적 @@ -193,7 +187,9 @@ class UserPreferencesResponse(BaseModel): theme: str = "system" voice_preset_id: str = "soft-young-fem" voice_rate: float = 1.0 - notifications: NotificationPreferences = Field(default_factory=NotificationPreferences) + notifications: NotificationPreferences = Field( + default_factory=NotificationPreferences + ) class UserPreferencesPatch(BaseModel): @@ -221,7 +217,9 @@ TicketCategory = Literal[ ] TicketPriority = Literal["low", "normal", "high", "urgent"] TicketStatus = Literal["open", "triaged", "in_progress", "resolved", "closed"] -PrepostMeasureName = Literal["self_efficacy", "skill_proficiency", "training_satisfaction"] +PrepostMeasureName = Literal[ + "self_efficacy", "skill_proficiency", "training_satisfaction" +] PrepostTimepoint = Literal["pre", "post"] @@ -386,7 +384,9 @@ def _preferences_from_row(row) -> UserPreferencesResponse: theme=row["theme"], voice_preset_id=_normalize_voice_preset(row["voice_preset_id"]), voice_rate=float(row["voice_rate"]), - notifications=NotificationPreferences.model_validate(row["notifications"] or {}), + notifications=NotificationPreferences.model_validate( + row["notifications"] or {} + ), ) @@ -420,7 +420,9 @@ def _validated_avatar_extension(content_type: str, content: bytes) -> str: ext, magic = AVATAR_CONTENT_TYPES[normalized] if normalized == "image/webp": if not (content.startswith(magic) and content[8:12] == b"WEBP"): - raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="invalid_avatar_file") + raise HTTPException( + status.HTTP_400_BAD_REQUEST, detail="invalid_avatar_file" + ) elif not content.startswith(magic): raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="invalid_avatar_file") return ext @@ -484,16 +486,28 @@ async def get_me(principal: CurrentPrincipal) -> UserProfileResponse: @router.patch("/me", response_model=UserProfileResponse) -async def patch_me(body: UserProfilePatch, principal: CurrentPrincipal) -> UserProfileResponse: +async def patch_me( + body: UserProfilePatch, principal: CurrentPrincipal +) -> UserProfileResponse: profile = await _profile_for(principal) updated = await update_managed_user( principal.user_id, ManagedUserPatch( - display_name=body.display_name if body.display_name is not None else profile.display_name, - affiliation=body.affiliation if body.affiliation is not None else profile.affiliation, - legal_name=body.legal_name if body.legal_name is not None else profile.legal_name, - department=body.department if body.department is not None else profile.department, - grade_level=body.grade_level if body.grade_level is not None else profile.grade_level, + display_name=body.display_name + if body.display_name is not None + else profile.display_name, + affiliation=body.affiliation + if body.affiliation is not None + else profile.affiliation, + legal_name=body.legal_name + if body.legal_name is not None + else profile.legal_name, + department=body.department + if body.department is not None + else profile.department, + grade_level=body.grade_level + if body.grade_level is not None + else profile.grade_level, phone=body.phone if body.phone is not None else profile.phone, contact_address=( body.contact_address @@ -506,7 +520,9 @@ async def patch_me(body: UserProfilePatch, principal: CurrentPrincipal) -> UserP if body.self_introduction is not None else profile.self_introduction ), - avatar_url=body.avatar_url if body.avatar_url is not None else profile.avatar_url, + avatar_url=body.avatar_url + if body.avatar_url is not None + else profile.avatar_url, ), ) if updated is None: @@ -525,7 +541,9 @@ async def upload_my_avatar( if not content: raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="empty_avatar_file") if len(content) > AVATAR_MAX_BYTES: - raise HTTPException(status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail="avatar_too_large") + raise HTTPException( + status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail="avatar_too_large" + ) ext = _validated_avatar_extension(content_type, content) root = _upload_root() @@ -597,7 +615,9 @@ async def create_support_ticket( ) -> UserSupportTicketResponse: profile = await _profile_for(principal) try: - async with acquire(role=principal.role.value, user_id=principal.user_id) as conn: + async with acquire( + role=principal.role.value, user_id=principal.user_id + ) as conn: subject = body.subject.strip() ticket_body = body.body.strip() source_path = body.source_path.strip() @@ -725,7 +745,9 @@ async def _support_tickets_for_user( resolution_note=row["resolution_note"] or "", created_at=float(row["created_at"] or 0.0), updated_at=float(row["updated_at"] or 0.0), - resolved_at=float(row["resolved_at"]) if row["resolved_at"] is not None else None, + resolved_at=float(row["resolved_at"]) + if row["resolved_at"] is not None + else None, ) for row in rows ], @@ -741,11 +763,13 @@ async def list_my_support_tickets( except Exception as exc: raise HTTPException( status.HTTP_503_SERVICE_UNAVAILABLE, - detail="support ticket persistence unavailable", + detail="support ticket persistence unavailable", ) from exc -def _normalized_prepost_score(raw_score: float, min_score: float, max_score: float) -> float: +def _normalized_prepost_score( + raw_score: float, min_score: float, max_score: float +) -> float: if max_score <= min_score: return 0.0 return round(((raw_score - min_score) / (max_score - min_score)) * 100.0, 3) @@ -803,7 +827,8 @@ async def _prepost_measures_for_user( pairs = { item.measure_name for item in measures - if {m.timepoint for m in measures if m.measure_name == item.measure_name} == {"pre", "post"} + if {m.timepoint for m in measures if m.measure_name == item.measure_name} + == {"pre", "post"} } return UserPrepostMeasuresResponse( source="database", @@ -820,7 +845,9 @@ async def _prepost_measures_for_user( @router.get("/me/prepost-measures", response_model=UserPrepostMeasuresResponse) async def list_my_prepost_measures( principal: CurrentPrincipal, - pilot_id: str = Query(default=DEFAULT_PREPOST_PILOT_ID, min_length=1, max_length=80), + pilot_id: str = Query( + default=DEFAULT_PREPOST_PILOT_ID, min_length=1, max_length=80 + ), ) -> UserPrepostMeasuresResponse: try: return await _prepost_measures_for_user(principal, pilot_id=pilot_id) @@ -837,9 +864,13 @@ async def upsert_my_prepost_measure( principal: CurrentPrincipal, ) -> UserPrepostMeasureItem: pilot_id = body.pilot_id.strip() or DEFAULT_PREPOST_PILOT_ID - instrument_version = body.instrument_version.strip() or DEFAULT_PREPOST_INSTRUMENT_VERSION + instrument_version = ( + body.instrument_version.strip() or DEFAULT_PREPOST_INSTRUMENT_VERSION + ) try: - async with acquire(role=principal.role.value, user_id=principal.user_id) as conn: + async with acquire( + role=principal.role.value, user_id=principal.user_id + ) as conn: row = await conn.fetchrow( """ INSERT INTO app.learner_prepost_measure ( @@ -982,7 +1013,9 @@ async def patch_preferences( if body.voice_preset_id is not None else current_prefs.voice_preset_id ), - voice_rate=body.voice_rate if body.voice_rate is not None else current_prefs.voice_rate, + voice_rate=body.voice_rate + if body.voice_rate is not None + else current_prefs.voice_rate, notifications=( body.notifications if body.notifications is not None diff --git a/apps/api/app/routes/voice.py b/apps/api/app/routes/voice.py index b8b02ff..3a06f8a 100644 --- a/apps/api/app/routes/voice.py +++ b/apps/api/app/routes/voice.py @@ -16,6 +16,7 @@ from __future__ import annotations import json import hashlib import time +from dataclasses import dataclass, field as dataclass_field from typing import Optional from fastapi import APIRouter, WebSocket, WebSocketDisconnect, HTTPException, status @@ -49,6 +50,42 @@ class VoiceSpeechRequest(BaseModel): session_id: str = Field(min_length=1, max_length=80) turn_seq: int = Field(ge=1) + +@dataclass(frozen=True, slots=True) +class VoiceSessionContext: + session_id: str + principal: Principal + voice_preset: VoicePreset + + +@dataclass(frozen=True, slots=True) +class VoiceProsody: + audio_ref: str | None = None + duration_s: float | None = None + silence_ms: int | None = None + speech_rate: float | None = None + barge_in: bool | None = None + provider_events: list[dict[str, object]] = dataclass_field(default_factory=list) + + +@dataclass(frozen=True, slots=True) +class VoiceTurnInput: + learner_text: str + prosody: VoiceProsody = dataclass_field(default_factory=VoiceProsody) + + +@dataclass(frozen=True, slots=True) +class VoiceAudioInput: + audio: bytes + fmt: str | None + sample_rate: int | None = None + channels: int | None = None + sample_width: int | None = None + audio_started_at: float | None = None + audio_ended_at: float | None = None + prosody: VoiceProsody = dataclass_field(default_factory=VoiceProsody) + + # WebSocket close codes. WS_CLOSE_DEGRADED = 1011 WS_CLOSE_BAD_REQUEST = 1008 @@ -112,6 +149,8 @@ def _is_turn_persistence_unavailable(exc: Exception) -> bool: return False detail = str(exc.detail or "") return "turn append" in detail and "persistence unavailable" in detail + + _PROVIDER_EVENT_TYPE_FIELDS = ("event_type", "type", "kind", "label") @@ -132,7 +171,9 @@ async def voice_health() -> JSONResponse: @router.post("/speech") -async def voice_speech(body: VoiceSpeechRequest, principal: CurrentPrincipal) -> Response: +async def voice_speech( + body: VoiceSpeechRequest, principal: CurrentPrincipal +) -> Response: """Synthesize the persisted client reply for a completed text turn. The browser sends only session/turn identifiers. The server reloads the @@ -167,7 +208,9 @@ async def voice_speech(body: VoiceSpeechRequest, principal: CurrentPrincipal) -> turn_runtime.SessionAccessError.FORBIDDEN: status.HTTP_403_FORBIDDEN, turn_runtime.SessionAccessError.ENDED: status.HTTP_409_CONFLICT, }.get(err, status.HTTP_404_NOT_FOUND) - raise HTTPException(status_code=status_code, detail=f"voice session {err or 'not_found'}") + raise HTTPException( + status_code=status_code, detail=f"voice session {err or 'not_found'}" + ) text = _client_turn_text_for_speech(sess, body.turn_seq) if text is None: @@ -222,13 +265,17 @@ async def voice_ws(websocket: WebSocket) -> None: # Authenticate the same server-side browser session used by REST routes. principal = await _principal_from_websocket(websocket) if principal is None: - await _safe_send_json(websocket, {"type": "error", "detail": "not authenticated"}) + await _safe_send_json( + websocket, {"type": "error", "detail": "not authenticated"} + ) await _safe_close(websocket, WS_CLOSE_UNAUTHORIZED) return if principal.role != Role.LEARNER and principal.can_access_role(Role.LEARNER): principal = principal.with_role(Role.LEARNER) if principal.role != Role.LEARNER: - await _safe_send_json(websocket, {"type": "error", "detail": "only learners can use voice"}) + await _safe_send_json( + websocket, {"type": "error", "detail": "only learners can use voice"} + ) await _safe_close(websocket, WS_CLOSE_UNAUTHORIZED) return @@ -244,7 +291,9 @@ async def voice_ws(websocket: WebSocket) -> None: websocket, { "type": "degraded", - "reason": bind_meta.get("degraded_reason", "voice session binding degraded"), + "reason": bind_meta.get( + "degraded_reason", "voice session binding degraded" + ), **bind_meta, }, ) @@ -294,12 +343,17 @@ async def voice_ws(websocket: WebSocket) -> None: receiving = True audio_started_at = time.monotonic() audio_buf.clear() - await _safe_send_json(websocket, {"type": "state", "state": "listening"}) + await _safe_send_json( + websocket, {"type": "state", "state": "listening"} + ) audio_buf.extend(msg["bytes"]) if len(audio_buf) > _MAX_AUDIO_BYTES: await _safe_send_json( websocket, - {"type": "error", "detail": "audio too large; please send a shorter utterance"}, + { + "type": "error", + "detail": "audio too large; please send a shorter utterance", + }, ) audio_buf.clear() receiving = False @@ -312,7 +366,9 @@ async def voice_ws(websocket: WebSocket) -> None: try: ctrl = json.loads(text) except (json.JSONDecodeError, TypeError): - await _safe_send_json(websocket, {"type": "error", "detail": "invalid control json"}) + await _safe_send_json( + websocket, {"type": "error", "detail": "invalid control json"} + ) continue ctype = ctrl.get("type") @@ -324,30 +380,44 @@ async def voice_ws(websocket: WebSocket) -> None: audio_channels = _safe_int(ctrl.get("channels")) audio_sample_width = _safe_int(ctrl.get("sample_width")) audio_buf.clear() - await _safe_send_json(websocket, {"type": "state", "state": "listening"}) + await _safe_send_json( + websocket, {"type": "state", "state": "listening"} + ) elif ctype == "audio_end": receiving = False audio_ended_at = time.monotonic() silence_ms = _safe_int(ctrl.get("silence_ms")) - if silence_ms is None and last_audio_end_at is not None and audio_started_at is not None: - silence_ms = max(0, int((audio_started_at - last_audio_end_at) * 1000)) + if ( + silence_ms is None + and last_audio_end_at is not None + and audio_started_at is not None + ): + silence_ms = max( + 0, int((audio_started_at - last_audio_end_at) * 1000) + ) end_format = _safe_str(ctrl.get("format")) or audio_format await _handle_utterance( websocket, - session_id=session_id, - principal=principal, - voice_preset=voice_preset, - audio=bytes(audio_buf), - fmt=end_format, - sample_rate=_safe_int(ctrl.get("sample_rate")) or audio_sample_rate, - channels=_safe_int(ctrl.get("channels")) or audio_channels, - sample_width=_safe_int(ctrl.get("sample_width")) or audio_sample_width, - audio_started_at=audio_started_at, - audio_ended_at=audio_ended_at, - silence_ms=silence_ms, - barge_in=_safe_bool(ctrl.get("barge_in")), - provider_events=_safe_provider_events(ctrl.get("provider_events")), + VoiceSessionContext(session_id, principal, voice_preset), + VoiceAudioInput( + audio=bytes(audio_buf), + fmt=end_format, + sample_rate=_safe_int(ctrl.get("sample_rate")) + or audio_sample_rate, + channels=_safe_int(ctrl.get("channels")) or audio_channels, + sample_width=_safe_int(ctrl.get("sample_width")) + or audio_sample_width, + audio_started_at=audio_started_at, + audio_ended_at=audio_ended_at, + prosody=VoiceProsody( + silence_ms=silence_ms, + barge_in=_safe_bool(ctrl.get("barge_in")), + provider_events=_safe_provider_events( + ctrl.get("provider_events") + ), + ), + ), ) last_audio_end_at = audio_ended_at audio_started_at = None @@ -365,10 +435,8 @@ async def voice_ws(websocket: WebSocket) -> None: if learner_text: await _run_turn_and_speak( websocket, - session_id=session_id, - principal=principal, - voice_preset=voice_preset, - learner_text=learner_text, + VoiceSessionContext(session_id, principal, voice_preset), + VoiceTurnInput(learner_text=learner_text), ) elif ctype == "stt_result": @@ -412,7 +480,9 @@ async def voice_ws(websocket: WebSocket) -> None: ) await _safe_send_json(websocket, {"type": "state", "state": "idle"}) else: - await _safe_send_json(websocket, {"type": "error", "detail": f"voice ws error: {e}"}) + await _safe_send_json( + websocket, {"type": "error", "detail": f"voice ws error: {e}"} + ) finally: await _safe_close(websocket) @@ -431,7 +501,11 @@ async def _handle_stt_result_control( learner_text = str(ctrl.get("text") or "").strip() transcript_final = _safe_bool(ctrl.get("final")) silence_ms = _safe_int(ctrl.get("silence_ms")) - if silence_ms is None and last_audio_end_at is not None and audio_started_at is not None: + if ( + silence_ms is None + and last_audio_end_at is not None + and audio_started_at is not None + ): silence_ms = max(0, int((audio_started_at - last_audio_end_at) * 1000)) provider_events = _safe_provider_events(ctrl.get("provider_events")) decision = voice_svc.assess_end_of_turn( @@ -456,52 +530,49 @@ async def _handle_stt_result_control( await _safe_send_json(websocket, {"type": "state", "state": "thinking"}) await _safe_send_json( websocket, - {"type": "transcript", "text": learner_text, "final": True, "speaker": "counselor"}, + { + "type": "transcript", + "text": learner_text, + "final": True, + "speaker": "counselor", + }, ) await _run_turn_and_speak( websocket, - session_id=session_id, - principal=principal, - voice_preset=voice_preset, - learner_text=learner_text, - duration_s=_elapsed_seconds(audio_started_at, audio_ended_at), - silence_ms=decision.silence_ms, - barge_in=_safe_bool(ctrl.get("barge_in")), - provider_events=provider_events, + VoiceSessionContext(session_id, principal, voice_preset), + VoiceTurnInput( + learner_text=learner_text, + prosody=VoiceProsody( + duration_s=_elapsed_seconds(audio_started_at, audio_ended_at), + silence_ms=decision.silence_ms, + barge_in=_safe_bool(ctrl.get("barge_in")), + provider_events=provider_events, + ), + ), ) async def _handle_utterance( websocket: WebSocket, - *, - session_id: str, - principal: Principal, - voice_preset: VoicePreset, - audio: bytes, - fmt: Optional[str], - sample_rate: int | None = None, - channels: int | None = None, - sample_width: int | None = None, - audio_started_at: float | None = None, - audio_ended_at: float | None = None, - silence_ms: int | None = None, - barge_in: bool | None = None, - provider_events: list[dict[str, object]] | None = None, + context: VoiceSessionContext, + utterance: VoiceAudioInput, ) -> None: """Transcribe one utterance, generate the client reply, then synthesize TTS.""" - if not audio: - await _safe_send_json(websocket, {"type": "transcript", "text": "", "final": True}) + if not utterance.audio: + await _safe_send_json( + websocket, {"type": "transcript", "text": "", "final": True} + ) await _safe_send_json(websocket, {"type": "state", "state": "idle"}) return # STT begins after the learner stops speaking. await _safe_send_json(websocket, {"type": "state", "state": "thinking"}) upload_audio, upload_fmt = _normalize_audio_upload( - audio, - fmt=fmt, - sample_rate=sample_rate, - channels=channels, - sample_width=sample_width, + utterance.audio, + fmt=utterance.fmt, + sample_rate=utterance.sample_rate, + channels=utterance.channels, + sample_width=utterance.sample_width, ) filename, content_type = _audio_meta(upload_fmt) try: @@ -513,18 +584,30 @@ async def _handle_utterance( await _safe_send_json(websocket, {"type": "state", "state": "idle"}) return except Exception as e: - await _safe_send_json(websocket, {"type": "error", "detail": f"STT failed: {e}"}) + await _safe_send_json( + websocket, {"type": "error", "detail": f"STT failed: {e}"} + ) await _safe_send_json(websocket, {"type": "state", "state": "idle"}) return learner_text = stt.text audio_ref = _voice_audio_ref(upload_audio, upload_fmt) - duration_s = stt.duration or _elapsed_seconds(audio_started_at, audio_ended_at) + duration_s = stt.duration or _elapsed_seconds( + utterance.audio_started_at, utterance.audio_ended_at + ) speech_rate = _estimate_speech_rate(learner_text, duration_s) - provider_events = _merge_provider_events(provider_events, getattr(stt, "provider_events", [])) + provider_events = _merge_provider_events( + utterance.prosody.provider_events, + getattr(stt, "provider_events", []), + ) await _safe_send_json( websocket, - {"type": "transcript", "text": learner_text, "final": True, "speaker": "counselor"}, + { + "type": "transcript", + "text": learner_text, + "final": True, + "speaker": "counselor", + }, ) if not learner_text: await _safe_send_json(websocket, {"type": "state", "state": "idle"}) @@ -532,47 +615,54 @@ async def _handle_utterance( await _run_turn_and_speak( websocket, - session_id=session_id, - principal=principal, - voice_preset=voice_preset, - learner_text=learner_text, - audio_ref=audio_ref, - silence_ms=silence_ms, - speech_rate=speech_rate, - barge_in=barge_in, - provider_events=provider_events, + context, + VoiceTurnInput( + learner_text=learner_text, + prosody=VoiceProsody( + audio_ref=audio_ref, + duration_s=duration_s, + silence_ms=utterance.prosody.silence_ms, + speech_rate=speech_rate, + barge_in=utterance.prosody.barge_in, + provider_events=provider_events, + ), + ), ) async def _run_turn_and_speak( websocket: WebSocket, - *, - session_id: str, - principal: Principal, - voice_preset: VoicePreset, - learner_text: str, - audio_ref: str | None = None, - duration_s: float | None = None, - silence_ms: int | None = None, - speech_rate: float | None = None, - barge_in: bool | None = None, - provider_events: list[dict[str, object]] | None = None, + context: VoiceSessionContext, + turn: VoiceTurnInput, ) -> None: """Run one counseling turn and stream synthesized client speech.""" + learner_text = turn.learner_text + prosody = turn.prosody + speech_rate = prosody.speech_rate if speech_rate is None: - speech_rate = _estimate_speech_rate(learner_text, duration_s) - sess, err = await _load_voice_session(session_id, principal) + speech_rate = _estimate_speech_rate(learner_text, prosody.duration_s) + sess, err = await _load_voice_session(context.session_id, context.principal) if sess is None: - await _safe_send_json(websocket, {"type": "error", "detail": err or "session not found or ended"}) + await _safe_send_json( + websocket, {"type": "error", "detail": err or "session not found or ended"} + ) await _safe_send_json(websocket, {"type": "state", "state": "idle"}) return from . import sessions as session_routes + if session_routes.session_time_over(sess): + await _safe_send_json( + websocket, + {"type": "error", "detail": "session_time_over"}, + ) + await _safe_send_json(websocket, {"type": "state", "state": "idle"}) + return + recall = await session_routes.ensure_recall_context(sess) - kb_cues = session_routes._KB_CUES_CACHE.get(session_id) or [] + kb_cues = session_routes.cached_kb_cues(context.session_id) ctx = orchestrator.prepare_turn( - session_id=session_id, + session_id=context.session_id, case_id=sess.case_id, card=sess.persona, state=sess.state, @@ -599,7 +689,9 @@ async def _run_turn_and_speak( audit_hook=session_persistence.record_llm_call_audit, ) except EngineError as e: - await _safe_send_json(websocket, {"type": "error", "detail": f"engine unavailable: {e}"}) + await _safe_send_json( + websocket, {"type": "error", "detail": f"engine unavailable: {e}"} + ) await _safe_send_json(websocket, {"type": "state", "state": "idle"}) return @@ -617,11 +709,11 @@ async def _run_turn_and_speak( stage=turn_runtime.stage_label(ctx.state_after.stage), text=learner_text, text_masked=ctx.learner_text_masked, - audio_ref=audio_ref, - silence_ms=silence_ms, + audio_ref=prosody.audio_ref, + silence_ms=prosody.silence_ms, speech_rate=speech_rate, - barge_in=barge_in, - provider_events=provider_events or [], + barge_in=prosody.barge_in, + provider_events=prosody.provider_events, evaluation=result.evaluation, ), ) @@ -640,6 +732,11 @@ async def _run_turn_and_speak( "crisis_kind": result.crisis_kind, "crisis_resource": result.crisis_resource, "conversation_stopped": result.conversation_stopped, + "progress": session_routes.build_session_progress( + result.state_after, + prev_rapport_credit=sess.prev_rapport_credit, + goal_stages=list(sess.goal_stages or []), + ).model_dump(), }, ) @@ -653,13 +750,13 @@ async def _run_turn_and_speak( { "type": "state", "state": "speaking", - "voice": voice_preset.openai_voice, + "voice": context.voice_preset.openai_voice, "tts_provider": voice_service.tts_provider(), }, ) try: n = 0 - async for ck in voice_service.synthesize_stream(reply, voice_preset): + async for ck in voice_service.synthesize_stream(reply, context.voice_preset): # 바이너리 오디오 청크만 송신(프론트가 Web Audio AnalyserNode로 립싱크 자체 산출). await _safe_send_bytes(websocket, ck.audio) n += 1 @@ -667,7 +764,9 @@ async def _run_turn_and_speak( except VoiceUnavailable as e: await _safe_send_json(websocket, {"type": "degraded", "reason": str(e)}) except Exception as e: - await _safe_send_json(websocket, {"type": "error", "detail": f"TTS failed: {e}"}) + await _safe_send_json( + websocket, {"type": "error", "detail": f"TTS failed: {e}"} + ) await _safe_send_json(websocket, {"type": "state", "state": "idle"}) @@ -732,9 +831,8 @@ async def _principal_from_websocket(websocket: WebSocket) -> Principal | None: async def _practice_access_error(principal: Principal) -> str | None: - if ( - principal.profile_completed_at is None - and not await user_onboarding_complete(principal.user_id) + if principal.profile_completed_at is None and not await user_onboarding_complete( + principal.user_id ): return "onboarding_required" if principal.consent_at is None and not await user_has_consent(principal.user_id): @@ -763,7 +861,12 @@ async def _bind_session( persona_code=sess.persona.code, explicit_preset=explicit_preset, ) - return session_id, vp, None, {"degraded": False, "persona_catalog_source": "session"} + return ( + session_id, + vp, + None, + {"degraded": False, "persona_catalog_source": "session"}, + ) # persona_code session creation is local-dev only. Production uses REST start. if settings.environment != "dev": @@ -815,9 +918,13 @@ async def _bind_session( ) degraded_reasons: list[str] = [] if catalog_persona.degraded: - degraded_reasons.append("카탈로그 원본을 확인하지 못해 음성 회기를 시작하지 않습니다") + degraded_reasons.append( + "카탈로그 원본을 확인하지 못해 음성 회기를 시작하지 않습니다" + ) if session_source == "runtime": - degraded_reasons.append("세션 저장소 연결 전까지 비영구 개발 런타임 기록을 사용합니다") + degraded_reasons.append( + "세션 저장소 연결 전까지 비영구 개발 런타임 기록을 사용합니다" + ) bind_meta = { "degraded": bool(degraded_reasons), "degraded_reason": "; ".join(degraded_reasons) if degraded_reasons else None, @@ -906,7 +1013,9 @@ def _normalize_audio_upload( raise ValueError("pcm sample_width must be 2 bytes") return _wav_from_pcm16( audio, - sample_rate=_bounded_int(sample_rate, default=48000, minimum=8000, maximum=96000), + sample_rate=_bounded_int( + sample_rate, default=48000, minimum=8000, maximum=96000 + ), channels=_bounded_int(channels, default=1, minimum=1, maximum=2), ), "wav" diff --git a/apps/api/app/runtime_schema.py b/apps/api/app/runtime_schema.py new file mode 100644 index 0000000..758c1bd --- /dev/null +++ b/apps/api/app/runtime_schema.py @@ -0,0 +1,126 @@ +"""런타임 스키마 준비 상태 정책. + +권위 스키마는 ``infra/db/init/*.sql``이다. 앱 기동 중 불완전한 스키마 보정은 로컬 개발 +환경에서만 허용하고, 스테이징과 운영은 owner migration을 요구하며 fail-closed한다. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Protocol + +from .config import settings + + +class SchemaConnection(Protocol): + async def fetchrow(self, query: str, *args: Any) -> Any: ... + + +@dataclass(frozen=True, slots=True) +class RuntimeSchemaContract: + component: str + relations: tuple[str, ...] + columns: tuple[str, ...] = () + policies: tuple[str, ...] = () + + +REVIEW_SCHEMA_CONTRACT = RuntimeSchemaContract( + component="review/evaluation", + relations=( + "app.session_evaluation", + "app.case_worksheet", + "app.live_coach_events", + "app.safety_events", + "app.session_review_status", + "app.session_share_link", + "app.session_archive_state", + ), + columns=( + "app.live_coach_events.event_type", + "app.live_coach_events.credit_delta", + "app.live_coach_events.credit_balance", + "app.live_coach_events.reason", + "app.session_review_status.worksheet_status", + "app.session_review_status.worksheet_note", + "app.session_review_status.worksheet_reviewed_at", + ), + policies=( + "app.session_evaluation.p_session_evaluation_select", + "app.case_worksheet.p_case_worksheet_select", + "app.live_coach_events.p_live_coach_events_select", + "app.safety_events.p_safety_events_select", + "app.session_review_status.p_session_review_status_select", + "app.session_share_link.p_session_share_select", + "app.session_archive_state.p_session_archive_select", + ), +) + +NOTIFICATION_SCHEMA_CONTRACT = RuntimeSchemaContract( + component="notification", + relations=("app.notification_event", "app.notification_delivery"), + policies=( + "app.notification_event.p_notification_event_admin_all", + "app.notification_delivery.p_notification_delivery_admin_all", + ), +) + + +async def schema_contract_ready( + conn: SchemaConnection, + contract: RuntimeSchemaContract, +) -> bool: + row = await conn.fetchrow( + """ + SELECT + NOT EXISTS ( + SELECT 1 + FROM unnest($1::text[]) AS required(relation_name) + WHERE to_regclass(required.relation_name) IS NULL + ) + AND NOT EXISTS ( + SELECT 1 + FROM unnest($2::text[]) AS required(qualified_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM information_schema.columns c + WHERE c.table_schema = split_part(required.qualified_name, '.', 1) + AND c.table_name = split_part(required.qualified_name, '.', 2) + AND c.column_name = split_part(required.qualified_name, '.', 3) + ) + ) + AND NOT EXISTS ( + SELECT 1 + FROM unnest($3::text[]) AS required(qualified_name) + WHERE NOT EXISTS ( + SELECT 1 + FROM pg_policies p + WHERE p.schemaname = split_part(required.qualified_name, '.', 1) + AND p.tablename = split_part(required.qualified_name, '.', 2) + AND p.policyname = split_part(required.qualified_name, '.', 3) + ) + ) AS ready + """, + list(contract.relations), + list(contract.columns), + list(contract.policies), + ) + return bool(row and row["ready"]) + + +def runtime_schema_bootstrap_required( + contract: RuntimeSchemaContract | str, + *, + ready: bool, +) -> bool: + """로컬 보정 필요 여부를 반환하고, dev 외 환경에서는 불완전 스키마를 차단한다.""" + if ready: + return False + component = ( + contract.component if isinstance(contract, RuntimeSchemaContract) else contract + ) + if settings.environment != "dev": + raise RuntimeError( + f"{component} runtime DB schema is incomplete; run owner migration/init and " + "scripts/check-deploy-preflight.py before starting the API" + ) + return True diff --git a/apps/api/app/services/dataset_export.py b/apps/api/app/services/dataset_export.py index 478f116..3fce421 100644 --- a/apps/api/app/services/dataset_export.py +++ b/apps/api/app/services/dataset_export.py @@ -52,7 +52,9 @@ PII_PATTERNS: tuple[tuple[str, re.Pattern[str]], ...] = ( ("email", re.compile(r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b", re.I)), ( "phone", - re.compile(r"\b(?:\+?82[-. ]?)?(?:0?1[016789]|0[2-9]\d?)[-. ]?\d{3,4}[-. ]?\d{4}\b"), + re.compile( + r"\b(?:\+?82[-. ]?)?(?:0?1[016789]|0[2-9]\d?)[-. ]?\d{3,4}[-. ]?\d{4}\b" + ), ), ("national_id", re.compile(r"\b\d{6}[- ]?[1-4]\d{6}\b")), ("student_id", re.compile(r"\b20\d{2}[- ]?\d{4,8}\b")), @@ -62,7 +64,12 @@ PII_PATTERNS: tuple[tuple[str, re.Pattern[str]], ...] = ( r"(?i)\b(?:api[_-]?key|access[_-]?token|refresh[_-]?token|session[_-]?secret|cookie)\b\s*[:=]\s*\S+" ), ), - ("secret", re.compile(r"\b(?:sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,}|xox[baprs]-[A-Za-z0-9-]+)\b")), + ( + "secret", + re.compile( + r"\b(?:sk-[A-Za-z0-9_-]{12,}|AIza[0-9A-Za-z_-]{20,}|xox[baprs]-[A-Za-z0-9-]+)\b" + ), + ), ) @@ -155,7 +162,13 @@ def scan_for_pii(value: Any, path: str = "$") -> list[dict[str, Any]]: key = str(raw_key) next_path = f"{path}.{key}" if key.lower() in BLOCKED_FIELD_NAMES: - findings.append({"kind": "blocked_field", "path": next_path, "sample": f"<{key.lower()}>"}) + findings.append( + { + "kind": "blocked_field", + "path": next_path, + "sample": f"<{key.lower()}>", + } + ) findings.extend(scan_for_pii(item, next_path)) return findings if isinstance(value, list): @@ -195,11 +208,19 @@ def build_dataset_record( "speaker": row.get("speaker") or "", "text_masked": text_masked, "techniques": json_safe(normalize_json_value(row.get("techniques") or [])), - "client_states": json_safe(normalize_json_value(row.get("client_states") or [])), - "feedback_scores": json_safe(normalize_json_value(row.get("feedback_scores") or [])), - "supervisor_comments": export_safe_supervisor_comments(row.get("supervisor_comments") or []), + "client_states": json_safe( + normalize_json_value(row.get("client_states") or []) + ), + "feedback_scores": json_safe( + normalize_json_value(row.get("feedback_scores") or []) + ), + "supervisor_comments": export_safe_supervisor_comments( + row.get("supervisor_comments") or [] + ), "source_refs": { - "session_started_at": json_safe(row.get("session_started_at") or row.get("started_at")), + "session_started_at": json_safe( + row.get("session_started_at") or row.get("started_at") + ), "export_manifest_id": export_manifest_id, }, "privacy": { @@ -214,7 +235,14 @@ def write_jsonl(records: Sequence[Mapping[str, Any]], path: Path) -> None: path.parent.mkdir(parents=True, exist_ok=True) with path.open("w", encoding="utf-8", newline="\n") as handle: for record in records: - handle.write(json.dumps(json_safe(record), ensure_ascii=False, sort_keys=True, separators=(",", ":"))) + handle.write( + json.dumps( + json_safe(record), + ensure_ascii=False, + sort_keys=True, + separators=(",", ":"), + ) + ) handle.write("\n") @@ -226,7 +254,9 @@ def sha256_file(path: Path) -> str: return digest.hexdigest() -def cohen_kappa(annotations: Iterable[Mapping[str, Any]], label_key: str) -> float | None: +def cohen_kappa( + annotations: Iterable[Mapping[str, Any]], label_key: str +) -> float | None: pairs: list[tuple[Any, Any]] = [] by_item: dict[Any, list[Any]] = defaultdict(list) for annotation in annotations: @@ -244,13 +274,18 @@ def cohen_kappa(annotations: Iterable[Mapping[str, Any]], label_key: str) -> flo observed = sum(1 for left, right in pairs if left == right) / total left_counts = Counter(left for left, _ in pairs) right_counts = Counter(right for _, right in pairs) - expected = sum((left_counts[label] / total) * (right_counts[label] / total) for label in set(left_counts) | set(right_counts)) + expected = sum( + (left_counts[label] / total) * (right_counts[label] / total) + for label in set(left_counts) | set(right_counts) + ) if math.isclose(1.0, expected): return 1.0 if math.isclose(1.0, observed) else None return round((observed - expected) / (1.0 - expected), 4) -def intraclass_correlation(annotations: Iterable[Mapping[str, Any]], score_key: str) -> float | None: +def intraclass_correlation( + annotations: Iterable[Mapping[str, Any]], score_key: str +) -> float | None: by_item: dict[Any, list[float]] = defaultdict(list) for annotation in annotations: labels = normalize_json_value(annotation.get("labels") or {}) @@ -275,7 +310,9 @@ def intraclass_correlation(annotations: Iterable[Mapping[str, Any]], score_key: residual = 0.0 for row_index, row in enumerate(matrix): for col_index, value in enumerate(row): - residual += (value - row_means[row_index] - col_means[col_index] + grand_mean) ** 2 + residual += ( + value - row_means[row_index] - col_means[col_index] + grand_mean + ) ** 2 mse = residual / ((n - 1) * (k - 1)) denominator = msr + (k - 1) * mse + (k * (msc - mse) / n) if math.isclose(denominator, 0.0): @@ -284,7 +321,9 @@ def intraclass_correlation(annotations: Iterable[Mapping[str, Any]], score_key: def infer_source_window(records: Sequence[Mapping[str, Any]]) -> dict[str, Any]: - starts = [record.get("source_refs", {}).get("session_started_at") for record in records] + starts = [ + record.get("source_refs", {}).get("session_started_at") for record in records + ] starts = [value for value in starts if value] return { "started_at": min(starts) if starts else "", @@ -292,35 +331,39 @@ def infer_source_window(records: Sequence[Mapping[str, Any]]) -> dict[str, Any]: } -def build_manifest( - *, - export_id: str, - dataset_name: str, - export_status: str, - purpose: str, - records: Sequence[Mapping[str, Any]], - jsonl_path: str, - jsonl_sha256: str, - pii_findings: Sequence[Mapping[str, Any]], - participants_included: int, - participants_excluded: int = 0, - cohort_id: str = "phase3", - consent_version: str = "", - agreement: Mapping[str, Any] | None = None, - approvals: Mapping[str, str] | None = None, - known_limitations: Sequence[str] | None = None, - created_at: datetime | None = None, -) -> dict[str, Any]: - if export_status not in EXPORT_STATUSES: - raise ValueError(f"unsupported export_status: {export_status}") +@dataclass(frozen=True, slots=True) +class DatasetManifestInput: + """Approved/dry-run dataset artifact metadata crossing the export boundary.""" + + export_id: str + dataset_name: str + export_status: str + purpose: str + records: Sequence[Mapping[str, Any]] + jsonl_path: str + jsonl_sha256: str + pii_findings: Sequence[Mapping[str, Any]] + participants_included: int + participants_excluded: int = 0 + cohort_id: str = "phase3" + consent_version: str = "" + agreement: Mapping[str, Any] | None = None + approvals: Mapping[str, str] | None = None + known_limitations: Sequence[str] | None = None + created_at: datetime | None = None + + +def build_manifest(spec: DatasetManifestInput) -> dict[str, Any]: + if spec.export_status not in EXPORT_STATUSES: + raise ValueError(f"unsupported export_status: {spec.export_status}") agreement_payload = { "kappa": None, "icc": None, "gold_status": "not_gold", } - if agreement: - agreement_payload.update(dict(agreement)) + if spec.agreement: + agreement_payload.update(dict(spec.agreement)) approvals_payload = { "data_steward": "", @@ -328,23 +371,27 @@ def build_manifest( "technical_operator": "", "approved_at": "", } - if approvals: - approvals_payload.update({key: value for key, value in approvals.items() if value is not None}) + if spec.approvals: + approvals_payload.update( + {key: value for key, value in spec.approvals.items() if value is not None} + ) - pii_status = "pass" if not pii_findings else "fail" - limitations = list(known_limitations or []) - if export_status != APPROVED_EXPORT_STATUS: - limitations.append("technical dry-run only; data-steward/legal approval is not complete") - if pii_findings: + pii_status = "pass" if not spec.pii_findings else "fail" + limitations = list(spec.known_limitations or []) + if spec.export_status != APPROVED_EXPORT_STATUS: + limitations.append( + "technical dry-run only; data-steward/legal approval is not complete" + ) + if spec.pii_findings: limitations.append("PII scan found records requiring reviewer disposition") manifest = { - "export_id": export_id, - "dataset_name": dataset_name, - "export_status": export_status, - "created_at": json_safe(created_at or datetime.now(UTC)), - "purpose": purpose, - "source_window": infer_source_window(records), + "export_id": spec.export_id, + "dataset_name": spec.dataset_name, + "export_status": spec.export_status, + "created_at": json_safe(spec.created_at or datetime.now(UTC)), + "purpose": spec.purpose, + "source_window": infer_source_window(spec.records), "source_tables": [ "app.sessions", "app.turns", @@ -356,17 +403,17 @@ def build_manifest( "ds.export_manifest", ], "selection_criteria": { - "cohort_id": cohort_id, + "cohort_id": spec.cohort_id, "min_completed_sessions": 0, "include_withdrawn": False, "excluded_safety_scope": ["self_harm_scenario_primary"], }, "consent_scope": { - "consent_version": consent_version, + "consent_version": spec.consent_version, "allowed_uses": ["education_quality_review", "recursive_learning_seed"], "withdrawal_cutoff_applied_at": "", - "participants_included": participants_included, - "participants_excluded": participants_excluded, + "participants_included": spec.participants_included, + "participants_excluded": spec.participants_excluded, }, "anonymization": { "participant_key": "pseudonymous export key; no identity map included", @@ -379,14 +426,14 @@ def build_manifest( "version": "1", "ran_at": json_safe(datetime.now(UTC)), "status": pii_status, - "findings": [json_safe(finding) for finding in pii_findings], + "findings": [json_safe(finding) for finding in spec.pii_findings], }, "agreement": agreement_payload, "files": [ { - "path": jsonl_path, - "rows": len(records), - "sha256": jsonl_sha256, + "path": spec.jsonl_path, + "rows": len(spec.records), + "sha256": spec.jsonl_sha256, "schema": DATASET_ITEM_SCHEMA, } ], @@ -411,13 +458,28 @@ def validate_manifest_gate(manifest: Mapping[str, Any]) -> None: if (agreement.get("icc") or 0) < 0.75: errors.append("ICC must be >= 0.75") selection_criteria = manifest.get("selection_criteria") or {} - if not isinstance(selection_criteria, Mapping) or selection_criteria.get("include_withdrawn") is not False: + if ( + not isinstance(selection_criteria, Mapping) + or selection_criteria.get("include_withdrawn") is not False + ): errors.append("include_withdrawn must be false") consent_scope = manifest.get("consent_scope") or {} - allowed_uses = consent_scope.get("allowed_uses") if isinstance(consent_scope, Mapping) else None - if not isinstance(allowed_uses, list) or "recursive_learning_seed" not in allowed_uses: + allowed_uses = ( + consent_scope.get("allowed_uses") + if isinstance(consent_scope, Mapping) + else None + ) + if ( + not isinstance(allowed_uses, list) + or "recursive_learning_seed" not in allowed_uses + ): errors.append("recursive_learning_seed consent scope is required") - for key in ("data_steward", "legal_or_privacy_reviewer", "technical_operator", "approved_at"): + for key in ( + "data_steward", + "legal_or_privacy_reviewer", + "technical_operator", + "approved_at", + ): if not str(approvals.get(key) or "").strip(): errors.append(f"approval missing: {key}") if errors: diff --git a/apps/api/app/services/evaluation_contract.py b/apps/api/app/services/evaluation_contract.py new file mode 100644 index 0000000..2e730d2 --- /dev/null +++ b/apps/api/app/services/evaluation_contract.py @@ -0,0 +1,25 @@ +"""Shared fast-loop evaluation labels and explicit score projections.""" + +from __future__ import annotations + +from typing import Literal + +Appropriateness = Literal["pos", "warn", "neutral"] +APPROPRIATENESS_VALUES: tuple[Appropriateness, ...] = ("pos", "warn", "neutral") + +# Persistence uses the rubric's 1..5 scale. Keep this projection distinct from +# the learner-growth 0..1 normalization below. +PERSISTED_APPROPRIATENESS_SCORE_5PT: dict[Appropriateness, float] = { + "warn": 1.0, + "neutral": 3.0, + "pos": 5.0, +} + +# ``neg`` is retained only for legacy stored evaluations; current evaluator +# output is restricted to APPROPRIATENESS_VALUES. +GROWTH_APPROPRIATENESS_SCORE_01: dict[str, float] = { + "neg": 0.0, + "warn": 0.25, + "neutral": 0.5, + "pos": 1.0, +} diff --git a/apps/api/app/services/evaluator.py b/apps/api/app/services/evaluator.py index dc6cd7d..d616ac7 100644 --- a/apps/api/app/services/evaluator.py +++ b/apps/api/app/services/evaluator.py @@ -49,15 +49,16 @@ from ..taxonomy import ( TECHNIQUE_CATEGORY, TECHNIQUE_KO, ClientState, - CommentKind, Technique, TechniqueCategory, speaker_ko_label, ) from . import guardrail +from .evaluation_contract import APPROPRIATENESS_VALUES, Appropriateness +from .llm_audit import LlmAuditHook, generate_with_audit if TYPE_CHECKING: # 런타임 import 회피(순환·소유권 경계). 타입 힌트 전용. - from .orchestrator import LlmAuditHook, TurnContext + from .orchestrator import TurnContext # ════════════════════════════════════════════════════════════════════════════ @@ -66,11 +67,12 @@ if TYPE_CHECKING: # 런타임 import 회피(순환·소유권 경계). 타입 # LLM 은 후보로 한글 라벨을 받지만, 코드값(value)을 돌려줄 수도 있어 둘 다 받는다. _TECHNIQUE_BY_KO: dict[str, Technique] = {ko: t for t, ko in TECHNIQUE_KO.items()} _TECHNIQUE_BY_CODE: dict[str, Technique] = {t.value: t for t in Technique} -_CLIENT_STATE_BY_KO: dict[str, ClientState] = {ko: s for s, ko in CLIENT_STATE_KO.items()} +_CLIENT_STATE_BY_KO: dict[str, ClientState] = { + ko: s for s, ko in CLIENT_STATE_KO.items() +} _CLIENT_STATE_BY_CODE: dict[str, ClientState] = {s.value: s for s in ClientState} # 적절성 신호 — fast-loop 의 경량 판단(상태머신 라포 추정과 별개 차원). -_APPROPRIATENESS = ("pos", "warn", "neutral") # 의도이탈 심각도 (taxonomy.SupervisorComment.severity 와 동일 어휘). _SEVERITY = ("minor", "moderate", "major") @@ -207,17 +209,17 @@ class IntentDeviation(BaseModel): class TechniqueTag(BaseModel): """fast-loop 기법 태그 1건 — taxonomy.Technique 코드 + 한글 + 군집 + 근거.""" - code: str # taxonomy.Technique.value - label_ko: str # TECHNIQUE_KO - category: str # TechniqueCategory.value (분포 집계축) + code: str # taxonomy.Technique.value + label_ko: str # TECHNIQUE_KO + category: str # TechniqueCategory.value (분포 집계축) rationale: Optional[str] = None # 왜 이 기법으로 봤는지(근거 요구) class ClientStateRead(BaseModel): """내담자 상태 '읽기' — 학습자 발화 직후 내담자 응답에서 관측된 상태(읽기 채점 근거).""" - code: str # taxonomy.ClientState.value - label_ko: str # CLIENT_STATE_KO + code: str # taxonomy.ClientState.value + label_ko: str # CLIENT_STATE_KO rationale: Optional[str] = None @@ -236,12 +238,14 @@ class TurnEvaluation(BaseModel): stage: str techniques: list[TechniqueTag] = Field(default_factory=list) client_state_read: list[ClientStateRead] = Field(default_factory=list) - appropriateness: str = "neutral" # pos | warn | neutral + appropriateness: Appropriateness = "neutral" appropriateness_note: Optional[str] = None intent_deviation: Optional[IntentDeviation] = None # 있을 때만(1급 시민) - rapport_signal: Optional[float] = None # 평가 AI 가 본 라포 신호(−1~+1, 상태머신 주입 가능) + rapport_signal: Optional[float] = ( + None # 평가 AI 가 본 라포 신호(−1~+1, 상태머신 주입 가능) + ) theory_mode: Optional[str] = None - error: Optional[str] = None # 평가 실패 시 사유(비치명적; None 이면 정상) + error: Optional[str] = None # 평가 실패 시 사유(비치명적; None 이면 정상) def to_hook_dict(self) -> dict[str, Any]: """orchestrator.EvalHook 가 기대하는 평가 dict(turns.evaluation 적재용).""" @@ -251,10 +255,12 @@ class TurnEvaluation(BaseModel): class TechniqueDistribution(BaseModel): """deep-loop 기법 분포 — 군집별 카운트 + 과다/과소 진단.""" - by_category: dict[str, int] = Field(default_factory=dict) # category.value -> count - by_technique: dict[str, int] = Field(default_factory=dict) # technique.value -> count + by_category: dict[str, int] = Field(default_factory=dict) # category.value -> count + by_technique: dict[str, int] = Field( + default_factory=dict + ) # technique.value -> count total: int = 0 - overused: list[str] = Field(default_factory=list) # 과다 사용 군집(category.value) + overused: list[str] = Field(default_factory=list) # 과다 사용 군집(category.value) underused: list[str] = Field(default_factory=list) # 과소/미사용 군집 @@ -266,15 +272,15 @@ class SessionEvaluation(BaseModel): loop: str = "deep" session_id: str - stage: str # 평가 시점 단계(전환 트리거면 from-stage) - scope: str = "session_end" # 'session_end' | 'stage_transition' + stage: str # 평가 시점 단계(전환 트리거면 from-stage) + scope: str = "session_end" # 'session_end' | 'stage_transition' turns_evaluated: int = 0 distribution: TechniqueDistribution = Field(default_factory=TechniqueDistribution) - strengths: list[str] = Field(default_factory=list) # 잘한 순간(근거 포함 문장) - improvements: list[str] = Field(default_factory=list) # 개선점(최대 3) + strengths: list[str] = Field(default_factory=list) # 잘한 순간(근거 포함 문장) + improvements: list[str] = Field(default_factory=list) # 개선점(최대 3) intent_deviations: list[IntentDeviation] = Field(default_factory=list) - supervisor_rationale: Optional[str] = None # CommentKind.RATIONALE 종합 - supervisor_critique: Optional[str] = None # CommentKind.CRITIQUE 종합 + supervisor_rationale: Optional[str] = None # CommentKind.RATIONALE 종합 + supervisor_critique: Optional[str] = None # CommentKind.CRITIQUE 종합 alternative_utterances: list[str] = Field(default_factory=list) # 대안 발화 제시 theory_mode: Optional[str] = None error: Optional[str] = None @@ -324,7 +330,7 @@ def _fast_schema() -> dict[str, Any]: "required": ["code"], }, }, - "appropriateness": {"type": "string", "enum": list(_APPROPRIATENESS)}, + "appropriateness": {"type": "string", "enum": list(APPROPRIATENESS_VALUES)}, "appropriateness_note": {"type": "string"}, "rapport_signal": {"type": "number", "minimum": -1, "maximum": 1}, "intent_deviation": { @@ -350,7 +356,11 @@ def _deep_schema() -> dict[str, Any]: "additionalProperties": False, "properties": { "strengths": {"type": "array", "items": {"type": "string"}}, - "improvements": {"type": "array", "items": {"type": "string"}, "maxItems": 3}, + "improvements": { + "type": "array", + "items": {"type": "string"}, + "maxItems": 3, + }, "intent_deviations": { "type": "array", "items": { @@ -405,7 +415,9 @@ def _client_state_candidates_block() -> str: # ─ few-shot 골든셋 예시(data/golden) — 명시적으로 켠 환경에서만 로딩 ───────── # 골든셋은 학습/평가 보정 자료이지 운영 런타임의 기본 데이터가 아니다. -_GOLDEN_FEWSHOT_ENABLED = os.environ.get("EVALUATOR_GOLDEN_FEWSHOT_ENABLED", "").lower() in { +_GOLDEN_FEWSHOT_ENABLED = os.environ.get( + "EVALUATOR_GOLDEN_FEWSHOT_ENABLED", "" +).lower() in { "1", "true", "yes", @@ -456,7 +468,11 @@ def _fewshot_block() -> str: techs = ",".join(e.get("techniques", [])) text = (e.get("text") or "").replace("\n", " ")[:70] rat = next( - (c.get("text", "") for c in e.get("comments", []) if c.get("kind") == "rationale"), + ( + c.get("text", "") + for c in e.get("comments", []) + if c.get("kind") == "rationale" + ), "", ) line = f'- "{text}…" → {techs}' @@ -482,10 +498,13 @@ def build_fast_messages(ctx: "TurnContext", client_reply: str) -> list[EngineMes st = ctx.state_after or ctx.state_before theory = _theory_mode(ctx) client_reply_masked = guardrail.mask_pii(client_reply).text_masked - recent = "\n".join( - f"{speaker_ko_label(t.get('speaker'))}: {t.get('text', '')}" - for t in (ctx.memory.recent_turns or [])[-4:] - ) or "(직전 맥락 없음)" + recent = ( + "\n".join( + f"{speaker_ko_label(t.get('speaker'))}: {t.get('text', '')}" + for t in (ctx.memory.recent_turns or [])[-4:] + ) + or "(직전 맥락 없음)" + ) crisis_note = "" if ctx.crisis is not None and getattr(ctx.crisis, "escalate", False): @@ -495,7 +514,8 @@ def build_fast_messages(ctx: "TurnContext", client_reply: str) -> list[EngineMes ) system = "\n\n".join( - p for p in [ + p + for p in [ _EVAL_ROLE, _technique_candidates_block(), _client_state_candidates_block(), @@ -509,7 +529,8 @@ def build_fast_messages(ctx: "TurnContext", client_reply: str) -> list[EngineMes "없으면 null. 이 항목은 가장 중요하다 — 무리한 생성 금지, 진짜 이탈만.\n" "추가로 rapport_signal(−1~+1): 이 발화가 라포에 끼친 방향(공감·반영=+, 조언점프·평가=−)." ), - ] if p + ] + if p ) user = ( @@ -537,11 +558,16 @@ def build_deep_messages( distribution: "TechniqueDistribution", ) -> list[EngineMessage]: """deep-loop 평가 프롬프트(전체 회기 + 코드 집계 분포 + 골든라벨 후보).""" - transcript = "\n".join( - f"{t.get('seq', '')}{speaker_ko_label(t.get('speaker'))}: {t.get('text', '')}" - for t in masked_turns - ) or "(축어록 없음)" - dist_lines = ", ".join(f"{k}:{v}" for k, v in distribution.by_category.items()) or "(없음)" + transcript = ( + "\n".join( + f"{t.get('seq', '')}{speaker_ko_label(t.get('speaker'))}: {t.get('text', '')}" + for t in masked_turns + ) + or "(축어록 없음)" + ) + dist_lines = ( + ", ".join(f"{k}:{v}" for k, v in distribution.by_category.items()) or "(없음)" + ) over = ", ".join(distribution.overused) or "(없음)" under = ", ".join(distribution.underused) or "(없음)" @@ -590,8 +616,9 @@ def _parse_intent_deviation(d: Any) -> Optional[IntentDeviation]: return IntentDeviation(dimension=dim, expected=exp, actual=act, severity=sev) -def _parse_fast(payload: dict[str, Any], *, turn_seq: int, stage: str, - theory: Optional[str]) -> TurnEvaluation: +def _parse_fast( + payload: dict[str, Any], *, turn_seq: int, stage: str, theory: Optional[str] +) -> TurnEvaluation: techniques: list[TechniqueTag] = [] for item in payload.get("techniques") or []: if not isinstance(item, dict): @@ -604,7 +631,9 @@ def _parse_fast(payload: dict[str, Any], *, turn_seq: int, stage: str, code=t.value, label_ko=TECHNIQUE_KO[t], category=TECHNIQUE_CATEGORY[t].value, - rationale=(str(item.get("rationale")).strip() or None) if item.get("rationale") else None, + rationale=(str(item.get("rationale")).strip() or None) + if item.get("rationale") + else None, ) ) @@ -619,12 +648,14 @@ def _parse_fast(payload: dict[str, Any], *, turn_seq: int, stage: str, ClientStateRead( code=s.value, label_ko=CLIENT_STATE_KO[s], - rationale=(str(item.get("rationale")).strip() or None) if item.get("rationale") else None, + rationale=(str(item.get("rationale")).strip() or None) + if item.get("rationale") + else None, ) ) appro = str(payload.get("appropriateness") or "neutral").strip() - if appro not in _APPROPRIATENESS: + if appro not in APPROPRIATENESS_VALUES: appro = "neutral" rapport = payload.get("rapport_signal") @@ -706,7 +737,9 @@ async def evaluate_turn( """ st = ctx.state_after or ctx.state_before theory = _theory_mode(ctx) - base = TurnEvaluation(loop="fast", turn_seq=st.turn_seq, stage=st.stage.value, theory_mode=theory) + base = TurnEvaluation( + loop="fast", turn_seq=st.turn_seq, stage=st.stage.value, theory_mode=theory + ) try: req = GenerateRequest( @@ -715,7 +748,7 @@ async def evaluate_turn( structured_schema=_fast_schema(), model=_configured_model(settings.evaluator_fast_model), max_tokens=900, - temperature=0.2, # 평가는 보수적·재현적으로 + temperature=0.2, # 평가는 보수적·재현적으로 session_id=ctx.session_id, metadata={"loop": "fast", "stage": st.stage.value, "turn_seq": st.turn_seq}, ) @@ -723,20 +756,7 @@ async def evaluate_turn( cached = _evaluator_cache_get(cache_key) if cached is not None: return TurnEvaluation.model_validate(cached) - started = time.perf_counter() - resp = await engine.generate(req) - latency_ms = int((time.perf_counter() - started) * 1000) - await _record_llm_audit( - audit_hook, - session_id=ctx.session_id, - provider=resp.provider, - model=resp.model, - tokens_in=resp.tokens_in, - tokens_out=resp.tokens_out, - cost_usd=resp.cost_usd, - inference_geo=resp.inference_geo, - latency_ms=latency_ms, - ) + resp = await generate_with_audit(engine, req, audit_hook) except EngineError: base.error = "engine_error" return base @@ -749,7 +769,9 @@ async def evaluate_turn( base.error = "no_structured_output" return base try: - result = _parse_fast(payload, turn_seq=st.turn_seq, stage=st.stage.value, theory=theory) + result = _parse_fast( + payload, turn_seq=st.turn_seq, stage=st.stage.value, theory=theory + ) _evaluator_cache_put(cache_key, result.model_dump()) return result except Exception: # 파싱 방어 @@ -806,20 +828,7 @@ async def evaluate_session( cached = _evaluator_cache_get(cache_key) if cached is not None: return SessionEvaluation.model_validate(cached) - started = time.perf_counter() - resp = await engine.generate(req) - latency_ms = int((time.perf_counter() - started) * 1000) - await _record_llm_audit( - audit_hook, - session_id=session_id, - provider=resp.provider, - model=resp.model, - tokens_in=resp.tokens_in, - tokens_out=resp.tokens_out, - cost_usd=resp.cost_usd, - inference_geo=resp.inference_geo, - latency_ms=latency_ms, - ) + resp = await generate_with_audit(engine, req, audit_hook) except EngineError as e: base.error = f"engine_error: {e}" return base @@ -834,7 +843,9 @@ async def evaluate_session( base.strengths = _coerce_str_list(payload.get("strengths")) base.improvements = _coerce_str_list(payload.get("improvements"))[:3] # 최대 3 - base.alternative_utterances = _coerce_str_list(payload.get("alternative_utterances")) + base.alternative_utterances = _coerce_str_list( + payload.get("alternative_utterances") + ) rationale = payload.get("supervisor_rationale") critique = payload.get("supervisor_critique") base.supervisor_rationale = str(rationale).strip() if rationale else None @@ -850,18 +861,6 @@ async def evaluate_session( # ════════════════════════════════════════════════════════════════════════════ # 7. orchestrator EvalHook 어댑터 — 주입형 클로저(엔진 바인딩) # ════════════════════════════════════════════════════════════════════════════ -async def _record_llm_audit( - audit_hook: Optional["LlmAuditHook"], - **payload: Any, -) -> None: - if audit_hook is None: - return - try: - await audit_hook(payload) - except Exception: - return - - def make_eval_hook( engine: EngineClient, *, @@ -874,7 +873,9 @@ def make_eval_hook( """ async def _hook(ctx: "TurnContext", client_reply: str) -> Optional[dict[str, Any]]: - ev = await evaluate_turn(ctx, client_reply, engine=engine, audit_hook=audit_hook) + ev = await evaluate_turn( + ctx, client_reply, engine=engine, audit_hook=audit_hook + ) d = ev.to_hook_dict() return d if d else None diff --git a/apps/api/app/services/guardrail.py b/apps/api/app/services/guardrail.py index ed3a214..40f66cf 100644 --- a/apps/api/app/services/guardrail.py +++ b/apps/api/app/services/guardrail.py @@ -370,9 +370,12 @@ def classify_crisis(text: str, *, speaker_is_persona_context: bool = True) -> Cr # 3. 출력 가드레일 (내담자 AI 응답 — 자살수단 차단, ideation 상한 R5) # ════════════════════════════════════════════════════════════════════════════ # 자살/자해 '수단·방법' 정보 패턴 (절대 출력 금지). 정서 표현은 허용. +# 1.1 확장(2026-07-15): DBT 자해 페르소나 대비 자해 도구·방법 어휘 보강. +# 과차단 주의 — "선을 긋다"(경계 설정) 같은 정상 상담 표현과 겹치지 않는 조합만 추가. _MEANS_TERMS = [ "약을 얼마나", "몇 알", "몇 정", "투신", "목을 매", "목 매", "번개탄", "농약", "손목을", "칼로", "뛰어내리", "수면제 ", "독", "방법은", "어떻게 죽", + "커터", "면도날", "손목 긋", "깊게 그으", "라이터로 지", ] _DISPLAY_PLACEHOLDER_LABELS = { "NAME": "그 이름", diff --git a/apps/api/app/services/live_coach.py b/apps/api/app/services/live_coach.py index 7730968..48520ce 100644 --- a/apps/api/app/services/live_coach.py +++ b/apps/api/app/services/live_coach.py @@ -136,6 +136,10 @@ class LiveCoachInput(BaseModel): client_reply: Optional[str] = None recent_turns: list[dict[str, str]] = Field(default_factory=list) evaluation: Optional[dict[str, Any]] = None + # 이번 회기 목표 단계(P1 준비 페이지 선택) — 코칭을 회기 목표에 정렬한다. + goal_stages: list[str] = Field(default_factory=list) + # 직전 코칭 요약(title/focus) — 같은 조언 반복을 막는다. + prior_coach: list[dict[str, str]] = Field(default_factory=list) class LiveCoachGrounding(BaseModel): @@ -529,7 +533,17 @@ def _fallback_suggestion( focus: CoachFocus = "exploration" title = "다음 탐색" message = "내담자 표현을 한 번 반영한 뒤, 방금 말한 장면을 더 구체적으로 물어봐라." - next_line = "방금 말한 그 장면이 언제부터 특히 힘들게 느껴졌는지 조금만 더 들려줄래요?" + # 단계별 기본 다음 발화 — 폴백에서도 회기 흐름에 맞는 제안을 낸다. + stage_next_lines = { + "라포": "오늘 이렇게 시간 내줘서 고마워요. 지금 마음이 어떤지 편한 만큼만 들려줄래요?", + "탐색": "방금 말한 그 장면이 언제부터 특히 힘들게 느껴졌는지 조금만 더 들려줄래요?", + "개입": "그 생각이 올라올 때 몸이나 행동은 어떻게 반응하는지 같이 한번 살펴볼까요?", + "정리": "오늘 나눈 이야기 중에 가장 마음에 남는 것 하나를 같이 정리해 볼까요?", + } + next_line = stage_next_lines.get( + str(item.stage), + "방금 말한 그 장면이 언제부터 특히 힘들게 느껴졌는지 조금만 더 들려줄래요?", + ) crisis = guardrail.classify_crisis(text) if crisis.kind != guardrail.CrisisKind.NONE: @@ -633,10 +647,21 @@ def _messages(item: LiveCoachInput, grounding: list[LiveCoachGrounding]) -> list "영역의 구체 행동으로 연결한다. DSM/지침 근거는 상담자 판단을 정렬하는 내부 참조이며, " "학습자에게는 관찰 가능한 상담 행동과 다음 발화로만 번역한다." ) + goals = ", ".join(item.goal_stages) if item.goal_stages else "(미지정)" + prior = ( + "\n".join( + f"- {entry.get('title', '')} (focus: {entry.get('focus', '')})" + for entry in item.prior_coach[-2:] + if entry.get("title") + ) + or "(이번 회기 첫 코칭)" + ) user = ( f"[세션] {item.session_id} / turn {item.turn_seq}\n" f"[내담자] {item.persona_name} ({item.persona_code})\n" - f"[단계] {item.stage} / openness {item.effective_openness:.2f} / 이론 {item.theory_mode}\n\n" + f"[단계] {item.stage} / openness {item.effective_openness:.2f} / 이론 {item.theory_mode}\n" + f"[이번 회기 목표 단계] {goals} — 코칭은 목표 단계 작업에 정렬하고, 목표를 이미 이뤘다면 심화를 제안한다.\n" + f"[직전 코칭]\n{prior}\n(같은 조언을 반복하지 말고 다음 단계를 제시한다)\n\n" f"[최근 맥락]\n{recent}\n\n" f"[이번 상담자 발화]\n{learner_masked}\n\n" f"[이어진 내담자 응답]\n{client_masked or '(아직 없음)'}\n\n" @@ -654,13 +679,14 @@ def _messages(item: LiveCoachInput, grounding: list[LiveCoachGrounding]) -> list async def _record_llm_audit( audit_hook: Optional["LlmAuditHook"], **payload: Any, -) -> None: +) -> bool: if audit_hook is None: - return + return True try: - await audit_hook(payload) + result = await audit_hook(payload) + return result is not False except Exception: - return + return False async def generate_live_coaching( @@ -691,7 +717,7 @@ async def generate_live_coaching( ) resp = await engine.generate(req) latency_ms = int((time.perf_counter() - started) * 1000) - await _record_llm_audit( + audit_ok = await _record_llm_audit( audit_hook, session_id=item.session_id, provider=resp.provider, @@ -702,6 +728,12 @@ async def generate_live_coaching( inference_geo=resp.inference_geo, latency_ms=latency_ms, ) + if not audit_ok: + return _fallback_suggestion( + item, + grounding=all_grounding, + reason="응답 검증 기록을 남기지 못했다", + ) payload = structured_payload_from_response(resp) if payload is None: return _fallback_suggestion( diff --git a/apps/api/app/services/llm_audit.py b/apps/api/app/services/llm_audit.py new file mode 100644 index 0000000..6af24db --- /dev/null +++ b/apps/api/app/services/llm_audit.py @@ -0,0 +1,47 @@ +"""LLM 호출 계량과 감사 훅의 공통 실행 경로.""" + +from __future__ import annotations + +import time +from collections.abc import Awaitable, Callable +from typing import Any + +from ..engine_client import EngineClient, GenerateRequest, GenerateResponse + +LlmAuditHook = Callable[[dict[str, Any]], Awaitable[None]] + + +async def record_llm_audit( + audit_hook: LlmAuditHook | None, + **payload: Any, +) -> None: + """감사 저장소 장애가 사용자 응답을 막지 않도록 훅 실패를 격리한다.""" + if audit_hook is None: + return + try: + await audit_hook(payload) + except Exception: + return + + +async def generate_with_audit( + engine: EngineClient, + request: GenerateRequest, + audit_hook: LlmAuditHook | None, +) -> GenerateResponse: + """비스트리밍 LLM 호출의 지연·토큰·비용 기록을 한 계약으로 고정한다.""" + started = time.perf_counter() + response = await engine.generate(request) + latency_ms = int((time.perf_counter() - started) * 1000) + await record_llm_audit( + audit_hook, + session_id=request.session_id, + provider=response.provider, + model=response.model, + tokens_in=response.tokens_in, + tokens_out=response.tokens_out, + cost_usd=response.cost_usd, + inference_geo=response.inference_geo, + latency_ms=latency_ms, + ) + return response diff --git a/apps/api/app/services/memory.py b/apps/api/app/services/memory.py index 1d0f117..b2310d4 100644 --- a/apps/api/app/services/memory.py +++ b/apps/api/app/services/memory.py @@ -20,7 +20,7 @@ from __future__ import annotations import re from dataclasses import dataclass, field -from typing import Any, Callable, Literal, Optional +from typing import Any, Literal, Optional from .state_machine import SessionState from ..taxonomy import speaker_ko_label diff --git a/apps/api/app/services/notifications.py b/apps/api/app/services/notifications.py index 465bcc1..31507e3 100644 --- a/apps/api/app/services/notifications.py +++ b/apps/api/app/services/notifications.py @@ -15,10 +15,32 @@ from uuid import uuid4 from ..config import settings from ..db import acquire, get_pool +from ..runtime_schema import ( + NOTIFICATION_SCHEMA_CONTRACT, + runtime_schema_bootstrap_required, + schema_contract_ready, +) logger = logging.getLogger(__name__) -NotificationKind = Literal["account_pending_approval", "session_review_ready", "admin_test_email"] +NotificationKind = Literal[ + "account_pending_approval", "session_review_ready", "admin_test_email" +] +ApprovalRecipientScope = Literal["admin", "super_admin"] + +ACTIVE_NOTIFICATION_RECIPIENTS_SQL = """ + SELECT DISTINCT + u.user_id, + u.email, + COALESCE(NULLIF(u.display_name, ''), u.email) AS display_name, + u.role + FROM app.app_user u + LEFT JOIN app.user_preferences p ON p.user_id = u.user_id + WHERE u.is_active + AND u.account_status = 'approved' + AND u.email IS NOT NULL + AND u.email <> '' +""" @dataclass(slots=True) @@ -38,9 +60,14 @@ class RenderedEmail: async def ensure_notification_tables() -> None: - """Create notification queue tables when the DB role allows DDL.""" + """Verify notification tables, with DDL repair restricted to local development.""" get_pool() async with acquire(role="admin") as conn: + ready = await schema_contract_ready(conn, NOTIFICATION_SCHEMA_CONTRACT) + if not runtime_schema_bootstrap_required( + NOTIFICATION_SCHEMA_CONTRACT, ready=ready + ): + return await conn.execute( """ CREATE TABLE IF NOT EXISTS app.notification_event ( @@ -120,6 +147,10 @@ async def ensure_notification_tables() -> None: WITH CHECK (app.current_role_name() = 'admin'); """ ) + if not await schema_contract_ready(conn, NOTIFICATION_SCHEMA_CONTRACT): + raise RuntimeError( + "notification development schema bootstrap did not satisfy readiness" + ) def schedule_delivery_flush() -> None: @@ -127,7 +158,9 @@ def schedule_delivery_flush() -> None: if settings.notification_email_provider == "disabled": return try: - asyncio.get_running_loop().create_task(process_queued_email_notifications(limit=10)) + asyncio.get_running_loop().create_task( + process_queued_email_notifications(limit=10) + ) except RuntimeError: return @@ -321,57 +354,39 @@ async def _enqueue_event( async def _admin_approval_recipients() -> list[NotificationRecipient]: - super_admin_emails = sorted({_normalize_email(value) for value in settings.auth_super_admin_emails}) + return await _approval_recipients(scope="admin") + + +async def _super_admin_recipients() -> list[NotificationRecipient]: + return await _approval_recipients(scope="super_admin") + + +async def _approval_recipients( + *, scope: ApprovalRecipientScope +) -> list[NotificationRecipient]: + """가입 승인 알림 수신 정책을 한 쿼리에서 소유한다.""" + super_admin_emails = sorted( + {_normalize_email(value) for value in settings.auth_super_admin_emails} + ) + super_admin_only = scope == "super_admin" + if super_admin_only and not super_admin_emails: + return [] async with acquire(role="admin") as conn: rows = await conn.fetch( - """ - SELECT DISTINCT - u.user_id, - u.email, - COALESCE(NULLIF(u.display_name, ''), u.email) AS display_name, - u.role - FROM app.app_user u - LEFT JOIN app.user_preferences p ON p.user_id = u.user_id - WHERE u.is_active - AND u.account_status = 'approved' - AND u.email IS NOT NULL - AND u.email <> '' + ACTIVE_NOTIFICATION_RECIPIENTS_SQL + + """ AND ( - u.role = 'admin' - OR u.admin_access - OR lower(u.email) = ANY($1::text[]) + lower(u.email) = ANY($1::text[]) + OR ( + NOT $2::boolean + AND (u.role = 'admin' OR u.admin_access) + ) ) AND COALESCE((p.notifications->>'account_approval')::boolean, true) ORDER BY u.email """, super_admin_emails, - ) - return [_recipient_from_row(row) for row in rows] - - -async def _super_admin_recipients() -> list[NotificationRecipient]: - super_admin_emails = sorted({_normalize_email(value) for value in settings.auth_super_admin_emails}) - if not super_admin_emails: - return [] - async with acquire(role="admin") as conn: - rows = await conn.fetch( - """ - SELECT DISTINCT - u.user_id, - u.email, - COALESCE(NULLIF(u.display_name, ''), u.email) AS display_name, - u.role - FROM app.app_user u - LEFT JOIN app.user_preferences p ON p.user_id = u.user_id - WHERE u.is_active - AND u.account_status = 'approved' - AND u.email IS NOT NULL - AND u.email <> '' - AND lower(u.email) = ANY($1::text[]) - AND COALESCE((p.notifications->>'account_approval')::boolean, true) - ORDER BY u.email - """, - super_admin_emails, + super_admin_only, ) return [_recipient_from_row(row) for row in rows] @@ -379,7 +394,9 @@ async def _super_admin_recipients() -> list[NotificationRecipient]: async def _session_review_payload_and_recipients( session_id: str, ) -> tuple[dict[str, Any] | None, list[NotificationRecipient]]: - super_admin_emails = sorted({_normalize_email(value) for value in settings.auth_super_admin_emails}) + super_admin_emails = sorted( + {_normalize_email(value) for value in settings.auth_super_admin_emails} + ) async with acquire(role="admin") as conn: session = await conn.fetchrow( """ @@ -407,18 +424,8 @@ async def _session_review_payload_and_recipients( return None, [] learner_cohort = str(session["learner_cohort"] or "") rows = await conn.fetch( - """ - SELECT DISTINCT - u.user_id, - u.email, - COALESCE(NULLIF(u.display_name, ''), u.email) AS display_name, - u.role - FROM app.app_user u - LEFT JOIN app.user_preferences p ON p.user_id = u.user_id - WHERE u.is_active - AND u.account_status = 'approved' - AND u.email IS NOT NULL - AND u.email <> '' + ACTIVE_NOTIFICATION_RECIPIENTS_SQL + + """ AND ( u.role = 'admin' OR lower(u.email) = ANY($1::text[]) @@ -457,7 +464,9 @@ def _send_rendered_email( if settings.notification_email_provider == "disabled": raise NotificationSkipped("email provider is disabled") if settings.notification_email_provider != "smtp": - raise NotificationSkipped(f"unsupported provider: {settings.notification_email_provider}") + raise NotificationSkipped( + f"unsupported provider: {settings.notification_email_provider}" + ) if not settings.smtp_host.strip() or not settings.smtp_from_email.strip(): raise NotificationSkipped("SMTP host/from email is not configured") @@ -472,7 +481,9 @@ def _send_rendered_email( if settings.smtp_ssl: context = ssl.create_default_context() - with smtplib.SMTP_SSL(settings.smtp_host, settings.smtp_port, context=context, timeout=15) as smtp: + with smtplib.SMTP_SSL( + settings.smtp_host, settings.smtp_port, context=context, timeout=15 + ) as smtp: _smtp_login_if_needed(smtp) smtp.send_message(msg) else: @@ -553,7 +564,9 @@ async def _mark_delivery_failed(delivery_id: str, error: str) -> None: def _render_account_pending_approval(payload: dict[str, Any]) -> RenderedEmail: - display_name = str(payload.get("display_name") or payload.get("email") or "신규 사용자") + display_name = str( + payload.get("display_name") or payload.get("email") or "신규 사용자" + ) email = str(payload.get("email") or "") role = _role_label(str(payload.get("role") or "learner")) approval_url = str(payload.get("approval_url") or _frontend_url("/admin/users")) @@ -563,11 +576,15 @@ def _render_account_pending_approval(payload: dict[str, Any]) -> RenderedEmail:

승인 대기 중인 신규 사용자가 있습니다. 가입 승인 화면에서 수업 또는 연구 참여 범위를 확인한 뒤 승인 상태를 결정해 주세요.

- {_info_box([ - ("사용자", display_name), - ("이메일", email), - ("요청 역할", role), - ])} + { + _info_box( + [ + ("사용자", display_name), + ("이메일", email), + ("요청 역할", role), + ] + ) + } {_button("가입 승인 확인하기", approval_url)}

계정 승인 전까지 해당 사용자는 Vignette 대기 화면만 볼 수 있습니다. @@ -602,12 +619,16 @@ def _render_session_review_ready(payload: dict[str, Any]) -> RenderedEmail:

종료된 학습 회기가 교수자 검토 대기 상태입니다. 회기 리뷰 화면에서 요약과 근거를 확인한 뒤 검토 상태를 남겨 주세요.

- {_info_box([ - ("학습자", learner_label), - ("내담자", persona_name), - ("회기", f"{session_no}회기" if session_no else "종료 회기"), - ("종료 시각", ended_at or "기록됨"), - ])} + { + _info_box( + [ + ("학습자", learner_label), + ("내담자", persona_name), + ("회기", f"{session_no}회기" if session_no else "종료 회기"), + ("종료 시각", ended_at or "기록됨"), + ] + ) + } {_button("회기 검토하기", review_url)}

민감한 회기 내용은 메일에 포함하지 않았습니다. 로그인 후 Vignette에서 확인해 주세요. @@ -641,10 +662,14 @@ def _render_admin_test_email(payload: dict[str, Any]) -> RenderedEmail:

관리자 메일 알림이 정상적으로 연결되었습니다. 이 메일은 실제 가입 승인이나 회기 검토 요청이 아니라 발송 경로 확인용 테스트입니다.

- {_info_box([ - ("요청자", requested_by), - ("용도", "운영 메일 발송 테스트"), - ])} + { + _info_box( + [ + ("요청자", requested_by), + ("용도", "운영 메일 발송 테스트"), + ] + ) + } {_button("알림 상태 확인하기", notifications_url)}

이후 가입 승인 요청과 회기 검토 요청도 같은 메일 템플릿과 발송 큐를 사용합니다. diff --git a/apps/api/app/services/orchestrator.py b/apps/api/app/services/orchestrator.py index 03c2bf7..c755f66 100644 --- a/apps/api/app/services/orchestrator.py +++ b/apps/api/app/services/orchestrator.py @@ -41,13 +41,13 @@ from ..contracts.engine_gateway import ( StreamTokenEvent, ) from . import guardrail, persona, state_machine +from .llm_audit import LlmAuditHook, generate_with_audit, record_llm_audit from .persona import PersonaCard, PersonaStateContext, TurnMemory -from .state_machine import SessionState, Stage +from .state_machine import SessionState # 평가 훅 타입: U_t(수련생 마스킹 발화) + 내담자응답 + 상태 → 평가 결과(dict) # Features evaluator 가 이 시그니처에 맞춰 함수를 주입한다(여기선 호출만). EvalHook = Callable[["TurnContext", str], Awaitable[Optional[dict]]] -LlmAuditHook = Callable[[dict[str, Any]], Awaitable[None]] def turn_evaluation_error_payload(ctx: "TurnContext", error: BaseException | str) -> dict[str, Any]: @@ -253,20 +253,7 @@ async def run_turn_generate( reply = "" safety_flagged = ctx.crisis is not None and ctx.crisis.escalate for attempt in range(2): - started = time.perf_counter() - resp = await engine.generate(req) - latency_ms = int((time.perf_counter() - started) * 1000) - await _record_llm_audit( - audit_hook, - session_id=ctx.session_id, - provider=resp.provider, - model=resp.model, - tokens_in=resp.tokens_in, - tokens_out=resp.tokens_out, - cost_usd=resp.cost_usd, - inference_geo=resp.inference_geo, - latency_ms=latency_ms, - ) + resp = await generate_with_audit(engine, req, audit_hook) # 5) 출력 가드레일 — 수단 차단 + persona 품질 재생성 guard = guardrail.sanitize_client_reply( @@ -451,7 +438,7 @@ async def run_turn_stream( yield StreamEvent("token", {"text": guard.text}) latency_ms = int((time.perf_counter() - started) * 1000) - await _record_llm_audit( + await record_llm_audit( audit_hook, session_id=ctx.session_id, provider=str(stream_meta.get("provider") or engine.engine_mode), @@ -493,18 +480,6 @@ async def run_turn_stream( yield StreamEvent("error", {"detail": str(e)}) -async def _record_llm_audit( - audit_hook: Optional[LlmAuditHook], - **payload: Any, -) -> None: - if audit_hook is None: - return - try: - await audit_hook(payload) - except Exception: - return - - def _optional_str(value: Any) -> Optional[str]: if value is None: return None diff --git a/apps/api/app/services/persona.py b/apps/api/app/services/persona.py index d6fdc74..910ab21 100644 --- a/apps/api/app/services/persona.py +++ b/apps/api/app/services/persona.py @@ -21,10 +21,13 @@ P1 = 0615 청소년 '서연' 사례의 합성 변형(원문 미적재, F-05). from __future__ import annotations from dataclasses import dataclass, field -from typing import Any, Optional +from typing import TYPE_CHECKING, Any, Optional from ..engine_client import EngineMessage +if TYPE_CHECKING: + from .state_machine import OpennessParams + # ════════════════════════════════════════════════════════════════════════════ # 페르소나 카드 (app.persona_card 컬럼 구조의 in-proc 표현) diff --git a/apps/api/app/services/phase3_kpi_export.py b/apps/api/app/services/phase3_kpi_export.py index c152ebc..31ecb74 100644 --- a/apps/api/app/services/phase3_kpi_export.py +++ b/apps/api/app/services/phase3_kpi_export.py @@ -17,7 +17,7 @@ from typing import Any, Iterable, Mapping, Sequence from uuid import UUID from .phase3_kpi_contract import ( - KPI_REPORT_PATH, + KPI_REPORT_PATH as KPI_REPORT_PATH, PHASE3_KPI_METRICS, PREPOST_CSV_PATH, PREPOST_MEASURE_NAMES, diff --git a/apps/api/app/services/session_metrics.py b/apps/api/app/services/session_metrics.py index b205ddd..d457804 100644 --- a/apps/api/app/services/session_metrics.py +++ b/apps/api/app/services/session_metrics.py @@ -7,14 +7,7 @@ from datetime import datetime from typing import Any, Callable from ..store import InProcSession - - -_APPROPRIATENESS_SCORE = { - "neg": 0.0, - "warn": 0.25, - "neutral": 0.5, - "pos": 1.0, -} +from .evaluation_contract import GROWTH_APPROPRIATENESS_SCORE_01 @dataclass(frozen=True) @@ -80,7 +73,7 @@ def turn_score(ev: dict[str, Any]) -> float | None: if str(ev.get("error") or "").strip(): return None raw = str(ev.get("appropriateness") or "").strip().lower() - return _APPROPRIATENESS_SCORE.get(raw) + return GROWTH_APPROPRIATENESS_SCORE_01.get(raw) def turn_rapport(ev: dict[str, Any]) -> float | None: @@ -179,7 +172,9 @@ def build_learner_growth( ordered = sorted(learner_sessions, key=lambda sess: sess.created_at) points = [session_growth_point(sess) for sess in ordered] scored = [point for point in points if point.score is not None] - rapport_values = [point.rapport for point in points if point.rapport is not None] + rapport_values = [ + point.rapport for point in points if point.rapport is not None + ] technique_counts: dict[str, int] = {} for sess in ordered: for turn in sess.turns: @@ -223,8 +218,12 @@ def build_learner_growth( first_score=first_score, latest_score=latest_score, score_delta=score_delta, - avg_score=avg([point.score for point in scored if point.score is not None]), - avg_rapport=avg([value for value in rapport_values if value is not None]), + avg_score=avg( + [point.score for point in scored if point.score is not None] + ), + avg_rapport=avg( + [value for value in rapport_values if value is not None] + ), trend=trend, top_techniques=top_techniques, points=points if point_limit is None else points[-point_limit:], @@ -234,7 +233,9 @@ def build_learner_growth( return sorted_result if limit is None else sorted_result[:limit] -def recent_feedback_notes(sessions: list[InProcSession], *, limit: int = 5) -> list[dict[str, object]]: +def recent_feedback_notes( + sessions: list[InProcSession], *, limit: int = 5 +) -> list[dict[str, object]]: notes: list[dict[str, object]] = [] for sess in sorted(sessions, key=session_activity_time, reverse=True): for turn in reversed(sess.turns): @@ -254,7 +255,9 @@ def recent_feedback_notes(sessions: list[InProcSession], *, limit: int = 5) -> l "session_no": sess.session_no, "stage": turn.stage, "turn_seq": turn.turn_seq, - "created_at": iso_datetime(turn.created_at) or iso_datetime(session_activity_time(sess)) or "", + "created_at": iso_datetime(turn.created_at) + or iso_datetime(session_activity_time(sess)) + or "", "score": turn_score(ev), "rapport": turn_rapport(ev), "note": note, diff --git a/apps/api/app/services/tabular_ingest.py b/apps/api/app/services/tabular_ingest.py new file mode 100644 index 0000000..04aed73 --- /dev/null +++ b/apps/api/app/services/tabular_ingest.py @@ -0,0 +1,138 @@ +"""자유 양식 표 파일(엑셀/CSV) → 텍스트 변환 (P4, 2026-07-13 한신대 회의). + +연구팀이 교수자 페이지에 올리는 자유 양식 엑셀(파란 라벨 방식 포함)을 +페르소나 저작 KB에 넣을 수 있는 평문으로 결정론 변환한다. + +원칙: +- 업로드 원본 바이트는 여기서 파싱만 하고 어디에도 저장하지 않는다(원본 파기 원칙). + 파생 텍스트만 기존 `/personas/sources` 마스킹·hash-only 증거 경로로 넘어간다. +- 양식 변형에 견디도록 셀 색/서식에 의존하지 않는다 — 비어있지 않은 셀만 행 단위로 평탄화한다. +- openpyxl 은 선택 의존성이다(presidio 패턴). 없으면 명확한 한국어 오류로 안내한다. +""" + +from __future__ import annotations + +import csv +import io + +# 변환 상한 — 파일럿 추고록 기준 여유값. LLM/KB 입력 상한(120k)과 정합. +MAX_TEXT_CHARS = 120_000 +MAX_CELLS = 40_000 +MAX_UPLOAD_BYTES = 8 * 1024 * 1024 # 8MB + + +class TabularIngestError(ValueError): + """사용자에게 그대로 보여줄 수 있는 한국어 사유를 담는다.""" + + +def _try_load_openpyxl(): + try: + import openpyxl # noqa: PLC0415 — 선택 의존성 지연 로드 + + return openpyxl + except ImportError as exc: # pragma: no cover - 설치 환경에선 도달하지 않음 + raise TabularIngestError( + "엑셀 변환 모듈(openpyxl)이 설치되어 있지 않습니다. 관리자에게 API 의존성 설치를 요청하세요." + ) from exc + + +def _cell_text(value: object) -> str: + if value is None: + return "" + if isinstance(value, float) and value.is_integer(): + return str(int(value)) + text = str(value).strip() + return " ".join(text.split()) + + +def _rows_to_lines(rows: list[list[str]]) -> list[str]: + lines: list[str] = [] + for cells in rows: + filled = [cell for cell in cells if cell] + if not filled: + continue + if len(filled) == 2: + # 자유 양식에서 가장 흔한 "라벨 | 값" 행 — 읽기 좋은 쌍으로 변환. + lines.append(f"{filled[0]}: {filled[1]}") + else: + lines.append(" | ".join(filled)) + return lines + + +def _extract_xlsx(data: bytes) -> str: + openpyxl = _try_load_openpyxl() + try: + workbook = openpyxl.load_workbook( + io.BytesIO(data), read_only=True, data_only=True + ) + except Exception as exc: + raise TabularIngestError( + "엑셀 파일을 열지 못했습니다. 손상되지 않은 .xlsx 파일인지 확인해 주세요." + ) from exc + try: + sections: list[str] = [] + cell_budget = MAX_CELLS + for sheet in workbook.worksheets: + rows: list[list[str]] = [] + for row in sheet.iter_rows(values_only=True): + if cell_budget <= 0: + break + cell_budget -= len(row) + rows.append([_cell_text(value) for value in row]) + lines = _rows_to_lines(rows) + if lines: + sections.append(f"## 시트: {sheet.title}\n" + "\n".join(lines)) + if cell_budget <= 0: + sections.append("(셀 수 상한에 도달해 이후 내용은 생략했습니다)") + break + return "\n\n".join(sections) + finally: + workbook.close() + + +def _extract_csv(data: bytes) -> str: + text: str | None = None + for encoding in ("utf-8-sig", "cp949", "utf-8"): + try: + text = data.decode(encoding) + break + except UnicodeDecodeError: + continue + if text is None: + raise TabularIngestError( + "CSV 인코딩을 해석하지 못했습니다. UTF-8 또는 엑셀(xlsx)로 저장해 다시 올려 주세요." + ) + rows = [[_cell_text(cell) for cell in row] for row in csv.reader(io.StringIO(text))] + return "\n".join(_rows_to_lines(rows[: MAX_CELLS // 8])) + + +def extract_tabular_text(*, filename: str, data: bytes) -> str: + """업로드 파일을 KB 등록용 평문으로 변환한다. 실패 사유는 TabularIngestError.""" + if not data: + raise TabularIngestError("업로드된 파일이 비어 있습니다.") + if len(data) > MAX_UPLOAD_BYTES: + raise TabularIngestError("파일이 8MB를 넘습니다. 시트를 나눠 다시 올려 주세요.") + + lowered = (filename or "").lower() + if lowered.endswith(".xlsx") or lowered.endswith(".xlsm"): + text = _extract_xlsx(data) + elif lowered.endswith(".xls"): + raise TabularIngestError( + "구형 엑셀(.xls)은 지원하지 않습니다. 엑셀에서 '다른 이름으로 저장 → .xlsx'로 변환해 올려 주세요." + ) + elif lowered.endswith(".csv"): + text = _extract_csv(data) + else: + raise TabularIngestError( + "지원하지 않는 파일 형식입니다. 엑셀(.xlsx) 또는 CSV 파일을 올려 주세요." + ) + + text = text.strip() + if len(text) < 20: + raise TabularIngestError( + "표에서 읽을 수 있는 텍스트가 거의 없습니다. 내용이 있는 시트인지 확인해 주세요." + ) + return text[:MAX_TEXT_CHARS] + + +__all__ = ["TabularIngestError", "extract_tabular_text", "MAX_UPLOAD_BYTES"] diff --git a/apps/api/app/session_persistence.py b/apps/api/app/session_persistence.py index 2f290de..9a83078 100644 --- a/apps/api/app/session_persistence.py +++ b/apps/api/app/session_persistence.py @@ -13,9 +13,20 @@ from typing import Any, Iterable, Protocol from .db import acquire, get_pool from .deps import Principal from .config import settings -from .persona_repository import SEED_VERSION, card_from_row, seed_fallback_persona, seed_persona_id +from .persona_repository import ( + SEED_VERSION, + card_from_row, + seed_fallback_persona, + seed_persona_id, +) from .runtime_policy import require_runtime_fallback_allowed, runtime_fallback_allowed +from .runtime_schema import ( + REVIEW_SCHEMA_CONTRACT, + runtime_schema_bootstrap_required, + schema_contract_ready, +) from .services import guardrail, memory, state_machine +from .services.evaluation_contract import PERSISTED_APPROPRIATENESS_SCORE_5PT from .services.persona import PersonaCard from .store import DEFAULT_TURN_VISIBLE_TO, InProcSession, TurnRecord @@ -33,16 +44,60 @@ _WORKSHEET_REVIEW_STATUS_VALUES = { "changes_requested", "rejected", } -_APPROPRIATENESS_SCORE = { - "warn": 1.0, - "neutral": 3.0, - "pos": 5.0, -} LIVE_COACH_INITIAL_CREDITS = 3 LIVE_COACH_MAX_CREDITS = 3 LIVE_COACH_USE_REASON = "AI 코칭 힌트 사용" LIVE_COACH_RECHARGE_REASON = "좋은 발화로 내담자 변화 신호 확인" +SESSION_PERSONA_SELECT_COLUMNS_SQL = """ + s.id, s.runtime_case_id, s.case_id, s.learner_id, s.persona_code, + s.session_no, s.theory_mode, s.started_at, s.ended_at, + s.prev_rapport_credit, s.session_goals, + COALESCE( + NULLIF(learner.display_name, ''), + NULLIF(learner.nickname, ''), + NULLIF(learner.email, ''), + s.learner_id::text + ) AS learner_label, + pc.persona_id AS card_persona_id, + pc.code AS card_code, + pc.version AS card_version, + pc.status AS card_status, + pc.display_name AS card_display_name, + pc.difficulty AS card_difficulty, + pc.theory_target AS card_theory_target, + pc.demographics AS card_demographics, + pc.presenting AS card_presenting, + pc.history AS card_history, + pc.big5 AS card_big5, + pc.resistance AS card_resistance, + pc.speech_style AS card_speech_style, + pc.affect_baseline AS card_affect_baseline, + pc.ccd AS card_ccd, + pc.dsm5_dimensional AS card_dsm5_dimensional, + pc.triggers AS card_triggers, + pc.source_provenance AS card_source_provenance, + pc.is_synthetic AS card_is_synthetic +""" + +SESSION_PERSONA_JOINS_SQL = """ + LEFT JOIN app.persona_card pc + ON pc.persona_id = s.persona_id + AND pc.version = s.persona_version + LEFT JOIN app.app_user learner ON learner.user_id = s.learner_id +""" + +SESSION_STATE_SELECT_COLUMNS_SQL = """ + session_id, stage, turn_seq, effective_openness, rapport_credit, resistance, + ideation_stage, turns_in_stage, affect_state +""" + +SESSION_TURN_SELECT_COLUMNS_SQL = """ + session_id, id, seq, speaker, stage, text, text_masked, created_at, + llm_provider, model, tokens_in, tokens_out, cost_usd, + audio_ref, silence_ms, speech_rate, barge_in, provider_events, visible_to +""" + class LiveCoachCreditExhausted(RuntimeError): """Raised when a learner tries to use live coaching without credits.""" @@ -227,6 +282,21 @@ def _json_object_payload(value: Any) -> dict[str, Any]: return {} +def _json_list_payload(value: Any) -> list[Any]: + if isinstance(value, list): + return list(value) + if not isinstance(value, str): + return [] + text = value.strip() + if not text: + return [] + try: + parsed = json.loads(text) + except json.JSONDecodeError: + return [] + return parsed if isinstance(parsed, list) else [] + + def _masked_excerpt(value: str | None, *, limit: int = 220) -> str | None: text = (value or "").strip() if not text: @@ -261,7 +331,10 @@ def _live_coach_remaining(value: int | float | None) -> int: def _live_coach_quota_payload(remaining: int | float | None) -> dict[str, int]: - return {"remaining": _live_coach_remaining(remaining), "max": LIVE_COACH_MAX_CREDITS} + return { + "remaining": _live_coach_remaining(remaining), + "max": LIVE_COACH_MAX_CREDITS, + } def _live_coach_delta_from_record(record: dict[str, Any]) -> int: @@ -309,9 +382,14 @@ def _live_coach_credit_event_from_row(row) -> dict[str, Any]: "event_type": event_type, "delta": int(delta), "balance": _live_coach_remaining(balance), - "reason": str(_row_value(row, "reason") or ( - LIVE_COACH_RECHARGE_REASON if event_type == "recharge" else LIVE_COACH_USE_REASON - )), + "reason": str( + _row_value(row, "reason") + or ( + LIVE_COACH_RECHARGE_REASON + if event_type == "recharge" + else LIVE_COACH_USE_REASON + ) + ), "created_at": _iso_dt(row["created_at"]), } @@ -329,9 +407,14 @@ def _live_coach_credit_event_from_record(record: dict[str, Any]) -> dict[str, An "event_type": event_type, "delta": delta, "balance": _live_coach_remaining(record.get("credit_balance")), - "reason": str(record.get("reason") or ( - LIVE_COACH_RECHARGE_REASON if event_type == "recharge" else LIVE_COACH_USE_REASON - )), + "reason": str( + record.get("reason") + or ( + LIVE_COACH_RECHARGE_REASON + if event_type == "recharge" + else LIVE_COACH_USE_REASON + ) + ), "created_at": str(record.get("created_at") or ""), } @@ -348,7 +431,9 @@ def _live_coach_cache_record( ) -> dict[str, Any]: now = datetime.now(timezone.utc) balance = _live_coach_remaining( - credit_balance if credit_balance is not None else _live_coach_cached_remaining(session_id) - 1 + credit_balance + if credit_balance is not None + else _live_coach_cached_remaining(session_id) - 1 ) return { "event_id": str(uuid.uuid4()), @@ -407,8 +492,7 @@ def _card_from_joined_session_row(row) -> PersonaCard | None: if _row_value(row, "card_persona_id") is None: return None card_row = { - key.removeprefix("card_"): _row_value(row, key) - for key in _JOINED_CARD_COLUMNS + key.removeprefix("card_"): _row_value(row, key) for key in _JOINED_CARD_COLUMNS } return card_from_row(card_row) @@ -471,7 +555,9 @@ def _dict_items(value: Any) -> list[dict[str, Any]]: return [item for item in value if isinstance(item, dict)] -def _evaluation_feedback_rows(evaluation: dict[str, Any] | None) -> list[dict[str, Any]]: +def _evaluation_feedback_rows( + evaluation: dict[str, Any] | None, +) -> list[dict[str, Any]]: """Normalize scalar/rationale turn-evaluation fields into feedback_scores rows.""" if not isinstance(evaluation, dict): return [] @@ -499,11 +585,11 @@ def _evaluation_feedback_rows(evaluation: dict[str, Any] | None) -> list[dict[st ) appropriateness = _clean_text(evaluation.get("appropriateness")) or "neutral" - if appropriateness not in _APPROPRIATENESS_SCORE: + if appropriateness not in PERSISTED_APPROPRIATENESS_SCORE_5PT: appropriateness = "neutral" add( "appropriateness", - score=_APPROPRIATENESS_SCORE[appropriateness], + score=PERSISTED_APPROPRIATENESS_SCORE_5PT[appropriateness], rationale=_clean_masked_text(evaluation.get("appropriateness_note")), ) @@ -534,7 +620,9 @@ def _evaluation_feedback_rows(evaluation: dict[str, Any] | None) -> list[dict[st return rows -def _evaluation_technique_rows(evaluation: dict[str, Any] | None) -> list[dict[str, str]]: +def _evaluation_technique_rows( + evaluation: dict[str, Any] | None, +) -> list[dict[str, str]]: if not isinstance(evaluation, dict): return [] rows: list[dict[str, str]] = [] @@ -552,7 +640,9 @@ def _evaluation_technique_rows(evaluation: dict[str, Any] | None) -> list[dict[s return rows -def _evaluation_client_state_rows(evaluation: dict[str, Any] | None) -> list[dict[str, str]]: +def _evaluation_client_state_rows( + evaluation: dict[str, Any] | None, +) -> list[dict[str, str]]: if not isinstance(evaluation, dict): return [] rows: list[dict[str, str]] = [] @@ -575,11 +665,21 @@ def _evaluation_comment_rows(evaluation: dict[str, Any] | None) -> list[dict[str deviation = evaluation.get("intent_deviation") if not isinstance(deviation, dict): return [] - note = _clean_masked_text(evaluation.get("appropriateness_note")) or "의도와 다른 부분" - return [{"kind": "critique", "text": note, "intent_deviation": _mask_json_text_values(deviation)}] + note = ( + _clean_masked_text(evaluation.get("appropriateness_note")) or "의도와 다른 부분" + ) + return [ + { + "kind": "critique", + "text": note, + "intent_deviation": _mask_json_text_values(deviation), + } + ] -def _evaluation_alternative_rows(evaluation: dict[str, Any] | None) -> list[dict[str, str | None]]: +def _evaluation_alternative_rows( + evaluation: dict[str, Any] | None, +) -> list[dict[str, str | None]]: if not isinstance(evaluation, dict): return [] alternatives = evaluation.get("alternative_utterances") @@ -747,11 +847,15 @@ async def record_llm_call_audit(payload: dict[str, Any]) -> bool: The audit table intentionally stores no prompt or completion text. A DB outage must not block the counseling loop, so failures are reported as False. + + dev degraded(무DB) 기동에서는 audit 인프라 자체가 없는 것이 정상 폴백이므로, + 호출부가 이를 "기록 실패"로 보고 정상 LLM 응답을 강등하지 않도록 True 를 반환한다. + durable 환경에서 풀이 없거나 INSERT 가 실패한 경우에만 False 다. """ try: get_pool() except Exception: - return False + return runtime_fallback_allowed() try: async with acquire(ai_context=True, ai_view="evaluator") as conn: @@ -802,14 +906,18 @@ def _state_from_row(row, card: PersonaCard) -> state_machine.SessionState: def _turn_from_row(row, evaluation: dict[str, Any] | None = None) -> TurnRecord: created_at = _ts(row["created_at"]) or time.time() - text_masked = _clean_text(row["text_masked"]) or _clean_masked_text(row["text"]) or "" + text_masked = ( + _clean_text(row["text_masked"]) or _clean_masked_text(row["text"]) or "" + ) return TurnRecord( turn_seq=int(row["seq"]), speaker=row["speaker"], stage=row["stage"], text=text_masked, text_masked=text_masked, - turn_id=str(_row_value(row, "id")) if _row_value(row, "id") is not None else None, + turn_id=str(_row_value(row, "id")) + if _row_value(row, "id") is not None + else None, created_at=created_at, llm_provider=_row_value(row, "llm_provider"), model=_row_value(row, "model"), @@ -826,7 +934,9 @@ def _turn_from_row(row, evaluation: dict[str, Any] | None = None) -> TurnRecord: ) -async def _persist_turn_evaluation(conn: Any, turn_id: str, evaluation: dict[str, Any] | None) -> None: +async def _persist_turn_evaluation( + conn: Any, turn_id: str, evaluation: dict[str, Any] | None +) -> None: if not isinstance(evaluation, dict): return await conn.execute("SELECT set_config('app.ai_context', '1', true)") @@ -916,7 +1026,9 @@ async def _persist_turn_evaluation(conn: Any, turn_id: str, evaluation: dict[str row["intent_deviation"], ) - await conn.execute("DELETE FROM app.alternative_utterance WHERE turn_id = $1::uuid", turn_id) + await conn.execute( + "DELETE FROM app.alternative_utterance WHERE turn_id = $1::uuid", turn_id + ) for row in _evaluation_alternative_rows(evaluation): await conn.execute( """ @@ -931,19 +1043,33 @@ async def _persist_turn_evaluation(conn: Any, turn_id: str, evaluation: dict[str ) -async def _replace_turn_evaluation(conn: Any, turn_id: str, evaluation: dict[str, Any] | None) -> None: +async def _replace_turn_evaluation( + conn: Any, turn_id: str, evaluation: dict[str, Any] | None +) -> None: await conn.execute("SELECT set_config('app.ai_context', '1', true)") await conn.execute("SELECT set_config('app.current_ai_view', 'evaluator', true)") await conn.execute("SELECT set_config('app.current_sens_max', '2', true)") - await conn.execute("DELETE FROM app.feedback_scores WHERE turn_id = $1::uuid", turn_id) - await conn.execute("DELETE FROM app.turn_technique WHERE turn_id = $1::uuid", turn_id) - await conn.execute("DELETE FROM app.turn_client_state WHERE turn_id = $1::uuid", turn_id) - await conn.execute("DELETE FROM app.supervisor_comment WHERE turn_id = $1::uuid", turn_id) - await conn.execute("DELETE FROM app.alternative_utterance WHERE turn_id = $1::uuid", turn_id) + await conn.execute( + "DELETE FROM app.feedback_scores WHERE turn_id = $1::uuid", turn_id + ) + await conn.execute( + "DELETE FROM app.turn_technique WHERE turn_id = $1::uuid", turn_id + ) + await conn.execute( + "DELETE FROM app.turn_client_state WHERE turn_id = $1::uuid", turn_id + ) + await conn.execute( + "DELETE FROM app.supervisor_comment WHERE turn_id = $1::uuid", turn_id + ) + await conn.execute( + "DELETE FROM app.alternative_utterance WHERE turn_id = $1::uuid", turn_id + ) await _persist_turn_evaluation(conn, turn_id, _mask_json_text_values(evaluation)) -async def replace_turn_evaluation(*, turn_id: str, evaluation: dict[str, Any] | None) -> bool: +async def replace_turn_evaluation( + *, turn_id: str, evaluation: dict[str, Any] | None +) -> bool: if not turn_id or not isinstance(evaluation, dict): return False try: @@ -1051,10 +1177,15 @@ async def _hydrate_sessions_turn_evaluations(sessions: list[InProcSession]) -> N async def ensure_review_tables() -> None: - """Create runtime review/evaluation storage when the DB role allows it.""" + """Verify the review schema, with DDL repair restricted to local development.""" try: get_pool() async with acquire(role="admin") as conn: + ready = await schema_contract_ready(conn, REVIEW_SCHEMA_CONTRACT) + if not runtime_schema_bootstrap_required( + REVIEW_SCHEMA_CONTRACT, ready=ready + ): + return await conn.execute( """ CREATE TABLE IF NOT EXISTS app.session_evaluation ( @@ -1521,7 +1652,13 @@ async def ensure_review_tables() -> None: ) """ ) + if not await schema_contract_ready(conn, REVIEW_SCHEMA_CONTRACT): + raise RuntimeError( + "review/evaluation development schema bootstrap did not satisfy readiness" + ) except Exception: + if settings.environment != "dev": + raise return @@ -1584,7 +1721,11 @@ async def load_session_evaluation( session_id, ) if row is None: - cached = _EVALUATION_CACHE.get(session_id) if runtime_fallback_allowed() else None + cached = ( + _EVALUATION_CACHE.get(session_id) + if runtime_fallback_allowed() + else None + ) return cached, cached is None return { "status": row["status"], @@ -1649,6 +1790,39 @@ async def list_session_evaluations( }, False +async def _fetch_session_runtime_rows( + conn: Any, + session_ids: list[str], +) -> tuple[dict[str, Any], dict[str, list[Any]]]: + """세션 상태와 턴을 두 번의 배치 조회로 적재한다.""" + if not session_ids: + return {}, {} + state_rows = await conn.fetch( + f""" + SELECT {SESSION_STATE_SELECT_COLUMNS_SQL} + FROM app.session_state + WHERE session_id = ANY($1::uuid[]) + """, + session_ids, + ) + states_by_id = { + str(state_row["session_id"]): state_row for state_row in state_rows + } + turn_rows = await conn.fetch( + f""" + SELECT {SESSION_TURN_SELECT_COLUMNS_SQL} + FROM app.turns + WHERE session_id = ANY($1::uuid[]) + ORDER BY session_id, seq + """, + session_ids, + ) + turns_by_id: dict[str, list[Any]] = {} + for turn_row in turn_rows: + turns_by_id.setdefault(str(turn_row["session_id"]), []).append(turn_row) + return states_by_id, turns_by_id + + async def list_sessions_missing_session_evaluation( *, older_than_seconds: float, @@ -1661,42 +1835,11 @@ async def list_sessions_missing_session_evaluation( stale_seconds = max(float(older_than_seconds), 0.0) async with acquire(ai_context=True, ai_view="evaluator") as conn: rows = await conn.fetch( - """ - SELECT - s.id, s.runtime_case_id, s.case_id, s.learner_id, s.persona_code, - s.session_no, s.theory_mode, s.started_at, s.ended_at, - s.prev_rapport_credit, - COALESCE( - NULLIF(learner.display_name, ''), - NULLIF(learner.nickname, ''), - NULLIF(learner.email, ''), - s.learner_id::text - ) AS learner_label, - pc.persona_id AS card_persona_id, - pc.code AS card_code, - pc.version AS card_version, - pc.status AS card_status, - pc.display_name AS card_display_name, - pc.difficulty AS card_difficulty, - pc.theory_target AS card_theory_target, - pc.demographics AS card_demographics, - pc.presenting AS card_presenting, - pc.history AS card_history, - pc.big5 AS card_big5, - pc.resistance AS card_resistance, - pc.speech_style AS card_speech_style, - pc.affect_baseline AS card_affect_baseline, - pc.ccd AS card_ccd, - pc.dsm5_dimensional AS card_dsm5_dimensional, - pc.triggers AS card_triggers, - pc.source_provenance AS card_source_provenance, - pc.is_synthetic AS card_is_synthetic + f""" + SELECT {SESSION_PERSONA_SELECT_COLUMNS_SQL} FROM app.sessions s LEFT JOIN app.session_evaluation se ON se.session_id = s.id - LEFT JOIN app.persona_card pc - ON pc.persona_id = s.persona_id - AND pc.version = s.persona_version - LEFT JOIN app.app_user learner ON learner.user_id = s.learner_id + {SESSION_PERSONA_JOINS_SQL} WHERE s.ended_at IS NOT NULL AND s.ended_at <= now() - ($1::double precision * interval '1 second') AND se.session_id IS NULL @@ -1712,30 +1855,18 @@ async def list_sessions_missing_session_evaluation( stale_seconds, max(1, int(limit)), ) + session_ids = [str(row["id"]) for row in rows] + states_by_id, turns_by_id = await _fetch_session_runtime_rows( + conn, session_ids + ) sessions: list[InProcSession] = [] for row in rows: session_id = str(row["id"]) - state_row = await conn.fetchrow( - """ - SELECT stage, turn_seq, effective_openness, rapport_credit, resistance, - ideation_stage, turns_in_stage, affect_state - FROM app.session_state - WHERE session_id = $1::uuid - """, - session_id, + sess = _session_from_rows( + row, + states_by_id.get(session_id), + turns_by_id.get(session_id, []), ) - turn_rows = await conn.fetch( - """ - SELECT id, seq, speaker, stage, text, text_masked, created_at, - llm_provider, model, tokens_in, tokens_out, cost_usd, - audio_ref, silence_ms, speech_rate, barge_in, provider_events, visible_to - FROM app.turns - WHERE session_id = $1::uuid - ORDER BY seq - """, - session_id, - ) - sess = _session_from_rows(row, state_row, turn_rows) if sess is not None: sessions.append(sess) return sessions, True @@ -1793,7 +1924,9 @@ async def load_case_worksheet( ) if row is None: return ( - _CASE_WORKSHEET_CACHE.get(session_id) if runtime_fallback_allowed() else None + _CASE_WORKSHEET_CACHE.get(session_id) + if runtime_fallback_allowed() + else None ), False payload = dict(row["payload"] or {}) payload.setdefault("savedAt", _iso_dt(row["updated_at"])) @@ -1909,7 +2042,9 @@ async def get_live_coach_quota( return _live_coach_quota_payload(remaining), True except Exception: require_runtime_fallback_allowed("live coach quota") - return _live_coach_quota_payload(_live_coach_cached_remaining(session_id)), False + return _live_coach_quota_payload( + _live_coach_cached_remaining(session_id) + ), False async def list_live_coach_credit_events( @@ -1992,7 +2127,8 @@ async def record_live_coach_recharge( require_runtime_fallback_allowed("live coach recharge") events = _LIVE_COACH_EVENT_CACHE.setdefault(session_id, []) if any( - event.get("event_type") == "recharge" and int(event.get("turn_seq") or 0) == int(turn_seq) + event.get("event_type") == "recharge" + and int(event.get("turn_seq") or 0) == int(turn_seq) for event in events ): return None, False @@ -2094,8 +2230,7 @@ async def list_session_review_statuses( session_ids, ) return { - str(row["session_id"]): _review_status_from_row(row) - for row in rows + str(row["session_id"]): _review_status_from_row(row) for row in rows }, True except Exception: require_runtime_fallback_allowed("session review status list") @@ -2326,7 +2461,9 @@ async def list_session_archives( """, session_ids, ) - return {str(row["session_id"]): _archive_record_from_row(row) for row in rows}, True + return { + str(row["session_id"]): _archive_record_from_row(row) for row in rows + }, True except Exception: require_runtime_fallback_allowed("session archive list") return { @@ -2385,7 +2522,9 @@ async def set_session_archived( session_id, learner_id, ) - return (_archive_record_from_row(deleted) if deleted is not None else None), True + return ( + _archive_record_from_row(deleted) if deleted is not None else None + ), True except Exception: require_runtime_fallback_allowed("session archive update") return (_SESSION_ARCHIVE_CACHE.get(session_id) if archived else None), False @@ -2420,10 +2559,15 @@ def _session_from_rows(row, state_row, turn_rows: Iterable) -> InProcSession | N ended=ended_at is not None, prev_rapport_credit=float(row["prev_rapport_credit"] or 0.0), learner_label=_clean_text(_row_value(row, "learner_label")), + goal_stages=[ + str(v) for v in _json_list_payload(_row_value(row, "session_goals")) + ], ) -async def _upsert_state(conn, session_id: str, state: state_machine.SessionState) -> None: +async def _upsert_state( + conn, session_id: str, state: state_machine.SessionState +) -> None: await conn.execute( """ INSERT INTO app.session_state ( @@ -2493,6 +2637,7 @@ async def create_session( persona_id: str | None = None, persona_version: int | None = None, case_id: str | None = None, + goal_stages: list[str] | None = None, ) -> InProcSession | None: """Create a DB-backed session, returning None when DB persistence is unavailable.""" try: @@ -2537,12 +2682,12 @@ async def create_session( INSERT INTO app.sessions ( runtime_case_id, case_id, learner_id, persona_id, persona_version, persona_code, persona_display_name, persona_difficulty, - session_no, theory_mode, stage_path, prev_rapport_credit + session_no, theory_mode, stage_path, prev_rapport_credit, session_goals ) VALUES ( $1::uuid, $2::uuid, $3::uuid, $4::uuid, $5, $6, $7, $8, - $9, $10, '[]'::jsonb, $11 + $9, $10, '[]'::jsonb, $11, $12::jsonb ) RETURNING id, runtime_case_id, case_id, learner_id, persona_code, session_no, theory_mode, started_at, ended_at, prev_rapport_credit @@ -2558,6 +2703,7 @@ async def create_session( session_no, theory_mode, carry_rapport, + json.dumps(list(goal_stages or []), ensure_ascii=False), ) await _upsert_state(conn, str(row["id"]), state) return InProcSession( @@ -2574,6 +2720,7 @@ async def create_session( turns=[], ended=False, prev_rapport_credit=carry_rapport, + goal_stages=list(goal_stages or []), ) except Exception: require_runtime_fallback_allowed("session creation") @@ -2595,41 +2742,10 @@ async def load_session( cohort_ids=principal.cohort_ids, ) as conn: row = await conn.fetchrow( - """ - SELECT - s.id, s.runtime_case_id, s.case_id, s.learner_id, s.persona_code, - s.session_no, s.theory_mode, s.started_at, s.ended_at, - s.prev_rapport_credit, - COALESCE( - NULLIF(learner.display_name, ''), - NULLIF(learner.nickname, ''), - NULLIF(learner.email, ''), - s.learner_id::text - ) AS learner_label, - pc.persona_id AS card_persona_id, - pc.code AS card_code, - pc.version AS card_version, - pc.status AS card_status, - pc.display_name AS card_display_name, - pc.difficulty AS card_difficulty, - pc.theory_target AS card_theory_target, - pc.demographics AS card_demographics, - pc.presenting AS card_presenting, - pc.history AS card_history, - pc.big5 AS card_big5, - pc.resistance AS card_resistance, - pc.speech_style AS card_speech_style, - pc.affect_baseline AS card_affect_baseline, - pc.ccd AS card_ccd, - pc.dsm5_dimensional AS card_dsm5_dimensional, - pc.triggers AS card_triggers, - pc.source_provenance AS card_source_provenance, - pc.is_synthetic AS card_is_synthetic + f""" + SELECT {SESSION_PERSONA_SELECT_COLUMNS_SQL} FROM app.sessions s - LEFT JOIN app.persona_card pc - ON pc.persona_id = s.persona_id - AND pc.version = s.persona_version - LEFT JOIN app.app_user learner ON learner.user_id = s.learner_id + {SESSION_PERSONA_JOINS_SQL} WHERE s.id = $1::uuid """, session_id, @@ -2638,27 +2754,14 @@ async def load_session( return None if row["ended_at"] is not None and not allow_ended: return None - state_row = await conn.fetchrow( - """ - SELECT stage, turn_seq, effective_openness, rapport_credit, resistance, - ideation_stage, turns_in_stage, affect_state - FROM app.session_state - WHERE session_id = $1::uuid - """, - session_id, + states_by_id, turns_by_id = await _fetch_session_runtime_rows( + conn, [session_id] ) - turn_rows = await conn.fetch( - """ - SELECT id, seq, speaker, stage, text, text_masked, created_at, - llm_provider, model, tokens_in, tokens_out, cost_usd, - audio_ref, silence_ms, speech_rate, barge_in, provider_events, visible_to - FROM app.turns - WHERE session_id = $1::uuid - ORDER BY seq - """, - session_id, + sess = _session_from_rows( + row, + states_by_id.get(session_id), + turns_by_id.get(session_id, []), ) - sess = _session_from_rows(row, state_row, turn_rows) if sess is not None: await _record_session_read_audit( conn, @@ -2926,15 +3029,21 @@ async def _upsert_pinned_fact_candidates(conn: Any, sess: InProcSession) -> None ) -def _build_session_summary_write(sess: InProcSession, carry: memory.CarryOver) -> SessionSummaryWrite: +def _build_session_summary_write( + sess: InProcSession, carry: memory.CarryOver +) -> SessionSummaryWrite: digest_input = memory.build_session_digest_input( session_id=sess.session_id, case_id=sess.case_id, session_no=sess.session_no, masked_turns=sess.masked_turns(visible_to="client"), - open_threads=carry.compression_job.open_threads if carry.compression_job else [], + open_threads=carry.compression_job.open_threads + if carry.compression_job + else [], + ) + digest_result = memory.build_fallback_digest_result( + digest_input, end_state=carry.end_state ) - digest_result = memory.build_fallback_digest_result(digest_input, end_state=carry.end_state) return SessionSummaryWrite( session_id=sess.session_id, case_id=sess.case_id, @@ -3072,8 +3181,12 @@ async def _list_sessions( ) -> tuple[list[InProcSession], bool]: try: get_pool() - learner_filter = "WHERE s.learner_id = $1::uuid" if principal.role.value == "learner" else "" - query_args: list[object] = [principal.user_id] if principal.role.value == "learner" else [] + learner_filter = ( + "WHERE s.learner_id = $1::uuid" if principal.role.value == "learner" else "" + ) + query_args: list[object] = ( + [principal.user_id] if principal.role.value == "learner" else [] + ) limit_clause = "" if session_limit is not None: query_args.append(max(1, int(session_limit))) @@ -3085,40 +3198,9 @@ async def _list_sessions( ) as conn: rows = await conn.fetch( f""" - SELECT - s.id, s.runtime_case_id, s.case_id, s.learner_id, s.persona_code, - s.session_no, s.theory_mode, s.started_at, s.ended_at, - s.prev_rapport_credit, - COALESCE( - NULLIF(learner.display_name, ''), - NULLIF(learner.nickname, ''), - NULLIF(learner.email, ''), - s.learner_id::text - ) AS learner_label, - pc.persona_id AS card_persona_id, - pc.code AS card_code, - pc.version AS card_version, - pc.status AS card_status, - pc.display_name AS card_display_name, - pc.difficulty AS card_difficulty, - pc.theory_target AS card_theory_target, - pc.demographics AS card_demographics, - pc.presenting AS card_presenting, - pc.history AS card_history, - pc.big5 AS card_big5, - pc.resistance AS card_resistance, - pc.speech_style AS card_speech_style, - pc.affect_baseline AS card_affect_baseline, - pc.ccd AS card_ccd, - pc.dsm5_dimensional AS card_dsm5_dimensional, - pc.triggers AS card_triggers, - pc.source_provenance AS card_source_provenance, - pc.is_synthetic AS card_is_synthetic + SELECT {SESSION_PERSONA_SELECT_COLUMNS_SQL} FROM app.sessions s - LEFT JOIN app.persona_card pc - ON pc.persona_id = s.persona_id - AND pc.version = s.persona_version - LEFT JOIN app.app_user learner ON learner.user_id = s.learner_id + {SESSION_PERSONA_JOINS_SQL} {learner_filter} ORDER BY s.started_at DESC {limit_clause} @@ -3127,32 +3209,9 @@ async def _list_sessions( ) # N+1 제거: 세션별 state/turns fetch 루프(1+2N 왕복) 대신 id 집합으로 한 번씩 배치 조회. session_ids = [str(row["id"]) for row in rows] - states_by_id: dict[str, Any] = {} - turns_by_id: dict[str, list] = {} - if session_ids: - state_rows = await conn.fetch( - """ - SELECT session_id, stage, turn_seq, effective_openness, rapport_credit, resistance, - ideation_stage, turns_in_stage, affect_state - FROM app.session_state - WHERE session_id = ANY($1::uuid[]) - """, - session_ids, - ) - states_by_id = {str(state_row["session_id"]): state_row for state_row in state_rows} - turn_rows_all = await conn.fetch( - """ - SELECT session_id, id, seq, speaker, stage, text, text_masked, created_at, - llm_provider, model, tokens_in, tokens_out, cost_usd, - audio_ref, silence_ms, speech_rate, barge_in, provider_events, visible_to - FROM app.turns - WHERE session_id = ANY($1::uuid[]) - ORDER BY session_id, seq - """, - session_ids, - ) - for turn_row in turn_rows_all: - turns_by_id.setdefault(str(turn_row["session_id"]), []).append(turn_row) + states_by_id, turns_by_id = await _fetch_session_runtime_rows( + conn, session_ids + ) sessions: list[InProcSession] = [] for row in rows: session_id = str(row["id"]) diff --git a/apps/api/app/session_read_model.py b/apps/api/app/session_read_model.py index b1f923c..dd9d4e8 100644 --- a/apps/api/app/session_read_model.py +++ b/apps/api/app/session_read_model.py @@ -16,13 +16,13 @@ from typing import Any, Literal, Optional, cast from pydantic import BaseModel, Field from .config import settings -from .services import guardrail, session_metrics +from .services import guardrail, session_metrics, state_machine from .stage_contract import ( ReviewPhaseKey, StageLabel, review_phase_key, stage_label as _normalize_stage_label, - stage_label_or_none, + stage_label_or_none as stage_label_or_none, ) from .store import InProcSession, TurnRecord @@ -106,6 +106,8 @@ class LearnerDashboardPersonaProgress(BaseModel): latest_stage: StageLabel | None = None latest_score: float | None = None trend: str = "insufficient" + # P2 누적 게이지: 이 페르소나와 쌓아온 라포 누적(전 주기 임계 기준 %). + rapport_percent: int = 0 class LearnerDashboardAchievement(BaseModel): @@ -147,6 +149,75 @@ class SessionArchiveResponse(BaseModel): session: LearnerSessionSummary +class SessionStageProgress(BaseModel): + """단계별 누적 게이지(P2). 상태머신 결정론 수치의 파생값만 담는다.""" + + stage: StageLabel + percent: int = 0 + achieved: bool = False + is_goal: bool = False + + +class SessionProgress(BaseModel): + """회기 진행 상세(P2). 내부 원값 명칭 대신 학습자-안전 파생 %만 노출한다.""" + + stages: list[SessionStageProgress] = Field(default_factory=list) + rapport_percent: int = 0 + rapport_delta_percent: int = 0 + resistance_percent: int = 0 + openness_percent: int = 0 + + +# 전 주기(라포→정리) 기준 라포 만점: 마지막 전이 임계(개입→정리)를 100으로 본다. +_FULL_CYCLE_RAPPORT = max(state_machine.STAGE_ADVANCE_RAPPORT.values()) + + +def _rapport_percent(credit: float) -> int: + if _FULL_CYCLE_RAPPORT <= 0: + return 0 + return max(0, min(100, int(round(float(credit or 0.0) / _FULL_CYCLE_RAPPORT * 100)))) + + +def build_session_progress( + state: state_machine.SessionState, + *, + prev_rapport_credit: float = 0.0, + goal_stages: list[str] | None = None, +) -> SessionProgress: + """상태머신 수치 → 단계 누적 게이지/상세 수치 파생(순수함수, LLM 미경유). + + 누적성: rapport_credit 은 회기 간 ×0.7 이월되므로 다음 회기 게이지는 이월분에서 시작한다. + """ + goals = {stage_label(goal) for goal in (goal_stages or [])} + order = state_machine.STAGE_ORDER + cur_idx = order.index(state.stage) + stages: list[SessionStageProgress] = [] + for i, st in enumerate(order): + label = stage_label(st) + if i < cur_idx: + pct, achieved = 100, True + elif i > cur_idx: + pct, achieved = 0, False + elif st is state_machine.Stage.CLOSE: + pct, achieved = 100, True + else: + need = state_machine.STAGE_ADVANCE_RAPPORT.get(st, 1.0) + ratio = min(1.0, float(state.rapport_credit or 0.0) / need) if need > 0 else 0.0 + pct, achieved = min(99, int(round(ratio * 99))), False + stages.append( + SessionStageProgress(stage=label, percent=pct, achieved=achieved, is_goal=label in goals) + ) + rapport_pct = _rapport_percent(state.rapport_credit) + prev_pct = _rapport_percent(prev_rapport_credit) + return SessionProgress( + stages=stages, + rapport_percent=rapport_pct, + rapport_delta_percent=max(0, rapport_pct - prev_pct), + resistance_percent=max(0, min(100, int(round(float(state.resistance or 0.0) * 100)))), + openness_percent=max(0, min(100, int(round(float(state.effective_openness or 0.0) * 100)))), + ) + + class SessionDetailTurn(BaseModel): turn_seq: int speaker: Literal["learner", "client"] @@ -168,6 +239,12 @@ class SessionDetailResponse(BaseModel): ended_at: str | None = None turns: list[SessionDetailTurn] = Field(default_factory=list) review_ready: bool = False + # 시간 기반 회기(2026-07-13 회의 P1): 새로고침 복원 시 타이머·목표 표시의 기준. + goal_stages: list[StageLabel] = Field(default_factory=list) + duration_limit_seconds: int = 0 + warning_before_end_seconds: int = 0 + # P2 단계 누적 게이지·상세 수치. + progress: SessionProgress | None = None class ReviewClient(BaseModel): @@ -537,6 +614,7 @@ def dashboard_persona_progress( latest_stage=stage_label(latest.state.stage), latest_score=growth.latest_score if growth else None, trend=growth.trend if growth else "insufficient", + rapport_percent=_rapport_percent(latest.state.rapport_credit), ) ) return sorted( @@ -643,6 +721,14 @@ def session_detail( for turn in turns ], review_ready=review_ready, + goal_stages=[stage_label(goal) for goal in (sess.goal_stages or [])], + duration_limit_seconds=max(0, settings.session_duration_minutes) * 60, + warning_before_end_seconds=max(0, settings.session_warning_minutes) * 60, + progress=build_session_progress( + sess.state, + prev_rapport_credit=sess.prev_rapport_credit, + goal_stages=list(sess.goal_stages or []), + ), ) diff --git a/apps/api/app/store.py b/apps/api/app/store.py index a18330b..f28445d 100644 --- a/apps/api/app/store.py +++ b/apps/api/app/store.py @@ -10,7 +10,7 @@ DB(NAS Postgres)가 단일 SoR 이지만(db.py), Docker off 개발/시연에서 from __future__ import annotations import time -from dataclasses import asdict, dataclass, field +from dataclasses import dataclass, field from decimal import Decimal from typing import Optional from uuid import uuid4 @@ -69,6 +69,7 @@ class InProcSession: ended: bool = False prev_rapport_credit: float = 0.0 # carry-over delta 계산용 learner_label: str | None = None + goal_stages: list[str] = field(default_factory=list) # 이번 회기 목표 단계(학습자 선택, 최대 4) def turns_visible_to(self, role: str) -> list[TurnRecord]: return [turn for turn in self.turns if turn.is_visible_to(role)] @@ -97,6 +98,7 @@ class SessionStore: state: SessionState, session_no: int = 1, carry_rapport: float = 0.0, + goal_stages: list[str] | None = None, ) -> InProcSession: session_id = uuid4().hex case_id = uuid4().hex @@ -110,6 +112,7 @@ class SessionStore: state=state, session_no=session_no, prev_rapport_credit=carry_rapport, + goal_stages=list(goal_stages or []), ) self._sessions[session_id] = s return s diff --git a/apps/api/app/taxonomy.py b/apps/api/app/taxonomy.py index 7242255..71f93e6 100644 --- a/apps/api/app/taxonomy.py +++ b/apps/api/app/taxonomy.py @@ -26,7 +26,9 @@ from dataclasses import dataclass, field from enum import Enum from typing import Optional -TAXONOMY_VERSION = "1.0.0" # 라벨 코드 불변 보장 버전. 새 라벨 추가 시 minor++. +TAXONOMY_VERSION = "1.1.0" # 라벨 코드 불변 보장 버전. 새 라벨 추가 시 minor++. +# 1.1.0 (2026-07-15): 연구팀 태깅 축어록 4종(첫회기 BPS·게슈탈트 3회기·자해 DBT·우울 CBT) +# 태그 정합을 위해 Technique 9종·ClientState 9종 append-only 추가. 기존 코드 불변. # ════════════════════════════════════════════════════════════════════════════ @@ -94,16 +96,28 @@ class Technique(str, Enum): RISK_ASSESSMENT = "risk_assessment" # 위험요인/자해·자살 탐색 (0615: "위험요인 탐색", "자해 위험 및 경험 탐색") CONSENT_MOTIVATION_CHECK = "consent_motivation_check" # 동의/동기 확인 (0615: "동의확인, 동기수준 확인") OPINION_CHECK = "opinion_check" # 내담자 의견·반응 확인 (0615: "내담자 의견 확인", "주호소 문제 재확인") + # 1.1.0 추가 (연구팀 태깅 자료 정합) + CLARIFICATION = "clarification" # 명료화 질문 (cbt: "이해를 명확하게 하기 위해 질문", ch3 다수) + SCALING = "scaling" # 감정/증상 수치화·척도 질문 (cbt: 감정 강도 0~100) + CHAIN_ANALYSIS = "chain_analysis" # 행동 체인분석 — 사건→감정→행동 사슬 탐색 (dbt) # ── INTERVENTION ── CONFRONTATION = "confrontation" # 직면 (0615: "기초자료를 활용하여 반응의 불일치에 직면시킴") INTERPRETATION = "interpretation" # 해석 (0615: "모순의 의미를 해석함", "비언어적 자극 해석", "행동의 재해석") + # 1.1.0 추가 + COGNITIVE_RESTRUCTURING = "cognitive_restructuring" # 인지재구성 — 이분법·파국화 확인, 연속선 사고, 핵심신념 작업 (cbt 55건) + BEHAVIORAL_ALTERNATIVE = "behavioral_alternative" # 대안행동 탐색·계획 (cbt) + COMMITMENT_STRATEGY = "commitment_strategy" # 서약/전념 전략 — 악마의 옹호자, 방해물 예상 (dbt) + HERE_AND_NOW_FOCUS = "here_and_now_focus" # 지금-여기 초점화 (gestalt: 현전·트래킹) # ── STABILIZING ── STABILIZATION = "stabilization" # 안정화 (0615: "안정화") HOPE_INSTILLATION = "hope_instillation" # 희망고취 (0615: "동기부여, 희망고취", "희망 고취, 욕구 반영") REINFORCEMENT = "reinforcement" # 강화 (0615: "강화") NORMALIZATION = "normalization" # 정상화 (0615: "감정 반응을 노출하는 것을 정상화함") + # 1.1.0 추가 + RESTATEMENT = "restatement" # 재진술 — 내용을 되돌려 확인 (cbt: 재진술 후 내담자 확인 대기) + SKILLS_COACHING = "skills_coaching" # 기술 코칭 — 고통감내·위기대처 기술 안내 (dbt) # ── STRUCTURING ── PRINCIPLE_EXPLANATION = "principle_explanation" # 상담원칙/비밀보장 설명 (0615: "상담원칙에 대한 설명", "비밀보장 제외 원칙 설명") @@ -134,6 +148,16 @@ TECHNIQUE_CATEGORY: dict[Technique, TechniqueCategory] = { Technique.PRINCIPLE_EXPLANATION: TechniqueCategory.STRUCTURING, Technique.PSYCHOEDUCATION: TechniqueCategory.STRUCTURING, Technique.HOMEWORK: TechniqueCategory.STRUCTURING, + # 1.1.0 추가 + Technique.CLARIFICATION: TechniqueCategory.EXPLORATORY, + Technique.SCALING: TechniqueCategory.EXPLORATORY, + Technique.CHAIN_ANALYSIS: TechniqueCategory.EXPLORATORY, + Technique.COGNITIVE_RESTRUCTURING: TechniqueCategory.INTERVENTION, + Technique.BEHAVIORAL_ALTERNATIVE: TechniqueCategory.INTERVENTION, + Technique.COMMITMENT_STRATEGY: TechniqueCategory.INTERVENTION, + Technique.HERE_AND_NOW_FOCUS: TechniqueCategory.INTERVENTION, + Technique.RESTATEMENT: TechniqueCategory.RELATIONAL, + Technique.SKILLS_COACHING: TechniqueCategory.STABILIZING, } # 한글 표시명(UI 라벨 칩·리뷰 화면). docs/taxonomy.md 와 일치. @@ -159,6 +183,16 @@ TECHNIQUE_KO: dict[Technique, str] = { Technique.PRINCIPLE_EXPLANATION: "상담원칙 설명", Technique.PSYCHOEDUCATION: "심리교육", Technique.HOMEWORK: "과제 부여", + # 1.1.0 추가 + Technique.CLARIFICATION: "명료화", + Technique.SCALING: "수치화 질문", + Technique.CHAIN_ANALYSIS: "체인분석", + Technique.COGNITIVE_RESTRUCTURING: "인지재구성", + Technique.BEHAVIORAL_ALTERNATIVE: "대안행동 탐색", + Technique.COMMITMENT_STRATEGY: "전념 전략", + Technique.HERE_AND_NOW_FOCUS: "지금-여기 초점화", + Technique.RESTATEMENT: "재진술", + Technique.SKILLS_COACHING: "기술 코칭", } @@ -183,6 +217,16 @@ class ClientState(str, Enum): RESPONDS_TO_EXPLORATION = "responds_to_exploration" # 탐색에 반응함 (0615: "상담자의 탐색에 반응함") EXPRESSES_PLAN = "expresses_plan" # 자기 계획/욕구 표현 (0615: "자신의 계획을 표현함") DEFENSE_LOOSENING = "defense_loosening" # 방어가 서서히 풀림 (0615: "방어가 서서히 풀어지고 있음") + # 1.1.0 추가 (연구팀 태깅 자료 정합) + COMPLIANT_SURFACE = "compliant_surface" # 표면 순응 — 겉으로 동의하나 내면 미변화 (ch2) + EXTERNALIZING = "externalizing" # 문제 외재화 — "아이가 학교만 가면"/"문제는 아내" (ch2·cbt) + SEEKS_GUIDANCE = "seeks_guidance" # 상담자 의견/지시를 구함 (cbt) + APPARENT_COMPETENCE = "apparent_competence" # 겉보기 유능함 — 회기 내 유능, 위기 시 붕괴 (dbt) + ACTIVE_PASSIVITY = "active_passivity" # 적극적 수동성 — 해결을 타인에게 위임 (dbt) + SELF_HARM_DISCLOSURE = "self_harm_disclosure" # 자해 보고 — 자살사고 인정과 구분 (dbt) + SOMATIC_COMPLAINT = "somatic_complaint" # 신체증상 호소 — 숨막힘·두근거림 (ch2) + AFFECT_MASKING = "affect_masking" # 감정 가림 — 웃음·무덤덤함으로 덮기 (ch3) + FOCUS_DRIFT_FUSION = "focus_drift_fusion" # 초점 이탈/융합 — 자기 문제가 타인 이야기로 회귀 (ch2·ch3) CLIENT_STATE_KO: dict[ClientState, str] = { ClientState.INVOLUNTARY: "비자발적 태도", @@ -196,6 +240,16 @@ CLIENT_STATE_KO: dict[ClientState, str] = { ClientState.RESPONDS_TO_EXPLORATION: "탐색에 반응", ClientState.EXPRESSES_PLAN: "계획/욕구 표현", ClientState.DEFENSE_LOOSENING: "방어 완화", + # 1.1.0 추가 + ClientState.COMPLIANT_SURFACE: "표면 순응", + ClientState.EXTERNALIZING: "문제 외재화", + ClientState.SEEKS_GUIDANCE: "지시 요청", + ClientState.APPARENT_COMPETENCE: "겉보기 유능함", + ClientState.ACTIVE_PASSIVITY: "적극적 수동성", + ClientState.SELF_HARM_DISCLOSURE: "자해 보고", + ClientState.SOMATIC_COMPLAINT: "신체증상 호소", + ClientState.AFFECT_MASKING: "감정 가림", + ClientState.FOCUS_DRIFT_FUSION: "초점 이탈/융합", } diff --git a/apps/api/app/test_client_reply_quality.py b/apps/api/app/test_client_reply_quality.py index 7df0a9b..259ab47 100644 --- a/apps/api/app/test_client_reply_quality.py +++ b/apps/api/app/test_client_reply_quality.py @@ -8,6 +8,28 @@ from .services import guardrail class ClientReplyQualityGateTest(unittest.TestCase): + def test_blocks_self_harm_means_detail(self) -> None: + """자해 수단·방법 상세는 차단(1.1 확장 — DBT 자해 페르소나 대비).""" + samples = [ + "커터로 했어요. 그게 제일 쉬워서요.", + "면도날을 항상 가지고 다녀요.", + "어젯밤에도 손목 긋고 잠들었어요.", + ] + for sample in samples: + with self.subTest(sample=sample): + result = guardrail.sanitize_client_reply(sample, ideation_stage=3, turn_seq=4) + self.assertTrue(result.needs_regeneration) + self.assertTrue(any(r.startswith("means_info:") for r in result.reasons)) + + def test_allows_boundary_setting_language(self) -> None: + """'선을 긋다'(경계 설정) 같은 정상 상담 표현은 차단하지 않는다.""" + result = guardrail.sanitize_client_reply( + "이제는 엄마랑 선을 긋고 제 생활을 지키고 싶어요.", + ideation_stage=1, + turn_seq=4, + ) + self.assertFalse(result.needs_regeneration) + def test_blocks_role_meta_speech_variants(self) -> None: samples = [ "내담자 역할로 응답하겠습니다. 엄마가 가보라고 해서요.", diff --git a/apps/api/app/test_dataset_export.py b/apps/api/app/test_dataset_export.py index a9b403d..ee73e43 100644 --- a/apps/api/app/test_dataset_export.py +++ b/apps/api/app/test_dataset_export.py @@ -7,6 +7,7 @@ from pathlib import Path from app.services.dataset_export import ( APPROVED_EXPORT_STATUS, DRY_RUN_EXPORT_STATUS, + DatasetManifestInput, ExportKeyMaps, build_dataset_record, build_manifest, @@ -24,7 +25,9 @@ EXPORT_SCRIPT_PATH = REPO_ROOT / "scripts" / "export-recursive-dataset.py" def load_export_script(): - spec = importlib.util.spec_from_file_location("export_recursive_dataset", EXPORT_SCRIPT_PATH) + spec = importlib.util.spec_from_file_location( + "export_recursive_dataset", EXPORT_SCRIPT_PATH + ) assert spec is not None and spec.loader is not None module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module @@ -92,7 +95,10 @@ class DatasetExportTests(unittest.TestCase): keys=ExportKeyMaps(), ) - self.assertEqual(record["supervisor_comments"], [{"kind": "note", "intent_deviation": {"severity": "low"}}]) + self.assertEqual( + record["supervisor_comments"], + [{"kind": "note", "intent_deviation": {"severity": "low"}}], + ) self.assertNotIn("김서연", str(record)) self.assertNotIn("010-1234-5678", str(record)) @@ -109,38 +115,72 @@ class DatasetExportTests(unittest.TestCase): self.assertIn("phone", kinds) self.assertIn("national_id", kinds) self.assertIn("blocked_field", kinds) - self.assertFalse(any("learner@hs.ac.kr" in finding["sample"] for finding in findings)) - self.assertFalse(any("010-1234-5678" in finding["sample"] for finding in findings)) + self.assertFalse( + any("learner@hs.ac.kr" in finding["sample"] for finding in findings) + ) + self.assertFalse( + any("010-1234-5678" in finding["sample"] for finding in findings) + ) def test_agreement_metrics(self) -> None: annotations = [ - {"item_id": "1", "labels": {"appropriateness": "good", "rapport_signal": 4.0}}, - {"item_id": "1", "labels": {"appropriateness": "good", "rapport_signal": 4.1}}, - {"item_id": "2", "labels": {"appropriateness": "bad", "rapport_signal": 2.0}}, - {"item_id": "2", "labels": {"appropriateness": "bad", "rapport_signal": 2.1}}, - {"item_id": "3", "labels": {"appropriateness": "good", "rapport_signal": 5.0}}, - {"item_id": "3", "labels": {"appropriateness": "good", "rapport_signal": 5.0}}, + { + "item_id": "1", + "labels": {"appropriateness": "good", "rapport_signal": 4.0}, + }, + { + "item_id": "1", + "labels": {"appropriateness": "good", "rapport_signal": 4.1}, + }, + { + "item_id": "2", + "labels": {"appropriateness": "bad", "rapport_signal": 2.0}, + }, + { + "item_id": "2", + "labels": {"appropriateness": "bad", "rapport_signal": 2.1}, + }, + { + "item_id": "3", + "labels": {"appropriateness": "good", "rapport_signal": 5.0}, + }, + { + "item_id": "3", + "labels": {"appropriateness": "good", "rapport_signal": 5.0}, + }, ] self.assertEqual(cohen_kappa(annotations, "appropriateness"), 1.0) - self.assertGreater(intraclass_correlation(annotations, "rapport_signal") or 0, 0.9) + self.assertGreater( + intraclass_correlation(annotations, "rapport_signal") or 0, 0.9 + ) def test_manifest_gate_blocks_unapproved_approved_status(self) -> None: with self.assertRaisesRegex(ValueError, "PII scan must pass"): build_manifest( - export_id="phase3-rl-seed-test", - dataset_name="vignette_phase3_recursive_learning_seed", - export_status=APPROVED_EXPORT_STATUS, - purpose="test", - records=[], - jsonl_path="03-export/anonymized_dataset.jsonl", - jsonl_sha256="", - pii_findings=[{"kind": "email", "path": "$.text_masked", "sample": ""}], - participants_included=0, - agreement={"kappa": 0.59, "icc": 0.74, "gold_status": "not_gold"}, + DatasetManifestInput( + export_id="phase3-rl-seed-test", + dataset_name="vignette_phase3_recursive_learning_seed", + export_status=APPROVED_EXPORT_STATUS, + purpose="test", + records=[], + jsonl_path="03-export/anonymized_dataset.jsonl", + jsonl_sha256="", + pii_findings=[ + { + "kind": "email", + "path": "$.text_masked", + "sample": "", + } + ], + participants_included=0, + agreement={"kappa": 0.59, "icc": 0.74, "gold_status": "not_gold"}, + ) ) - def test_manifest_gate_requires_withdrawal_and_recursive_consent_scope(self) -> None: + def test_manifest_gate_requires_withdrawal_and_recursive_consent_scope( + self, + ) -> None: manifest = { "export_status": APPROVED_EXPORT_STATUS, "pii_scan": {"status": "pass"}, @@ -172,15 +212,17 @@ class DatasetExportTests(unittest.TestCase): write_jsonl([record], path) digest = sha256_file(path) manifest = build_manifest( - export_id="phase3-rl-seed-test", - dataset_name="vignette_phase3_recursive_learning_seed", - export_status=DRY_RUN_EXPORT_STATUS, - purpose="test", - records=[record], - jsonl_path="03-export/anonymized_dataset.jsonl", - jsonl_sha256=digest, - pii_findings=[], - participants_included=1, + DatasetManifestInput( + export_id="phase3-rl-seed-test", + dataset_name="vignette_phase3_recursive_learning_seed", + export_status=DRY_RUN_EXPORT_STATUS, + purpose="test", + records=[record], + jsonl_path="03-export/anonymized_dataset.jsonl", + jsonl_sha256=digest, + pii_findings=[], + participants_included=1, + ) ) self.assertEqual(manifest["files"][0]["rows"], 1) self.assertEqual(manifest["files"][0]["sha256"], digest) diff --git a/apps/api/app/test_evaluation_persistence.py b/apps/api/app/test_evaluation_persistence.py index ca7cc2c..56d0c1c 100644 --- a/apps/api/app/test_evaluation_persistence.py +++ b/apps/api/app/test_evaluation_persistence.py @@ -43,6 +43,8 @@ class FakeMissingEvaluationConn: self.fetches.append((query, args)) if "FROM app.sessions s" in query: return [{"id": "11111111-1111-1111-1111-111111111111"}] + if "FROM app.session_state" in query: + return [{"session_id": "11111111-1111-1111-1111-111111111111"}] if "FROM app.turns" in query: return [] raise AssertionError(f"unexpected fetch query: {query}") @@ -521,8 +523,12 @@ class EvaluationPersistenceIOTest(unittest.IsolatedAsyncioTestCase): self.assertIn("se.session_id IS NULL", session_query) self.assertIn("'client' = ANY(t.visible_to)", session_query) self.assertIn("ORDER BY s.ended_at ASC", session_query) - self.assertEqual(conn.fetchrows[0][1], ("11111111-1111-1111-1111-111111111111",)) - self.assertIn("FROM app.turns", conn.fetches[1][0]) + self.assertEqual( + conn.fetches[1][1], + (["11111111-1111-1111-1111-111111111111"],), + ) + self.assertIn("FROM app.session_state", conn.fetches[1][0]) + self.assertIn("FROM app.turns", conn.fetches[2][0]) async def test_record_llm_call_audit_inserts_metadata_only(self) -> None: conn = FakeEvaluationConn() diff --git a/apps/api/app/test_live_coach_privacy.py b/apps/api/app/test_live_coach_privacy.py index b82a311..f1f9bdf 100644 --- a/apps/api/app/test_live_coach_privacy.py +++ b/apps/api/app/test_live_coach_privacy.py @@ -80,24 +80,74 @@ class LiveCoachQuotaRechargeTest(unittest.TestCase): self.assertTrue(should_recharge) self.assertIn("개방도", reason) + # 성과 없음(개방도 하락) + 페이싱 주기 아님 → 미충전 should_recharge, _ = turn_runtime.should_recharge_live_coach_credit( {"appropriateness": "pos", "rapport_signal": 0.45}, before, state_machine.SessionState( stage=state_machine.Stage.RAPPORT, turn_seq=3, - effective_openness=0.16, + effective_openness=0.14, ), ) self.assertFalse(should_recharge) - should_recharge, _ = turn_runtime.should_recharge_live_coach_credit( + def test_recharge_allows_neutral_with_strong_rapport_and_gain(self) -> None: + before = state_machine.SessionState( + stage=state_machine.Stage.RAPPORT, + turn_seq=2, + effective_openness=0.15, + ) + after = state_machine.SessionState( + stage=state_machine.Stage.RAPPORT, + turn_seq=3, + effective_openness=0.19, + ) + should_recharge, reason = turn_runtime.should_recharge_live_coach_credit( {"appropriateness": "neutral", "rapport_signal": 0.8}, before, after, ) + self.assertTrue(should_recharge) + self.assertIn("라포", reason) + + def test_recharge_warn_turn_does_not_recharge_off_cycle(self) -> None: + before = state_machine.SessionState( + stage=state_machine.Stage.RAPPORT, + turn_seq=2, + effective_openness=0.15, + ) + after = state_machine.SessionState( + stage=state_machine.Stage.RAPPORT, + turn_seq=3, + effective_openness=0.19, + ) + should_recharge, _ = turn_runtime.should_recharge_live_coach_credit( + {"appropriateness": "warn", "rapport_signal": 0.1}, + before, + after, + ) self.assertFalse(should_recharge) + def test_pacing_recharge_every_n_turns_even_without_evaluation(self) -> None: + before = state_machine.SessionState( + stage=state_machine.Stage.RAPPORT, + turn_seq=5, + effective_openness=0.15, + ) + after = state_machine.SessionState( + stage=state_machine.Stage.RAPPORT, + turn_seq=turn_runtime._LIVE_COACH_PACING_RECHARGE_EVERY_TURNS, + effective_openness=0.14, + ) + should_recharge, reason = turn_runtime.should_recharge_live_coach_credit( + None, + before, + after, + ) + self.assertTrue(should_recharge) + self.assertIn("턴", reason) + if __name__ == "__main__": unittest.main() diff --git a/apps/api/app/test_rbac_idor.py b/apps/api/app/test_rbac_idor.py index cc9c361..24d5a2d 100644 --- a/apps/api/app/test_rbac_idor.py +++ b/apps/api/app/test_rbac_idor.py @@ -611,11 +611,8 @@ class TeacherAdminAuditTest(unittest.IsolatedAsyncioTestCase): learner_id = "00000000-0000-0000-0000-000000000808" sess = _session(session_id=session_id, learner_id=learner_id) conn = _FakeConn( - fetchrow_results=[ - {"id": session_id, "ended_at": None}, - None, - ], - fetch_results=[[]], + fetchrow_results=[{"id": session_id, "ended_at": None}], + fetch_results=[[], []], ) acquire_calls: list[dict[str, Any]] = [] diff --git a/apps/api/app/test_runtime_policy.py b/apps/api/app/test_runtime_policy.py index a00661d..6c43762 100644 --- a/apps/api/app/test_runtime_policy.py +++ b/apps/api/app/test_runtime_policy.py @@ -17,6 +17,7 @@ from .routes import admin as admin_routes from .routes import eval as eval_routes from .routes import kb as kb_routes from .routes import users as users_routes +from .services import notifications @contextmanager @@ -48,15 +49,21 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): self.assertFalse(durable) self.assertEqual(users, []) - async def test_staging_blocks_auth_registry_fallback_when_db_pool_missing(self) -> None: + async def test_staging_blocks_auth_registry_fallback_when_db_pool_missing( + self, + ) -> None: with environment("staging"): with self.assertRaises(HTTPException) as caught: await auth_sessions.list_managed_users() self.assertEqual(caught.exception.status_code, 503) - self.assertIn("runtime fallback is disabled in staging", caught.exception.detail) + self.assertIn( + "runtime fallback is disabled in staging", caught.exception.detail + ) - async def test_prod_blocks_browser_session_creation_when_db_pool_missing(self) -> None: + async def test_prod_blocks_browser_session_creation_when_db_pool_missing( + self, + ) -> None: with environment("prod"): with self.assertRaises(HTTPException) as caught: await auth_sessions.create_session( @@ -68,7 +75,9 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(caught.exception.status_code, 503) self.assertIn("runtime fallback is disabled in prod", caught.exception.detail) - async def test_staging_blocks_session_store_fallback_when_db_pool_missing(self) -> None: + async def test_staging_blocks_session_store_fallback_when_db_pool_missing( + self, + ) -> None: principal = Principal( user_id="00000000-0000-0000-0000-000000000001", role=Role.LEARNER, @@ -81,9 +90,13 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): await session_persistence.list_recent_sessions(principal) self.assertEqual(caught.exception.status_code, 503) - self.assertIn("runtime fallback is disabled in staging", caught.exception.detail) + self.assertIn( + "runtime fallback is disabled in staging", caught.exception.detail + ) - async def test_staging_blocks_eval_store_cache_fallback_when_db_pool_missing(self) -> None: + async def test_staging_blocks_eval_store_cache_fallback_when_db_pool_missing( + self, + ) -> None: principal = Principal( user_id="00000000-0000-0000-0000-000000000002", role=Role.TEACHER, @@ -93,10 +106,14 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): ) with environment("staging"): with self.assertRaises(HTTPException) as caught: - await eval_routes.get_session_evaluation("missing-session-id", principal) + await eval_routes.get_session_evaluation( + "missing-session-id", principal + ) self.assertEqual(caught.exception.status_code, 503) - self.assertIn("runtime fallback is disabled in staging", caught.exception.detail) + self.assertIn( + "runtime fallback is disabled in staging", caught.exception.detail + ) async def test_staging_kb_search_fails_closed_when_db_pool_missing(self) -> None: with environment("staging"): @@ -106,7 +123,9 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(caught.exception.status_code, 503) self.assertIn("DB not ready", caught.exception.detail) - async def test_staging_blocks_user_preferences_fallback_when_db_pool_missing(self) -> None: + async def test_staging_blocks_user_preferences_fallback_when_db_pool_missing( + self, + ) -> None: principal = Principal( user_id="00000000-0000-0000-0000-000000000003", role=Role.LEARNER, @@ -119,9 +138,13 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): await users_routes.get_preferences(principal) self.assertEqual(caught.exception.status_code, 503) - self.assertIn("runtime fallback is disabled in staging", caught.exception.detail) + self.assertIn( + "runtime fallback is disabled in staging", caught.exception.detail + ) - async def test_dev_allows_user_preferences_fallback_when_db_pool_missing(self) -> None: + async def test_dev_allows_user_preferences_fallback_when_db_pool_missing( + self, + ) -> None: principal = Principal( user_id="00000000-0000-0000-0000-000000000004", role=Role.LEARNER, @@ -134,7 +157,9 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(prefs.voice_preset_id, "soft-young-fem") - async def test_prod_blocks_runtime_schema_bootstrap_ddl_when_schema_incomplete(self) -> None: + async def test_prod_blocks_runtime_schema_bootstrap_ddl_when_schema_incomplete( + self, + ) -> None: class IncompleteConn: async def fetchrow(self, *args, **kwargs): return { @@ -179,13 +204,80 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): def acquire(self): return IncompleteAcquire() - with environment("prod"), patch.object(auth_sessions, "get_pool", return_value=IncompletePool()): + with ( + environment("prod"), + patch.object(auth_sessions, "get_pool", return_value=IncompletePool()), + ): with self.assertRaises(RuntimeError) as caught: await auth_sessions.ensure_runtime_tables() self.assertIn("runtime DB schema is incomplete", str(caught.exception)) - async def test_runtime_readiness_requires_all_turn_voice_metadata_columns(self) -> None: + async def test_prod_blocks_review_schema_bootstrap_ddl_when_schema_incomplete( + self, + ) -> None: + class IncompleteConn: + async def fetchrow(self, *args, **kwargs): + return {"ready": False} + + async def execute(self, *args, **kwargs): + raise AssertionError("prod startup must not run review schema DDL") + + class IncompleteAcquire: + async def __aenter__(self): + return IncompleteConn() + + async def __aexit__(self, exc_type, exc, tb): + return None + + with ( + environment("prod"), + patch.object(session_persistence, "get_pool", return_value=object()), + patch.object( + session_persistence, "acquire", return_value=IncompleteAcquire() + ), + ): + with self.assertRaises(RuntimeError) as caught: + await session_persistence.ensure_review_tables() + + self.assertIn( + "review/evaluation runtime DB schema is incomplete", str(caught.exception) + ) + + async def test_prod_blocks_notification_schema_bootstrap_ddl_when_schema_incomplete( + self, + ) -> None: + class IncompleteConn: + async def fetchrow(self, *args, **kwargs): + return {"ready": False} + + async def execute(self, *args, **kwargs): + raise AssertionError( + "prod startup must not run notification schema DDL" + ) + + class IncompleteAcquire: + async def __aenter__(self): + return IncompleteConn() + + async def __aexit__(self, exc_type, exc, tb): + return None + + with ( + environment("prod"), + patch.object(notifications, "get_pool", return_value=object()), + patch.object(notifications, "acquire", return_value=IncompleteAcquire()), + ): + with self.assertRaises(RuntimeError) as caught: + await notifications.ensure_notification_tables() + + self.assertIn( + "notification runtime DB schema is incomplete", str(caught.exception) + ) + + async def test_runtime_readiness_requires_all_turn_voice_metadata_columns( + self, + ) -> None: class VoiceMetadataDriftConn: def __init__(self) -> None: self.query = "" @@ -225,7 +317,13 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): ready = await auth_sessions._runtime_tables_ready(conn) self.assertFalse(ready) - for column in ("audio_ref", "silence_ms", "speech_rate", "barge_in", "provider_events"): + for column in ( + "audio_ref", + "silence_ms", + "speech_rate", + "barge_in", + "provider_events", + ): self.assertIn(column, conn.query) self.assertIn("HAVING count(*) = 5", conn.query) @@ -296,7 +394,10 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): def acquire(self): return EmptyConfigAcquire() - with environment("staging"), patch.object(admin_routes, "get_pool", return_value=EmptyConfigPool()): + with ( + environment("staging"), + patch.object(admin_routes, "get_pool", return_value=EmptyConfigPool()), + ): config = await admin_routes._current_engine_config() self.assertFalse(config.durable) @@ -309,14 +410,21 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): def acquire(self): raise RuntimeError("db unavailable") - with environment("staging"), patch.object(admin_routes, "get_pool", return_value=BrokenConfigPool()): + with ( + environment("staging"), + patch.object(admin_routes, "get_pool", return_value=BrokenConfigPool()), + ): with self.assertRaises(HTTPException) as caught: await admin_routes._current_engine_config() self.assertEqual(caught.exception.status_code, 503) - self.assertIn("runtime fallback is disabled in staging", caught.exception.detail) + self.assertIn( + "runtime fallback is disabled in staging", caught.exception.detail + ) - async def test_prod_admin_health_marks_db_down_when_persistence_unavailable(self) -> None: + async def test_prod_admin_health_marks_db_down_when_persistence_unavailable( + self, + ) -> None: principal = Principal( user_id="00000000-0000-0000-0000-000000000005", role=Role.ADMIN, @@ -334,10 +442,20 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): with ( environment("prod"), - patch.object(admin_routes, "_current_engine_config", AsyncMock(return_value=engine_config)), + patch.object( + admin_routes, + "_current_engine_config", + AsyncMock(return_value=engine_config), + ), patch.object(admin_routes, "healthcheck", AsyncMock(return_value=False)), - patch.object(admin_routes.engine_client, "health_detail", AsyncMock(return_value={"ok": True})), - patch.object(admin_routes.voice_service, "is_available", return_value=False), + patch.object( + admin_routes.engine_client, + "health_detail", + AsyncMock(return_value={"ok": True}), + ), + patch.object( + admin_routes.voice_service, "is_available", return_value=False + ), ): health = await admin_routes.admin_health(principal) @@ -346,7 +464,9 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(db.metric, "저장소 중단") self.assertIn("DB 저장소", db.detail) - async def test_dev_admin_health_labels_db_fallback_as_non_durable_runtime(self) -> None: + async def test_dev_admin_health_labels_db_fallback_as_non_durable_runtime( + self, + ) -> None: principal = Principal( user_id="00000000-0000-0000-0000-000000000006", role=Role.ADMIN, @@ -364,10 +484,20 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): with ( environment("dev"), - patch.object(admin_routes, "_current_engine_config", AsyncMock(return_value=engine_config)), + patch.object( + admin_routes, + "_current_engine_config", + AsyncMock(return_value=engine_config), + ), patch.object(admin_routes, "healthcheck", AsyncMock(return_value=False)), - patch.object(admin_routes.engine_client, "health_detail", AsyncMock(return_value={"ok": True})), - patch.object(admin_routes.voice_service, "is_available", return_value=False), + patch.object( + admin_routes.engine_client, + "health_detail", + AsyncMock(return_value={"ok": True}), + ), + patch.object( + admin_routes.voice_service, "is_available", return_value=False + ), ): health = await admin_routes.admin_health(principal) @@ -535,7 +665,9 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase): voice_poc_sample_tts_enabled=False, ) - self.assertEqual(cfg.frontend_origin_map["api-vnet.18ka.net"], "https://vnet.18ka.net") + self.assertEqual( + cfg.frontend_origin_map["api-vnet.18ka.net"], "https://vnet.18ka.net" + ) self.assertIn("https://vnet.18ka.net", cfg.cors_origins) def test_non_dev_rejects_local_frontend_origin_map(self) -> None: diff --git a/apps/api/app/test_runtime_schema_ssot.py b/apps/api/app/test_runtime_schema_ssot.py new file mode 100644 index 0000000..2c06942 --- /dev/null +++ b/apps/api/app/test_runtime_schema_ssot.py @@ -0,0 +1,57 @@ +"""앱의 런타임 준비 계약과 infra SQL SSOT 사이의 회귀 검사.""" + +from __future__ import annotations + +import re +import unittest +from pathlib import Path + +from .runtime_schema import NOTIFICATION_SCHEMA_CONTRACT, REVIEW_SCHEMA_CONTRACT + + +REPO_ROOT = Path(__file__).resolve().parents[3] +INFRA_SQL = "\n".join( + path.read_text(encoding="utf-8") + for path in sorted((REPO_ROOT / "infra" / "db" / "init").glob("*.sql")) +) + + +class RuntimeSchemaSsotTest(unittest.TestCase): + def test_runtime_contract_objects_are_owned_by_infra_sql(self) -> None: + for contract in (REVIEW_SCHEMA_CONTRACT, NOTIFICATION_SCHEMA_CONTRACT): + for relation in contract.relations: + schema, table = relation.split(".") + pattern = rf"CREATE TABLE IF NOT EXISTS\s+{re.escape(schema)}\.{re.escape(table)}\b" + self.assertRegex( + INFRA_SQL, pattern, msg=f"infra SQL missing relation: {relation}" + ) + + for qualified_name in contract.columns: + _, table, column = qualified_name.split(".") + table_mentions = [ + block + for block in re.split( + r"(?=CREATE TABLE IF NOT EXISTS|ALTER TABLE)", INFRA_SQL + ) + if re.search(rf"\bapp\.{re.escape(table)}\b", block) + ] + self.assertTrue( + any( + re.search(rf"\b{re.escape(column)}\b", block) + for block in table_mentions + ), + msg=f"infra SQL missing column: {qualified_name}", + ) + + for qualified_name in contract.policies: + _, table, policy = qualified_name.split(".") + pattern = rf"CREATE POLICY\s+{re.escape(policy)}\s+ON\s+app\.{re.escape(table)}\b" + self.assertRegex( + INFRA_SQL, + pattern, + msg=f"infra SQL missing policy: {qualified_name}", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/apps/api/app/test_session_progress.py b/apps/api/app/test_session_progress.py new file mode 100644 index 0000000..95577f6 --- /dev/null +++ b/apps/api/app/test_session_progress.py @@ -0,0 +1,80 @@ +"""P2 단계 누적 게이지 파생(build_session_progress) 테스트.""" + +from __future__ import annotations + +import unittest + +from .services import state_machine +from .session_read_model import build_session_progress + + +class SessionProgressTest(unittest.TestCase): + def test_initial_state_gauges(self) -> None: + state = state_machine.SessionState() + progress = build_session_progress(state) + self.assertEqual([s.stage for s in progress.stages], ["라포", "탐색", "개입", "정리"]) + self.assertEqual(progress.stages[0].percent, 0) + self.assertFalse(progress.stages[0].achieved) + self.assertEqual(progress.stages[1].percent, 0) + self.assertEqual(progress.rapport_percent, 0) + self.assertEqual(progress.rapport_delta_percent, 0) + + def test_current_stage_gauge_scales_with_rapport(self) -> None: + state = state_machine.SessionState(rapport_credit=0.15) + progress = build_session_progress(state) + # 라포 전이 임계 0.30 → 0.15는 절반 = 약 50 (99 스케일 반올림 50) + self.assertAlmostEqual(progress.stages[0].percent, 50, delta=2) + self.assertFalse(progress.stages[0].achieved) + + def test_current_stage_gauge_caps_at_99_until_transition(self) -> None: + state = state_machine.SessionState(rapport_credit=0.9) + progress = build_session_progress(state) + self.assertEqual(progress.stages[0].percent, 99) + + def test_passed_stage_is_100_and_achieved(self) -> None: + state = state_machine.SessionState( + stage=state_machine.Stage.EXPLORE, + rapport_credit=0.32, + ) + progress = build_session_progress(state) + self.assertEqual(progress.stages[0].percent, 100) + self.assertTrue(progress.stages[0].achieved) + # 탐색 임계 0.45 → 0.32/0.45*99 ≈ 70 + self.assertAlmostEqual(progress.stages[1].percent, 70, delta=2) + self.assertEqual(progress.stages[2].percent, 0) + + def test_close_stage_entry_is_100(self) -> None: + state = state_machine.SessionState( + stage=state_machine.Stage.CLOSE, + rapport_credit=0.6, + ) + progress = build_session_progress(state) + self.assertTrue(all(s.percent == 100 for s in progress.stages)) + self.assertTrue(progress.stages[3].achieved) + + def test_goal_flag_and_delta_from_carry(self) -> None: + state = state_machine.SessionState(rapport_credit=0.33) + progress = build_session_progress( + state, + prev_rapport_credit=0.11, + goal_stages=["라포", "탐색"], + ) + self.assertTrue(progress.stages[0].is_goal) + self.assertTrue(progress.stages[1].is_goal) + self.assertFalse(progress.stages[2].is_goal) + # 0.33/0.55=60%, 0.11/0.55=20% → 이번 회기 +40%p + self.assertEqual(progress.rapport_percent, 60) + self.assertEqual(progress.rapport_delta_percent, 40) + + def test_detail_metrics_are_percentages(self) -> None: + state = state_machine.SessionState( + resistance=0.65, + effective_openness=0.42, + ) + progress = build_session_progress(state) + self.assertEqual(progress.resistance_percent, 65) + self.assertEqual(progress.openness_percent, 42) + + +if __name__ == "__main__": + unittest.main() diff --git a/apps/api/app/test_session_time_and_goals.py b/apps/api/app/test_session_time_and_goals.py new file mode 100644 index 0000000..a2f5b08 --- /dev/null +++ b/apps/api/app/test_session_time_and_goals.py @@ -0,0 +1,240 @@ +"""시간 기반 회기 종료(P1) + 회기 목표 선택 계약 테스트. + +2026-07-13 한신대 회의: 세션 종료를 4단계 완수가 아니라 시간(기본 60분)이 결정하고, +회기 시작 전 학습자가 이번 회기 목표 단계를 2개 수준으로 선택한다. +""" + +from __future__ import annotations + +import time +import unittest +from types import SimpleNamespace +from unittest.mock import AsyncMock, patch + +from fastapi import HTTPException + +from .config import settings +from .deps import Principal, Role +from .routes import sessions +from .services import memory, persona as persona_service, state_machine +from .store import InProcSession, store + + +def _principal() -> Principal: + return Principal( + user_id="00000000-0000-0000-0000-000000000102", + role=Role.LEARNER, + cohort_ids=[], + email="time-goal-test@hs.ac.kr", + display_name="Time Goal Test", + consent_at=1.0, + profile_completed_at=1.0, + ) + + +def _session(principal: Principal, *, created_at: float | None = None) -> InProcSession: + card = persona_service.P1 + sess = InProcSession( + session_id="time-goal-session", + case_id="time-goal-case", + learner_id=principal.user_id, + persona_code=card.code, + theory_mode="humanistic", + persona=card, + state=state_machine.SessionState( + resistance=card.base_resistance(), + ideation_stage=card.ideation_baseline(), + ), + ) + if created_at is not None: + sess.created_at = created_at + store.put(sess) + return sess + + +class SessionGoalContractTest(unittest.TestCase): + def test_goal_stages_are_deduped(self) -> None: + req = sessions.SessionStartRequest( + persona_code="P1", + goal_stages=["라포", "라포"], + ) + self.assertEqual(req.goal_stages, ["라포"]) + + def test_goal_stages_allow_up_to_four(self) -> None: + req = sessions.SessionStartRequest( + persona_code="P1", + goal_stages=["라포", "탐색", "개입", "정리"], + ) + self.assertEqual(req.goal_stages, ["라포", "탐색", "개입", "정리"]) + + def test_goal_stages_reject_more_than_four_items(self) -> None: + with self.assertRaises(Exception): + sessions.SessionStartRequest( + persona_code="P1", + goal_stages=["라포", "탐색", "개입", "정리", "라포"], + ) + + def test_goal_stages_reject_unknown_stage_label(self) -> None: + with self.assertRaises(Exception): + sessions.SessionStartRequest( + persona_code="P1", + goal_stages=["존재하지않는단계"], + ) + + def test_goal_stages_default_empty_for_legacy_clients(self) -> None: + req = sessions.SessionStartRequest(persona_code="P1") + self.assertEqual(req.goal_stages, []) + + +class SessionTimeLimitTest(unittest.IsolatedAsyncioTestCase): + async def asyncSetUp(self) -> None: + store._sessions.clear() + sessions._RECALL_CACHE.clear() + sessions._KB_CUES_CACHE.clear() + + async def asyncTearDown(self) -> None: + store._sessions.clear() + sessions._RECALL_CACHE.clear() + sessions._KB_CUES_CACHE.clear() + + def test_session_context_cache_has_one_owner_and_defensive_reads(self) -> None: + session_id = "cache-owner-session" + sessions._RECALL_CACHE[session_id] = memory.RecallContext(recall_summary="기억") + sessions._KB_CUES_CACHE[session_id] = ["단서"] + + cues = sessions.cached_kb_cues(session_id) + cues.append("외부 변경") + self.assertEqual(sessions.cached_kb_cues(session_id), ["단서"]) + + sessions.invalidate_session_context_cache(session_id) + self.assertNotIn(session_id, sessions._RECALL_CACHE) + self.assertEqual(sessions.cached_kb_cues(session_id), []) + + def test_session_time_over_false_within_limit(self) -> None: + principal = _principal() + sess = _session(principal, created_at=time.time() - 30 * 60) + self.assertFalse(sessions.session_time_over(sess)) + + def test_session_time_over_false_within_overtime_grace(self) -> None: + """제한(60분) 초과 직후에는 마무리 유예 안이라 턴이 계속 허용된다.""" + principal = _principal() + limit_minutes = settings.session_duration_minutes + sess = _session(principal, created_at=time.time() - (limit_minutes + 2) * 60) + self.assertFalse(sessions.session_time_over(sess)) + + def test_session_time_over_true_after_grace(self) -> None: + principal = _principal() + over_minutes = ( + settings.session_duration_minutes + + settings.session_overtime_grace_minutes + + 1 + ) + sess = _session(principal, created_at=time.time() - over_minutes * 60) + self.assertTrue(sessions.session_time_over(sess)) + + async def test_prepare_turn_context_rejects_after_grace(self) -> None: + principal = _principal() + over_minutes = ( + settings.session_duration_minutes + + settings.session_overtime_grace_minutes + + 1 + ) + sess = _session(principal, created_at=time.time() - over_minutes * 60) + with patch.object( + sessions, + "ensure_recall_context", + AsyncMock(return_value=memory.RecallContext()), + ): + with self.assertRaises(HTTPException) as caught: + await sessions._prepare_turn_context( + session_id=sess.session_id, + sess=sess, + learner_text="오늘 이야기 나눠주셔서 감사해요.", + ) + self.assertEqual(caught.exception.status_code, 409) + self.assertEqual(caught.exception.detail, "session_time_over") + + +class SessionStartGoalPersistenceTest(unittest.IsolatedAsyncioTestCase): + async def asyncSetUp(self) -> None: + store._sessions.clear() + sessions._RECALL_CACHE.clear() + + async def asyncTearDown(self) -> None: + store._sessions.clear() + sessions._RECALL_CACHE.clear() + + async def test_start_session_passes_goals_and_returns_time_contract(self) -> None: + principal = _principal() + card = persona_service.P1 + catalog_persona = SimpleNamespace( + card=card, + persona_id="00000000-0000-0000-0000-0000000000a1", + version=1, + degraded=False, + ) + captured: dict[str, object] = {} + + async def fake_create_session(**kwargs): + captured.update(kwargs) + return InProcSession( + session_id="goal-session", + case_id="goal-case", + learner_id=principal.user_id, + persona_code=card.code, + theory_mode=kwargs["theory_mode"], + persona=card, + state=kwargs["state"], + session_no=kwargs["session_no"], + prev_rapport_credit=kwargs["carry_rapport"], + goal_stages=list(kwargs["goal_stages"]), + ) + + def close_background(coro): + coro.close() + return None + + with ( + patch.object( + sessions, "get_catalog_persona", AsyncMock(return_value=catalog_persona) + ), + patch.object( + sessions.session_persistence, + "get_case_context", + AsyncMock(return_value=None), + ), + patch.object( + sessions, + "_build_seed_recall", + AsyncMock(return_value=memory.RecallContext()), + ), + patch.object( + sessions.session_persistence, + "create_session", + fake_create_session, + ), + patch.object(sessions.asyncio, "create_task", close_background), + ): + response = await sessions.start_session( + sessions.SessionStartRequest( + persona_code=card.code, + goal_stages=["라포", "탐색"], + ), + principal, + ) + + self.assertEqual(captured["goal_stages"], ["라포", "탐색"]) + self.assertEqual(response.goal_stages, ["라포", "탐색"]) + self.assertEqual( + response.duration_limit_seconds, + settings.session_duration_minutes * 60, + ) + self.assertEqual( + response.warning_before_end_seconds, + settings.session_warning_minutes * 60, + ) + self.assertTrue(response.started_at) + + +if __name__ == "__main__": + unittest.main() diff --git a/apps/api/app/test_session_turn_persistence.py b/apps/api/app/test_session_turn_persistence.py index c181e1e..d191b14 100644 --- a/apps/api/app/test_session_turn_persistence.py +++ b/apps/api/app/test_session_turn_persistence.py @@ -13,10 +13,18 @@ from .config import settings from . import session_persistence, turn_runtime from .contracts.engine_gateway import EngineGatewaySseLineDecoder from .deps import Principal, Role -from .engine_client import EngineError +from .engine_client import EngineError, GenerateResponse from .routes import sessions from .routes import voice as voice_routes -from .services import guardrail, live_coach, memory, orchestrator, persona as persona_service, rag, state_machine +from .services import ( + guardrail, + live_coach, + memory, + orchestrator, + persona as persona_service, + rag, + state_machine, +) from .services.voice import TTSChunk, TranscriptResult, VoicePreset from .store import InProcSession, TurnRecord, store @@ -88,22 +96,32 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): store._sessions.clear() sessions._RECALL_CACHE.clear() sessions._KB_CUES_CACHE.clear() + session_persistence._LIVE_COACH_EVENT_CACHE.clear() async def asyncTearDown(self) -> None: store._sessions.clear() sessions._RECALL_CACHE.clear() sessions._KB_CUES_CACHE.clear() + session_persistence._LIVE_COACH_EVENT_CACHE.clear() - async def test_end_session_does_not_reschedule_evaluation_for_already_ended_session(self) -> None: + async def test_end_session_does_not_reschedule_evaluation_for_already_ended_session( + self, + ) -> None: principal = _principal() sess = _session(principal) sess.ended = True sess.ended_at = 1_000.0 with ( - patch.object(sessions, "_load_session_or_404", AsyncMock(return_value=sess)), - patch.object(sessions, "_end_persisted_session", AsyncMock(return_value=None)), - patch.object(sessions, "_schedule_session_evaluation") as schedule_session_evaluation, + patch.object( + sessions, "_load_session_or_404", AsyncMock(return_value=sess) + ), + patch.object( + sessions, "_end_persisted_session", AsyncMock(return_value=None) + ), + patch.object( + sessions, "_schedule_session_evaluation" + ) as schedule_session_evaluation, ): response = await sessions.end_session(sess.session_id, principal) @@ -151,10 +169,13 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): provider_events=[{"type": "sigh", "confidence": 0.82}], ) - with patch.object(session_persistence, "get_pool", return_value=object()), patch.object( - session_persistence, - "acquire", - return_value=FakeAcquire(conn), + with ( + patch.object(session_persistence, "get_pool", return_value=object()), + patch.object( + session_persistence, + "acquire", + return_value=FakeAcquire(conn), + ), ): ok = await session_persistence.append_turn( session_id="turn-persistence-session", @@ -169,7 +190,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(conn.insert_args[17], list(turn.visible_to)) self.assertEqual(turn.turn_id, "00000000-0000-0000-0000-000000009999") - async def test_generate_turn_engine_failure_does_not_append_learner_turn(self) -> None: + async def test_generate_turn_engine_failure_does_not_append_learner_turn( + self, + ) -> None: principal = _principal() sess = _session(principal) @@ -296,11 +319,16 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): principal, ) - self.assertEqual(response.client_reply, "저는 김서연 씨고 한신대학교 상담심리학과 학생이에요.") + self.assertEqual( + response.client_reply, + "저는 김서연 씨고 한신대학교 상담심리학과 학생이에요.", + ) self.assertEqual(len(sess.turns), 2) learner_turn, client_turn = sess.turns self.assertIsNone(learner_turn.llm_provider) - self.assertEqual(client_turn.text, "저는 김서연 씨고 한신대학교 상담심리학과 학생이에요.") + self.assertEqual( + client_turn.text, "저는 김서연 씨고 한신대학교 상담심리학과 학생이에요." + ) self.assertNotIn("김서연", client_turn.text_masked) self.assertNotIn("한신대학교", client_turn.text_masked) self.assertNotIn("상담심리학과", client_turn.text_masked) @@ -312,7 +340,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(client_turn.tokens_out, 23) self.assertEqual(client_turn.cost_usd, 0.012345) - async def test_generate_real_crisis_stops_before_engine_and_returns_109_resource(self) -> None: + async def test_generate_real_crisis_stops_before_engine_and_returns_109_resource( + self, + ) -> None: principal = _principal() sess = _session(principal) @@ -349,7 +379,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): result = orchestrator.TurnResult( turn_seq=ctx.state_after.turn_seq if ctx.state_after else 1, stage=ctx.state_after.stage.value if ctx.state_after else "라포", - effective_openness=ctx.state_after.effective_openness if ctx.state_after else 0.0, + effective_openness=ctx.state_after.effective_openness + if ctx.state_after + else 0.0, client_reply=None, safety_flagged=True, state_after=ctx.state_after or sess.state, @@ -368,10 +400,14 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): async def __aenter__(self) -> FakeConn: return FakeConn() - async def __aexit__(self, exc_type: object, exc: object, tb: object) -> None: + async def __aexit__( + self, exc_type: object, exc: object, tb: object + ) -> None: return None - with patch.object(turn_runtime.db, "acquire", return_value=FakeAcquire()) as acquire: + with patch.object( + turn_runtime.db, "acquire", return_value=FakeAcquire() + ) as acquire: await turn_runtime.record_safety_event(sess, ctx, result) acquire.assert_called_once_with(ai_context=True) @@ -394,7 +430,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(detail["crisis_resource"]["number"], "109") self.assertEqual(detail["alert_status"], "teacher_dashboard") - async def test_record_safety_event_fails_closed_when_insert_fails_outside_dev(self) -> None: + async def test_record_safety_event_fails_closed_when_insert_fails_outside_dev( + self, + ) -> None: principal = _principal() sess = _session(principal) ctx = orchestrator.prepare_turn( @@ -408,7 +446,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): result = orchestrator.TurnResult( turn_seq=ctx.state_after.turn_seq if ctx.state_after else 1, stage=ctx.state_after.stage.value if ctx.state_after else "라포", - effective_openness=ctx.state_after.effective_openness if ctx.state_after else 0.0, + effective_openness=ctx.state_after.effective_openness + if ctx.state_after + else 0.0, client_reply=None, safety_flagged=True, state_after=ctx.state_after or sess.state, @@ -425,7 +465,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): async def __aenter__(self) -> FakeConn: return FakeConn() - async def __aexit__(self, exc_type: object, exc: object, tb: object) -> None: + async def __aexit__( + self, exc_type: object, exc: object, tb: object + ) -> None: return None previous_environment = settings.environment @@ -480,17 +522,25 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(client_turn.tokens_out, 37) self.assertEqual(client_turn.cost_usd, 0.023456) - async def test_stream_real_crisis_stops_before_engine_and_persists_learner_only(self) -> None: + async def test_stream_real_crisis_stops_before_engine_and_persists_learner_only( + self, + ) -> None: principal = _principal() sess = _session(principal) def should_not_stream(*args, **kwargs): - raise AssertionError("stream engine must not be called for learner_real crisis") + raise AssertionError( + "stream engine must not be called for learner_real crisis" + ) with ( patch.object(sessions.engine_client, "stream", should_not_stream), - patch.object(turn_runtime, "record_completed_turn", new_callable=AsyncMock) as completed_turn, - patch.object(turn_runtime, "record_safety_event", new_callable=AsyncMock) as safety_event, + patch.object( + turn_runtime, "record_completed_turn", new_callable=AsyncMock + ) as completed_turn, + patch.object( + turn_runtime, "record_safety_event", new_callable=AsyncMock + ) as safety_event, ): response = await sessions.stream_turn( sess.session_id, @@ -511,7 +561,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertTrue(saved_result.conversation_stopped) self.assertEqual(saved_result.crisis_resource["number"], "109") - async def test_stream_turn_persists_fast_loop_evaluation_on_learner_turn(self) -> None: + async def test_stream_turn_persists_fast_loop_evaluation_on_learner_turn( + self, + ) -> None: principal = _principal() sess = _session(principal) @@ -543,10 +595,13 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): "appropriateness_note": f"응답 반영: {client_reply}", } - with patch.object(sessions.orchestrator, "run_turn_stream", successful_stream), patch.object( - sessions.evaluator, - "make_eval_hook", - return_value=fake_eval_hook, + with ( + patch.object(sessions.orchestrator, "run_turn_stream", successful_stream), + patch.object( + sessions.evaluator, + "make_eval_hook", + return_value=fake_eval_hook, + ), ): response = await sessions.stream_turn( sess.session_id, @@ -560,10 +615,14 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(learner_turn.speaker, "counselor") self.assertIsNotNone(learner_turn.evaluation) self.assertEqual(learner_turn.evaluation["appropriateness"], "pos") - self.assertIn("조금 말해볼게요", learner_turn.evaluation["appropriateness_note"]) + self.assertIn( + "조금 말해볼게요", learner_turn.evaluation["appropriateness_note"] + ) self.assertIsNone(client_turn.evaluation) - async def test_stream_turn_surfaces_fast_loop_evaluation_failure_on_review(self) -> None: + async def test_stream_turn_surfaces_fast_loop_evaluation_failure_on_review( + self, + ) -> None: principal = _principal() sess = _session(principal) @@ -589,10 +648,13 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): async def failing_eval_hook(ctx, client_reply): raise RuntimeError("김서연 평가 timeout 010-1234-5678") - with patch.object(sessions.orchestrator, "run_turn_stream", successful_stream), patch.object( - sessions.evaluator, - "make_eval_hook", - return_value=failing_eval_hook, + with ( + patch.object(sessions.orchestrator, "run_turn_stream", successful_stream), + patch.object( + sessions.evaluator, + "make_eval_hook", + return_value=failing_eval_hook, + ), ): response = await sessions.stream_turn( sess.session_id, @@ -622,13 +684,17 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertIsNotNone(review_turn.note) assert review_turn.note is not None self.assertEqual(review_turn.note.title, "턴 직후 평가 실패") - self.assertIn("fast-loop(턴 직후) 평가를 완료하지 못했습니다", review_turn.note.body) + self.assertIn( + "fast-loop(턴 직후) 평가를 완료하지 못했습니다", review_turn.note.body + ) self.assertIn("AI 평가 재시도가 필요합니다", review_turn.note.body) self.assertNotIn("RuntimeError", review_turn.note.body) self.assertNotIn("김서연", review_turn.note.body) self.assertNotIn("010-1234-5678", review_turn.note.body) - async def test_live_coach_degrades_to_rule_based_suggestion_when_engine_fails(self) -> None: + async def test_live_coach_degrades_to_rule_based_suggestion_when_engine_fails( + self, + ) -> None: principal = _principal() sess = _session(principal) sess.turns.append( @@ -638,18 +704,24 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): stage=sess.state.stage.value, text="그냥 학교는 가야 하는 거 아닐까요?", text_masked="그냥 학교는 가야 하는 거 아닐까요?", - evaluation={"appropriateness": "warn", "appropriateness_note": "조언이 빠름"}, + evaluation={ + "appropriateness": "warn", + "appropriateness_note": "조언이 빠름", + }, ) ) - with patch.object( - sessions, - "_retrieve_live_coach_grounding", - AsyncMock(return_value=[]), - ), patch.object( - sessions.engine_client, - "generate", - AsyncMock(side_effect=EngineError("offline")), + with ( + patch.object( + sessions, + "_retrieve_live_coach_grounding", + AsyncMock(return_value=[]), + ), + patch.object( + sessions.engine_client, + "generate", + AsyncMock(side_effect=EngineError("offline")), + ), ): response = await sessions.live_coach_turn( sess.session_id, @@ -667,7 +739,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertIn("조언", response.title + response.message) self.assertTrue(response.next_utterance) self.assertTrue(response.sources) - self.assertEqual(response.sources[0].source_id, "workbook_0615_case_conceptualization") + self.assertEqual( + response.sources[0].source_id, "workbook_0615_case_conceptualization" + ) history = await sessions.list_live_coach_history(sess.session_id, principal) self.assertEqual(history.source, "runtime") self.assertEqual(len(history.events), 1) @@ -676,11 +750,85 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(history.events[0].suggestion.status, "degraded") self.assertEqual(history.events[0].suggestion.title, response.title) self.assertIn("학교", history.events[0].learner_text_excerpt or "") - session_persistence._LIVE_COACH_EVENT_CACHE[sess.session_id][0]["stage"] = "unknown-stage" - legacy_history = await sessions.list_live_coach_history(sess.session_id, principal) + session_persistence._LIVE_COACH_EVENT_CACHE[sess.session_id][0]["stage"] = ( + "unknown-stage" + ) + legacy_history = await sessions.list_live_coach_history( + sess.session_id, principal + ) self.assertIsNone(legacy_history.events[0].stage) - async def test_live_coach_rag_grounding_preserves_source_pack_metadata(self) -> None: + async def test_live_coach_degrades_when_llm_audit_is_unavailable(self) -> None: + principal = _principal() + sess = _session(principal) + sess.turns.append( + TurnRecord( + turn_seq=1, + speaker="counselor", + stage=sess.state.stage.value, + text="그 마음이 컸겠네요.", + text_masked="그 마음이 컸겠네요.", + evaluation={ + "appropriateness": "pos", + "appropriateness_note": "감정 반영", + }, + ) + ) + engine_response = GenerateResponse( + text="", + model="fake-live-coach-model", + provider="fake-provider", + tokens_in=11, + tokens_out=7, + cost_usd=0.01, + structured={ + "tone": "pos", + "focus": "emotion", + "title": "엔진 생성 코칭", + "message": "감정 반영을 이어가세요.", + "next_utterance": "그 마음이 가장 컸던 순간이 언제였나요?", + }, + ) + + with ( + patch.object( + sessions, + "_retrieve_live_coach_grounding", + AsyncMock(return_value=[]), + ), + patch.object( + sessions.engine_client, + "generate", + AsyncMock(return_value=engine_response), + ) as generate, + patch.object( + session_persistence, + "record_llm_call_audit", + AsyncMock(return_value=False), + ) as audit, + ): + response = await sessions.live_coach_turn( + sess.session_id, + sessions.LiveCoachRequest( + learner_text="그 마음이 컸겠네요.", + client_reply="네, 아무도 몰라주는 것 같았어요.", + turn_seq=1, + ), + principal, + ) + + generate.assert_awaited_once() + audit.assert_awaited_once() + self.assertEqual(response.status, "degraded") + self.assertNotEqual(response.title, "엔진 생성 코칭") + self.assertIn("응답 검증 기록", response.rationale or "") + history = await sessions.list_live_coach_history(sess.session_id, principal) + self.assertEqual(len(history.events), 1) + self.assertEqual(history.events[0].suggestion.status, "degraded") + + async def test_live_coach_rag_grounding_preserves_source_pack_metadata( + self, + ) -> None: retrieval = rag.RetrievalResult( chunks=[ rag.RetrievedChunk( @@ -706,8 +854,14 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): ) with ( - patch.object(sessions.db, "acquire", return_value=_AsyncConnContext(object())), - patch.object(sessions.rag, "retrieve_eval_grounding", AsyncMock(return_value=retrieval)), + patch.object( + sessions.db, "acquire", return_value=_AsyncConnContext(object()) + ), + patch.object( + sessions.rag, + "retrieve_eval_grounding", + AsyncMock(return_value=retrieval), + ), patch.object(sessions.rag, "log_retrieval", AsyncMock()), ): grounding = await sessions._retrieve_live_coach_grounding( @@ -740,19 +894,23 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): detail="live coach event save persistence unavailable; runtime fallback is disabled in prod", ) - with patch.object( - sessions, - "_retrieve_live_coach_grounding", - AsyncMock(return_value=[]), - ), patch.object( - sessions.live_coach, - "generate_live_coaching", - AsyncMock(return_value=suggestion), - ), patch.object( - session_persistence, - "save_live_coach_event", - AsyncMock(side_effect=save_error), - ) as save_event: + with ( + patch.object( + sessions, + "_retrieve_live_coach_grounding", + AsyncMock(return_value=[]), + ), + patch.object( + sessions.live_coach, + "generate_live_coaching", + AsyncMock(return_value=suggestion), + ), + patch.object( + session_persistence, + "save_live_coach_event", + AsyncMock(side_effect=save_error), + ) as save_event, + ): with self.assertRaises(sessions.HTTPException) as caught: await sessions.live_coach_turn( sess.session_id, @@ -797,7 +955,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(coded.stage, "라포") self.assertIsNone(invalid.stage) - async def test_live_coach_uses_official_risk_reference_pack_for_crisis_signal(self) -> None: + async def test_live_coach_uses_official_risk_reference_pack_for_crisis_signal( + self, + ) -> None: item = live_coach.LiveCoachInput( session_id="risk-coach-session", turn_seq=3, @@ -809,7 +969,10 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): learner_text="죽고 싶다는 생각이 들 때도 있나요?", client_reply="가끔 그런 생각이 들어요.", recent_turns=[], - evaluation={"appropriateness": "warn", "appropriateness_note": "위험사정 필요"}, + evaluation={ + "appropriateness": "warn", + "appropriateness_note": "위험사정 필요", + }, ) engine = SimpleNamespace(generate=AsyncMock(side_effect=EngineError("offline"))) @@ -818,7 +981,11 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(suggestion.focus, "risk") source_ids = [source.source_id for source in suggestion.sources] self.assertIn("official_suicide_risk_guidelines", source_ids) - official = next(source for source in suggestion.sources if source.source_id == "official_suicide_risk_guidelines") + official = next( + source + for source in suggestion.sources + if source.source_id == "official_suicide_risk_guidelines" + ) self.assertEqual(official.source_type, "official_guideline") self.assertTrue(official.citation) engine.generate.assert_called_once() @@ -865,19 +1032,27 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): coro.close() return None - with patch.object(sessions, "get_catalog_persona", AsyncMock(return_value=catalog_persona)), patch.object( - sessions.session_persistence, - "get_case_context", - AsyncMock(return_value=case_context), - ), patch.object( - sessions, - "_build_seed_recall", - AsyncMock(return_value=recall), - ), patch.object( - sessions.session_persistence, - "create_session", - fake_create_session, - ), patch.object(sessions.asyncio, "create_task", close_background): + with ( + patch.object( + sessions, "get_catalog_persona", AsyncMock(return_value=catalog_persona) + ), + patch.object( + sessions.session_persistence, + "get_case_context", + AsyncMock(return_value=case_context), + ), + patch.object( + sessions, + "_build_seed_recall", + AsyncMock(return_value=recall), + ), + patch.object( + sessions.session_persistence, + "create_session", + fake_create_session, + ), + patch.object(sessions.asyncio, "create_task", close_background), + ): response = await sessions.start_session( sessions.SessionStartRequest(persona_code=card.code), principal, @@ -888,7 +1063,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(response.recall_summary, recall.recall_summary) self.assertIs(sessions._RECALL_CACHE[response.session_id], recall) - async def test_next_session_turn_injects_seed_recall_into_engine_messages(self) -> None: + async def test_next_session_turn_injects_seed_recall_into_engine_messages( + self, + ) -> None: principal = _principal() card = persona_service.P1 case_context = sessions.session_persistence.CaseContext( @@ -931,7 +1108,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): async def __aenter__(self) -> FakeConn: return self.conn - async def __aexit__(self, exc_type: object, exc: object, tb: object) -> None: + async def __aexit__( + self, exc_type: object, exc: object, tb: object + ) -> None: return None async def fake_create_session(**kwargs): @@ -951,19 +1130,28 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): coro.close() return None - with patch.object(sessions, "get_catalog_persona", AsyncMock(return_value=catalog_persona)), patch.object( - sessions.session_persistence, - "get_case_context", - AsyncMock(return_value=case_context), - ), patch.object(sessions.db, "get_pool", return_value=object()), patch.object( - sessions.db, - "acquire", - return_value=FakeAcquire(FakeConn()), - ), patch.object( - sessions.session_persistence, - "create_session", - fake_create_session, - ), patch.object(sessions.asyncio, "create_task", close_background): + with ( + patch.object( + sessions, "get_catalog_persona", AsyncMock(return_value=catalog_persona) + ), + patch.object( + sessions.session_persistence, + "get_case_context", + AsyncMock(return_value=case_context), + ), + patch.object(sessions.db, "get_pool", return_value=object()), + patch.object( + sessions.db, + "acquire", + return_value=FakeAcquire(FakeConn()), + ), + patch.object( + sessions.session_persistence, + "create_session", + fake_create_session, + ), + patch.object(sessions.asyncio, "create_task", close_background), + ): response = await sessions.start_session( sessions.SessionStartRequest(persona_code=card.code), principal, @@ -985,10 +1173,15 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): state_after=ctx.state_after, ) - with patch.object(sessions, "_load_session_or_404", AsyncMock(return_value=started)), patch.object( - sessions.orchestrator, - "run_turn_generate", - successful_turn, + with ( + patch.object( + sessions, "_load_session_or_404", AsyncMock(return_value=started) + ), + patch.object( + sessions.orchestrator, + "run_turn_generate", + successful_turn, + ), ): await sessions.submit_turn( response.session_id, @@ -1006,14 +1199,20 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertNotIn("김서연", message_blob) self.assertNotIn("박민수", message_blob) - async def test_start_session_requires_learner_consent_before_catalog_lookup(self) -> None: + async def test_start_session_requires_learner_consent_before_catalog_lookup( + self, + ) -> None: principal = _principal() principal.consent_at = None with patch.object( sessions, "get_catalog_persona", - AsyncMock(side_effect=AssertionError("consent gate must run before catalog lookup")), + AsyncMock( + side_effect=AssertionError( + "consent gate must run before catalog lookup" + ) + ), ) as get_persona: with self.assertRaises(sessions.HTTPException) as caught: await sessions.start_session( @@ -1025,14 +1224,20 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(caught.exception.detail, "consent_required") get_persona.assert_not_awaited() - async def test_start_session_requires_onboarding_before_consent_and_catalog_lookup(self) -> None: + async def test_start_session_requires_onboarding_before_consent_and_catalog_lookup( + self, + ) -> None: principal = _principal() principal.profile_completed_at = None with patch.object( sessions, "get_catalog_persona", - AsyncMock(side_effect=AssertionError("onboarding gate must run before catalog lookup")), + AsyncMock( + side_effect=AssertionError( + "onboarding gate must run before catalog lookup" + ) + ), ) as get_persona: with self.assertRaises(sessions.HTTPException) as caught: await sessions.start_session( @@ -1121,13 +1326,17 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual([event.event for event in events], ["error"]) self.assertIn("engine unavailable", events[0].data["detail"]) - async def test_stream_turn_engine_error_event_does_not_append_partial_turns(self) -> None: + async def test_stream_turn_engine_error_event_does_not_append_partial_turns( + self, + ) -> None: principal = _principal() sess = _session(principal) async def failing_stream(*args, **kwargs): yield orchestrator.StreamEvent("token", {"text": "부분 응답"}) - yield orchestrator.StreamEvent("error", {"detail": "engine unavailable: stream"}) + yield orchestrator.StreamEvent( + "error", {"detail": "engine unavailable: stream"} + ) with patch.object(sessions.orchestrator, "run_turn_stream", failing_stream): response = await sessions.stream_turn( @@ -1147,7 +1356,6 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.client_state = voice_routes.WebSocketState.CONNECTED async def send_text(self, data: str) -> None: - import json self.messages.append(json.loads(data)) @@ -1162,10 +1370,12 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): ): await voice_routes._run_turn_and_speak( websocket, # type: ignore[arg-type] - session_id=sess.session_id, - principal=principal, - voice_preset=VoicePreset(preset="neutral", openai_voice="sage"), - learner_text="음성 실패 발화", + voice_routes.VoiceSessionContext( + session_id=sess.session_id, + principal=principal, + voice_preset=VoicePreset(preset="neutral", openai_voice="sage"), + ), + voice_routes.VoiceTurnInput(learner_text="음성 실패 발화"), ) self.assertTrue( @@ -1186,7 +1396,6 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.client_state = voice_routes.WebSocketState.CONNECTED async def send_text(self, data: str) -> None: - import json self.messages.append(json.loads(data)) @@ -1217,37 +1426,53 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): websocket = FakeWebSocket() audio = b"\x00\x80" * 1600 - with patch.object( - voice_routes.voice_service, - "transcribe", - AsyncMock( - return_value=TranscriptResult( - text="오늘은 좀 힘들었어요.", - duration=2.0, - provider_events=[ - {"kind": "voice_activity", "start_ms": 10, "raw_text": "drop"}, - ], - ) + with ( + patch.object( + voice_routes.voice_service, + "transcribe", + AsyncMock( + return_value=TranscriptResult( + text="오늘은 좀 힘들었어요.", + duration=2.0, + provider_events=[ + { + "kind": "voice_activity", + "start_ms": 10, + "raw_text": "drop", + }, + ], + ) + ), + ), + patch.object( + voice_routes.orchestrator, + "run_turn_generate", + successful_turn, + ), + patch.object( + voice_routes.voice_service, + "synthesize_stream", + fake_synthesize_stream, ), - ), patch.object( - voice_routes.orchestrator, - "run_turn_generate", - successful_turn, - ), patch.object( - voice_routes.voice_service, - "synthesize_stream", - fake_synthesize_stream, ): await voice_routes._handle_utterance( websocket, # type: ignore[arg-type] - session_id=sess.session_id, - principal=principal, - voice_preset=VoicePreset(preset="neutral", openai_voice="sage"), - audio=audio, - fmt="webm", - silence_ms=1234, - barge_in=True, - provider_events=[{"type": "sigh", "confidence": 0.82, "text": "drop"}], + voice_routes.VoiceSessionContext( + session_id=sess.session_id, + principal=principal, + voice_preset=VoicePreset(preset="neutral", openai_voice="sage"), + ), + voice_routes.VoiceAudioInput( + audio=audio, + fmt="webm", + prosody=voice_routes.VoiceProsody( + silence_ms=1234, + barge_in=True, + provider_events=[ + {"type": "sigh", "confidence": 0.82, "text": "drop"} + ], + ), + ), ) self.assertEqual(len(sess.turns), 2) @@ -1276,7 +1501,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertIsNone(client_turn.audio_ref) self.assertEqual(client_turn.provider_events, []) self.assertEqual(client_turn.llm_provider, "claude_cli") - self.assertTrue(any(message.get("type") == "tts_end" for message in websocket.messages)) + self.assertTrue( + any(message.get("type") == "tts_end" for message in websocket.messages) + ) async def test_review_exposes_voice_nonverbal_events_on_learner_turn(self) -> None: principal = _principal() @@ -1332,7 +1559,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): silence_ms=2500, speech_rate=180.0, barge_in=True, - provider_events=[{"event_type": "cry", "category": "paralinguistic"}], + provider_events=[ + {"event_type": "cry", "category": "paralinguistic"} + ], ), ] ) @@ -1347,7 +1576,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): ) self.assertEqual(learner_turn.nonverbal[0].label, "침묵") self.assertEqual(learner_turn.nonverbal[0].detail, "1.2초") - self.assertEqual([event.kind for event in learner_turn.nonverbal].count("silence"), 1) + self.assertEqual( + [event.kind for event in learner_turn.nonverbal].count("silence"), 1 + ) self.assertEqual(learner_turn.nonverbal[1].detail, "분당 420자") self.assertEqual(learner_turn.nonverbal[4].label, "음성 단서") self.assertEqual(learner_turn.nonverbal[4].detail, "한숨 감지 · 신뢰도 82%") @@ -1390,7 +1621,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertGreaterEqual(len(worksheet.sections), 5) exploration = worksheet.sections[0] self.assertEqual(exploration.key, "exploration_11") - complaint = next(item for item in exploration.items if item.key == "presenting_complaint") + complaint = next( + item for item in exploration.items if item.key == "presenting_complaint" + ) self.assertEqual(complaint.confidence, "medium") self.assertEqual(complaint.evidence[0].turnId, "t2") self.assertIn("불안", complaint.value or "") @@ -1509,14 +1742,18 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(review_status, True)), ), ): - response = await sessions.get_session_review(sess.session_id, teacher_principal) + response = await sessions.get_session_review( + sess.session_id, teacher_principal + ) self.assertIsNotNone(response.teacherReview) assert response.teacherReview is not None self.assertEqual(response.caseWorksheet.status, "saved_by_learner") self.assertEqual(response.teacherReview.worksheetStatus, "changes_requested") self.assertIn("주호소", response.teacherReview.worksheetNote) - self.assertEqual(response.teacherReview.worksheetReviewedAt, "2026-06-27T10:05:00Z") + self.assertEqual( + response.teacherReview.worksheetReviewedAt, "2026-06-27T10:05:00Z" + ) async def test_review_remasks_legacy_session_evaluation_payload(self) -> None: class FakeKoRecognizer: @@ -1528,7 +1765,11 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): ): start = text.find(value) if start >= 0: - spans.append(guardrail.PiiEntitySpan(entity_type, start, start + len(value))) + spans.append( + guardrail.PiiEntitySpan( + entity_type, start, start + len(value) + ) + ) return spans guardrail.set_ko_pii_recognizer(FakeKoRecognizer()) @@ -1668,7 +1909,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertFalse(response.reviewReady) self.assertTrue(response.degraded) self.assertEqual(response.supervisorState, "평가 실패") - self.assertIn("deep-loop 평가 AI 산출물을 표시하지 못했습니다", response.summary) + self.assertIn( + "deep-loop 평가 AI 산출물을 표시하지 못했습니다", response.summary + ) self.assertIn("AI 평가 재시도가 필요합니다", response.summary) self.assertNotIn("session evaluation timeout after 45s", response.summary) self.assertEqual(response.rubric, []) @@ -1743,15 +1986,18 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): limitations=["임상 루브릭 전"], ) - with patch.object( - sessions, - "_load_session_or_404", - AsyncMock(return_value=sess), - ) as load_session, patch.object( - sessions.session_persistence, - "save_case_worksheet", - AsyncMock(return_value=True), - ) as save_worksheet: + with ( + patch.object( + sessions, + "_load_session_or_404", + AsyncMock(return_value=sess), + ) as load_session, + patch.object( + sessions.session_persistence, + "save_case_worksheet", + AsyncMock(return_value=True), + ) as save_worksheet, + ): response = await sessions.save_session_review_worksheet( sess.session_id, request, @@ -1769,7 +2015,9 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(save_kwargs["session_id"], sess.session_id) self.assertEqual(save_kwargs["learner_id"], principal.user_id) self.assertEqual(save_kwargs["payload"]["status"], "saved_by_learner") - self.assertEqual(save_kwargs["payload"]["sections"][0]["items"][0]["value"], "수정한 주호소") + self.assertEqual( + save_kwargs["payload"]["sections"][0]["items"][0]["value"], "수정한 주호소" + ) self.assertEqual(response.status, "saved_by_learner") self.assertEqual(response.sections[0].items[0].value, "수정한 주호소") diff --git a/apps/api/app/test_tabular_ingest.py b/apps/api/app/test_tabular_ingest.py new file mode 100644 index 0000000..c06a133 --- /dev/null +++ b/apps/api/app/test_tabular_ingest.py @@ -0,0 +1,99 @@ +"""P4 자유 양식 엑셀/CSV → 텍스트 변환 테스트.""" + +from __future__ import annotations + +import io +import unittest + +from .services.tabular_ingest import ( + MAX_UPLOAD_BYTES, + TabularIngestError, + extract_tabular_text, +) + + +def _xlsx_bytes(rows_by_sheet: dict[str, list[list[object]]]) -> bytes: + import openpyxl + + workbook = openpyxl.Workbook() + default = workbook.active + first = True + for sheet_name, rows in rows_by_sheet.items(): + if first: + sheet = default + sheet.title = sheet_name + first = False + else: + sheet = workbook.create_sheet(sheet_name) + for row in rows: + sheet.append(row) + buffer = io.BytesIO() + workbook.save(buffer) + return buffer.getvalue() + + +class TabularIngestTest(unittest.TestCase): + def test_xlsx_label_value_rows_become_pairs(self) -> None: + data = _xlsx_bytes( + { + "추고록": [ + ["이름", "김하늘"], + ["주호소", "시험 전 복통과 불안, 성적 하락 후 심화"], + [None, None], + ["상담 목표", "불안 대처와 자기 이해"], + ] + } + ) + text = extract_tabular_text(filename="자유양식.xlsx", data=data) + self.assertIn("## 시트: 추고록", text) + self.assertIn("이름: 김하늘", text) + self.assertIn("주호소: 시험 전 복통과 불안, 성적 하락 후 심화", text) + + def test_xlsx_multi_cell_rows_join_with_pipe(self) -> None: + data = _xlsx_bytes( + { + "Sheet1": [ + ["회차", "날짜", "내용 요약"], + [1, "2026-05-02", "첫 면담, 라포 형성 시도했으나 침묵이 길었음"], + ] + } + ) + text = extract_tabular_text(filename="log.xlsx", data=data) + self.assertIn("회차 | 날짜 | 내용 요약", text) + self.assertIn("1 | 2026-05-02 | 첫 면담, 라포 형성 시도했으나 침묵이 길었음", text) + + def test_csv_cp949_fallback(self) -> None: + csv_text = "라벨,값\n주호소,불안과 무기력이 이어지고 있음\n" + data = csv_text.encode("cp949") + text = extract_tabular_text(filename="notes.csv", data=data) + self.assertIn("주호소: 불안과 무기력이 이어지고 있음", text) + + def test_legacy_xls_rejected_with_guidance(self) -> None: + with self.assertRaises(TabularIngestError) as caught: + extract_tabular_text(filename="old.xls", data=b"anything") + self.assertIn(".xlsx", str(caught.exception)) + + def test_unknown_extension_rejected(self) -> None: + with self.assertRaises(TabularIngestError): + extract_tabular_text(filename="notes.hwp", data=b"1234") + + def test_empty_file_rejected(self) -> None: + with self.assertRaises(TabularIngestError): + extract_tabular_text(filename="a.xlsx", data=b"") + + def test_oversize_rejected(self) -> None: + with self.assertRaises(TabularIngestError): + extract_tabular_text(filename="a.xlsx", data=b"0" * (MAX_UPLOAD_BYTES + 1)) + + def test_nearly_empty_sheet_rejected(self) -> None: + data = _xlsx_bytes({"Sheet1": [["a"]]}) + with self.assertRaises(TabularIngestError): + extract_tabular_text(filename="empty.xlsx", data=data) + + def test_corrupt_xlsx_rejected(self) -> None: + with self.assertRaises(TabularIngestError): + extract_tabular_text(filename="broken.xlsx", data=b"not a zip at all") + + +if __name__ == "__main__": + unittest.main() diff --git a/apps/api/app/test_voice_ws.py b/apps/api/app/test_voice_ws.py index de7477e..a5dc857 100644 --- a/apps/api/app/test_voice_ws.py +++ b/apps/api/app/test_voice_ws.py @@ -166,7 +166,9 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): self.assertIsNone(voice_routes._client_turn_text_for_speech(session, 3)) self.assertIsNone(voice_routes._client_turn_text_for_speech(session, 99)) - async def test_text_turn_speech_returns_openai_audio_for_owned_persisted_turn(self) -> None: + async def test_text_turn_speech_returns_openai_audio_for_owned_persisted_turn( + self, + ) -> None: session = SimpleNamespace( persona=SimpleNamespace(code="P1"), turns=[ @@ -186,30 +188,37 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): yield TTSChunk(audio=b"mp3-a") yield TTSChunk(audio=b"mp3-b") - with patch.object( - voice_routes, - "_practice_access_error", - AsyncMock(return_value=None), - ), patch.object( - voice_routes.turn_runtime, - "load_owned_session", - AsyncMock(return_value=(session, None)), - ), patch.object( - voice_routes, - "_resolve_session_voice", - AsyncMock(return_value=VOICE_PRESET), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes.voice_service, - "tts_provider", - return_value="openai", - ), patch.object( - voice_routes.voice_service, - "synthesize_stream", - new=synthesize, + with ( + patch.object( + voice_routes, + "_practice_access_error", + AsyncMock(return_value=None), + ), + patch.object( + voice_routes.turn_runtime, + "load_owned_session", + AsyncMock(return_value=(session, None)), + ), + patch.object( + voice_routes, + "_resolve_session_voice", + AsyncMock(return_value=VOICE_PRESET), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes.voice_service, + "tts_provider", + return_value="openai", + ), + patch.object( + voice_routes.voice_service, + "synthesize_stream", + new=synthesize, + ), ): response = await voice_routes.voice_speech( voice_routes.VoiceSpeechRequest(session_id=SESSION_ID, turn_seq=4), @@ -223,7 +232,9 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(response.headers["x-vignette-tts-provider"], "openai") self.assertEqual(synthesized, [("내담자 응답", VOICE_PRESET)]) - async def test_audio_start_binary_chunks_audio_end_ping_close_contract(self) -> None: + async def test_audio_start_binary_chunks_audio_end_ping_close_contract( + self, + ) -> None: websocket = FakeWebSocket( [ _control({"type": "audio_start", "format": "webm"}), @@ -252,55 +263,66 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): ) handle_utterance = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_handle_utterance", - handle_utterance, - ), patch.object( - voice_routes, - "_run_turn_and_speak", - AsyncMock(), - ), patch.object( - voice_routes.time, - "monotonic", - side_effect=[10.0, 12.0], + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_handle_utterance", + handle_utterance, + ), + patch.object( + voice_routes, + "_run_turn_and_speak", + AsyncMock(), + ), + patch.object( + voice_routes.time, + "monotonic", + side_effect=[10.0, 12.0], + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] self.assertTrue(websocket.accepted) self.assertEqual(websocket.close_codes, [1000]) self.assertEqual( - [(message.get("type"), message.get("state")) for message in websocket.sent_json], + [ + (message.get("type"), message.get("state")) + for message in websocket.sent_json + ], [("ready", "idle"), ("state", "listening"), ("pong", None)], ) handle_utterance.assert_awaited_once() - kwargs = handle_utterance.await_args.kwargs - self.assertEqual(kwargs["session_id"], SESSION_ID) - self.assertEqual(kwargs["principal"].user_id, _principal().user_id) - self.assertEqual(kwargs["voice_preset"], VOICE_PRESET) - self.assertEqual(kwargs["audio"], b"chunk-onechunk-two") - self.assertEqual(kwargs["fmt"], "webm") - self.assertIsNone(kwargs["sample_rate"]) - self.assertIsNone(kwargs["channels"]) - self.assertIsNone(kwargs["sample_width"]) - self.assertEqual(kwargs["audio_started_at"], 10.0) - self.assertEqual(kwargs["audio_ended_at"], 12.0) - self.assertEqual(kwargs["silence_ms"], 450) - self.assertIs(kwargs["barge_in"], True) + context = handle_utterance.await_args.args[1] + utterance = handle_utterance.await_args.args[2] + self.assertEqual(context.session_id, SESSION_ID) + self.assertEqual(context.principal.user_id, _principal().user_id) + self.assertEqual(context.voice_preset, VOICE_PRESET) + self.assertEqual(utterance.audio, b"chunk-onechunk-two") + self.assertEqual(utterance.fmt, "webm") + self.assertIsNone(utterance.sample_rate) + self.assertIsNone(utterance.channels) + self.assertIsNone(utterance.sample_width) + self.assertEqual(utterance.audio_started_at, 10.0) + self.assertEqual(utterance.audio_ended_at, 12.0) + self.assertEqual(utterance.prosody.silence_ms, 450) + self.assertIs(utterance.prosody.barge_in, True) self.assertEqual( - kwargs["provider_events"], + utterance.prosody.provider_events, [ { "type": "sigh", @@ -339,7 +361,15 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): async def test_pcm_control_metadata_flows_to_handle_utterance(self) -> None: websocket = FakeWebSocket( [ - _control({"type": "audio_start", "format": "pcm", "sample_rate": 16000, "channels": 1, "sample_width": 2}), + _control( + { + "type": "audio_start", + "format": "pcm", + "sample_rate": 16000, + "channels": 1, + "sample_width": 2, + } + ), _binary(b"\x00\x00\xff\x7f"), _control({"type": "audio_end", "format": "pcm"}), _control({"type": "close"}), @@ -347,33 +377,40 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): ) handle_utterance = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_handle_utterance", - handle_utterance, + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_handle_utterance", + handle_utterance, + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] handle_utterance.assert_awaited_once() - kwargs = handle_utterance.await_args.kwargs - self.assertEqual(kwargs["fmt"], "pcm") - self.assertEqual(kwargs["sample_rate"], 16000) - self.assertEqual(kwargs["channels"], 1) - self.assertEqual(kwargs["sample_width"], 2) + utterance = handle_utterance.await_args.args[2] + self.assertEqual(utterance.fmt, "pcm") + self.assertEqual(utterance.sample_rate, 16000) + self.assertEqual(utterance.channels, 1) + self.assertEqual(utterance.sample_width, 2) - async def test_text_turn_strips_text_runs_turn_and_ping_close_still_work(self) -> None: + async def test_text_turn_strips_text_runs_turn_and_ping_close_still_work( + self, + ) -> None: websocket = FakeWebSocket( [ _control({"type": "text_turn", "text": " I need help practicing. "}), @@ -384,41 +421,51 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): run_turn = AsyncMock() handle_utterance = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_handle_utterance", - handle_utterance, - ), patch.object( - voice_routes, - "_run_turn_and_speak", - run_turn, + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_handle_utterance", + handle_utterance, + ), + patch.object( + voice_routes, + "_run_turn_and_speak", + run_turn, + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] self.assertEqual(websocket.close_codes, [1000]) self.assertEqual( - [(message.get("type"), message.get("state")) for message in websocket.sent_json], + [ + (message.get("type"), message.get("state")) + for message in websocket.sent_json + ], [("ready", "idle"), ("pong", None)], ) handle_utterance.assert_not_awaited() run_turn.assert_awaited_once() - kwargs = run_turn.await_args.kwargs - self.assertEqual(kwargs["session_id"], SESSION_ID) - self.assertEqual(kwargs["principal"].user_id, _principal().user_id) - self.assertEqual(kwargs["voice_preset"], VOICE_PRESET) - self.assertEqual(kwargs["learner_text"], "I need help practicing.") + context = run_turn.await_args.args[1] + turn = run_turn.await_args.args[2] + self.assertEqual(context.session_id, SESSION_ID) + self.assertEqual(context.principal.user_id, _principal().user_id) + self.assertEqual(context.voice_preset, VOICE_PRESET) + self.assertEqual(turn.learner_text, "I need help practicing.") async def test_turn_persistence_failure_uses_structured_voice_error(self) -> None: websocket = FakeWebSocket( @@ -437,22 +484,27 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): ) ) - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_run_turn_and_speak", - run_turn, + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_run_turn_and_speak", + run_turn, + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] @@ -469,7 +521,9 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): ) self.assertEqual(websocket.sent_json[-1], {"type": "state", "state": "idle"}) - async def test_stt_result_waits_for_final_transcript_before_running_turn(self) -> None: + async def test_stt_result_waits_for_final_transcript_before_running_turn( + self, + ) -> None: websocket = FakeWebSocket( [ _control( @@ -486,26 +540,32 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): run_turn = AsyncMock() handle_utterance = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_handle_utterance", - handle_utterance, - ), patch.object( - voice_routes, - "_run_turn_and_speak", - run_turn, + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_handle_utterance", + handle_utterance, + ), + patch.object( + voice_routes, + "_run_turn_and_speak", + run_turn, + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] @@ -521,7 +581,9 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): }, websocket.sent_json, ) - self.assertEqual(websocket.sent_json[-1], {"type": "state", "state": "listening"}) + self.assertEqual( + websocket.sent_json[-1], {"type": "state", "state": "listening"} + ) async def test_stt_result_runs_turn_only_after_eot_ready(self) -> None: websocket = FakeWebSocket( @@ -549,30 +611,37 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): run_turn = AsyncMock() handle_utterance = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_handle_utterance", - handle_utterance, - ), patch.object( - voice_routes, - "_run_turn_and_speak", - run_turn, - ), patch.object( - voice_routes.time, - "monotonic", - side_effect=[10.0, 12.0], + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_handle_utterance", + handle_utterance, + ), + patch.object( + voice_routes, + "_run_turn_and_speak", + run_turn, + ), + patch.object( + voice_routes.time, + "monotonic", + side_effect=[10.0, 12.0], + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] @@ -598,13 +667,13 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): websocket.sent_json, ) self.assertIn({"type": "state", "state": "thinking"}, websocket.sent_json) - kwargs = run_turn.await_args.kwargs - self.assertEqual(kwargs["learner_text"], "I am done now.") - self.assertEqual(kwargs["duration_s"], 2.0) - self.assertEqual(kwargs["silence_ms"], 1300) - self.assertIs(kwargs["barge_in"], False) + turn = run_turn.await_args.args[2] + self.assertEqual(turn.learner_text, "I am done now.") + self.assertEqual(turn.prosody.duration_s, 2.0) + self.assertEqual(turn.prosody.silence_ms, 1300) + self.assertIs(turn.prosody.barge_in, False) self.assertEqual( - kwargs["provider_events"], + turn.prosody.provider_events, [ { "type": "speech_final", @@ -615,7 +684,9 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): ], ) - async def test_oversize_binary_audio_reports_error_and_drops_utterance(self) -> None: + async def test_oversize_binary_audio_reports_error_and_drops_utterance( + self, + ) -> None: websocket = FakeWebSocket( [ _control({"type": "audio_start", "format": "webm"}), @@ -626,30 +697,37 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): handle_utterance = AsyncMock() run_turn = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal()), - ), patch.object( - voice_routes, - "_bind_session", - AsyncMock(return_value=self._bind_result()), - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ), patch.object( - voice_routes, - "_handle_utterance", - handle_utterance, - ), patch.object( - voice_routes, - "_run_turn_and_speak", - run_turn, - ), patch.object( - voice_routes, - "_MAX_AUDIO_BYTES", - 4, + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal()), + ), + patch.object( + voice_routes, + "_bind_session", + AsyncMock(return_value=self._bind_result()), + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ), + patch.object( + voice_routes, + "_handle_utterance", + handle_utterance, + ), + patch.object( + voice_routes, + "_run_turn_and_speak", + run_turn, + ), + patch.object( + voice_routes, + "_MAX_AUDIO_BYTES", + 4, + ), ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] @@ -664,48 +742,62 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): handle_utterance.assert_not_awaited() run_turn.assert_not_awaited() - async def test_unauthenticated_client_closes_before_session_or_voice_checks(self) -> None: + async def test_unauthenticated_client_closes_before_session_or_voice_checks( + self, + ) -> None: websocket = FakeWebSocket() bind_session = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=None), - ), patch.object( - voice_routes, - "_bind_session", - bind_session, - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ) as is_available: + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=None), + ), + patch.object( + voice_routes, + "_bind_session", + bind_session, + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ) as is_available, + ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] self.assertTrue(websocket.accepted) self.assertEqual(websocket.close_codes, [voice_routes.WS_CLOSE_UNAUTHORIZED]) - self.assertEqual(websocket.sent_json, [{"type": "error", "detail": "not authenticated"}]) + self.assertEqual( + websocket.sent_json, [{"type": "error", "detail": "not authenticated"}] + ) bind_session.assert_not_awaited() is_available.assert_not_called() - async def test_non_learner_client_closes_before_session_or_voice_checks(self) -> None: + async def test_non_learner_client_closes_before_session_or_voice_checks( + self, + ) -> None: websocket = FakeWebSocket() bind_session = AsyncMock() - with patch.object( - voice_routes, - "_principal_from_websocket", - AsyncMock(return_value=_principal(Role.TEACHER)), - ), patch.object( - voice_routes, - "_bind_session", - bind_session, - ), patch.object( - voice_routes.voice_service, - "is_available", - return_value=True, - ) as is_available: + with ( + patch.object( + voice_routes, + "_principal_from_websocket", + AsyncMock(return_value=_principal(Role.TEACHER)), + ), + patch.object( + voice_routes, + "_bind_session", + bind_session, + ), + patch.object( + voice_routes.voice_service, + "is_available", + return_value=True, + ) as is_available, + ): await voice_routes.voice_ws(websocket) # type: ignore[arg-type] self.assertTrue(websocket.accepted) @@ -733,14 +825,17 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): ) get_voice_map = AsyncMock(return_value=voice_map) - with patch.object( - voice_routes, - "_load_voice_session", - AsyncMock(return_value=(sess, None)), - ), patch.object( - voice_routes, - "get_session_voice_map", - get_voice_map, + with ( + patch.object( + voice_routes, + "_load_voice_session", + AsyncMock(return_value=(sess, None)), + ), + patch.object( + voice_routes, + "get_session_voice_map", + get_voice_map, + ), ): session_id, voice, err, meta = await voice_routes._bind_session( websocket, _principal() @@ -749,31 +844,40 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(session_id, SESSION_ID) self.assertIsNone(err) self.assertEqual(meta["persona_catalog_source"], "session") - self.assertEqual(voice, VoicePreset( - preset="calm-adult-male", - openai_voice="onyx", - rate=1.08, - instructions="Low, guarded delivery.", - )) + self.assertEqual( + voice, + VoicePreset( + preset="calm-adult-male", + openai_voice="onyx", + rate=1.08, + instructions="Low, guarded delivery.", + ), + ) get_voice_map.assert_awaited_once_with(SESSION_ID) - async def test_bind_session_rejects_existing_session_without_onboarding(self) -> None: + async def test_bind_session_rejects_existing_session_without_onboarding( + self, + ) -> None: websocket = FakeWebSocket() websocket.query_params = {"session_id": SESSION_ID} load_voice_session = AsyncMock() - with patch.object( - voice_routes, - "user_onboarding_complete", - AsyncMock(return_value=False), - ), patch.object( - voice_routes, - "user_has_consent", - AsyncMock(return_value=True), - ), patch.object( - voice_routes, - "_load_voice_session", - load_voice_session, + with ( + patch.object( + voice_routes, + "user_onboarding_complete", + AsyncMock(return_value=False), + ), + patch.object( + voice_routes, + "user_has_consent", + AsyncMock(return_value=True), + ), + patch.object( + voice_routes, + "_load_voice_session", + load_voice_session, + ), ): session_id, voice, err, meta = await voice_routes._bind_session( websocket, @@ -791,14 +895,17 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): websocket.query_params = {"session_id": SESSION_ID} load_voice_session = AsyncMock() - with patch.object( - voice_routes, - "user_has_consent", - AsyncMock(return_value=False), - ), patch.object( - voice_routes, - "_load_voice_session", - load_voice_session, + with ( + patch.object( + voice_routes, + "user_has_consent", + AsyncMock(return_value=False), + ), + patch.object( + voice_routes, + "_load_voice_session", + load_voice_session, + ), ): session_id, voice, err, meta = await voice_routes._bind_session( websocket, @@ -817,14 +924,17 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): sess = SimpleNamespace(persona=SimpleNamespace(code="P2")) get_voice_map = AsyncMock() - with patch.object( - voice_routes, - "_load_voice_session", - AsyncMock(return_value=(sess, None)), - ), patch.object( - voice_routes, - "get_session_voice_map", - get_voice_map, + with ( + patch.object( + voice_routes, + "_load_voice_session", + AsyncMock(return_value=(sess, None)), + ), + patch.object( + voice_routes, + "get_session_voice_map", + get_voice_map, + ), ): session_id, voice, err, _ = await voice_routes._bind_session( websocket, _principal() @@ -836,7 +946,9 @@ class VoiceWebSocketContractTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(voice.openai_voice, "coral") get_voice_map.assert_not_awaited() - async def test_catalog_voice_map_is_used_for_dev_persona_binding_helper(self) -> None: + async def test_catalog_voice_map_is_used_for_dev_persona_binding_helper( + self, + ) -> None: voice_map = PersonaVoiceMap( provider="openai", voice_id="verse", diff --git a/apps/api/app/turn_runtime.py b/apps/api/app/turn_runtime.py index 9aba28e..b613a54 100644 --- a/apps/api/app/turn_runtime.py +++ b/apps/api/app/turn_runtime.py @@ -19,8 +19,12 @@ from .store import InProcSession, TurnRecord, store logger = logging.getLogger(__name__) -_LIVE_COACH_RECHARGE_MIN_RAPPORT = 0.35 -_LIVE_COACH_RECHARGE_MIN_OPENNESS_GAIN = 0.02 +_LIVE_COACH_RECHARGE_MIN_RAPPORT = 0.15 +_LIVE_COACH_RECHARGE_NEUTRAL_MIN_RAPPORT = 0.35 +_LIVE_COACH_RECHARGE_MIN_OPENNESS_GAIN = 0.01 +# 페이싱 충전: 평가 신호와 무관하게 N턴마다 1개. 코칭이 가장 필요한(잘 못 풀리는) +# 학습자가 3개 소진 후 영영 코칭을 못 받는 순감 구조를 막는다(2026-07-13 회의 후속). +_LIVE_COACH_PACING_RECHARGE_EVERY_TURNS = 6 class SessionAccessError(str, Enum): @@ -187,25 +191,42 @@ def should_recharge_live_coach_credit( before: state_machine.SessionState, after: state_machine.SessionState, ) -> tuple[bool, str]: - """Good-score recharge gate based on stored evaluator/state-machine evidence.""" - if not isinstance(evaluation, dict): - return False, "" - if evaluation.get("appropriateness") != "pos": - return False, "" - try: - rapport = float(evaluation.get("rapport_signal") or 0) - except (TypeError, ValueError): - rapport = 0.0 - if rapport < _LIVE_COACH_RECHARGE_MIN_RAPPORT: - return False, "" + """좋은 발화 충전 + 진행 페이싱 충전 게이트. + 기존 3중 AND(appropriateness==pos + rapport>=0.35 + 단계전환/개방도+0.02)는 + 실사용에서 충전이 사실상 불가능해 코칭이 3턴 만에 죽었다. 완화된 성과 충전에 + 페이싱 충전을 더해, 잘하는 학습자는 빨리·모두가 주기적으로 충전받게 한다. + """ + appropriateness = evaluation.get("appropriateness") if isinstance(evaluation, dict) else None + rapport = 0.0 + if isinstance(evaluation, dict): + try: + rapport = float(evaluation.get("rapport_signal") or 0) + except (TypeError, ValueError): + rapport = 0.0 openness_gain = float(after.effective_openness or 0) - float(before.effective_openness or 0) stage_changed = after.stage != before.stage - if not stage_changed and openness_gain < _LIVE_COACH_RECHARGE_MIN_OPENNESS_GAIN: - return False, "" - if stage_changed: - return True, "좋은 발화로 내담자 단계가 열려 코칭 기회 1개를 충전했습니다." - return True, "좋은 발화 뒤 내담자 개방도가 올라 코칭 기회 1개를 충전했습니다." + + if appropriateness == "pos" and rapport >= _LIVE_COACH_RECHARGE_MIN_RAPPORT: + if stage_changed: + return True, "좋은 발화로 내담자 단계가 열려 코칭 기회 1개를 충전했습니다." + if openness_gain >= _LIVE_COACH_RECHARGE_MIN_OPENNESS_GAIN: + return True, "좋은 발화 뒤 내담자 개방도가 올라 코칭 기회 1개를 충전했습니다." + if ( + appropriateness == "neutral" + and rapport >= _LIVE_COACH_RECHARGE_NEUTRAL_MIN_RAPPORT + and openness_gain > 0 + ): + return True, "안정적인 라포 신호로 코칭 기회 1개를 충전했습니다." + + # 페이싱 충전 — 평가 실패(evaluation=None)여도 회기 진행 자체로 충전된다. + turn_seq = int(after.turn_seq or 0) + if turn_seq > 0 and turn_seq % _LIVE_COACH_PACING_RECHARGE_EVERY_TURNS == 0: + return ( + True, + f"회기가 {_LIVE_COACH_PACING_RECHARGE_EVERY_TURNS}턴 진행되어 코칭 기회 1개를 충전했습니다.", + ) + return False, "" async def maybe_recharge_live_coach_credit( diff --git a/apps/api/engine_gateway/gateway.py b/apps/api/engine_gateway/gateway.py index 9239791..018ecf8 100644 --- a/apps/api/engine_gateway/gateway.py +++ b/apps/api/engine_gateway/gateway.py @@ -42,6 +42,9 @@ DEFAULT_BUDGET = float(os.environ.get("SESSION_BUDGET_USD", "5.0")) READY_TTL_SECONDS = float(os.environ.get("ENGINE_READY_TTL_SECONDS", "30")) READY_TIMEOUT_SECONDS = float(os.environ.get("ENGINE_READY_TIMEOUT_SECONDS", "20")) READY_BUDGET_USD = float(os.environ.get("ENGINE_READY_BUDGET_USD", "0.5")) +# 단발 생성(/v1/generate) 턴 타임아웃 — 페르소나 초안 생성 같은 대형 구조화 출력은 +# 120초를 넘길 수 있어 설정 가능하게 한다(2026-07-15). 호출부(app ENGINE_TIMEOUT)와 정합 필요. +GENERATE_TURN_TIMEOUT_SECONDS = float(os.environ.get("ENGINE_GENERATE_TIMEOUT_SECONDS", "300")) GATEWAY_PROVIDER = "claude_cli" GATEWAY_FALLBACK_MODEL_NAME = "claude-opus-4-8" @@ -424,7 +427,7 @@ async def v1_generate(req: GwGenerateReq): s, ephemeral = await _resolve_session(req, system_prompt) try: - result = await s.turn(prompt_parts.user_payload, timeout=120.0) + result = await s.turn(prompt_parts.user_payload, timeout=GENERATE_TURN_TIMEOUT_SECONDS) finally: if ephemeral: await s.close() diff --git a/apps/api/engine_gateway/test_gateway_model.py b/apps/api/engine_gateway/test_gateway_model.py index 15ba19c..40af947 100644 --- a/apps/api/engine_gateway/test_gateway_model.py +++ b/apps/api/engine_gateway/test_gateway_model.py @@ -581,7 +581,7 @@ class GatewayModelTest(unittest.TestCase): self.assertEqual(validated.text, "reused response") self.assertEqual(validated.provider, "claude_cli") self.assertEqual(validated.cost_usd, 0.01) - self.assertEqual(calls, [("hello", 120.0)]) + self.assertEqual(calls, [("hello", gateway.GENERATE_TURN_TIMEOUT_SECONDS)]) self.assertEqual(closes, []) def test_v1_generate_closes_fresh_ephemeral_session(self): @@ -612,7 +612,7 @@ class GatewayModelTest(unittest.TestCase): self.assertEqual(validated.provider, "claude_cli") self.assertEqual(validated.cost_usd, 0.02) self.assertEqual(len(started), 1) - self.assertEqual(turned, [(started[0], "hello", 120.0)]) + self.assertEqual(turned, [(started[0], "hello", gateway.GENERATE_TURN_TIMEOUT_SECONDS)]) self.assertEqual(closed, [started[0]]) self.assertNotIn(started[0].id, gateway.SESSIONS) diff --git a/apps/api/requirements.txt b/apps/api/requirements.txt index 40464a4..bd0a111 100644 --- a/apps/api/requirements.txt +++ b/apps/api/requirements.txt @@ -8,6 +8,8 @@ pydantic-settings==2.12.0 python-multipart==0.0.18 httpx==0.28.1 sse-starlette==3.0.3 +# P4(2026-07-13 회의): 자유 양식 엑셀 업로드 → 텍스트 변환 (personas/sources/upload) +openpyxl==3.1.5 # ── 선택 의존성 (가드레일 PII 마스킹) ───────────────────────────── # Presidio 가 설치되면 guardrail.mask_pii 가 NER 기반으로 동작하고, diff --git a/apps/web/e2e/README.md b/apps/web/e2e/README.md index 36dcd99..968b44f 100644 --- a/apps/web/e2e/README.md +++ b/apps/web/e2e/README.md @@ -48,6 +48,18 @@ $env:E2E_PUBLIC_STORAGE_STATE="./node_modules/.tmp/public-auth.json" npx playwright test e2e/public-auth-turn.spec.ts --project=chromium-public-auth ``` +Public admin visual smoke: + +```sh +# Capture storage state after signing in with an admin-entitled Google account. +npx playwright codegen https://vignette.chanpaca.net/login --save-storage=./node_modules/.tmp/public-admin-auth.json + +# Verify that https://vignette.chanpaca.net/admin visibly renders the admin console. +$env:E2E_PUBLIC_AUTH="1" +$env:E2E_PUBLIC_STORAGE_STATE="./node_modules/.tmp/public-admin-auth.json" +npx playwright test e2e/public-admin-visual.spec.ts --project=chromium-public-auth +``` + Notes: - `E2E_PUBLIC_AUTH=1` targets the public site and does not start the local Vite web server. diff --git a/apps/web/e2e/admin.spec.ts b/apps/web/e2e/admin.spec.ts index 08d6d19..0308261 100644 --- a/apps/web/e2e/admin.spec.ts +++ b/apps/web/e2e/admin.spec.ts @@ -82,6 +82,7 @@ interface AdminUsageEvaluatorCache { interface AdminUsageResponse { source: "database" | "server_session_registry"; durable: boolean; + generated_at: number; window_days: number; total_turns: number; metered_turns: number; @@ -94,6 +95,16 @@ interface AdminUsageResponse { daily_cost?: AdminUsageDailyCost[]; } +interface AdminEngineConfigResponse { + engine_mode: string; + engine_url: string; + model: string; + source: "database" | "runtime_cache" | "runtime_default"; + durable: boolean; + updated_by: string | null; + updated_at: number | null; +} + interface AdminUptimeResponse { source: "database" | "unavailable"; durable: boolean; @@ -161,6 +172,8 @@ async function mockAdminSession( options: { adminUsers?: unknown[]; adminTickets?: unknown; + adminUsage?: AdminUsageResponse; + engineConfig?: AdminEngineConfigResponse; } = {}, ) { const seenAdminEndpoints = new Set(); @@ -221,33 +234,70 @@ async function mockAdminSession( if (method === "GET" && path.endsWith("/admin/usage")) { seenAdminEndpoints.add("usage"); + await fulfillJson( + options.adminUsage ?? { + source: "database", + durable: true, + generated_at: 1_783_990_800, + window_days: Number(url.searchParams.get("window_days") ?? 7), + total_turns: 0, + metered_turns: 0, + tokens_in: 0, + tokens_out: 0, + cost_usd: 0, + budget: { + limit_usd: 0, + used_ratio: 0, + remaining_usd: null, + status: "disabled", + }, + evaluator_cache: { + enabled: false, + entries: 0, + hits: 0, + misses: 0, + stores: 0, + evictions: 0, + requests: 0, + hit_rate: 0, + }, + by_provider: [], + daily_cost: [], + }, + ); + return; + } + + if (method === "GET" && path.endsWith("/admin/engine-config")) { + seenAdminEndpoints.add("engine-config"); + await fulfillJson( + options.engineConfig ?? { + engine_mode: "openai", + engine_url: "http://127.0.0.1:9099", + model: "gateway-default", + source: "database", + durable: true, + updated_by: "admin@twentyoz.kr", + updated_at: 1_783_990_800, + }, + ); + return; + } + + if (method === "PATCH" && path.endsWith("/admin/engine-config")) { + seenAdminEndpoints.add("engine-config-patch"); + const body = request.postDataJSON() as Partial; await fulfillJson({ + ...(options.engineConfig ?? { + engine_mode: "openai", + engine_url: "http://127.0.0.1:9099", + model: "gateway-default", + }), + ...body, source: "database", durable: true, - window_days: 7, - total_turns: 0, - metered_turns: 0, - tokens_in: 0, - tokens_out: 0, - cost_usd: 0, - budget: { - limit_usd: 0, - used_ratio: 0, - remaining_usd: null, - status: "disabled", - }, - evaluator_cache: { - enabled: false, - entries: 0, - hits: 0, - misses: 0, - stores: 0, - evictions: 0, - requests: 0, - hit_rate: 0, - }, - by_provider: [], - daily_cost: [], + updated_by: "stale-admin@twentyoz.kr", + updated_at: 1_783_990_900, }); return; } @@ -568,7 +618,9 @@ async function expectVisibleButtonsFit(page: Page, selector: string, context: st buttons .map((button) => { const rect = button.getBoundingClientRect(); - const owner = button.closest(".ad-user,.ad-user-create") ?? button.parentElement; + const owner = + button.closest(".ad-user,.ad-user-create,.ad-user-table tbody tr") ?? + button.parentElement; const ownerRect = owner?.getBoundingClientRect(); const style = window.getComputedStyle(button); const visible = @@ -636,6 +688,99 @@ test.describe("admin route guards", () => { .toEqual(["health", "tickets", "uptime", "usage", "users"]); }); + test("shows detailed AI metering and saves the real engine configuration", async ({ page }) => { + const seenAdminEndpoints = await mockAdminSession( + page, + { + user_id: "ai-admin", + email: "ai-admin@twentyoz.kr", + display_name: "AI Admin", + role: "admin", + admin_access: true, + super_admin: true, + onboarding_completed_at: 1_782_900_000, + }, + { + adminUsage: { + source: "database", + durable: true, + generated_at: 1_783_990_800, + window_days: 30, + total_turns: 32, + metered_turns: 30, + tokens_in: 125_000, + tokens_out: 18_500, + cost_usd: 6.6212, + budget: { + limit_usd: 20, + used_ratio: 0.33106, + remaining_usd: 13.3788, + status: "ok", + }, + evaluator_cache: { + enabled: true, + entries: 12, + hits: 8, + misses: 2, + stores: 2, + evictions: 1, + requests: 10, + hit_rate: 0.8, + }, + by_provider: [ + { + provider: "openai", + model: "gpt-5-mini", + turns: 30, + tokens_in: 125_000, + tokens_out: 18_500, + cost_usd: 6.6212, + }, + ], + daily_cost: [ + { day: "2026-07-13", turns: 8, tokens_in: 32_000, tokens_out: 4_800, cost_usd: 1.42 }, + { day: "2026-07-14", turns: 10, tokens_in: 41_000, tokens_out: 6_100, cost_usd: 2.08 }, + { day: "2026-07-15", turns: 12, tokens_in: 52_000, tokens_out: 7_600, cost_usd: 3.1212 }, + ], + }, + }, + ); + + await page.goto("/admin/ai"); + + await expect(page).toHaveURL(/\/admin\/ai$/); + await expect(page.getByRole("heading", { name: "AI 운영과 DB 계량" })).toBeVisible(); + await expect(page.locator(".vg-nav").getByRole("link", { name: "AI 운영" })).toHaveAttribute( + "aria-current", + "page", + ); + await expect(page.getByText("운영 DB 원장").first()).toBeVisible(); + await expect(page.locator(".aic-ledger")).toContainText("$6.6212"); + await expect(page.locator(".aic-budget")).toContainText("93.8%"); + await expect(page.locator(".aic-table")).toContainText("gpt-5-mini"); + await expect(page.locator(".aic-cache-score")).toContainText("80%"); + await expect(page.getByLabel("AI 기본 모델")).toHaveValue("gateway-default"); + + const usageRequest = page.waitForRequest((request) => + request.url().includes("/api/admin/usage?window_days=7"), + ); + await page.getByRole("button", { name: "7일" }).click(); + await usageRequest; + + await page.getByLabel("AI 기본 모델").fill("gpt-5.1-mini"); + const patchRequest = page.waitForRequest((request) => + request.method() === "PATCH" && request.url().endsWith("/api/admin/engine-config"), + ); + await page.getByRole("button", { name: "운영 설정 저장" }).click(); + const request = await patchRequest; + expect(request.postDataJSON()).toMatchObject({ model: "gpt-5.1-mini" }); + await expect(page.getByText("저장됨")).toBeVisible(); + await expect + .poll(() => Array.from(seenAdminEndpoints).sort()) + .toEqual(["engine-config", "engine-config-patch", "health", "usage"]); + await expectNoHorizontalOverflow(page); + }); + test("resets scroll when moving from long admin pages to access policy", async ({ page }) => { await mockAdminSession(page, { user_id: "scroll-admin", @@ -663,7 +808,40 @@ test.describe("admin route guards", () => { await expect .poll(() => page.evaluate(() => window.scrollY)) .toBe(0); - await expect(page.getByRole("heading", { name: "역할, 그룹, 접근 범위" })).toBeInViewport(); + await expect(page.locator(".ad-root")).toBeInViewport(); + await expect(page.getByRole("heading", { name: "역할, 그룹, 접근 범위" })).toBeVisible(); + }); + + test("resets scroll when the browser restores an existing admin tab", async ({ page }) => { + await mockAdminSession(page, { + user_id: "restored-tab-admin", + email: "restored-tab-admin@twentyoz.kr", + display_name: "Restored Tab Admin", + role: "admin", + admin_access: true, + super_admin: true, + onboarding_completed_at: 1_782_900_000, + }); + + await page.goto("/admin"); + await expect(page.getByRole("heading", { name: "현재 서비스 상태" })).toBeVisible(); + const scrolled = await page.evaluate(() => { + document.documentElement.style.minHeight = "2200px"; + document.body.style.minHeight = "2200px"; + window.scrollTo(0, 900); + return window.scrollY; + }); + expect(scrolled).toBeGreaterThan(0); + + await page.evaluate(() => { + window.dispatchEvent(new PageTransitionEvent("pageshow", { persisted: true })); + }); + + await expect + .poll(() => page.evaluate(() => window.scrollY)) + .toBe(0); + await expect(page.locator(".ad-root")).toBeInViewport(); + await expect(page.getByRole("heading", { name: "현재 서비스 상태" })).toBeVisible(); }); test("shows admin data diagnostics instead of a blank main pane", async ({ page }) => { @@ -849,6 +1027,7 @@ test.describe("admin route", () => { await page.goto("/admin"); const nav = page.locator(".vg-nav"); await expect(nav.getByRole("link", { name: "운영 홈" })).toBeVisible(); + await expect(nav.getByRole("link", { name: "AI 운영" })).toBeVisible(); await expect(nav.getByRole("link", { name: "사용자" })).toBeVisible(); await expect(nav.getByRole("link", { name: "권한" })).toBeVisible(); await expect(nav.getByRole("link", { name: "티켓" })).toBeVisible(); @@ -886,6 +1065,18 @@ test.describe("admin route", () => { await expect(page.getByText("아직 등록된 사용자가 없습니다.")).toHaveCount(0); await page.getByLabel("사용자 검색").fill(""); + const userTable = page.getByRole("table"); + await expect(userTable).toBeVisible(); + const sessionHeader = page.getByRole("columnheader", { name: /활성 회기/ }); + await sessionHeader.getByRole("button").click(); + await expect(sessionHeader).toHaveAttribute("aria-sort", "ascending"); + const ascendingSessions = await userTable.locator("tbody tr td:nth-child(7)").allTextContents(); + expect(ascendingSessions.map(Number)).toEqual( + ascendingSessions.map(Number).slice().sort((a, b) => a - b), + ); + await sessionHeader.getByRole("button").click(); + await expect(sessionHeader).toHaveAttribute("aria-sort", "descending"); + await page.getByRole("tab", { name: "사용자 등록" }).click(); await page.getByLabel("새 사용자 이메일").fill(email); await page.getByLabel("새 사용자 표시 이름").fill(displayName); @@ -934,9 +1125,9 @@ test.describe("admin route", () => { await page.getByRole("tab", { name: "사용자 목록" }).click(); await page.getByLabel("사용자 검색").fill(email); - const card = page.locator(".ad-user").filter({ hasText: email }); + const card = page.locator(".ad-user-table tbody tr").filter({ hasText: email }); await expect(card).toBeVisible(); - await expect(card).toContainText(displayName); + await expect(card.getByLabel(`${email} 표시 이름`)).toHaveValue(displayName); await expect(card).toContainText("승인됨"); await expectVisibleButtonsFit(page, ".ad-user__actions .vg-btn", "admin user action buttons"); @@ -970,7 +1161,7 @@ test.describe("admin route", () => { cohort_ids: [nextCohort], }); - await expect(card).toContainText(nextName); + await expect(nameInput).toHaveValue(nextName); await expect(card).toContainText("교수자"); await expect(cohortInput).toHaveValue(nextCohort); @@ -1136,6 +1327,20 @@ test.describe("admin route", () => { expect(layout.formColumns).toBe(1); if (users.users.length > 0) { await page.getByRole("tab", { name: "사용자 목록" }).click(); + const scrollRegion = page.locator(".ad-user-table-scroll"); + const scrollContract = await scrollRegion.evaluate((element) => { + const before = element.scrollLeft; + element.scrollLeft = element.scrollWidth; + return { + before, + after: element.scrollLeft, + clientWidth: element.clientWidth, + scrollWidth: element.scrollWidth, + }; + }); + expect(scrollContract.scrollWidth).toBeGreaterThan(scrollContract.clientWidth); + expect(scrollContract.after).toBeGreaterThan(scrollContract.before); + await expect(page.getByRole("columnheader", { name: /작업/ })).toBeVisible(); await expectVisibleButtonsFit(page, ".ad-user__actions .vg-btn", "mobile admin user actions"); } }); diff --git a/apps/web/e2e/auth-visual.spec.ts b/apps/web/e2e/auth-visual.spec.ts new file mode 100644 index 0000000..549df91 --- /dev/null +++ b/apps/web/e2e/auth-visual.spec.ts @@ -0,0 +1,117 @@ +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { expect, test, type Page } from "@playwright/test"; +import { expectNoHorizontalOverflow } from "./support"; + +const SHOT_DIR = path.join(process.cwd(), "node_modules", ".tmp", "auth-visual"); +const THEMES = ["light", "dark"] as const; +const VIEWPORTS = [ + { width: 390, height: 844, label: "mobile" }, + { width: 1280, height: 800, label: "desktop" }, +] as const; + +async function setTheme(page: Page, theme: (typeof THEMES)[number], pathName: string) { + await page.goto(pathName); + await page.evaluate((nextTheme) => { + localStorage.setItem("vignette.theme", nextTheme); + }, theme); + await page.reload(); +} + +async function expectGlassSurface(page: Page, selector: string) { + const surface = await page.locator(selector).evaluate((element) => { + const style = getComputedStyle(element); + return { + backgroundImage: style.backgroundImage, + backdropFilter: style.backdropFilter || style.webkitBackdropFilter, + borderColor: style.borderColor, + boxShadow: style.boxShadow, + }; + }); + expect(surface.backgroundImage.match(/linear-gradient/g)?.length ?? 0).toBeGreaterThanOrEqual(2); + expect(surface.backdropFilter).toContain("blur("); + expect(surface.borderColor).not.toBe("rgba(0, 0, 0, 0)"); + expect(surface.boxShadow).not.toBe("none"); +} + +test("@single-run 인증 화면이 공통 테마와 전체 viewport를 유지한다", async ({ page }) => { + await fs.mkdir(SHOT_DIR, { recursive: true }); + const loginRoomBackgrounds = new Map<(typeof THEMES)[number], string>(); + + for (const theme of THEMES) { + for (const viewport of VIEWPORTS) { + await page.setViewportSize(viewport); + await setTheme(page, theme, "/login"); + await expect(page.locator(".lg-panel")).toBeVisible(); + await expect(page.locator("html")).toHaveAttribute("data-theme", theme); + await expectNoHorizontalOverflow(page); + await expectGlassSurface(page, ".lg-panel"); + + const themeColors = await page.locator(".lg-root").evaluate((root) => { + const brand = root.querySelector(".lg-brand"); + const panel = root.querySelector(".lg-panel"); + const shellStyle = getComputedStyle(root); + if (!brand || !panel) throw new Error("login theme surfaces are missing"); + const luma = (color: string) => { + const [red = 0, green = 0, blue = 0] = color.match(/\d+(?:\.\d+)?/g)?.map(Number) ?? []; + return red * 0.2126 + green * 0.7152 + blue * 0.0722; + }; + return { + brandLuma: luma(getComputedStyle(brand).color), + panelLuma: luma(getComputedStyle(panel).color), + roomBackground: shellStyle.backgroundImage, + }; + }); + if (theme === "light") { + expect(themeColors.brandLuma).toBeLessThan(96); + expect(themeColors.panelLuma).toBeLessThan(96); + } else { + expect(themeColors.brandLuma).toBeGreaterThan(180); + expect(themeColors.panelLuma).toBeGreaterThan(180); + } + loginRoomBackgrounds.set(theme, themeColors.roomBackground); + + const canvas = await page.locator(".vg-auth-shell").evaluate((element) => { + const rect = element.getBoundingClientRect(); + return { + width: Math.round(rect.width), + minHeight: Math.round(rect.height), + viewportWidth: document.documentElement.clientWidth, + viewportHeight: window.innerHeight, + }; + }); + expect(canvas.width).toBe(canvas.viewportWidth); + expect(canvas.minHeight).toBeGreaterThanOrEqual(canvas.viewportHeight); + + await page.screenshot({ + path: path.join(SHOT_DIR, `login-${theme}-${viewport.label}.png`), + fullPage: true, + }); + } + } + expect(loginRoomBackgrounds.get("light")).not.toBe(loginRoomBackgrounds.get("dark")); + + const login = await page.request.post("/api/auth/dev-login", { + data: { + email: `auth-visual.${Date.now()}@hs.ac.kr`, + role: "learner", + display_name: "인증 화면 검증 학습자", + }, + }); + expect(login.ok(), await login.text()).toBeTruthy(); + + for (const theme of THEMES) { + for (const viewport of VIEWPORTS) { + await page.setViewportSize(viewport); + await setTheme(page, theme, "/onboarding"); + await expect(page.getByRole("heading", { name: "가입 정보를 입력합니다." })).toBeVisible(); + await expect(page.locator("html")).toHaveAttribute("data-theme", theme); + await expectNoHorizontalOverflow(page); + await expectGlassSurface(page, ".ob-shell"); + await page.screenshot({ + path: path.join(SHOT_DIR, `onboarding-${theme}-${viewport.label}.png`), + fullPage: true, + }); + } + } +}); diff --git a/apps/web/e2e/avatar-expression.spec.ts b/apps/web/e2e/avatar-expression.spec.ts index e6af696..f2012a0 100644 --- a/apps/web/e2e/avatar-expression.spec.ts +++ b/apps/web/e2e/avatar-expression.spec.ts @@ -106,7 +106,7 @@ async function mockSessionDetail( test.describe("persona avatar expression rig", () => { test.beforeEach(async ({ page }) => { - await page.route("**/api/auth/me", (route) => + await page.route(/\/(?:api\/)?auth\/me(?:\?.*)?$/, (route) => route.fulfill({ status: 200, contentType: "application/json", @@ -122,7 +122,7 @@ test.describe("persona avatar expression rig", () => { }), ); - await page.route("**/api/personas", (route) => + await page.route(/\/(?:api\/)?personas(?:\?.*)?$/, (route) => route.fulfill({ status: 200, contentType: "application/json", @@ -137,6 +137,34 @@ test.describe("persona avatar expression rig", () => { ), }), ); + + await page.route(/\/(?:api\/)?voice\/health(?:\?.*)?$/, (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ available: false, reason: "test fixture" }), + }), + ); + + await page.route(/\/(?:api\/)?sessions\/[^/]+\/live-coach(?:\?.*)?$/, (route) => { + if (route.request().method() === "GET") { + return route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + session_id: "avatar-expression-session", + events: [], + quota: { used: 0, limit: 3, remaining: 3 }, + source: "database", + }), + }); + } + return route.fulfill({ + status: 503, + contentType: "application/json", + body: JSON.stringify({ detail: "live coach is outside this avatar fixture" }), + }); + }); }); test("renders every persona with a distinct baseline expression and at least 20 expressions", async ({ @@ -158,17 +186,12 @@ test.describe("persona avatar expression rig", () => { live2dMotionFile: el.getAttribute("data-live2d-motion-file"), live2dExpressionCount: Number(el.getAttribute("data-live2d-expression-count")), renderMode: el.getAttribute("data-render-mode"), - rasterArtSet: el.querySelector(".vg-raster")?.getAttribute("data-raster-art-set"), - rasterMode: el.querySelector(".vg-raster")?.getAttribute("data-raster-mode"), - rasterLayerCount: el.querySelectorAll(".vg-raster__layer").length, - rasterBox: el.querySelector(".vg-raster")?.getBoundingClientRect().toJSON(), + rasterCount: el.querySelectorAll(".vg-raster").length, primitiveCount: el.querySelectorAll("svg path, svg ellipse, svg circle, svg line, svg rect") .length, neckBox: el.querySelector('[data-avatar-neck="true"]')?.getBoundingClientRect().toJSON(), svgBox: el.querySelector("svg")?.getBoundingClientRect().toJSON(), })); - const expectedArtSet = `${persona.code.toLowerCase()}-live2d-generated`; - expect(metrics.expressionCount, `${persona.code} expression count`).toBeGreaterThanOrEqual(20); expect(metrics.live2dSchema, `${persona.code} Live2D schema`).toBe("vignette.live2d.v1"); expect(metrics.live2dModel, `${persona.code} Live2D model`).toBe(persona.expectedModel); @@ -180,15 +203,12 @@ test.describe("persona avatar expression rig", () => { `expressions/${persona.expectedExpression}.exp3.json`, ); expect(metrics.live2dExpressionCount, `${persona.code} Live2D expressions`).toBeGreaterThanOrEqual(20); - expect(metrics.renderMode, `${persona.code} render mode`).toBe("raster"); - expect(metrics.rasterArtSet, `${persona.code} generated art set`).toBe(expectedArtSet); - expect(metrics.rasterMode, `${persona.code} raster rig mode`).toBe("psb-detailed"); - expect(metrics.rasterLayerCount, `${persona.code} raster layers`).toBeGreaterThanOrEqual(20); - expect(metrics.rasterBox?.width, `${persona.code} raster width`).toBeGreaterThan(0); - expect(metrics.rasterBox?.height, `${persona.code} raster height`).toBeGreaterThan(0); - expect(metrics.primitiveCount, `${persona.code} fallback SVG primitives`).toBe(0); - expect(metrics.neckBox, `${persona.code} SVG neck fallback`).toBeUndefined(); - expect(metrics.svgBox, `${persona.code} SVG fallback`).toBeUndefined(); + expect(metrics.renderMode, `${persona.code} render mode`).toBe("svg"); + expect(metrics.rasterCount, `${persona.code} raster rig should be disabled`).toBe(0); + expect(metrics.primitiveCount, `${persona.code} SVG primitives`).toBeGreaterThan(10); + expect(metrics.neckBox?.width, `${persona.code} SVG neck`).toBeGreaterThan(0); + expect(metrics.svgBox?.width, `${persona.code} SVG rig`).toBeGreaterThan(0); + expect(metrics.svgBox?.height, `${persona.code} SVG rig`).toBeGreaterThan(0); } }); @@ -221,8 +241,9 @@ test.describe("persona avatar expression rig", () => { const activeAvatar = page.locator(".sx-page--active .vg-avatar").first(); await expect(activeAvatar).toBeVisible(); - await expect(activeAvatar).toHaveAttribute("data-render-mode", "raster"); - await expect(activeAvatar.locator('.vg-raster[data-raster-art-set="p4-live2d-generated"]')).toBeVisible(); + await expect(activeAvatar).toHaveAttribute("data-render-mode", "svg"); + await expect(activeAvatar.locator(".vg-raster")).toHaveCount(0); + await expect(activeAvatar.locator(".vg-avatar__svg")).toBeVisible(); await expect(activeAvatar).toHaveAttribute("data-expression-count", "28"); await expect(activeAvatar).toHaveAttribute("data-live2d-expression-count", "28"); await expect(activeAvatar).toHaveAttribute("data-live2d-model", "vignette-p4-live2d"); @@ -275,8 +296,9 @@ test.describe("persona avatar expression rig", () => { await page.getByRole("button", { name: "회기 시작" }).click(); await expect(page.locator(".sx-page--active")).toBeVisible(); const activeAvatar = page.locator(".sx-page--active .vg-avatar").first(); - await expect(activeAvatar).toHaveAttribute("data-render-mode", "raster"); - await expect(activeAvatar.locator('.vg-raster[data-raster-art-set="p4-live2d-generated"]')).toBeVisible(); + await expect(activeAvatar).toHaveAttribute("data-render-mode", "svg"); + await expect(activeAvatar.locator(".vg-raster")).toHaveCount(0); + await expect(activeAvatar.locator(".vg-avatar__svg")).toBeVisible(); await expect(activeAvatar).toHaveAttribute("data-live2d-motion", "anxious"); await setLearnerUtterance(page, "조금 안정된 것 같아요."); @@ -295,18 +317,15 @@ test.describe("persona avatar expression rig", () => { await expect(page.locator(".sx-stage__now")).toContainText("온화함"); }); - test("uses the PSD v2 crying raster rig for P1 Seoyeon", async ({ page }) => { + test("uses the original SVG parameter rig for P1 Seoyeon", async ({ page }) => { await page.goto("/learn/session/P1"); const avatar = page.locator('.vg-avatar[data-persona-code="P1"]').first(); await expect(avatar).toBeVisible(); - await expect(avatar).toHaveAttribute("data-render-mode", "raster"); + await expect(avatar).toHaveAttribute("data-render-mode", "svg"); await expect(avatar).toHaveAttribute("data-affect", "sad"); - - const raster = avatar.locator('.vg-raster[data-raster-art-set="seoyeon-live2d-psd-v2"]'); - await expect(raster).toBeVisible(); - await expect(raster).toHaveAttribute("data-raster-variant", "sad"); - await expect(raster.locator('.vg-raster__layer--tear[src$="/tear-left.png"]')).toHaveCount(1); - await expect(raster.locator('.vg-raster__layer--tear[src$="/tear-right.png"]')).toHaveCount(1); + await expect(avatar.locator(".vg-raster")).toHaveCount(0); + await expect(avatar.locator(".vg-avatar__svg")).toBeVisible(); + await expect(avatar.locator('[data-avatar-neck="true"]')).toBeVisible(); }); }); diff --git a/apps/web/e2e/layout-visual-gate.spec.ts b/apps/web/e2e/layout-visual-gate.spec.ts index 96ecbf5..9e4d009 100644 --- a/apps/web/e2e/layout-visual-gate.spec.ts +++ b/apps/web/e2e/layout-visual-gate.spec.ts @@ -260,12 +260,37 @@ async function gateScreen( await page.setViewportSize({ width: vp.width, height: vp.height }); await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => r(null)))); await prepareReady(); + await page.evaluate(() => { + window.scrollTo(0, 0); + return new Promise((resolve) => requestAnimationFrame(() => resolve(null))); + }); await expect( page.locator("html"), `[${screen} @ ${vp.label}] layout gate must capture the dark UI surface`, ).toHaveAttribute("data-theme", "dark"); await expectNoHorizontalOverflow(page); + const navGeometry = await page.evaluate(() => { + const nav = document.querySelector(".vg-nav"); + const label = document.querySelector(".vg-nav__label"); + const topbar = document.querySelector(".vg-topbar"); + if (!nav || !label || !topbar || getComputedStyle(label).display === "none") return null; + const navRect = nav.getBoundingClientRect(); + return { + innerGap: Math.round(label.getBoundingClientRect().top - navRect.top), + shellGap: Math.round(navRect.top - topbar.getBoundingClientRect().bottom), + }; + }); + if (navGeometry !== null) { + expect( + navGeometry.innerGap, + `[${screen} @ ${vp.label}] GNB should start near the topbar without a dead top zone`, + ).toBeLessThanOrEqual(40); + expect( + Math.abs(navGeometry.shellGap), + `[${screen} @ ${vp.label}] GNB should begin directly below the topbar`, + ).toBeLessThanOrEqual(1); + } const report = await auditClipping(page); expect( report.horizontalOverflow, @@ -287,6 +312,14 @@ async function gateScreen( } } +/** 리뷰는 모든 폭에서 가로 탭으로 영역을 전환한다. */ +async function openReviewTabIfPresent(page: Page, label: string) { + const tab = page.locator(".sr-tabs button", { hasText: label }); + if (await tab.isVisible().catch(() => false)) { + await tab.click(); + } +} + async function expectEmptyReviewNoDeadThirdColumn(page: Page) { const report = await page.evaluate(() => { const root = document.querySelector(".sr-root--empty"); @@ -312,10 +345,7 @@ async function expectEmptyReviewNoDeadThirdColumn(page: Page) { }); expect(report.present, "empty review layout should be mounted").toBe(true); - expect( - report.columnCount, - "empty review should collapse the desktop masonry layout instead of leaving a sparse third column", - ).toBeLessThanOrEqual(2); + expect(report.columnCount, "empty review should keep the single-pane tab layout").toBe(1); } async function expectFilledReviewLearnerWorkbench(page: Page) { @@ -323,52 +353,35 @@ async function expectFilledReviewLearnerWorkbench(page: Page) { const cols = document.querySelector(".sr-cols--learner"); const transcript = document.querySelector(".sr-card--transcript"); const overview = document.querySelector(".sr-overview"); - const rubric = document.querySelector(".sr-card--rubric"); - const worksheet = document.querySelector(".sr-card--worksheet"); - const prepost = document.querySelector(".sr-card--prepost"); - if (!cols || !transcript || !overview || !rubric || !worksheet || !prepost) { + if (!cols || !transcript || !overview) { return { present: false, - viewportWidth: window.innerWidth, columnCount: 0, - overviewRight: 0, - transcriptLeft: 0, - transcriptRight: 0, - rubricLeft: 0, - worksheetLeft: 0, - worksheetRight: 0, - prepostLeft: 0, + overviewBottom: 0, + colsTop: 0, + transcriptWidth: 0, + colsWidth: 0, }; } const columnCount = getComputedStyle(cols).gridTemplateColumns.split(" ").filter(Boolean).length; + const colsRect = cols.getBoundingClientRect(); const transcriptRect = transcript.getBoundingClientRect(); const overviewRect = overview.getBoundingClientRect(); - const rubricRect = rubric.getBoundingClientRect(); - const worksheetRect = worksheet.getBoundingClientRect(); - const prepostRect = prepost.getBoundingClientRect(); return { present: true, - viewportWidth: window.innerWidth, columnCount, - overviewRight: Math.ceil(overviewRect.right), - transcriptLeft: Math.floor(transcriptRect.left), - transcriptRight: Math.ceil(transcriptRect.right), - rubricLeft: Math.floor(rubricRect.left), - worksheetLeft: Math.floor(worksheetRect.left), - worksheetRight: Math.ceil(worksheetRect.right), - prepostLeft: Math.floor(prepostRect.left), + overviewBottom: Math.ceil(overviewRect.bottom), + colsTop: Math.floor(colsRect.top), + transcriptWidth: Math.round(transcriptRect.width), + colsWidth: Math.round(colsRect.width), }; }); expect(report.present, "filled learner review layout should be mounted").toBe(true); - if (report.viewportWidth > 1180) { - expect(report.columnCount, "desktop learner review should use a 3-column workbench").toBe(3); - expect(report.overviewRight).toBeLessThanOrEqual(report.transcriptLeft); - expect(report.transcriptRight).toBeLessThanOrEqual(report.rubricLeft); - expect(report.worksheetLeft).toBeLessThan(report.transcriptLeft); - expect(report.worksheetRight).toBeLessThanOrEqual(report.prepostLeft); - } + expect(report.columnCount, "learner review should use one active tab pane").toBe(1); + expect(report.overviewBottom).toBeLessThanOrEqual(report.colsTop + 1); + expect(Math.abs(report.transcriptWidth - report.colsWidth)).toBeLessThanOrEqual(2); } async function expectSupervisorReviewNoDeadGaps(page: Page) { @@ -379,7 +392,6 @@ async function expectSupervisorReviewNoDeadGaps(page: Page) { if (!cols || !left || !right) { return { present: false, - viewportWidth: window.innerWidth, columnCount: 0, leftDisplay: "", rightDisplay: "", @@ -388,57 +400,23 @@ async function expectSupervisorReviewNoDeadGaps(page: Page) { }; } - function maxVerticalGap(container: HTMLElement) { - const intervals = Array.from(container.children) - .map((child) => child.getBoundingClientRect()) - .filter((rect) => rect.width > 0 && rect.height > 0) - .map((rect) => ({ top: rect.top, bottom: rect.bottom })) - .sort((a, b) => a.top - b.top || a.bottom - b.bottom); - if (intervals.length < 2) return 0; - - let maxGap = 0; - let currentBottom = intervals[0].bottom; - for (let index = 1; index < intervals.length; index += 1) { - const next = intervals[index]; - if (next.top <= currentBottom) { - currentBottom = Math.max(currentBottom, next.bottom); - continue; - } - const gap = next.top - currentBottom; - if (gap > maxGap) maxGap = gap; - currentBottom = next.bottom; - } - return Math.round(maxGap); - } - const colsStyle = getComputedStyle(cols); const leftStyle = getComputedStyle(left); const rightStyle = getComputedStyle(right); return { present: true, - viewportWidth: window.innerWidth, columnCount: colsStyle.gridTemplateColumns.split(" ").filter(Boolean).length, leftDisplay: leftStyle.display, rightDisplay: rightStyle.display, - maxMainGap: maxVerticalGap(left), - maxSideGap: maxVerticalGap(right), + maxMainGap: 0, + maxSideGap: 0, }; }); expect(report.present, "supervisor review layout should be mounted").toBe(true); - if (report.viewportWidth > 1180) { - expect(report.columnCount, "desktop supervisor review should use main + review rail").toBe(2); - expect(report.leftDisplay).toBe("grid"); - expect(report.rightDisplay).toBe("grid"); - expect( - report.maxMainGap, - "desktop supervisor review main column should not leave a dead vertical void", - ).toBeLessThanOrEqual(40); - expect( - report.maxSideGap, - "desktop supervisor review side rail should not leave a dead vertical void", - ).toBeLessThanOrEqual(40); - } + expect(report.columnCount, "supervisor review should use one active tab pane").toBe(1); + expect(report.leftDisplay).toBe("none"); + expect(report.rightDisplay).toBe("none"); } test.describe("layout visual gate @single-run", () => { @@ -484,10 +462,99 @@ test.describe("layout visual gate @single-run", () => { timeout: 15_000, }); await expect(page.locator(".lh-work-cluster")).toBeVisible({ timeout: 15_000 }); - await expect(page.locator(".lh-compact-list li").first()).toBeVisible({ - timeout: 15_000, + // 대시보드 탭은 모든 폭에서 상시 노출된다 — 탭별 콘텐츠를 확인 후 기본 탭으로 복귀. + const dashTabs = page.locator(".lh-tabs"); + if (await dashTabs.isVisible().catch(() => false)) { + await dashTabs.locator("button", { hasText: "기록 · 리뷰" }).click(); + await expect(page.locator(".lh-compact-list li").first()).toBeVisible({ + timeout: 15_000, + }); + await dashTabs.locator("button", { hasText: "오늘의 회기" }).click(); + await expect(page.locator(".lh-work-cluster")).toBeVisible({ timeout: 15_000 }); + } else { + await expect(page.locator(".lh-compact-list li").first()).toBeVisible({ + timeout: 15_000, + }); + } + + const shellScroll = await page.evaluate(async () => { + const nav = document.querySelector(".vg-nav"); + const main = document.querySelector(".vg-main"); + if (!nav || !main) return null; + + const navTopBefore = nav.getBoundingClientRect().top; + const windowScrollBefore = window.scrollY; + const maxScroll = Math.max(0, main.scrollHeight - main.clientHeight); + main.scrollTop = Math.min(240, maxScroll); + await new Promise((resolve) => requestAnimationFrame(() => resolve(null))); + const report = { + maxScroll, + mainScrollTop: main.scrollTop, + navTopDelta: Math.abs(nav.getBoundingClientRect().top - navTopBefore), + windowScrollDelta: Math.abs(window.scrollY - windowScrollBefore), + }; + main.scrollTop = 0; + return report; }); + expect(shellScroll, "learner shell should include nav and main scroll frame").not.toBeNull(); + expect(shellScroll!.maxScroll, "main content should own the vertical overflow").toBeGreaterThan(0); + expect(shellScroll!.mainScrollTop, "main content should scroll independently").toBeGreaterThan(0); + expect(shellScroll!.navTopDelta, "GNB should remain fixed while main content scrolls").toBeLessThanOrEqual(1); + expect(shellScroll!.windowScrollDelta, "document should not be the app scroll owner").toBeLessThanOrEqual(1); }); + + await page.setViewportSize({ width: 1200, height: 1320 }); + await expect(page.locator(".lh-tabs")).toBeVisible(); + await page.screenshot({ + path: path.join(SHOT_DIR, "learner-home__1200-reference-dark.png"), + fullPage: true, + }); + await page.getByRole("button", { name: "라이트 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); + const lightAssets = await page.evaluate(() => { + const card = document.querySelector(".lh-metric-card"); + const shellBody = document.querySelector(".vg-shell--learner-dashboard .vg-shell__body"); + const nav = document.querySelector(".vg-shell--learner-dashboard .vg-nav"); + return { + card: card ? getComputedStyle(card, "::after").backgroundImage : "", + cardSurface: card + ? { + backgroundImage: getComputedStyle(card).backgroundImage, + backdropFilter: getComputedStyle(card).backdropFilter, + boxShadow: getComputedStyle(card).boxShadow, + } + : null, + shellBody: shellBody ? getComputedStyle(shellBody).backgroundImage : "", + nav: nav ? getComputedStyle(nav).backgroundImage : "", + }; + }); + expect(lightAssets.card).toContain("card-leaf-sprig-light.png"); + expect(lightAssets.cardSurface).not.toBeNull(); + expect(lightAssets.cardSurface!.backgroundImage.match(/linear-gradient/g)?.length ?? 0).toBeGreaterThanOrEqual(2); + expect(lightAssets.cardSurface!.backdropFilter).toContain("blur("); + expect(lightAssets.cardSurface!.boxShadow).not.toBe("none"); + expect(lightAssets.shellBody).toContain("background-light-corner.png"); + expect(lightAssets.nav).toContain("background-light-sidebar.png"); + await page.screenshot({ + path: path.join(SHOT_DIR, "learner-home__1200-reference-light.png"), + fullPage: true, + }); + await page.setViewportSize({ width: 390, height: 844 }); + await page.evaluate(() => window.scrollTo(0, 0)); + const lightMobileReport = await auditClipping(page); + expect(lightMobileReport.horizontalOverflow, "[learner-home light @ 390] horizontal overflow").toBeLessThanOrEqual(1); + expect(lightMobileReport.offenders, "[learner-home light @ 390] clipped controls").toEqual([]); + await expect + .poll(() => + page.locator(".vg-shell--learner-dashboard .vg-shell__body").evaluate((element) => getComputedStyle(element).backgroundImage), + ) + .toContain("background-light-corner.png"); + await page.screenshot({ + path: path.join(SHOT_DIR, "learner-home__390-reference-light.png"), + fullPage: true, + }); + await page.getByRole("button", { name: "다크 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "dark"); }); test("session prestart stays contained across all widths", async ({ page }) => { @@ -498,7 +565,46 @@ test.describe("layout visual gate @single-run", () => { await expect(page.getByRole("button", { name: "회기 시작" })).toBeVisible({ timeout: 15_000, }); + const plan = page.locator(".sx-prestart__plan"); + await expect(plan).toContainText("시작 과업"); + await expect(plan).toContainText("선택 접근"); + await expect(plan).toContainText("이번 목표"); + await expect(plan).toContainText("운영 기준"); + + const widths = await page.evaluate(() => { + const head = document.querySelector(".sx-page--prestart .sx-head"); + const prestart = document.querySelector(".sx-page--prestart .sx-prestart"); + return { + head: head?.getBoundingClientRect().width ?? 0, + prestart: prestart?.getBoundingClientRect().width ?? 0, + }; + }); + expect(widths.head).toBeGreaterThan(0); + expect( + Math.abs(widths.head - widths.prestart), + `prestart width ${widths.prestart}px should align with head width ${widths.head}px`, + ).toBeLessThanOrEqual(1); }); + + await page.setViewportSize({ width: 1280, height: 800 }); + await page.getByRole("button", { name: "라이트 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); + await expect(page.locator(".sx-prestart")).toHaveClass(/vg-surface--panel/); + await expect(page.locator(".sx-prestart__plan")).toHaveClass(/vg-surface--inset/); + const insetSurface = await page.locator(".sx-prestart__plan").evaluate((element) => { + const style = getComputedStyle(element); + return { + borderTopWidth: style.borderTopWidth, + borderRadius: style.borderRadius, + }; + }); + expect(insetSurface).toEqual({ borderTopWidth: "0px", borderRadius: "0px" }); + await page.screenshot({ + path: path.join(SHOT_DIR, "session-prestart__1280-reference-light.png"), + fullPage: true, + }); + await page.getByRole("button", { name: "다크 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "dark"); }); test("active session keeps controls contained across all widths", async ({ page }) => { @@ -519,7 +625,9 @@ test.describe("layout visual gate @single-run", () => { await page.goto(`/learn/session/${FILLED_REVIEW_SESSION_ID}/review`); await gateScreen(page, "session-review", async () => { await expect(page.locator(".sr-overview")).toBeVisible({ timeout: 15_000 }); + await openReviewTabIfPresent(page, "워크시트"); await expect(page.getByText("사례개념화 워크시트")).toBeVisible(); + await openReviewTabIfPresent(page, "축어록"); await expectFilledReviewLearnerWorkbench(page); }); }); @@ -530,7 +638,9 @@ test.describe("layout visual gate @single-run", () => { await page.goto(`/teach/session/${TEACHER_REVIEW_SESSION_ID}/review`); await gateScreen(page, "session-review-professor", async () => { await expect(page.locator(".sr-cols--supervisor")).toBeVisible({ timeout: 15_000 }); + await openReviewTabIfPresent(page, "피드백"); await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible(); + await openReviewTabIfPresent(page, "축어록"); await expect(page.getByText("세션 트랜스크립트")).toBeVisible(); await expectSupervisorReviewNoDeadGaps(page); }); @@ -544,7 +654,9 @@ test.describe("layout visual gate @single-run", () => { await gateScreen(page, "session-review-empty", async () => { await expect(page.locator(".sr-root--empty")).toBeVisible({ timeout: 15_000 }); await expect(page.getByText("축어록 저장 후 생성")).toBeVisible(); + await openReviewTabIfPresent(page, "피드백"); await expect(page.getByText("감정 타임라인 대기")).toBeVisible(); + await openReviewTabIfPresent(page, "축어록"); await expectEmptyReviewNoDeadThirdColumn(page); }); }); @@ -618,16 +730,23 @@ test.describe("layout visual gate @single-run", () => { }); }); - test("persona studio stays contained across all widths", async ({ page }) => { + test("persona workspace stays contained across all widths", async ({ page }) => { await signInAsTeacher(page); await page.goto("/teach/personas"); - await gateScreen(page, "persona-studio", async () => { - await expect(page.locator(".ps-layout")).toBeVisible({ timeout: 15_000 }); - await expect(page.getByText("내담자 설계·검수 작업면")).toBeVisible(); - await page.getByRole("tab", { name: "프롬프트" }).click(); - const promptPreview = page.getByLabel("프롬프트 미리보기"); - await expect(promptPreview).toBeVisible(); - await expect(promptPreview.getByRole("textbox")).toHaveCount(0); + await gateScreen(page, "persona-workspace", async () => { + await expect(page.locator(".ps-overview")).toBeVisible({ timeout: 15_000 }); + await expect(page.getByRole("heading", { name: "페르소나 운영" })).toBeVisible(); + await expect(page.getByRole("navigation", { name: "페르소나 관리 영역" })).toBeVisible(); + }); + }); + + test("persona authoring steps stay contained across all widths", async ({ page }) => { + await signInAsTeacher(page); + await page.goto("/teach/personas?view=personas&mode=create&step=edit"); + await gateScreen(page, "persona-authoring", async () => { + await expect(page.locator(".ps-authoring-layout")).toBeVisible({ timeout: 15_000 }); + await expect(page.getByRole("navigation", { name: "페르소나 작성 단계" })).toBeVisible(); + await expect(page.getByRole("tab", { name: "개요" })).toBeVisible(); }); }); @@ -641,6 +760,69 @@ test.describe("layout visual gate @single-run", () => { }); }); + test("admin AI operations stays contained across all widths", async ({ page }) => { + await page.request.post("/api/auth/dev-login", { + data: { email: "admin@twentyoz.kr", role: "admin", display_name: "E2E Admin" }, + }); + await page.goto("/admin/ai"); + await gateScreen(page, "admin-ai", async () => { + await expect(page.getByRole("heading", { name: "AI 운영과 DB 계량" })).toBeVisible({ + timeout: 15_000, + }); + await expect(page.locator(".aic-grid")).toBeVisible(); + }); + }); + + test("admin user table keeps its own horizontal scroll", async ({ page }) => { + test.setTimeout(60_000); + await page.request.post("/api/auth/dev-login", { + data: { email: "admin@twentyoz.kr", role: "admin", display_name: "E2E Admin" }, + }); + await page.goto("/admin/users"); + await page.getByRole("tab", { name: "사용자 목록" }).evaluate((element) => element.click()); + await expect(page.getByRole("table")).toBeVisible({ timeout: 15_000 }); + + for (const viewport of [ + { width: 1280, height: 800, label: "desktop" }, + { width: 390, height: 844, label: "mobile" }, + ]) { + await page.setViewportSize(viewport); + await expectNoHorizontalOverflow(page); + const scrollReport = await page.locator(".ad-user-table-scroll").evaluate((element) => { + element.scrollLeft = 0; + const report = { + clientWidth: element.clientWidth, + scrollWidth: element.scrollWidth, + initialScroll: element.scrollLeft, + }; + element.scrollLeft = element.scrollWidth; + return { ...report, finalScroll: element.scrollLeft }; + }); + expect(scrollReport.scrollWidth, `[admin users @ ${viewport.label}] table min width`).toBeGreaterThan( + scrollReport.clientWidth, + ); + expect(scrollReport.finalScroll, `[admin users @ ${viewport.label}] horizontal scroll`).toBeGreaterThan( + scrollReport.initialScroll, + ); + await expect(page.getByRole("columnheader", { name: /작업/ })).toBeVisible(); + await page.locator(".ad-user-table-scroll").evaluate((element) => { + element.scrollLeft = 0; + }); + await page.screenshot({ + path: path.join(SHOT_DIR, `admin-users__${viewport.width}-reference-dark.png`), + fullPage: true, + }); + } + + await page.setViewportSize({ width: 1280, height: 800 }); + await page.getByRole("button", { name: "라이트 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); + await page.screenshot({ + path: path.join(SHOT_DIR, "admin-users__1280-reference-light.png"), + fullPage: true, + }); + }); + test("settings stays contained across all widths", async ({ page }) => { await page.request.post("/api/auth/dev-login", { data: { email: "admin@twentyoz.kr", role: "admin", display_name: "E2E Admin" }, @@ -649,5 +831,15 @@ test.describe("layout visual gate @single-run", () => { await gateScreen(page, "settings", async () => { await expect(page.locator("main").first()).toBeVisible({ timeout: 15_000 }); }); + await page.setViewportSize({ width: 1280, height: 800 }); + await page.getByRole("button", { name: "라이트 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); + await expect(page.locator(".vg-set__group").first()).toHaveAttribute("data-surface", "panel"); + await page.screenshot({ + path: path.join(SHOT_DIR, "settings__1280-reference-light.png"), + fullPage: true, + }); + await page.getByRole("button", { name: "다크 모드로" }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "dark"); }); }); diff --git a/apps/web/e2e/learner.spec.ts b/apps/web/e2e/learner.spec.ts index 668f1e8..e1cd3d1 100644 --- a/apps/web/e2e/learner.spec.ts +++ b/apps/web/e2e/learner.spec.ts @@ -228,7 +228,10 @@ test.describe("learner app shell and session launcher", () => { await expect(page).toHaveURL(new RegExp(`/learn/session/${persona.code}$`)); await expect(page.getByRole("button", { name: "회기 시작" })).toBeVisible(); - await expectNoDocumentOverflow(page); + // 프리플라이트(목표 선택 포함)는 좁은 뷰포트에서 세로 스크롤을 허용한다 — 데스크톱만 무스크롤 계약. + if ((page.viewportSize()?.width ?? 0) > 1180) { + await expectNoDocumentOverflow(page); + } await page.getByRole("button", { name: "회기 시작" }).click(); @@ -313,7 +316,9 @@ test.describe("learner app shell and session launcher", () => { }); await expect(page.getByText("페르소나 P9")).toHaveCount(0); await expect(page.getByRole("button", { name: "회기 시작" })).toBeDisabled(); - await expectNoDocumentOverflow(page); + if ((page.viewportSize()?.width ?? 0) > 1180) { + await expectNoDocumentOverflow(page); + } await expectNoHorizontalOverflow(page); }); @@ -346,7 +351,9 @@ test.describe("learner app shell and session launcher", () => { ).toBeVisible(); await expect(page.getByText("카탈로그 원본을 확인하지 못해 현재 연습에 사용할 수 없습니다.")).toBeVisible(); await expect(page.getByRole("button", { name: "회기 시작" })).toBeDisabled(); - await expectNoDocumentOverflow(page); + if ((page.viewportSize()?.width ?? 0) > 1180) { + await expectNoDocumentOverflow(page); + } await expectNoHorizontalOverflow(page); }); }); diff --git a/apps/web/e2e/public-admin-visual.spec.ts b/apps/web/e2e/public-admin-visual.spec.ts new file mode 100644 index 0000000..fd93549 --- /dev/null +++ b/apps/web/e2e/public-admin-visual.spec.ts @@ -0,0 +1,223 @@ +import { promises as fs } from "node:fs"; +import path from "node:path"; +import { expect, test } from "@playwright/test"; + +const publicApiBase = process.env.E2E_PUBLIC_API_BASE ?? "https://api-vignette.chanpaca.net"; +const SHOT_DIR = path.join(process.cwd(), "node_modules", ".tmp", "public-admin-visual"); + +interface AuthMeResponse { + email?: string; + role?: string; + admin_access?: boolean; + super_admin?: boolean; +} + +async function browserFetchJson( + page: import("@playwright/test").Page, + pathName: string, +) { + return page.evaluate( + async ({ apiBase, apiPath }) => { + const response = await fetch(`${apiBase}${apiPath}`, { + credentials: "include", + headers: { accept: "application/json" }, + }); + const text = await response.text(); + let json: unknown = null; + try { + json = text ? JSON.parse(text) : null; + } catch { + json = null; + } + return { status: response.status, ok: response.ok, text, json }; + }, + { apiBase: publicApiBase, apiPath: pathName }, + ) as Promise<{ status: number; ok: boolean; text: string; json: T | null }>; +} + +test.describe("public admin visual @public-auth", () => { + test("renders the public admin home with visible operational content", async ({ + page, + }, testInfo) => { + if (!process.env.E2E_PUBLIC_STORAGE_STATE) { + throw new Error( + "Set E2E_PUBLIC_STORAGE_STATE to a storageState file captured after Google admin login.", + ); + } + + await fs.mkdir(SHOT_DIR, { recursive: true }); + await page.setViewportSize({ width: 1440, height: 900 }); + + await page.goto("/admin", { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle", { timeout: 15_000 }).catch(() => undefined); + + const me = await browserFetchJson(page, "/auth/me"); + expect(me.status, me.text).toBe(200); + expect( + me.json?.role === "admin" || me.json?.admin_access === true || me.json?.super_admin === true, + JSON.stringify(me.json), + ).toBeTruthy(); + + await expect(page).toHaveURL(/\/admin(?:$|[?#])/); + await expect(page.locator(".ad-root")).toBeVisible({ timeout: 15_000 }); + await expect(page.locator(".vg-shell")).toBeVisible(); + await expect(page.locator(".vg-nav").getByRole("link", { name: "운영 홈" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "현재 서비스 상태" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "AI 비용" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "가용성" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "운영 티켓" })).toBeVisible(); + await expect(page.locator(".ad-kpi b")).toHaveCount(4); + await expect(page.locator("#vignette-boot-diagnostic")).toHaveCount(0); + + const visualReport = await page.evaluate(() => { + const adminRoot = document.querySelector(".ad-root"); + const main = document.querySelector("main"); + const heading = Array.from(document.querySelectorAll("h1,h2,h3")).find( + (node) => node.textContent?.includes("현재 서비스 상태"), + ); + const visibleNodes = Array.from( + document.querySelectorAll( + ".ad-root,.ad-head,.ad-kpi,.ad-panel,.ad-service,.vg-shell,.vg-nav,h1,h2,h3,p,a,button", + ), + ).filter((node) => { + const rect = node.getBoundingClientRect(); + const style = getComputedStyle(node); + return ( + rect.width > 4 && + rect.height > 4 && + style.display !== "none" && + style.visibility !== "hidden" && + style.opacity !== "0" + ); + }); + const rootRect = adminRoot?.getBoundingClientRect(); + const mainRect = main?.getBoundingClientRect(); + const headingRect = heading?.getBoundingClientRect(); + return { + url: location.href, + bodyTextLength: document.body.innerText.trim().length, + visibleNodeCount: visibleNodes.length, + adminRootRect: rootRect + ? { x: rootRect.x, y: rootRect.y, width: rootRect.width, height: rootRect.height } + : null, + mainRect: mainRect + ? { x: mainRect.x, y: mainRect.y, width: mainRect.width, height: mainRect.height } + : null, + headingRect: headingRect + ? { x: headingRect.x, y: headingRect.y, width: headingRect.width, height: headingRect.height } + : null, + horizontalOverflow: + document.documentElement.scrollWidth - document.documentElement.clientWidth, + }; + }); + + expect(visualReport.bodyTextLength, JSON.stringify(visualReport)).toBeGreaterThan(400); + expect(visualReport.visibleNodeCount, JSON.stringify(visualReport)).toBeGreaterThan(20); + expect(visualReport.adminRootRect?.width, JSON.stringify(visualReport)).toBeGreaterThan(900); + expect(visualReport.adminRootRect?.height, JSON.stringify(visualReport)).toBeGreaterThan(500); + expect(visualReport.mainRect?.width, JSON.stringify(visualReport)).toBeGreaterThan(800); + expect(visualReport.headingRect?.y, JSON.stringify(visualReport)).toBeGreaterThanOrEqual(0); + expect(visualReport.headingRect?.y, JSON.stringify(visualReport)).toBeLessThan(900); + expect(visualReport.horizontalOverflow, JSON.stringify(visualReport)).toBeLessThanOrEqual(1); + + const shotPath = path.join(SHOT_DIR, `admin-home-${testInfo.project.name}.png`); + await page.screenshot({ path: shotPath, fullPage: true }); + const shot = await fs.stat(shotPath); + expect(shot.size, `admin visual screenshot was too small: ${shotPath}`).toBeGreaterThan( + 80_000, + ); + }); + + test("renders every public admin section without a silent blank pane", async ({ page }) => { + if (!process.env.E2E_PUBLIC_STORAGE_STATE) { + throw new Error( + "Set E2E_PUBLIC_STORAGE_STATE to a storageState file captured after Google admin login.", + ); + } + + const pageErrors: string[] = []; + const consoleErrors: string[] = []; + page.on("pageerror", (error) => pageErrors.push(error.message)); + page.on("console", (message) => { + if (message.type() === "error") consoleErrors.push(message.text()); + }); + + const sections = [ + { path: "/admin", heading: "현재 서비스 상태" }, + { path: "/admin/users", heading: "가입 승인과 권한 관리" }, + { path: "/admin/access", heading: "역할, 그룹, 접근 범위" }, + { path: "/admin/tickets", heading: "사용자 문제 큐" }, + ] as const; + + for (const section of sections) { + await page.goto(section.path, { waitUntil: "domcontentloaded" }); + await expect(page.locator(".ad-root")).toBeVisible({ timeout: 15_000 }); + await expect(page.getByRole("heading", { name: section.heading })).toBeVisible(); + await expect(page.locator(".ad-diagnostic")).toHaveCount(0); + + const report = await page.evaluate(() => { + const root = document.querySelector(".ad-root"); + const rect = root?.getBoundingClientRect(); + const visibleNodes = root + ? Array.from(root.querySelectorAll("h1,h2,p,button,input,select,article,section")) + .filter((node) => { + const nodeRect = node.getBoundingClientRect(); + const style = getComputedStyle(node); + return ( + nodeRect.width > 1 && + nodeRect.height > 1 && + style.display !== "none" && + style.visibility !== "hidden" && + style.opacity !== "0" + ); + }).length + : 0; + return { + textLength: root?.innerText.trim().length ?? 0, + visibleNodes, + width: rect?.width ?? 0, + height: rect?.height ?? 0, + scrollY: window.scrollY, + }; + }); + + expect(report.textLength, JSON.stringify({ section, report })).toBeGreaterThan(120); + expect(report.visibleNodes, JSON.stringify({ section, report })).toBeGreaterThan(5); + expect(report.width, JSON.stringify({ section, report })).toBeGreaterThan(300); + expect(report.height, JSON.stringify({ section, report })).toBeGreaterThan(120); + expect(report.scrollY, JSON.stringify({ section, report })).toBe(0); + } + + expect(pageErrors, JSON.stringify(pageErrors)).toEqual([]); + expect(consoleErrors, JSON.stringify(consoleErrors)).toEqual([]); + }); + + test("returns a restored admin tab to visible content after pageshow", async ({ page }) => { + if (!process.env.E2E_PUBLIC_STORAGE_STATE) { + throw new Error( + "Set E2E_PUBLIC_STORAGE_STATE to a storageState file captured after Google admin login.", + ); + } + + await page.goto("/admin", { waitUntil: "domcontentloaded" }); + await expect(page.getByRole("heading", { name: "현재 서비스 상태" })).toBeVisible(); + await expect(page.locator(".ad-root")).toBeVisible(); + + const beforeRestore = await page.evaluate(() => { + document.documentElement.style.minHeight = "2200px"; + document.body.style.minHeight = "2200px"; + window.scrollTo(0, 900); + return window.scrollY; + }); + expect(beforeRestore).toBeGreaterThan(0); + + await page.evaluate(() => { + window.dispatchEvent(new PageTransitionEvent("pageshow", { persisted: true })); + }); + + await expect + .poll(() => page.evaluate(() => window.scrollY), { timeout: 5_000 }) + .toBe(0); + await expect(page.getByRole("heading", { name: "현재 서비스 상태" })).toBeInViewport(); + }); +}); diff --git a/apps/web/e2e/readiness.spec.ts b/apps/web/e2e/readiness.spec.ts index cc66ffe..8428079 100644 --- a/apps/web/e2e/readiness.spec.ts +++ b/apps/web/e2e/readiness.spec.ts @@ -60,10 +60,16 @@ test.describe("production readiness gates", () => { const statusRegion = page.getByRole("region", { name: "학습 현황" }); await expect(statusRegion.getByRole("button", { name: /진행 회기 0회/ })).toBeVisible(); await expect(statusRegion.getByRole("button", { name: /리뷰 대기 0건/ })).toBeVisible(); + // 중간 폭 이하에서는 기록 패널이 '기록 · 리뷰' 탭 안에 있다. + const dashTabs = page.locator(".lh-tabs"); + if (await dashTabs.isVisible().catch(() => false)) { + await dashTabs.locator("button", { hasText: "기록 · 리뷰" }).click(); + } await expect(page.getByText("최근 기록이 없습니다.")).toBeVisible(); - await expect( - page.locator(".lh-head").getByRole("button", { name: "학습 대상 선택" }), - ).toBeInViewport(); + if (await dashTabs.isVisible().catch(() => false)) { + await dashTabs.locator("button", { hasText: "오늘의 회기" }).click(); + } + await expect(page.getByRole("button", { name: "학습 대상 선택" })).toBeVisible(); await expectNoHorizontalOverflow(page); }); diff --git a/apps/web/e2e/session-mvp.spec.ts b/apps/web/e2e/session-mvp.spec.ts index a7378cd..a32fd3a 100644 --- a/apps/web/e2e/session-mvp.spec.ts +++ b/apps/web/e2e/session-mvp.spec.ts @@ -669,9 +669,14 @@ test.describe("P1 MVP core loop", () => { await page.getByRole("button", { name: "종료하고 리뷰 보기" }).click(); await expect(page).toHaveURL(new RegExp(`/learn/session/${sessionId}/review$`)); - await expect(page.getByText("내담자가 남긴 것")).toBeVisible(); - await expect(page.locator(".sr-feedback").getByText(clientReply)).toBeVisible(); await expect(page.getByText("평가 AI가 저장된 축어록을 분석했습니다.")).toBeVisible(); + // ≤1180 폭에서는 리뷰가 가로 탭 — 마지막 내담자 반응은 피드백 탭에 있다. + const feedbackTab = page.locator(".sr-tabs button", { hasText: "피드백" }); + if (await feedbackTab.isVisible().catch(() => false)) { + await feedbackTab.click(); + } + await expect(page.getByText("마지막 내담자 반응")).toBeVisible(); + await expect(page.locator(".sr-feedback").getByText(clientReply)).toBeVisible(); }); test("shows AI tutor coaching in coached mode after a completed turn", async ({ page }) => { diff --git a/apps/web/e2e/session-persistence.spec.ts b/apps/web/e2e/session-persistence.spec.ts index 57aa1cf..db3437f 100644 --- a/apps/web/e2e/session-persistence.spec.ts +++ b/apps/web/e2e/session-persistence.spec.ts @@ -529,6 +529,13 @@ async function evaluationState(page: Page, sessionId: string) { return `pending:${body.status ?? "none"}:${body.durable ? "durable" : "cache"}:${loop}`; } +/** 리뷰는 모든 폭에서 가로 탭으로 영역을 전환한다. */ +async function openReviewTab(page: Page, label: "축어록" | "피드백" | "워크시트") { + const tabs = page.locator(".sr-tabs"); + await tabs.waitFor({ state: "attached", timeout: 20_000 }); + await tabs.locator("button", { hasText: label }).click(); +} + test.describe("session persistence", () => { test("persists selected CBT theory mode from session UI into DB-backed detail @single-run", async ({ page, @@ -781,7 +788,7 @@ test.describe("session persistence", () => { }); test("persists AI tutor coaching history through reload @single-run", async ({ page }) => { - test.setTimeout(150_000); + test.setTimeout(240_000); const healthResponse = await page.request.get("/api/health"); await expectResponseOk(healthResponse); @@ -890,7 +897,7 @@ test.describe("session persistence", () => { }); test("persists voice nonverbal metadata into DB-backed review @single-run", async ({ page }) => { - test.setTimeout(120_000); + test.setTimeout(240_000); const healthResponse = await page.request.get("/api/health"); await expectResponseOk(healthResponse); @@ -937,7 +944,9 @@ test.describe("session persistence", () => { const timeout = window.setTimeout(() => { ws.close(); finish(-1); - }, 60_000); + // dev 엔진(Claude CLI 게이트웨이) 음성 턴이 90초를 넘기도 한다 — 게이트웨이 + // GENERATE_TURN_TIMEOUT_SECONDS(300초)와 정합하게 150초까지 기다린다. + }, 150_000); ws.onopen = () => { ws.send(JSON.stringify({ type: "audio_start", format: "webm" })); @@ -1085,7 +1094,7 @@ test.describe("session persistence", () => { reply: events.some((event) => event.type === "reply"), errors: events.filter((event) => event.type === "error" || event.type === "degraded").length, }; - }, { timeout: 60_000 }) + }, { timeout: 90_000 }) .toEqual({ transcript: true, reply: true, errors: 0 }); const reviewResponse = await page.request.get(`/api/sessions/${sessionId}/review`); @@ -1166,6 +1175,7 @@ test.describe("session persistence", () => { ).toBeGreaterThanOrEqual(2); await page.goto(`/learn/session/${sessionId}/review`); + await openReviewTab(page, "워크시트"); const worksheetCard = page.locator(".sr-card--worksheet"); await expect(worksheetCard).toBeVisible({ timeout: 20_000 }); await expect(worksheetCard).toContainText("축어록 기반 자동 초안"); @@ -1188,10 +1198,13 @@ test.describe("session persistence", () => { await signInAsTeacher(page); await page.goto(`/teach/session/${sessionId}/review`); + await openReviewTab(page, "워크시트"); await expect(worksheetCard).toBeVisible({ timeout: 20_000 }); await expect(worksheetCard).toContainText("학습자 저장본 읽기 전용"); await expect(worksheetCard.locator(".sr-ws-input").first()).toHaveValue(savedWorksheetValue); + // 워크시트 검수 메모·수정요청은 교수자 검토 카드(피드백 탭)에 있다. + await openReviewTab(page, "피드백"); const teacherWorksheetNote = `보호요인 보강 요청 ${Date.now()}`; await page.getByLabel("워크시트 검수 메모").fill(teacherWorksheetNote); await page.getByRole("button", { name: "수정요청" }).click(); @@ -1226,6 +1239,7 @@ test.describe("session persistence", () => { const sessionId = await createEndedSessionWithoutTurn(page); await page.goto(`/learn/session/${sessionId}/review`); + await openReviewTab(page, "피드백"); await expect(page.locator(".sr-card--prepost")).toBeVisible({ timeout: 15_000 }); await expect(page.locator(".sr-prepost__actions")).toContainText("저장된 값 기준"); @@ -1272,6 +1286,7 @@ test.describe("session persistence", () => { expect(byKey.get("training_satisfaction:post")).toBe(Number(targetValues[5])); await page.reload(); + await openReviewTab(page, "피드백"); await expect(page.locator(".sr-card--prepost")).toBeVisible({ timeout: 15_000 }); await expect(page.getByText("3/3 쌍")).toBeVisible(); for (let index = 0; index < targetValues.length; index += 1) { diff --git a/apps/web/e2e/session-review.spec.ts b/apps/web/e2e/session-review.spec.ts index 18de1e5..6d5c1af 100644 --- a/apps/web/e2e/session-review.spec.ts +++ b/apps/web/e2e/session-review.spec.ts @@ -97,63 +97,79 @@ async function createEndedSession(page: Page) { return session.session_id; } +/** 리뷰는 모든 폭에서 가로 탭으로 영역을 전환한다. */ +async function openReviewTab(page: Page, label: "축어록" | "피드백" | "워크시트") { + // 탭 바는 리뷰 레이아웃이 로드된 뒤에만 DOM에 존재한다 — attach를 먼저 기다린다. + const tabs = page.locator(".sr-tabs"); + await tabs.waitFor({ state: "attached", timeout: 10_000 }).catch(() => {}); + const tab = tabs.locator("button", { hasText: label }); + if (await tab.isVisible().catch(() => false)) { + await tab.click(); + } +} + async function expectReviewRegionsReachable(page: Page) { await expect(page.locator(".sr-overview")).toBeVisible(); + await expect(page.locator(".sr-tabs")).toBeVisible(); + + // 피드백 탭: 인사이트 흐름 + 평가 레일이 함께 도달 가능해야 한다. + await openReviewTab(page, "피드백"); await expect(page.locator(".sr-feedback")).toBeVisible(); await expect(page.locator(".sr-card--chart")).toBeVisible(); await expect(page.locator(".sr-card--flow")).toBeVisible(); await expect(page.locator(".sr-card--rubric")).toBeVisible(); - await expect(page.locator(".sr-card--transcript")).toBeVisible(); await expect(page.locator(".sr-card--prepost")).toBeVisible(); - const grid = await page.locator(".sr-cols").evaluate((el) => { + const insightsGrid = await page.locator(".sr-cols").evaluate((el) => { const style = window.getComputedStyle(el); return { display: style.display, areas: style.gridTemplateAreas, + columns: style.gridTemplateColumns.split(" ").filter(Boolean).length, }; }); - expect(grid.display).toBe("grid"); - expect(grid.areas).toContain("overview"); - expect(grid.areas).toContain("feedback"); - expect(grid.areas).toContain("transcript"); - expect(grid.areas).not.toContain("teacher"); + expect(insightsGrid.display).toBe("grid"); + expect(insightsGrid.areas).toContain("insight"); + expect(insightsGrid.areas).toContain("feedback"); + expect(insightsGrid.areas).not.toContain("teacher"); + // 전 폭 탭 계약: 활성 탭 하나가 전폭 단일 컬럼을 쓴다(인사이트 위 / 평가 아래). + expect(insightsGrid.columns).toBe(1); + const stackGeometry = await page.locator(".sr-cols").evaluate((el) => { + const insight = el.querySelector(".sr-left")?.getBoundingClientRect(); + const feedback = el.querySelector(".sr-right")?.getBoundingClientRect(); + return { + insightBottom: insight ? Math.ceil(insight.bottom) : 0, + feedbackTop: feedback ? Math.floor(feedback.top) : 0, + }; + }); + expect(stackGeometry.insightBottom).toBeLessThanOrEqual(stackGeometry.feedbackTop + 1); + + // 워크시트 탭. + await openReviewTab(page, "워크시트"); + await expect(page.locator(".sr-card--worksheet")).toBeVisible(); + + // 축어록 탭(기본)으로 복귀 — 축어록은 전폭 단일 컬럼. + await openReviewTab(page, "축어록"); + await expect(page.locator(".sr-card--transcript")).toBeVisible(); + const transcriptAreas = await page + .locator(".sr-cols") + .evaluate((el) => window.getComputedStyle(el).gridTemplateAreas); + expect(transcriptAreas).toContain("transcript"); await expect(page.locator(".sr-overview")).toBeInViewport(); await page.locator(".sr-card--transcript").scrollIntoViewIfNeeded(); await expect(page.locator(".sr-card--transcript")).toBeInViewport(); - const railGeometry = await page.locator(".sr-cols").evaluate((gridEl) => { - const root = gridEl.closest(".sr-root"); - const overview = gridEl.querySelector(".sr-overview")?.getBoundingClientRect(); - const transcript = gridEl.querySelector(".sr-card--transcript")?.getBoundingClientRect(); - const rubric = gridEl.querySelector(".sr-card--rubric")?.getBoundingClientRect(); - const prepost = gridEl.querySelector(".sr-card--prepost")?.getBoundingClientRect(); - const worksheet = gridEl.querySelector(".sr-card--worksheet")?.getBoundingClientRect(); - const columns = window - .getComputedStyle(gridEl) - .gridTemplateColumns.split(" ") - .filter(Boolean).length; + // 리뷰 요약은 본문 그리드 위의 전폭 밴드다(도킹/스크롤 부유 금지). + const band = await page.locator(".sr-root").evaluate((rootEl) => { + const overview = rootEl.querySelector(".sr-overview")?.getBoundingClientRect(); + const gridRect = rootEl.querySelector(".sr-cols")?.getBoundingClientRect(); return { - isEmpty: root?.classList.contains("sr-root--empty") ?? false, - columns, - overviewLeft: overview ? Math.floor(overview.left) : 0, - overviewRight: overview ? Math.ceil(overview.right) : 0, - transcriptLeft: transcript ? Math.floor(transcript.left) : 0, - transcriptRight: transcript ? Math.ceil(transcript.right) : 0, - rubricLeft: rubric ? Math.floor(rubric.left) : 0, - prepostLeft: prepost ? Math.floor(prepost.left) : 0, - worksheetLeft: worksheet ? Math.floor(worksheet.left) : 0, + overviewBottom: overview ? Math.ceil(overview.bottom) : 0, + gridTop: gridRect ? Math.floor(gridRect.top) : 0, }; }); - if (!railGeometry.isEmpty && railGeometry.columns === 2) { - expect(railGeometry.transcriptRight).toBeLessThanOrEqual(railGeometry.overviewLeft); - expect(railGeometry.rubricLeft).toBeGreaterThanOrEqual(railGeometry.overviewLeft); - expect(railGeometry.prepostLeft).toBeGreaterThanOrEqual(railGeometry.overviewLeft); - expect(railGeometry.worksheetLeft).toBeGreaterThanOrEqual(railGeometry.overviewLeft); - } else if (railGeometry.columns >= 3) { - expect(railGeometry.overviewRight).toBeLessThanOrEqual(railGeometry.transcriptLeft); - } + expect(band.overviewBottom).toBeLessThanOrEqual(band.gridTop + 1); } test.describe("session review", () => { @@ -186,6 +202,7 @@ test.describe("session review", () => { await page.goto(`/learn/session/${sessionId}/review`); await expect(page).toHaveURL(new RegExp(`/teach/session/${sessionId}/review$`)); + await openReviewTab(page, "피드백"); await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible(); expect(reviewAttempts).toBeGreaterThanOrEqual(2); }); @@ -221,6 +238,7 @@ test.describe("session review", () => { await page.goto(`/learn/session/${sessionId}/review`); await expect(page).toHaveURL(new RegExp(`/teach/session/${sessionId}/review$`)); + await openReviewTab(page, "피드백"); await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible(); await expect(page.locator(".sr-card--prepost")).toHaveCount(0); expect(reviewAttempts).toBeGreaterThanOrEqual(2); @@ -233,6 +251,7 @@ test.describe("session review", () => { await page.goto(`/teach/session/${sessionId}/review`); + await openReviewTab(page, "피드백"); await expect(page.getByText("AI 평가 범위", { exact: true })).toBeVisible(); await expect(page.getByText("현재 구현", { exact: true })).toBeVisible(); await expect(page.getByText(/fast-loop.*deep-loop/)).toBeVisible(); @@ -348,6 +367,7 @@ test.describe("session review", () => { await page.goto(`/teach/session/${sessionId}/review`); + await openReviewTab(page, "피드백"); await expect(page.getByText("평가 대기", { exact: true })).toBeVisible(); await expect(page.getByText("평가 AI가 저장된 축어록을 분석 중입니다.")).toBeVisible(); await expect(page.getByRole("button", { name: "AI 평가 재시도" })).toHaveCount(0); @@ -413,6 +433,7 @@ test.describe("session review", () => { }); await page.goto(`/teach/session/${sessionId}/review`); + await openReviewTab(page, "피드백"); await expect(page.getByText("평가 실패")).toBeVisible(); await expect( page.getByText("저장된 축어록은 확인했지만 deep-loop 평가 AI 산출물을 표시하지 못했습니다. AI 평가 재시도가 필요합니다."), @@ -507,6 +528,7 @@ test.describe("session review", () => { await expect(page).toHaveURL(new RegExp(`/learn/session/${sessionId}/review$`)); await expect(page.getByText("축어록 없음")).toBeVisible(); + await openReviewTab(page, "피드백"); await expect(page.getByText("감정 타임라인 대기")).toBeVisible(); await expect(page.getByText("개선점 대기")).toBeVisible(); await expect(page.getByRole("button", { name: "오디오 다시 듣기" })).toBeDisabled(); @@ -547,9 +569,12 @@ test.describe("session review", () => { await expect(page).toHaveURL(new RegExp(`/learn/session/${FILLED_REVIEW_SESSION_ID}/review$`)); await expect(page.getByText("관계 신호가 안정적으로 회복된 회기")).toBeVisible(); + await expect(page.getByText("세션 트랜스크립트")).toBeVisible(); + await expect(page.getByText("감정 반영").first()).toBeVisible(); + await expect(page.getByText("AI 슈퍼바이저").first()).toBeVisible(); + await openReviewTab(page, "피드백"); await expect(page.getByText("감정 밸런스 타임라인")).toBeVisible(); await expect(page.getByText("회기 흐름")).toBeVisible(); - await expect(page.getByText("세션 트랜스크립트")).toBeVisible(); await expect(page.getByText("기법 사용 분포")).toBeVisible(); await expect(page.getByText("파일럿 증거 원장")).toBeVisible(); await expect(page.getByText("1/3 쌍")).toBeVisible(); @@ -569,13 +594,12 @@ test.describe("session review", () => { "수련만족도 사후 점수", ]); await expect(page.getByText("효과 판정과 통계 검정은 평가설계 확정 후 별도 산출합니다.")).toBeVisible(); - await expect(page.getByText("사례개념화 워크시트")).toBeVisible(); - await expect(page.getByText("감정 반영").first()).toBeVisible(); - await expect(page.getByText("AI 슈퍼바이저").first()).toBeVisible(); await expect(page.getByText("다음에 시도할 문장")).toBeVisible(); await expect(page.getByText("방어를 낮춘 감정 반영")).toBeVisible(); await expect(page.getByText("개입 전 준비도 확인")).toBeVisible(); await expect(page.locator(".sr-chart__svg")).toBeVisible(); + await openReviewTab(page, "워크시트"); + await expect(page.getByText("사례개념화 워크시트")).toBeVisible(); await expect(page.locator(".sr-ws-input").first()).toBeVisible(); await expect(page.getByRole("button", { name: "오디오 다시 듣기" })).toBeEnabled(); await expect(page.getByRole("button", { name: "PDF 내보내기" })).toBeEnabled(); @@ -624,6 +648,7 @@ test.describe("session review", () => { "저는 익명 내담자이고 소속 기관에서 상담받고 있어요.", ); await expect(transcript).not.toContainText("[NAME]"); + await openReviewTab(page, "워크시트"); await expect(page.locator(".sr-ws-evidence").filter({ hasText: "[NAME]" })).toBeVisible(); }); @@ -633,6 +658,8 @@ test.describe("session review", () => { await routePrepostMeasures(page); await page.goto(`/learn/session/${FILLED_REVIEW_SESSION_ID}/review`); + await expect(page.locator(".sr-overview")).toBeVisible({ timeout: 15_000 }); + await openReviewTab(page, "피드백"); await expect(page.locator(".sr-card--prepost")).toBeVisible({ timeout: 15_000 }); const prepostInputs = page.locator(".sr-card--prepost input"); diff --git a/apps/web/e2e/teacher.spec.ts b/apps/web/e2e/teacher.spec.ts index 82c666f..efd8b3c 100644 --- a/apps/web/e2e/teacher.spec.ts +++ b/apps/web/e2e/teacher.spec.ts @@ -184,6 +184,13 @@ function teacherReviewResponse(sessionId: string) { }; } +/** 리뷰는 모든 폭에서 가로 탭으로 영역을 전환한다. */ +async function openReviewTab(page: Page, label: "축어록" | "피드백" | "워크시트") { + const tabs = page.locator(".sr-tabs"); + await tabs.waitFor({ state: "attached", timeout: 20_000 }); + await tabs.locator("button", { hasText: label }).click(); +} + test.describe("teacher console", () => { test("lets a teacher approve a pending persona review from the console @single-run", async ({ page, @@ -317,7 +324,7 @@ test.describe("teacher console", () => { }); }); - await page.goto("/teach/personas"); + await page.goto("/teach/personas?view=personas"); const row = page.locator('[data-approved-persona-row="true"]').filter({ hasText: "P1" }); await expect(row).toBeVisible(); await row.getByRole("button", { name: "수정" }).click(); @@ -373,7 +380,7 @@ test.describe("teacher console", () => { }); }); - await page.goto("/teach/personas"); + await page.goto("/teach/personas?view=personas&mode=create&step=edit"); await page.getByLabel("표시 이름").fill("항목형 페르소나"); await page.getByRole("tab", { name: "임상" }).click(); @@ -387,7 +394,7 @@ test.describe("teacher console", () => { const forbidden = page.locator(".ps-list-field").filter({ hasText: "상담자 금기" }); await forbidden.getByRole("textbox", { name: "상담자 금기 1", exact: true }).fill("네가 예민한 거라고 단정"); - await page.getByRole("button", { name: "초안 저장" }).click(); + await page.getByRole("button", { name: "중간 저장" }).click(); await expect(page.getByText("P1 v1 초안을 저장했습니다.")).toBeVisible(); expect(savedPayload).toBeTruthy(); @@ -408,7 +415,7 @@ test.describe("teacher console", () => { return route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([]) }); }); - await page.goto("/teach/personas"); + await page.goto("/teach/personas?view=personas&mode=create&step=edit"); await page.getByLabel("표시 이름").fill("프롬프트 검토 페르소나"); await page.getByRole("tab", { name: "임상" }).click(); const automaticThoughts = page.locator(".ps-list-field").filter({ hasText: "자동사고" }); @@ -484,7 +491,7 @@ test.describe("teacher console", () => { }); }); - await page.goto("/teach/personas"); + await page.goto("/teach/personas?view=personas"); const row = page.locator('[data-approved-persona-row="true"]').filter({ hasText: "P7" }); await expect(row).toBeVisible(); await expectVisibleButtonsFit(page, ".ps-approved-row__actions .vg-btn", "approved persona actions"); @@ -492,11 +499,104 @@ test.describe("teacher console", () => { await row.getByRole("button", { name: "삭제" }).click(); await expect(page.getByText("P7 페르소나를 보관 처리했습니다.")).toBeVisible(); - await expect(page.getByText("공개 목록 없음")).toBeVisible(); + await expect(page.getByText("조건에 맞는 페르소나 없음")).toBeVisible(); expect(archived).toBeTruthy(); await expectNoHorizontalOverflow(page); }); + test("navigates persona dashboard, catalog, table, detail, and review queue @single-run", async ({ page }) => { + const approvedPersona = { + persona_id: "00000000-0000-0000-0000-000000000721", + code: "P1", + version: 3, + status: "approved", + display_name: "서연(가명) · 고2 · 우울/자살사고", + difficulty: "hard", + theory_target: ["humanistic"], + demographics: { age_band: "F-teen" }, + presenting_summary: "최근 무기력과 관계 단절을 호소합니다.", + source: "database", + degraded: false, + voice_preset: null, + }; + const reviewPersona = { + persona_id: "00000000-0000-0000-0000-000000000722", + code: "P12", + version: 1, + status: "review", + display_name: "직장 적응 훈련 페르소나", + difficulty: "moderate", + theory_target: ["cbt"], + source_provenance: "교수자 작성 초안", + is_synthetic: true, + created_at: "2026-07-15T08:00:00Z", + approved_at: null, + }; + await signInAsTeacher(page); + await page.route("**/api/personas", (route) => { + if (route.request().method() !== "GET") return route.fallback(); + return route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([approvedPersona]) }); + }); + await page.route("**/api/personas/review", (route) => + route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([reviewPersona]) }), + ); + await page.route("**/api/teacher/dashboard", (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + source: "database", + cohort_label: "E2E cohort", + total_learners: 1, + active_sessions: 1, + ended_sessions: 1, + learner_growth: [{ + learner_id: "00000000-0000-0000-0000-000000000723", + learner_label: "학습자 A", + sessions: 2, + ended_sessions: 1, + latest_at: "2026-07-15T08:20:00Z", + avg_score: 0.75, + avg_rapport: 0.4, + trend: "up", + points: [{ session_id: "persona-session-1", session_no: 1, persona_code: "P1", stage: "탐색", started_at: "2026-07-15T08:00:00Z", ended_at: "2026-07-15T08:20:00Z", score: 0.75, rapport: 0.4, technique_count: 3, watch_count: 0 }], + }], + safety_alerts: [], + pending_reviews: [], + recent_sessions: [{ session_id: "persona-session-2", learner_id: "00000000-0000-0000-0000-000000000723", learner_label: "학습자 A", persona_code: "P1", persona_name: approvedPersona.display_name, session_no: 2, status: "active", stage: "탐색", turn_count: 4, learner_turn_count: 2, client_turn_count: 2, started_at: "2026-07-15T08:30:00Z", ended_at: null }], + message: "페르소나 운영 fixture", + }), + }), + ); + + await page.goto("/teach/personas"); + await expect(page.getByRole("heading", { name: "페르소나 운영" })).toBeVisible(); + await expect(page.getByText("상담 수행 점수")).toBeVisible(); + + await page.getByRole("button", { name: /카탈로그/ }).first().click(); + await expect(page.getByRole("heading", { name: "페르소나 카탈로그" })).toBeVisible(); + await expect(page.getByText("학습자가 회기를 시작할 때 선택하는 승인본 모음입니다.")).toBeVisible(); + + await page.getByRole("button", { name: /페르소나/ }).first().click(); + await expect(page.getByRole("button", { name: "새로운 페르소나 만들기" })).toBeVisible(); + const approvedRow = page.locator('[data-approved-persona-row="true"]').filter({ hasText: "P1" }); + await expect(approvedRow).toBeVisible(); + await approvedRow.getByRole("button", { name: "상세" }).click(); + await expect(page.getByRole("tab", { name: "소개·정보" })).toBeVisible(); + await page.getByRole("tab", { name: "학습 현황" }).click(); + await expect(page.getByText("75%").first()).toBeVisible(); + await page.getByRole("button", { name: "목록으로" }).click(); + await page.getByRole("tab", { name: /검수 현황/ }).click(); + await expect(page.getByText("직장 적응 훈련 페르소나")).toBeVisible(); + await page.getByRole("button", { name: "새로운 페르소나 만들기" }).click(); + await expect(page.getByRole("navigation", { name: "페르소나 작성 단계" })).toBeVisible(); + await page.getByRole("button", { name: /설정 임상·회기·말투 조정/ }).click(); + await expect(page.getByRole("tab", { name: "개요" })).toBeVisible(); + await page.getByRole("button", { name: "이전" }).click(); + await expect(page).toHaveURL(/step=generate/); + await expectNoHorizontalOverflow(page); + }); + test("renders real server sessions from server-owned rows", async ({ page }) => { const sessionId = await createEndedLearnerSession(page); await signInAsTeacher(page); @@ -768,9 +868,11 @@ test.describe("teacher console", () => { await expect(page).toHaveURL(new RegExp(`/teach/session/${sessionId}/review$`)); await expect(page.getByText("교수자 검토 화면", { exact: true })).toBeVisible(); - await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible(); + await openReviewTab(page, "워크시트"); await expect(page.getByText("축어록 자동 초안 읽기 전용")).toBeVisible(); await expect(page.getByText("검토 전용")).toBeVisible(); + await openReviewTab(page, "피드백"); + await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible(); await page.getByLabel("검토 메모").fill("다음 회기에서 감정 반영을 먼저 확인"); await page.getByRole("button", { name: "검토 완료" }).click(); await expect(page.getByText("완료 시각 2026-06-27T10:00:00Z")).toBeVisible(); @@ -953,6 +1055,7 @@ test.describe("teacher console", () => { await expect(page).toHaveURL(new RegExp(`/teach/session/${activeSession.session_id}/review$`)); await expect(page.getByText("교수자 검토 화면", { exact: true })).toBeVisible(); await expect(page.getByText("진행 중", { exact: true })).toBeVisible(); + await openReviewTab(page, "피드백"); await expect(page.getByText("평가 대기", { exact: true })).toBeVisible(); await expectNoHorizontalOverflow(page); }); @@ -1022,6 +1125,7 @@ test.describe("teacher console", () => { await expect(page).toHaveURL(new RegExp(`/teach/session/${sessionId}/review$`)); await expect(page.getByText("교수자 검토 화면", { exact: true })).toBeVisible(); await expect(page.getByText("진행 중", { exact: true })).toBeVisible(); + await openReviewTab(page, "워크시트"); await expect(page.getByText("검토 전용")).toBeVisible(); await expect(page.getByRole("button", { name: /^저장/ })).toHaveCount(0); await expectNoHorizontalOverflow(page); diff --git a/apps/web/knip.json b/apps/web/knip.json new file mode 100644 index 0000000..c73e03f --- /dev/null +++ b/apps/web/knip.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://unpkg.com/knip@6/schema.json", + "entry": [ + "functions/**/*.js", + "public/worklets/**/*.js", + "scripts/*.mjs", + "e2e/**/*.spec.ts" + ], + "project": [ + "src/**/*.{ts,tsx}", + "functions/**/*.js", + "public/worklets/**/*.js", + "scripts/*.mjs", + "e2e/**/*.ts" + ], + "ignoreFiles": ["src/components/avatar/RasterBust.tsx"], + "ignoreDependencies": ["openapi-typescript"], + "ignoreBinaries": ["taskkill"] +} diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index 72efa54..bff0273 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -8,6 +8,7 @@ "name": "@vignette/web", "version": "0.1.0", "dependencies": { + "@tanstack/react-table": "^8.21.3", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^6.30.1" @@ -17,8 +18,10 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", + "esbuild": "^0.25.12", + "jscpd": "5.0.12", + "knip": "^6.26.0", "openapi-typescript": "^7.13.0", - "puppeteer-core": "^25.2.1", "typescript": "^5.8.3", "vite": "^6.3.5" } @@ -305,6 +308,40 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -797,6 +834,719 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-parser/binding-android-arm-eabi": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-musl": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-openharmony-arm64": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-ia32-msvc": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxc-resolver/binding-android-arm-eabi": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-android-arm64": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-arm64": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-freebsd-x64": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-musl": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-musl": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-openharmony-arm64": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-x64-msvc": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@playwright/test": { "version": "1.61.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", @@ -813,31 +1563,6 @@ "node": ">=18" } }, - "node_modules/@puppeteer/browsers": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.0.5.tgz", - "integrity": "sha512-xYXNuEQmHNIPWWcbL/skf2KF7seyp7c1xmKFRk3wmdFx7VwBsKVrtOLKs8ecaezsKPsWeF1YsgwIiElAscaryA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "modern-tar": "^0.7.6", - "yargs": "^18.0.0" - }, - "bin": { - "browsers": "lib/main-cli.js" - }, - "engines": { - "node": ">=22.12.0" - }, - "peerDependencies": { - "proxy-agent": ">=8.0.1" - }, - "peerDependenciesMeta": { - "proxy-agent": { - "optional": true - } - } - }, "node_modules/@redocly/ajv": { "version": "8.11.2", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", @@ -1250,6 +1975,50 @@ "win32" ] }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1363,32 +2132,6 @@ "node": ">=6" } }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1488,38 +2231,6 @@ "dev": true, "license": "MIT" }, - "node_modules/chromium-bidi": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-16.0.1.tgz", - "integrity": "sha512-J63PGu/9PpeCwLIcKYyzWP6yaVL5pxuBc0shlYCYM8BaAkmlwiQboXO1iNbOgSDbVklEyYFfNEcHD8oOAWacUA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "engines": { - "node": ">=20.19.0 <22.0.0 || >=22.12.0" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", @@ -1559,13 +2270,6 @@ } } }, - "node_modules/devtools-protocol": { - "version": "0.0.1638949", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1638949.tgz", - "integrity": "sha512-mXwg4Fqnv0WR4iuAT/gYUmctNkjILwXFHyZ+m7Ty1dfr0ezZt2U3gnrrJTfRobJTHoXf+IbuFvFITzLrLFjwJA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/electron-to-chromium": { "version": "1.5.378", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", @@ -1573,13 +2277,6 @@ "dev": true, "license": "ISC" }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -1639,6 +2336,16 @@ "dev": true, "license": "MIT" }, + "node_modules/fd-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-up-path": "^4.0.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1657,6 +2364,22 @@ } } }, + "node_modules/formatly": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", + "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fd-package-json": "^2.0.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1682,27 +2405,17 @@ "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "resolve-pkg-maps": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, "node_modules/https-proxy-agent": { @@ -1732,6 +2445,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -1772,6 +2495,120 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jscpd": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-5.0.12.tgz", + "integrity": "sha512-87dC+akj2mCywlt8p3xnRlDg0B55u4GDbfE9cuwOOeIxbEuWuIoNqGoYi65A0516aJ4EzAjGwBj1Qb/Ahc8WAQ==", + "dev": true, + "license": "MIT", + "bin": { + "jscpd": "run-jscpd.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "jscpd-darwin-arm64": "5.0.12", + "jscpd-darwin-x64": "5.0.12", + "jscpd-linux-arm64-gnu": "5.0.12", + "jscpd-linux-x64-gnu": "5.0.12", + "jscpd-linux-x64-musl": "5.0.12", + "jscpd-windows-x64-msvc": "5.0.12" + } + }, + "node_modules/jscpd-darwin-arm64": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd-darwin-arm64/-/jscpd-darwin-arm64-5.0.12.tgz", + "integrity": "sha512-CQCDYhQY9yOGGeauzkRGYW/QtXurPCjfDkEhUoM/M5UdmpzxfG3i9mnNsaheJ0RdFRf73mL7JaLVRP8U2l5/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/jscpd-darwin-x64": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd-darwin-x64/-/jscpd-darwin-x64-5.0.12.tgz", + "integrity": "sha512-7XNCDfChP9fPkIWLepd0zGnTov7/5mR7rJ5Utc1MdWFdgkN//qz6FAJ1FhQA0E/qjFedy9XTucfUCgLybSBsjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/jscpd-linux-arm64-gnu": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd-linux-arm64-gnu/-/jscpd-linux-arm64-gnu-5.0.12.tgz", + "integrity": "sha512-i/usHD0/8twzb2Qo4vFWcnuAD4IDLS6K/mTXwYy1CiJZDw4gmjfH45jtjdKUyoutTAiNs+ZWZgx1gIRljUbFuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jscpd-linux-x64-gnu": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd-linux-x64-gnu/-/jscpd-linux-x64-gnu-5.0.12.tgz", + "integrity": "sha512-TqaeSTaGawcqyXSrQvYJFtLRn4aQeHgphGGsq2ZtVAg+lPeuMmh81c3bl0yi2dL3gDX1RGBQKBVul1XQknOuoA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jscpd-linux-x64-musl": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd-linux-x64-musl/-/jscpd-linux-x64-musl-5.0.12.tgz", + "integrity": "sha512-WelugY1/rdoo0Y0sqJ2XQOIvyIZTfRe+vP3MJe4XvEUwPF0v+9SQoS34FnfblRhsddVixyNkgl7x/sHid9UMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jscpd-windows-x64-msvc": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/jscpd-windows-x64-msvc/-/jscpd-windows-x64-msvc-5.0.12.tgz", + "integrity": "sha512-01x1klKjvfzI6Of5tI9u72x7TIQZQLLG6kMdsEPJKl/BXbskdknrfCepeTXRBbdFPKs25jfcd0klZ4OdDvww5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -1805,6 +2642,55 @@ "node": ">=6" } }, + "node_modules/knip": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.26.0.tgz", + "integrity": "sha512-e9eELEEpBpGTd4H4HB7818/DYj9dMzMyUqAddfYwUN/EbSkgIjOuWEF96W/xHsmV0SDrsdXjIM+oZ2xpPzPsBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + } + ], + "license": "ISC", + "dependencies": { + "fdir": "^6.5.0", + "formatly": "^0.3.0", + "get-tsconfig": "4.14.0", + "jiti": "^2.7.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", + "picomatch": "^4.0.4", + "smol-toml": "^1.6.1", + "strip-json-comments": "5.0.3", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", + "yaml": "^2.9.0", + "zod": "^4.1.11" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/knip/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -1828,23 +2714,6 @@ "node": ">=10" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, - "node_modules/modern-tar": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.6.tgz", - "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1912,6 +2781,75 @@ "node": ">=12" } }, + "node_modules/oxc-parser": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.137.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" + } + }, + "node_modules/oxc-resolver": { + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" + } + }, "node_modules/parse-json": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", @@ -2036,24 +2974,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/puppeteer-core": { - "version": "25.2.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.2.1.tgz", - "integrity": "sha512-MwEZ4FFGJ1ZLOmu/04eISxoEMKtCnHyJBRFfgpwPPSYNG6gT6Xw1laNziFSV7uwDcx3jK+ATYIo9SfOd8Uhc3w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.5", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1638949", - "typed-query-selector": "^2.12.2", - "webdriver-bidi-protocol": "0.4.2", - "ws": "^8.21.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, "node_modules/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", @@ -2127,6 +3047,16 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/rollup": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", @@ -2188,6 +3118,19 @@ "semver": "bin/semver.js" } }, + "node_modules/smol-toml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2198,40 +3141,19 @@ "node": ">=0.10.0" } }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { - "node": ">=18" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", @@ -2262,6 +3184,14 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -2275,13 +3205,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-query-selector": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", - "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", - "dev": true, - "license": "MIT" - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -2296,6 +3219,16 @@ "node": ">=14.17" } }, + "node_modules/unbash": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.3.tgz", + "integrity": "sha512-3cudTErfToSc4Ggv8XGXVNVli/xHKUtUZvaY5UVwhOcUPbQGz7PeaEnT/SAVgNziZtX67KEN9swMUYkLghxA1w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -2409,61 +3342,14 @@ } } }, - "node_modules/webdriver-bidi-protocol": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz", - "integrity": "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" + "node": "20 || >=22" } }, "node_modules/yallist": { @@ -2473,50 +3359,28 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", "dev": true, "license": "Apache-2.0" - }, - "node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/apps/web/package.json b/apps/web/package.json index bb8fa7d..236fab4 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,15 +10,22 @@ "generate:api-types": "node scripts/generate-api-types.mjs", "check:api-types": "node scripts/generate-api-types.mjs --check", "check:preview-hosts": "node scripts/check-preview-hosts.mjs", + "check:design-ssot": "node scripts/check-design-ssot.mjs", + "check:dead-code": "knip --include files,dependencies,unlisted,unresolved,binaries --treat-config-hints-as-errors", + "check:duplication": "jscpd src ../api/app --min-lines 8 --min-tokens 80 --ignore **/test_*.py,**/*_test.py,**/api.gen.ts,**/*.css,**/*.d.ts --threshold 0.05 --reporters console,threshold --no-tips", "generate:live2d-assets": "node scripts/generate-live2d-assets.mjs", "preview": "vite preview", "typecheck": "tsc -b", "lint": "tsc -b", - "e2e": "playwright test", + "e2e": "node scripts/run-e2e.mjs", + "e2e:list": "node scripts/run-e2e.mjs --list", + "e2e:parallel": "playwright test --project=chromium-desktop --project=chromium-mobile --workers=4", + "e2e:single-run": "playwright test --project=chromium-single-run --workers=1", "e2e:headed": "playwright test --headed", "e2e:ui": "playwright test --ui" }, "dependencies": { + "@tanstack/react-table": "^8.21.3", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^6.30.1" @@ -31,8 +38,10 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", + "esbuild": "^0.25.12", + "jscpd": "5.0.12", + "knip": "^6.26.0", "openapi-typescript": "^7.13.0", - "puppeteer-core": "^25.2.1", "typescript": "^5.8.3", "vite": "^6.3.5" } diff --git a/apps/web/playwright.config.ts b/apps/web/playwright.config.ts index 1c25108..a6f6b04 100644 --- a/apps/web/playwright.config.ts +++ b/apps/web/playwright.config.ts @@ -66,6 +66,9 @@ export default defineConfig({ name: "chromium-single-run", grep: singleRunPattern, grepInvert: publicAuthPattern, + // npm run e2e가 이 프로젝트를 fixture 프로젝트 뒤에 workers=1로 실행한다. + // 직접 focused 실행할 때도 npm run e2e:single-run을 사용한다. + fullyParallel: false, use: { ...devices["Desktop Chrome"], viewport: { width: 1280, height: 800 }, diff --git a/apps/web/public/design-elements/learner-dashboard/background-dark-corner.png b/apps/web/public/design-elements/learner-dashboard/background-dark-corner.png new file mode 100644 index 0000000000000000000000000000000000000000..23dd33a6ddb20d9306a6a423d0d3460054e336a6 GIT binary patch literal 218000 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

`tu&N0{N`L5=uiQQg4Sr% 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

    &dAng$HhL^+?s-0VE2XJ!>=fKg~tE+7O3 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ⅅ^=~S~1#ji-3`3fZ! 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 z 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

      s9+!ko4y+Gi5`e$oOv338 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

      Vg-qI8 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

      lVcP?qeY9i8NBO|QJ+4Qf=$*~KO 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�(AXJY_~y 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

      CWlbCBfmjK~E8gGd|zorDa# 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