Jev 기반 내담자 감정 상태와 응답 일관성 개선

This commit is contained in:
Yun Chan 2026-09-22 21:32:26 +09:00
parent 77f8421818
commit 8344bc2ad2
23 changed files with 3384 additions and 25 deletions

View file

@ -0,0 +1,104 @@
"""Jev 감정 공급자 설정 계약."""
from __future__ import annotations
import os
import unittest
from unittest.mock import AsyncMock, patch
from pydantic import SecretStr, ValidationError
from .config import Settings
from . import main
class JevSettingsTest(unittest.TestCase):
def test_defaults_keep_legacy_provider(self) -> None:
with patch.dict(
os.environ,
{
"VIGNETTE_CLIENT_AFFECT_PROVIDER": "legacy",
"TYPESAFE_API_KEY": "",
"OPENROUTER_API_KEY": "",
},
clear=False,
):
configured = Settings(_env_file=None)
self.assertEqual("legacy", configured.client_affect_provider)
self.assertEqual("openrouter", configured.jev_provider)
self.assertEqual("~typesafe/jev-latest", configured.jev_model)
self.assertEqual(1.2, configured.jev_timeout_seconds)
self.assertEqual(0.65, configured.jev_min_confidence)
def test_default_openrouter_jev_requires_its_own_key(self) -> None:
with self.assertRaisesRegex(ValidationError, "OPENROUTER_API_KEY"):
Settings(
_env_file=None,
client_affect_provider="jev",
typesafe_api_key=SecretStr("typesafe-only-key"),
openrouter_api_key=SecretStr(""),
)
def test_explicit_typesafe_jev_requires_typesafe_key(self) -> None:
with self.assertRaisesRegex(ValidationError, "TYPESAFE_API_KEY"):
Settings(
_env_file=None,
client_affect_provider="jev",
jev_provider="typesafe",
openrouter_api_key=SecretStr("openrouter-only-key"),
typesafe_api_key=SecretStr(""),
)
def test_jev_environment_aliases_and_limits_apply(self) -> None:
with patch.dict(
os.environ,
{
"VIGNETTE_CLIENT_AFFECT_PROVIDER": "jev",
"VIGNETTE_JEV_PROVIDER": "openrouter",
"OPENROUTER_API_KEY": "unit-test-openrouter-key",
"VIGNETTE_JEV_MODEL": "~typesafe/jev-latest",
"VIGNETTE_JEV_TIMEOUT_SECONDS": "0.4",
"VIGNETTE_JEV_MIN_CONFIDENCE": "0.8",
},
clear=False,
):
configured = Settings(_env_file=None)
self.assertEqual("jev", configured.client_affect_provider)
self.assertEqual("openrouter", configured.jev_provider)
self.assertEqual(
"unit-test-openrouter-key",
configured.openrouter_api_key.get_secret_value(),
)
self.assertEqual(0.4, configured.jev_timeout_seconds)
self.assertEqual(0.8, configured.jev_min_confidence)
def test_timeout_bounds_are_enforced(self) -> None:
with self.assertRaises(ValidationError):
Settings(_env_file=None, jev_timeout_seconds=0.09)
with self.assertRaises(ValidationError):
Settings(_env_file=None, jev_timeout_seconds=10.1)
class JevHealthStatusTest(unittest.IsolatedAsyncioTestCase):
async def test_health_exposes_configuration_without_claiming_live_readiness(self) -> None:
with (
patch.object(main, "healthcheck", AsyncMock(return_value=True)),
patch.object(
main.engine_client,
"health_detail",
AsyncMock(return_value={"ok": True}),
),
):
response = await main.health()
self.assertEqual(main.settings.client_affect_provider, response["client_affect_provider"])
self.assertEqual(main.settings.jev_model, response["jev"]["model"])
self.assertEqual(main.settings.jev_provider, response["jev"]["provider"])
self.assertIn("configured", response["jev"])
self.assertFalse(response["jev"]["live_verified"])
if __name__ == "__main__":
unittest.main()