feat(stage-ui): add emotion and relationship bond state - #2209
feat(stage-ui): add emotion and relationship bond state#2209Tonystarkw12 wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 034e5f23b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| // 稳定性不衰减,或者可以缓慢恢复 | ||
| const newStability = state.stability | ||
|
|
There was a problem hiding this comment.
Update decay timestamp when returning decayed state
When loadEmotionState saves the result of applyTimeDecay, this returned state keeps the original lastUpdatedAt. Any later load, such as switching back to a session or regenerating its system prompt, re-applies the full elapsed time to an already-decayed value, so emotions collapse toward baseline much faster than the configured half-life. Set lastUpdatedAt to now (or otherwise record that decay was applied) in the decayed snapshot.
Useful? React with 👍 / 👎.
| emotionStore.setCurrentSession(sessionId) | ||
| emotionStore.applyEmotionDelta(sessionId, evaluation.delta, evaluation.reason) | ||
| relationshipBondStore.recordUserMessageInteraction({ |
There was a problem hiding this comment.
Apply emotion only after the send is accepted
These store updates run before runtime.ingest(...) enqueues and actually appends the user message, so a send that never executes still changes emotion and relationship state. This occurs for queued sends canceled via cancelPendingSends while another response is streaming (and also for sends the runtime drops before append), leaving chat history without that turn but with the system prompt and bond already advanced. Move the updates into the accepted user-turn path, such as after the runtime appends the user message, or roll them back on rejection.
Useful? React with 👍 / 👎.
|
Could you please update your title to meet your changes, and rebase the latest |
Keep upstream Airi Card as the persona implementation while adding per-session five-dimensional emotion state, decay, prompt influence, persistence, and tests. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Build long-term per-character relationship stages on top of Airi Card and the emotion system, with local persistence, bounded significant-event history, and prompt context. Co-Authored-By: Claude <noreply@anthropic.com>
034e5f2 to
7d250ee
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d250eef8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| * 保存情绪状态 | ||
| */ | ||
| function saveEmotionState(sessionId: string, state: EmotionState): void { | ||
| emotionStatesMap.value[sessionId] = state |
There was a problem hiding this comment.
Persist emotion maps by replacing the top-level ref
This store only mutates properties inside the object returned by useLocalStorageManualReset; that helper writes storage from a watch on the top-level ref, so applyEmotionDelta and appendEmotionEvent can update Pinia memory without updating emotion/states or emotion/events in localStorage. After a reload, the new per-session emotion state and history are lost. Reassign the whole map when saving and appending.
Useful? React with 👍 / 👎.
| function generateInitialMessageFromPrompt(prompt: string) { | ||
| const content = codeBlockSystemPrompt + mathSyntaxSystemPrompt + prompt | ||
| function generateInitialMessageFromPrompt(prompt: string, sessionId = activeSessionId.value) { | ||
| const emotionState = emotionStore.loadEmotionState(sessionId) ?? emotionStore.currentEmotionState |
There was a problem hiding this comment.
Create emotion state for the requested session
When loadEmotionState(sessionId) returns undefined, this falls back to currentEmotionState, which is keyed by emotionStore.currentSessionId rather than the sessionId argument. After a user chats in session A and creates session B, B's initial system prompt can be seeded with A's affect. Use a default state, or create/load state for the requested session instead.
Useful? React with 👍 / 👎.
| // 遍历所有关键词配置 | ||
| for (const config of ALL_KEYWORD_CONFIGS) { | ||
| for (const keyword of config.keywords) { | ||
| if (lowerMessage.includes(keyword.toLowerCase())) { |
There was a problem hiding this comment.
Handle negated or embedded keyword matches
For messages where a sentiment token appears inside a negation or another word, this substring match applies the wrong delta. For example, 我不喜欢你 matches 喜欢 and records affection, while 麻烦你帮我 matches 烦 and records strong negativity. That wrong result is persisted into emotion and bond state, then injected into the next prompt. Add phrase-boundary or negation handling before applying the keyword delta.
Useful? React with 👍 / 👎.
|
Updated as requested:
The feature branch has been force-updated after the rebase. |
Add two complementary character-state layers to Stage UI while keeping Airi Card as the source of character identity:
User turns are evaluated locally with deterministic keyword rules. Emotion deltas are stability-modulated and decay toward baseline over time. Both
state summaries are injected into the session system prompt so responses can reflect recent affect and relationship history.
State ownership stays separated:
Implementation details:
content.
hydration, and character isolation.
Linked Issues
None.
Additional Context
This is intentionally smaller than a full RPG system. It adds only relationship behavior that directly affects conversations and reuses existing Airi
Card identity rather than introducing a competing persona model.
Validation performed:
@proj-airi/stage-uitypecheck: passedPlease focus review on: