관리자 감정 관측 기록과 조회 API 추가
This commit is contained in:
parent
d22cd9883d
commit
acb0d26338
16 changed files with 1445 additions and 18 deletions
100
apps/api/app/contracts/client_affect.py
Normal file
100
apps/api/app/contracts/client_affect.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
"""관리자 감정 관측용 Jev 감정 전이 trace 계약."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
CLIENT_AFFECT_DIMENSIONS = (
|
||||
"anxiety",
|
||||
"sadness",
|
||||
"anger",
|
||||
"shame",
|
||||
"guilt",
|
||||
"loneliness",
|
||||
"relief",
|
||||
"hope",
|
||||
"trust",
|
||||
)
|
||||
|
||||
ClientAffectDecision = Literal["accepted", "tentative", "held"]
|
||||
|
||||
|
||||
class ClientAffectPolicyV1(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", frozen=True, protected_namespaces=())
|
||||
|
||||
version: Literal["jev-affect-v1"]
|
||||
min_confidence: float = Field(ge=0.0, le=1.0)
|
||||
accepted_alpha: float = Field(ge=0.0, le=1.0)
|
||||
accepted_cap: float = Field(ge=0.0, le=1.0)
|
||||
tentative_alpha: float = Field(ge=0.0, le=1.0)
|
||||
tentative_cap: float = Field(ge=0.0, le=1.0)
|
||||
tentative_confidence_floor: float = Field(ge=0.0, le=1.0)
|
||||
adjacent_probability_threshold: float = Field(ge=0.0, le=1.0)
|
||||
|
||||
|
||||
class ClientAffectContextV1(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", frozen=True, protected_namespaces=())
|
||||
|
||||
stage: str
|
||||
resistance: float = Field(ge=0.0, le=1.0)
|
||||
effective_openness: float = Field(ge=0.0, le=1.0)
|
||||
rapport_credit: float = Field(ge=0.0)
|
||||
|
||||
|
||||
class ClientAffectDimensionTraceV1(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", frozen=True, protected_namespaces=())
|
||||
|
||||
key: str
|
||||
before: float = Field(ge=0.0, le=1.0)
|
||||
target: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
after: float = Field(ge=0.0, le=1.0)
|
||||
confidence: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
probabilities: tuple[float, float, float, float, float] | None = None
|
||||
decision: ClientAffectDecision
|
||||
|
||||
@model_validator(mode="after")
|
||||
def require_probability_distribution(self) -> "ClientAffectDimensionTraceV1":
|
||||
if self.probabilities is None:
|
||||
return self
|
||||
if any(
|
||||
not math.isfinite(value) or value < 0.0 or value > 1.0
|
||||
for value in self.probabilities
|
||||
):
|
||||
raise ValueError("probabilities must be finite values within 0..1")
|
||||
return self
|
||||
|
||||
|
||||
class ClientAffectTraceV1(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", frozen=True, protected_namespaces=())
|
||||
|
||||
schema_version: Literal[1]
|
||||
provider: str
|
||||
model: str
|
||||
latency_ms: int = Field(ge=0)
|
||||
input_tokens: int = Field(ge=0)
|
||||
output_tokens: int = Field(ge=0)
|
||||
cost_usd: float | None = Field(default=None, ge=0.0)
|
||||
turn_seq: int = Field(ge=1)
|
||||
policy: ClientAffectPolicyV1
|
||||
context: ClientAffectContextV1
|
||||
dimensions: tuple[ClientAffectDimensionTraceV1, ...] = Field(min_length=9, max_length=9)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def require_fixed_dimension_order(self) -> "ClientAffectTraceV1":
|
||||
if tuple(dimension.key for dimension in self.dimensions) != CLIENT_AFFECT_DIMENSIONS:
|
||||
raise ValueError("dimensions must use the fixed client affect order")
|
||||
return self
|
||||
|
||||
|
||||
__all__ = [
|
||||
"CLIENT_AFFECT_DIMENSIONS",
|
||||
"ClientAffectContextV1",
|
||||
"ClientAffectDecision",
|
||||
"ClientAffectDimensionTraceV1",
|
||||
"ClientAffectPolicyV1",
|
||||
"ClientAffectTraceV1",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue