fluffychat/.github/instructions/choreographer.instructions.md
wcjord 33719b3ee7
Add score field to LLMFeedbackModel for human audit integration (#5684)
- Add score: Optional[int] to LLMFeedbackModel with conditional serialization
- Add ModelKey.score constant
- Update choreographer.instructions.md with feedback architecture docs

Closes krille-chan/fluffychat#2560
2026-02-12 14:52:22 -05:00

167 lines
9.9 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
applyTo: "lib/pangea/choreographer/**"
---
# Choreographer — Writing Assistance Flow
The choreographer is the client-side orchestrator for real-time writing assistance. It coordinates user text input, API calls to `/grammar_v2`, match display, and the creation of choreo records saved with sent messages.
> **⚠️ IT (Interactive Translation) is deprecated.** The `it/` directory, `ITController`, and `/it_initialstep` endpoint are still wired into the choreographer but are being phased out. IT will become just another match type returned by IGC. Do not add new IT functionality.
## Architecture
```
Choreographer (ChangeNotifier)
├── PangeaTextController ← Extended TextEditingController (tracks edit types)
├── IgcController ← Grammar check matches (primary flow)
├── ITController ← ⚠️ DEPRECATED — Translation step-by-step flow
├── ChoreographerErrorController ← Error state + backoff
└── ChoreographerStateExtension ← AssistanceStateEnum derivation
```
### Key files
| File | Purpose |
|---|---|
| `choreographer.dart` | Main orchestrator (ChangeNotifier) |
| `choreographer_state_extension.dart` | Derives `AssistanceStateEnum` from current state |
| `assistance_state_enum.dart` | UI states: noSub, noMessage, notFetched, fetching, fetched, complete, error |
| `choreo_mode_enum.dart` | `igc` (active) or `it` (⚠️ deprecated) |
| `choreo_record_model.dart` | Record of edits saved with message |
| `igc/igc_controller.dart` | IGC state management (437 lines) |
| `igc/replacement_type_enum.dart` | Granular match type taxonomy (grammar, surface, word-choice, etc.) |
| `igc/autocorrect_popup.dart` | Undo popup for auto-applied corrections |
| `text_editing/pangea_text_controller.dart` | Text controller with edit type tracking |
| `it/it_controller.dart` | ⚠️ DEPRECATED — IT state |
## Flow
### 1. User types → debounce → IGC request
1. User types in chat input. `PangeaTextController` fires `_onChange`.
2. After debounce (`ChoreoConstants.msBeforeIGCStart`), `requestWritingAssistance()` is called.
3. `IgcController.getIGCTextData()` calls `/grammar_v2` via `IgcRepo`.
4. Response contains a list of `SpanData` (matches) — grammar errors, out-of-target markers, normalization fixes.
5. Auto-apply matches (punct, diacritics, spell, cap) are accepted automatically via `acceptNormalizationMatches()`. Grammar/word-choice matches become `openMatches`.
### 2. Matches displayed → Span cards
1. Open matches render as colored underlines in the text field (colors set by `ReplacementTypeEnum.underlineColor()`).
2. Tapping a match opens a **span card** overlay (`span_card.dart`) showing the error category (`ReplacementTypeEnum.displayName()`), choices, and the error message.
3. Auto-applied corrections show an `AutocorrectPopup` with undo capability.
### 3. User resolves matches
Each match goes through `PangeaMatchState` with status transitions:
- `open``accepted` (user chose a replacement)
- `open``ignored` (user dismissed)
- `open``automatic` (auto-apply correction)
- Any → `undo` (user reverted)
When a match is accepted/ignored, the `IgcController` fires `matchUpdateStream`. The `Choreographer` listens and:
- Updates the text via `textController.setSystemText()`
- Records the step in `ChoreoRecordModel`
### 4. Feedback rerun
If the user is unsatisfied with results, `rerunWithFeedback(feedbackText)` re-calls IGC with user feedback and the previous request/response context (`_lastRequest`, `_lastResponse`).
**What the client sends**: The feedback request sends `List<LLMFeedbackSchema>` items on the request body. Each item carries `feedback` (optional string), `content` (the previous response for context), and `score` (optional int, 010). The score lets native speakers approve content (910) or reject it (06), while learners typically send a low score with corrective text. We'll probably just do 0 or 10 corresponding to thumbs up/down, but the schema supports finer granularity if needed.
**What the server does with it**: The router extracts `matrix_user_id` from the auth token and passes it along with the feedback list to `get()`. When feedback is present, `get()` builds an `Audit` internally (score + auditor + feedback text) and appends it to the CMS document (fire-and-forget). If the score indicates rejection (< 7), `get()` regenerates with an escalated model. The human judgment (who rejected/approved, why, when) lives on the `res.audit` array. See the server-side inference doc's Feedback Architecture section for the full flow.
**Native speaker approval**: When a native speaker sends score 910, the server persists the audit (upgrading the doc to fine-tuning eligible) and returns the cached response without regeneration.
### 5. Sending
On send, `Choreographer.getMessageContent()`:
1. Calls `/tokenize` to get `PangeaToken` data for the final text (with exponential backoff on errors).
2. Builds `PangeaMessageContentModel` containing:
- The final message text
- `ChoreoRecordModel` (full editing history)
- `PangeaRepresentation` for original written text (if IT was used)
- `PangeaMessageTokens` (token/lemma/morph data)
## AssistanceStateEnum
Derived in `choreographer_state_extension.dart`. Drives the send-button color and UI hints:
| State | Meaning |
|---|---|
| `noSub` | User has no active subscription |
| `noMessage` | Text field is empty |
| `notFetched` | Text entered but IGC hasn't run yet |
| `fetching` | IGC request in flight |
| `fetched` | Matches present user needs to resolve them |
| `complete` | All matches resolved, ready to send |
| `error` | IGC error (backoff active) |
## ReplacementTypeEnum — Match Type Taxonomy
Defined in `igc/replacement_type_enum.dart`. Categories returned by `/grammar_v2`:
| Category | Types | Behavior |
|---|---|---|
| **Client-only** | `definition`, `practice`, `itStart` | Not from server; `itStart` triggers deprecated IT flow |
| **Grammar** (~21 types) | `verbConjugation`, `verbTense`, `verbMood`, `subjectVerbAgreement`, `genderAgreement`, `numberAgreement`, `caseError`, `article`, `preposition`, `pronoun`, `wordOrder`, `negation`, `questionFormation`, `relativeClause`, `connector`, `possessive`, `comparative`, `passiveVoice`, `conditional`, `infinitiveGerund`, `modal` | Orange underline, user must accept/ignore |
| **Surface corrections** | `punct`, `diacritics`, `spell`, `cap` | Auto-applied (no user interaction), undo via `AutocorrectPopup` |
| **Word choice** | `falseCognate`, `l1Interference`, `collocation`, `semanticConfusion` | Blue underline, user must accept/ignore |
| **Higher-level** | `transcription`, `style`, `fluency`, `didYouMean`, `translation`, `other` | Teal (style/fluency) or error color |
Key extension helpers: `isAutoApply`, `isGrammarType`, `isWordChoiceType`, `underlineColor()`, `displayName()`, `fromString()` (handles legacy snake_case and old type names like `grammar` `subjectVerbAgreement`).
## Key Models
| Model | File | Purpose |
|---|---|---|
| `SpanData` | `igc/span_data_model.dart` | A match span (offset, length, choices, message, rule, `ReplacementTypeEnum`) |
| `PangeaMatch` | `igc/pangea_match_model.dart` | SpanData + status |
| `PangeaMatchState` | `igc/pangea_match_state_model.dart` | Mutable wrapper tracking original vs updated match state |
| `ChoreoRecordModel` | `choreo_record_model.dart` | Full editing history: steps, open matches, original text |
| `ChoreoRecordStepModel` | `choreo_edit_model.dart` | Single edit step (text before/after, accepted match) |
| `IGCRequestModel` | `igc/igc_request_model.dart` | Request to `/grammar_v2` |
| `IGCResponseModel` | `igc/igc_response_model.dart` | Response from `/grammar_v2` |
| `MatchRuleIdModel` | `igc/match_rule_id_model.dart` | Rule ID constants (⚠ `tokenNeedsTranslation`, `tokenSpanNeedsTranslation`, `l1SpanAndGrammar` not currently sent by server) |
| `AutocorrectPopup` | `igc/autocorrect_popup.dart` | Undo widget for auto-applied corrections |
## API Endpoints
| Endpoint | Repo File | Status |
|---|---|---|
| `/choreo/grammar_v2` | `igc/igc_repo.dart` | Active primary IGC endpoint |
| `/choreo/tokenize` | `events/repo/tokens_repo.dart` | Active tokenizes final text on send |
| `/choreo/span_details` | `igc/span_data_repo.dart` | Dead code `SpanDataRepo` class is defined but never imported anywhere |
| `/choreo/it_initialstep` | `it/it_repo.dart` | Deprecated IT flow |
| `/choreo/contextual_definition` | `contextual_definition_repo.dart` | Deprecated only used by IT's `word_data_card.dart` |
## Edit Types (`EditTypeEnum`)
- `keyboard` User typing
- `igc` System applying IGC match
- `it` Deprecated System applying IT continuance
- `itDismissed` Deprecated IT dismissed, restoring source text
## Deprecated: SpanChoiceTypeEnum
In `igc/span_choice_type_enum.dart`:
- `bestCorrection` `@Deprecated('Use suggestion instead')`
- `bestAnswer` `@Deprecated('Use suggestion instead')`
- `suggestion` Active replacement
## Error Handling
- IGC and token errors trigger exponential backoff (`_igcErrorBackoff *= 2`, `_tokenErrorBackoff *= 2`)
- Backoff resets on next successful request
- Errors surfaced via `ChoreographerErrorController`
- Error state exposed in `AssistanceStateEnum.error`
## ⚠️ Deprecated: Interactive Translation (IT)
> **Do not extend.** IT is being deprecated. Translation will become a match type within IGC.
The `it/` directory still contains `ITController`, `ITRepo`, `ITStepModel`, `CompletedITStepModel`, `GoldRouteTrackerModel`, `it_bar.dart`, `it_feedback_card.dart`, and `word_data_card.dart`. The choreographer still wires up IT via `_onOpenIT()` / `_onCloseIT()` / `_onAcceptContinuance()`, triggered when an `itStart` match is found. The `it_bar.dart` widget is still imported by `chat_input_bar.dart`.
This entire flow will be removed once testing confirms IT is no longer needed as a separate mode.