Appraisal Schema¶
The appraisal_schema module provides a pluggable appraisal-theory mechanism.
Instead of hard-coding Scherer's 5 SECs everywhere, you define an AppraisalSchema
that bundles dimension definitions, an LLM prompt, and a projection function.
Classes¶
emotional_memory.appraisal_schema.AppraisalDimension
¶
Bases: BaseModel
One axis of an AppraisalSchema.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Machine-readable identifier (used as JSON key and dict key). |
range |
tuple[float, float]
|
|
neutral |
float
|
Value representing the neutral / resting point. |
description |
str
|
Human-readable description forwarded to the LLM prompt. |
emotional_memory.appraisal_schema.AppraisalSchema(name: str, dimensions: tuple[AppraisalDimension, ...], system_prompt: str, project_to_core_affect: Callable[[Mapping[str, float]], CoreAffect])
¶
A complete appraisal theory schema.
Bundles the dimension definitions, the LLM system prompt, and the
projection function that maps a dimension dict to CoreAffect.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
Short identifier string (e.g. |
|
dimensions |
Ordered tuple of |
|
system_prompt |
System prompt to send to the LLM. |
|
project_to_core_affect |
Callable mapping |
Source code in src/emotional_memory/appraisal_schema.py
to_json_schema() -> dict[str, Any]
¶
Derive a JSON schema dict from self.dimensions for LLM structured output.
Source code in src/emotional_memory/appraisal_schema.py
emotional_memory.appraisal.GenericAppraisalVector(dimensions: Mapping[str, object], schema: AppraisalSchema)
¶
An appraisal vector for non-Scherer schemas.
Produced by LLMAppraisalEngine when a custom AppraisalSchema is
configured. The Scherer-specific field names are not meaningful here;
call to_core_affect() to project into valence/arousal space via the
schema's mapping function.
Source code in src/emotional_memory/appraisal.py
Built-in schemas¶
emotional_memory.appraisal_schema.SCHERER_CPM_SCHEMA: AppraisalSchema = AppraisalSchema(name='scherer_cpm', dimensions=(AppraisalDimension(name='novelty', range=(-1.0, 1.0), neutral=0.0, description='How unexpected: -1=fully expected, 0=neutral, 1=totally new'), AppraisalDimension(name='goal_relevance', range=(-1.0, 1.0), neutral=0.0, description='Relation to goals: -1=obstructs, 0=irrelevant, 1=furthers'), AppraisalDimension(name='coping_potential', range=(0.0, 1.0), neutral=0.5, description='Perceived ability to handle: 0=helpless, 1=full control'), AppraisalDimension(name='norm_congruence', range=(-1.0, 1.0), neutral=0.0, description='Alignment with norms/values: -1=violates, 0=neutral, 1=conforms'), AppraisalDimension(name='self_relevance', range=(0.0, 1.0), neutral=0.0, description='Personal significance: 0=irrelevant, 1=deeply personal')), system_prompt=_SCHERER_SYSTEM_PROMPT, project_to_core_affect=_scherer_project)
module-attribute
¶
emotional_memory.appraisal_schema.DIRECT_VAD_SCHEMA: AppraisalSchema = AppraisalSchema(name='direct_vad', dimensions=(AppraisalDimension(name='valence', range=(-1.0, 1.0), neutral=0.0, description='Pleasantness: -1=unpleasant/negative, 0=neutral, 1=pleasant/positive'), AppraisalDimension(name='arousal', range=(0.0, 1.0), neutral=0.5, description='Activation: 0=calm/subdued, 1=excited/activated'), AppraisalDimension(name='dominance', range=(0.0, 1.0), neutral=0.5, description='Control: 0=controlled/submissive, 1=in-control/dominant')), system_prompt=_DIRECT_VAD_SYSTEM_PROMPT, project_to_core_affect=lambda d: CoreAffect(valence=d['valence'], arousal=d['arousal'], dominance=d['dominance']))
module-attribute
¶
Direct valence/arousal/dominance appraisal — the LLM rates V/A/D directly (identity projection) instead of the 5 Scherer SECs.
Opt-in alternative to the default SCHERER_CPM_SCHEMA. Against human-annotated
affect (EmoBank, N=300) it is better correlated on every axis — valence r=0.79
(near-zero bias), arousal r=0.58, dominance r=0.43 — vs the SEC->projection's
0.70 / 0.23 / 0.31 (Addendum V,
benchmarks/preregistration_addendum_v_direct_vad_closure.md). Trade-offs: it
yields a GenericAppraisalVector (no Scherer SEC fields), so the dual-path
elaborate() SEC-appraisal storage and any SEC-reading feature require the
default schema; and its arousal absolute scale is less calibrated (higher MAE)
than its correlation. Use when you only need CoreAffect and want stronger
human-gold agreement. Select via
LLMAppraisalConfig(appraisal_schema=DIRECT_VAD_SCHEMA).
Defining a custom schema¶
from emotional_memory import (
AppraisalDimension,
AppraisalSchema,
LLMAppraisalConfig,
LLMAppraisalEngine,
)
from emotional_memory.affect import CoreAffect
occ_schema = AppraisalSchema(
name="occ_subset",
dimensions=(
AppraisalDimension(
name="desirability",
range=(-1.0, 1.0),
neutral=0.0,
description="Desired outcome: -1=bad, 1=good",
),
AppraisalDimension(
name="praiseworthiness",
range=(-1.0, 1.0),
neutral=0.0,
description="Agent's action quality: -1=blame, 1=praise",
),
),
system_prompt=(
"Evaluate the event on OCC dimensions: desirability and praiseworthiness. "
"Return ONLY a JSON object with those two keys."
),
project_to_core_affect=lambda d: CoreAffect(
valence=0.6 * d["desirability"] + 0.4 * d["praiseworthiness"],
arousal=0.5 * abs(d["desirability"]),
dominance=0.5,
),
)
engine = LLMAppraisalEngine(
llm=my_llm,
config=LLMAppraisalConfig(appraisal_schema=occ_schema),
)
result = engine.appraise("She won the competition against all odds.")
# result is GenericAppraisalVector(schema_name="occ_subset", ...)
core = result.to_core_affect()
The project_to_core_affect callable receives a Mapping[str, float] where each
key is a dimension name and each value is the LLM's rating, clamped to dim.range.
It must return a CoreAffect instance.
Back-compatibility¶
LLMAppraisalEngine() without an explicit appraisal_schema continues to use
SCHERER_CPM_SCHEMA and returns AppraisalVector objects as before.
All existing code that constructs AppraisalVector directly or reads its named
fields is unaffected.