feat: 운영 안정성과 세션 음성 경험 개선
This commit is contained in:
parent
facc4ad2d9
commit
c788343467
95 changed files with 8431 additions and 1785 deletions
|
|
@ -6,6 +6,9 @@ import unittest
|
|||
from datetime import datetime, timedelta, timezone
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from .contracts.engine_gateway import EngineCapabilitiesResponse, EngineModelOption
|
||||
from .deps import Principal, Role
|
||||
from .routes import admin as admin_routes
|
||||
|
||||
|
|
@ -572,3 +575,224 @@ class AdminOpsTest(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertIn("measure_name IN ('self_efficacy','skill_proficiency','training_satisfaction')", schema)
|
||||
self.assertIn("ALTER TABLE app.learner_prepost_measure ENABLE ROW LEVEL SECURITY", schema)
|
||||
self.assertIn("CREATE POLICY p_learner_prepost_measure_insert", schema)
|
||||
|
||||
|
||||
class AdminEngineConfigTest(unittest.IsolatedAsyncioTestCase):
|
||||
async def asyncSetUp(self) -> None:
|
||||
self.previous_config = admin_routes._ENGINE_CONFIG
|
||||
admin_routes._ENGINE_CONFIG = admin_routes.AdminEngineConfigResponse(
|
||||
engine_mode="claude_cli",
|
||||
engine_url="http://127.0.0.1:9099",
|
||||
model="gateway-default",
|
||||
reasoning_effort="high",
|
||||
durable=True,
|
||||
source="database",
|
||||
)
|
||||
self.principal = Principal(
|
||||
user_id="00000000-0000-0000-0000-000000000099",
|
||||
role=Role.ADMIN,
|
||||
cohort_ids=[],
|
||||
email="admin@twentyoz.kr",
|
||||
display_name="Admin",
|
||||
)
|
||||
|
||||
async def asyncTearDown(self) -> None:
|
||||
admin_routes._ENGINE_CONFIG = self.previous_config
|
||||
|
||||
async def test_capabilities_endpoint_uses_requested_gateway_url(self) -> None:
|
||||
capabilities = EngineCapabilitiesResponse(
|
||||
provider="codex_cli",
|
||||
available=True,
|
||||
source="live_cli",
|
||||
models=[
|
||||
EngineModelOption(
|
||||
id="gpt-5.6-terra",
|
||||
label="GPT-5.6-Terra",
|
||||
reasoning_efforts=["medium"],
|
||||
default_reasoning_effort="medium",
|
||||
)
|
||||
],
|
||||
default_model="gpt-5.6-terra",
|
||||
default_reasoning_effort="medium",
|
||||
fetched_at=1,
|
||||
)
|
||||
with patch.object(
|
||||
admin_routes.engine_client,
|
||||
"capabilities",
|
||||
AsyncMock(return_value=capabilities),
|
||||
) as lookup:
|
||||
result = await admin_routes.get_engine_capabilities(
|
||||
self.principal,
|
||||
engine_mode="codex_cli",
|
||||
engine_url="http://127.0.0.1:9199/",
|
||||
force=True,
|
||||
)
|
||||
|
||||
self.assertEqual(result.default_model, "gpt-5.6-terra")
|
||||
lookup.assert_awaited_once_with(
|
||||
provider="codex_cli",
|
||||
base_url="http://127.0.0.1:9199",
|
||||
force=True,
|
||||
)
|
||||
|
||||
with patch.object(
|
||||
admin_routes.engine_client,
|
||||
"capabilities",
|
||||
AsyncMock(return_value=capabilities),
|
||||
) as current_lookup:
|
||||
await admin_routes.get_engine_capabilities(
|
||||
self.principal,
|
||||
engine_mode="codex_cli",
|
||||
engine_url=None,
|
||||
force=False,
|
||||
)
|
||||
current_lookup.assert_awaited_once_with(
|
||||
provider="codex_cli",
|
||||
base_url="http://127.0.0.1:9099",
|
||||
force=False,
|
||||
)
|
||||
|
||||
async def test_patch_rejects_model_missing_from_live_catalog(self) -> None:
|
||||
capabilities = EngineCapabilitiesResponse(
|
||||
provider="codex_cli",
|
||||
available=True,
|
||||
source="live_cli",
|
||||
models=[],
|
||||
fetched_at=1,
|
||||
)
|
||||
with patch.object(
|
||||
admin_routes.engine_client,
|
||||
"capabilities",
|
||||
AsyncMock(return_value=capabilities),
|
||||
):
|
||||
with self.assertRaises(HTTPException) as caught:
|
||||
await admin_routes.patch_engine_config(
|
||||
admin_routes.AdminEngineConfigPatch(
|
||||
engine_mode="codex_cli",
|
||||
model="made-up-model",
|
||||
reasoning_effort="medium",
|
||||
),
|
||||
self.principal,
|
||||
)
|
||||
|
||||
self.assertEqual(caught.exception.status_code, 422)
|
||||
self.assertIn("사용할 수 없는 모델", caught.exception.detail)
|
||||
|
||||
async def test_patch_persists_and_applies_reasoning_effort(self) -> None:
|
||||
capabilities = EngineCapabilitiesResponse(
|
||||
provider="codex_cli",
|
||||
available=True,
|
||||
source="live_cli",
|
||||
models=[
|
||||
EngineModelOption(
|
||||
id="gpt-5.6-terra",
|
||||
label="GPT-5.6-Terra",
|
||||
reasoning_efforts=["low", "medium", "high"],
|
||||
default_reasoning_effort="medium",
|
||||
)
|
||||
],
|
||||
default_model="gpt-5.6-terra",
|
||||
default_reasoning_effort="medium",
|
||||
fetched_at=1,
|
||||
)
|
||||
|
||||
class ConfigConn:
|
||||
def __init__(self):
|
||||
self.query = ""
|
||||
self.args = ()
|
||||
|
||||
async def fetchrow(self, query, *args):
|
||||
self.query = query
|
||||
self.args = args
|
||||
return {
|
||||
"engine_mode": args[0],
|
||||
"engine_url": args[1],
|
||||
"model": args[2],
|
||||
"reasoning_effort": args[3],
|
||||
"updated_by": args[4],
|
||||
"updated_at": None,
|
||||
}
|
||||
|
||||
conn = ConfigConn()
|
||||
|
||||
class ConfigPool:
|
||||
def acquire(self):
|
||||
return _Acquire(conn)
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
admin_routes.engine_client,
|
||||
"capabilities",
|
||||
AsyncMock(return_value=capabilities),
|
||||
),
|
||||
patch.object(admin_routes, "get_pool", return_value=ConfigPool()),
|
||||
patch.object(
|
||||
admin_routes.engine_client,
|
||||
"configure",
|
||||
AsyncMock(),
|
||||
) as configure,
|
||||
):
|
||||
result = await admin_routes.patch_engine_config(
|
||||
admin_routes.AdminEngineConfigPatch(
|
||||
engine_mode="codex_cli",
|
||||
engine_url="http://127.0.0.1:9099",
|
||||
model="gpt-5.6-terra",
|
||||
reasoning_effort="medium",
|
||||
),
|
||||
self.principal,
|
||||
)
|
||||
|
||||
self.assertEqual(result.reasoning_effort, "medium")
|
||||
self.assertIn("reasoning_effort", conn.query)
|
||||
self.assertEqual(conn.args[3], "medium")
|
||||
configure.assert_awaited_once_with(
|
||||
base_url="http://127.0.0.1:9099",
|
||||
engine_mode="codex_cli",
|
||||
default_model="gpt-5.6-terra",
|
||||
default_reasoning_effort="medium",
|
||||
)
|
||||
|
||||
async def test_patch_can_explicitly_clear_reasoning_effort(self) -> None:
|
||||
capabilities = EngineCapabilitiesResponse(
|
||||
provider="claude_api",
|
||||
available=True,
|
||||
source="live_api",
|
||||
models=[EngineModelOption(id="claude-legacy", label="Claude Legacy")],
|
||||
default_model="claude-legacy",
|
||||
fetched_at=1,
|
||||
)
|
||||
|
||||
class ConfigConn:
|
||||
async def fetchrow(self, _query, *args):
|
||||
return {
|
||||
"engine_mode": args[0],
|
||||
"engine_url": args[1],
|
||||
"model": args[2],
|
||||
"reasoning_effort": args[3],
|
||||
"updated_by": args[4],
|
||||
"updated_at": None,
|
||||
}
|
||||
|
||||
class ConfigPool:
|
||||
def acquire(self):
|
||||
return _Acquire(ConfigConn())
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
admin_routes.engine_client,
|
||||
"capabilities",
|
||||
AsyncMock(return_value=capabilities),
|
||||
),
|
||||
patch.object(admin_routes, "get_pool", return_value=ConfigPool()),
|
||||
patch.object(admin_routes.engine_client, "configure", AsyncMock()),
|
||||
):
|
||||
result = await admin_routes.patch_engine_config(
|
||||
admin_routes.AdminEngineConfigPatch(
|
||||
engine_mode="claude_api",
|
||||
model="claude-legacy",
|
||||
reasoning_effort=None,
|
||||
),
|
||||
self.principal,
|
||||
)
|
||||
|
||||
self.assertIsNone(result.reasoning_effort)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue