Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ jobs:
cache-dependency-path: python/requirements-dev.lock
- run: python -m pip install --require-hashes -r python/requirements-dev.lock
- run: python -m pip install --no-deps --no-build-isolation -e ./python
- run: python -m pytest python/tests scripts/tests --cov=anjo_core --cov-branch --cov-report=term-missing
- run: python -m ruff check python scripts examples
- run: python -m ruff format --check python scripts examples
- run: python -m pytest python/tests scripts/tests --cov=affect_kernel --cov-branch --cov-report=term-missing
- run: python -m ruff check python scripts examples bench
- run: python -m ruff format --check python scripts examples bench
- run: python -m mypy --config-file python/pyproject.toml python/src
# Examples are executable documentation and assert their own invariants.
- run: python examples/python-headless/main.py
- run: python examples/game-npc/main.py
# The benchmark report is generated; fail if the committed numbers drifted.
- run: python bench/run.py --check

python-package:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
tag="${GITHUB_REF_NAME#v}"
py=$(python -c "import tomllib,pathlib;print(tomllib.loads(pathlib.Path('python/pyproject.toml').read_text())['project']['version'])")
ts=$(node -p "require('./typescript/package.json').version")
init=$(python -c "import re,pathlib;print(re.search(r'__version__ = \"([^\"]+)\"', pathlib.Path('python/src/anjo_core/__init__.py').read_text()).group(1))")
init=$(python -c "import re,pathlib;print(re.search(r'__version__ = \"([^\"]+)\"', pathlib.Path('python/src/affect_kernel/__init__.py').read_text()).group(1))")
echo "tag=$tag python=$py typescript=$ts __version__=$init"
test "$tag" = "$py" && test "$tag" = "$ts" && test "$tag" = "$init"

Expand Down
2 changes: 1 addition & 1 deletion .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
title = "Anjo Core secret-scanning policy"
title = "Affect Kernel secret-scanning policy"

[extend]
useDefault = true
Expand Down
73 changes: 68 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,78 @@ to a pinned vector is called out here.

## [Unreleased]

### Added

- `AffectDynamics` and `RetrievalWeights`: the numeric coefficients are now
caller-owned data, on the same principle that already made stage names,
expectation cues, turn-shape rules, and presence labels replaceable. Inertia
terms, the resting-dominance coefficient, the baseline blend, per-emotion
carry decay and floor, the recency horizon and floor, the episode bonus, and
the mood-congruence threshold and asymmetry can all be changed without
forking. Defaults reproduce the pinned fixture exactly, so the 225 shared
vectors are unchanged.
- `docs/foundations.md`: per-constant provenance — literature, production-tuned,
or arbitrary-but-bounded — with the departures from the cited work stated,
and a list of results that would falsify the current choices.
- `CITATION.cff`, validated against CFF schema 1.2.0.
- `docs/threat-model.md`: assets, the one boundary the kernel actually enforces,
the ways an adapter can silently undo it, and an explicit list of what the
kernel does not defend against.
- Seeded property and fuzz suites in both runtimes covering clamping over
200-turn adversarial walks, determinism, non-mutation of caller state, ranking
total order, Unicode handling, and pickle/deepcopy round trips. No new
dependency: both use a fixed-seed PRNG so a failure is re-runnable from the
seed alone.
- `bench/`: a seeded, dependency-free retrieval benchmark over five regimes,
comparing the scorer against plain similarity and against the additive form
used by Generative Agents. `bench/RESULTS.md` is generated and drift-checked
in CI, so no document can quote a stale number. It reports results against the
current design, including that the additive form wins wherever salience
carries signal and that mood congruence is worth +0.012 MRR in a regime built
to favour it.

### Fixed

- Retracted a claim in `docs/foundations.md` and in the `recency_weight`
docstring that linear-to-a-floor recency was "the least defensible" curve in
the module. At a matched 30-day half-life it out-ranks both the exponential
and the power-law curve.

### Changed

- **Breaking (pre-release):** `CompanionState` is now `AffectState` and
`CompanionEngine` is `AffectEngine`, in both runtimes, along with
`createAffectState`, `AffectStateInput`, `ResolvedAffectState`, and
`AffectEngineOptions`. The last product-specific noun in the public API is
gone: a game NPC no longer instantiates a "companion". The presence vector's
`source` field changes from `"companion_state"` to `"affect_state"`, which
updates 8 expected values in `shared/golden/kernel_golden.json`. This is a
reviewed fixture change under `docs/parity-contract.md`; no version was ever
published, so nothing installed is affected.
- Renamed from `anjo-core` / `@anjo-ai/core` to `affect-kernel` on both
registries, and the Python module from `anjo_core` to `affect_kernel`. No
version was ever tagged or published under the old name.

### Documentation

- `docs/algorithm.md` now specifies the ambiguous-intent valence amplification
(`x1.10` negative, `x1.04` positive above `|v| >= 0.20`), which was
implemented but undocumented.

## [0.1.0]

First public release: the deterministic kernel extracted from
[Anjo](https://anjo.love) and generalized beyond conversation.

The repository was briefly public as `anjo-core` before this release and was
renamed to `affect-kernel` to name the library by what it does rather than by
the application it came from. No version was ever tagged or published under the
old name, so no installed artifact is affected.

### Added

- Behaviorally aligned Python (`anjo-core`) and TypeScript (`@anjo-ai/core`)
kernels with no runtime dependencies.
- Behaviorally aligned Python and TypeScript kernels with no runtime
dependencies, both published as `affect-kernel`.
- OCC-inspired appraisal, PAD mood dynamics, and Big Five N/E-conditioned affect
inertia.
- Bounded memory relevance, recency, salience, and mood-congruence scoring.
Expand Down Expand Up @@ -49,7 +112,7 @@ First public release: the deterministic kernel extracted from

- `FrozenMapping` is picklable. The default `dict` pickle protocol restores
items by mutating a fresh instance, which the class refuses, so every state
object holding one — `CompanionState` with a non-empty `occ_carry`,
object holding one — `AffectState` with a non-empty `occ_carry`,
`TurnShapePolicy`, `PromptPolicy` — raised `TypeError` on `pickle.dumps`.
That broke any `StateStore` serializing with `pickle` and any use across a
process boundary. Restored values remain immutable.
Expand All @@ -59,5 +122,5 @@ First public release: the deterministic kernel extracted from
construction step, so a lower-case declaration silently failed to suppress
there while working in Python.

[Unreleased]: https://github.com/kevindechang/anjo-core/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/kevindechang/anjo-core/releases/tag/v0.1.0
[Unreleased]: https://github.com/kevindechang/affect-kernel/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/kevindechang/affect-kernel/releases/tag/v0.1.0
107 changes: 107 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
title: "affect-kernel: a deterministic affect-state kernel for long-lived AI characters"
abstract: >-
A dependency-free Python and TypeScript library that turns appraised events
into bounded PAD mood, OCC-flavored emotion carry, mood-aware memory ranking,
response controls, and presence signals. The deterministic surfaces are pinned
by a shared cross-runtime fixture so that both runtimes reproduce the same
numbers. Constant provenance — published work, production tuning, or bounded
arbitrary choice — is documented per constant in docs/foundations.md.
type: software
authors:
- family-names: Chang
given-names: Chia Wei
alias: kevindechang
repository-code: "https://github.com/kevindechang/affect-kernel"
url: "https://github.com/kevindechang/affect-kernel"
license: Apache-2.0
version: 0.1.0
keywords:
- affective computing
- appraisal theory
- PAD model
- OCC model
- agent memory
- character agents
- deterministic simulation
references:
- type: book
title: "The Cognitive Structure of Emotions"
authors:
- family-names: Ortony
given-names: Andrew
- family-names: Clore
given-names: Gerald L.
- family-names: Collins
given-names: Allan
publisher:
name: Cambridge University Press
year: 1988
doi: 10.1017/CBO9780511571299
- type: article
title: >-
Pleasure-arousal-dominance: A general framework for describing and
measuring individual differences in temperament
authors:
- family-names: Mehrabian
given-names: Albert
journal: Current Psychology
volume: 14
issue: 4
start: 261
end: 292
year: 1996
doi: 10.1007/BF02686918
- type: conference-paper
title: "ALMA: A Layered Model of Affect"
authors:
- family-names: Gebhard
given-names: Patrick
collection-title: >-
Proceedings of the Fourth International Joint Conference on Autonomous
Agents and Multiagent Systems (AAMAS '05)
start: 29
end: 36
year: 2005
doi: 10.1145/1082473.1082478
- type: article
title: >-
Feelings change: Accounting for individual differences in the temporal
dynamics of affect
authors:
- family-names: Kuppens
given-names: Peter
- family-names: Oravecz
given-names: Zita
- family-names: Tuerlinckx
given-names: Francis
journal: Journal of Personality and Social Psychology
volume: 99
issue: 6
start: 1042
end: 1060
year: 2010
doi: 10.1037/a0020962
- type: conference-paper
title: "Generative Agents: Interactive Simulacra of Human Behavior"
authors:
- family-names: Park
given-names: Joon Sung
- family-names: O'Brien
given-names: Joseph C.
- family-names: Cai
given-names: Carrie J.
- family-names: Morris
given-names: Meredith Ringel
- family-names: Liang
given-names: Percy
- family-names: Bernstein
given-names: Michael S.
collection-title: >-
Proceedings of the 36th Annual ACM Symposium on User Interface Software
and Technology (UIST '23)
start: 1
end: 22
year: 2023
doi: 10.1145/3586183.3606763
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Contributing to Anjo Core
# Contributing to Affect Kernel

Thank you for helping make long-lived character systems more inspectable and
portable.
Expand Down
22 changes: 21 additions & 1 deletion GOVERNANCE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Governance

Anjo Core currently uses a maintainer-led model.
Affect Kernel currently uses a maintainer-led model.

- Maintainers set scope, merge changes, cut releases, and resolve security issues.
- Significant public API or parity-contract changes should begin as an issue or
Expand All @@ -14,3 +14,23 @@ As sustained contributors emerge, maintainership can be granted based on review
quality, reliability, and care for the public boundary. Governance changes are
made through pull requests to this file.

## Bandwidth

Worth knowing before you invest time in a contribution: this is maintained by
one person, alongside other work. Expect a first response to an issue or pull
request within about a week, and longer for anything touching the parity
contract, which needs a reviewed fixture change in both runtimes.

Things that get looked at fastest, in order:

1. A reproducible bug in a deterministic transform, with a failing case.
2. A benchmark or evaluation result — including one that contradicts something
this repository claims. `docs/foundations.md` lists the results that would
falsify its own choices, and the linear-recency claim has already been
retracted on evidence once.
3. A new storage or model adapter, or a domain preset.
4. API surface changes, which need a design discussion first.

If something here is stalled and you need it, say so on the issue rather than
assuming it was rejected.

9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: setup test lint package check examples
.PHONY: setup test lint package check examples bench

setup:
./scripts/setup.sh
Expand All @@ -8,8 +8,8 @@ test:
npm test --prefix typescript

lint:
python -m ruff check python scripts examples/python-headless
python -m ruff format --check python scripts examples/python-headless
python -m ruff check python scripts examples/python-headless bench
python -m ruff format --check python scripts examples/python-headless bench
python -m mypy --config-file python/pyproject.toml python/src
npm run typecheck --prefix typescript

Expand All @@ -23,3 +23,6 @@ check:
examples:
python examples/python-headless/main.py
npm run example --prefix typescript

bench:
python bench/run.py --write
Loading
Loading