Metadata-Version: 2.5
Name: affect-kernel
Version: 0.1.0
Summary: Deterministic affect-state kernel for long-lived AI characters, with injected adapters
Project-URL: Homepage, https://github.com/kevindechang/affect-kernel
Project-URL: Repository, https://github.com/kevindechang/affect-kernel
Project-URL: Issues, https://github.com/kevindechang/affect-kernel/issues
Project-URL: Changelog, https://github.com/kevindechang/affect-kernel/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/kevindechang/affect-kernel/tree/main/docs
Author: Chang Chia Wei
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: affect,agent,appraisal,character,companion,emotion,memory,npc,occ,pad
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: check-wheel-contents>=0.6; extra == 'dev'
Requires-Dist: editables>=0.3; extra == 'dev'
Requires-Dist: hatchling==1.32.0; extra == 'dev'
Requires-Dist: mypy>=1.15; extra == 'dev'
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.9; extra == 'dev'
Requires-Dist: twine>=6.2; extra == 'dev'
Description-Content-Type: text/markdown

# affect-kernel for Python

This directory contains the zero-runtime-dependency Python implementation of the
deterministic affect-state kernel for long-lived AI characters.

```bash
python -m pip install affect-kernel
```

```python
from affect_kernel import AffectState, appraise_turn

state = AffectState()
result = appraise_turn(state, "CURIOSITY")
print(result.state.mood)
```

The [repository README](https://github.com/kevindechang/affect-kernel#readme)
contains runnable headless examples, benchmark results, limitations, and the
cross-runtime contract. From a source checkout, run `./scripts/setup.sh` and
`./scripts/check.sh` at the repository root.

The package deliberately leaves model inference, persistence, and retrieval I/O
behind injected protocols. Its cross-runtime guarantee covers deterministic,
non-habituating affect, appraisal, memory scoring, and presence transforms only.
Prompt wording is supplied by the caller and is not part of production parity.

## Adapter security and transaction contract

`AffectEngine` sends trusted instructions in `GenerateInput.system_prompt` and
bounded retrieved/carried evidence in `GenerateInput.untrusted_context`. Model
adapters must place `untrusted_context` in a user/tool-data channel and obey its
evidence-only rule; they must never concatenate it, `GenerateInput.state`, or
retriever output into the system prompt. Gate, retrieval, and generation request
states have `carried_thought=None`, so the carried text is available only through
the explicit untrusted field on generation.

Gate failures and unsupported intents raise by default. Applications may opt in
to `gate_error_mode="respond"` or `"silent"`. Built-in intents are normalized to
uppercase. A caller can register `custom_intents`; custom labels are also
normalized, must match `[A-Z][A-Z0-9_]{0,63}`, and intentionally receive no
built-in appraisal impulse.

Domain kernels can inject a synchronous `AppraisalPolicy` into
`AffectEngine`. It receives an `AppraisalPolicyInput` containing the current
state, normalized intent/event, message, and expectation, and must return an
`AppraisalResult`. `default_appraisal_policy` explicitly preserves the reference
English conversational mapping implemented by `appraise_turn`.

`EngineLimits` bounds each message (16,000 characters), loaded history (200
messages / 128,000 characters), assembled system prompt (128,000 characters),
and buffered model output (32,000 characters) by default. Pass a replacement
`EngineLimits` value to tighten those ceilings for your deployment. The engine
checks an offending output chunk before invoking its token callback or committing
the turn.

Stores implement `StateStore.transaction(conversation_id)`. That context must
serialize the complete turn for the conversation across every engine instance,
and its `ConversationTransaction.commit(...)` must atomically persist the state
update and message batch. If gate, retrieval, generation, a token callback, or
cancellation fails before commit, the store must leave both state and transcript
unchanged. `InMemoryStateStore` is the dependency-free reference implementation.
