Skip to content

Améliorations qualité de complétion (handoff) — mots appris, proclitique nu, espacement FR, majuscule auto #2

Description

@titoo-dev

Contexte

Suite des travaux qualité de complétion (session 2026-06-19). Déjà livré sur main :

  • bbc69ba — fix panneau QML (font.familiesfont.family) : déblocage des frappes avalées.
  • 7685f71 — langue stricte : lang=fr exclut réellement l'anglais (ré-étiquetage par dominance + exclusion + filtre des mots appris).
  • ca4369baccord grammatical nombre/genre via Lefff (morph.tsv) + contexte de phrase via SurroundingText (fenêtre 8). Spec + plan : docs/superpowers/specs/2026-06-19-sentence-context-agreement-design.md et docs/superpowers/plans/2026-06-19-sentence-context-agreement.md.
  • 3db29a4élisions combinées (j'ai, c'est, qu'il, d'un, aujourd'hui…) ajoutées au vocab (scripts/extract_elisions.py), donc proposées ET exploitées par les n-grammes.

Ce ticket regroupe 4 améliorations restantes (audit UX), classées par priorité retenue.

Méthode de test (rappel)

  • Daemon en direct : printf '{"context":["..."],"prefix":"..."}\n' | nc -U /tmp/ime-predictord.sock (réponse JSON candidates). {"stats":true} → état. Daemon de test isolé : predictord <model>/words.tsv /tmp/test.sock (~3 s de chargement).
  • e2e : kwrite + wtype "...", vérifier le texte committé.
  • Build : nix build .#predictord / .#model ; e2e via nix build .#nixosConfigurations.thorfinn.config.i18n.inputMethod.package --override-input predictive-ime path:..

A — Mots appris trop dominants (priorité haute)

Problème. Un mot appris est offert avec un score 1e18 + count qui court-circuite la fréquence → un mot tapé 1-2 fois (j'aimerai) coiffe un mot du modèle bien plus fréquent (j'ai, 1,6M).

Code. daemon/predictord.cpp :

  • completePrefix : boucle userUnioffer(kv.first, 1e18 + double(kv.second)).
  • predictNext : boucle userBipush(p.first) (priorité absolue).

Approche proposée. Mettre le boost appris sur la même échelle que les scores du modèle au lieu d'un plancher écrasant : p.ex. multiplier le prior fréquence du mot par un facteur fonction de la confiance (userTrusted/count) plutôt que d'ajouter 1e18. Un mot appris fréquent reste devant ; un mot appris rare ne bat plus un mot modèle massivement plus courant. Garder une borne min pour que l'apprentissage reste visible.

Test. {"context":[],"prefix":"j'a"}j'ai devant j'aimerai (si j'aimerai peu appris). Non-régression : un mot vraiment appris souvent reste prioritaire.

B — Proclitique nu rétrogradé (priorité moyenne, petit)

Problème. Taper j' propose j' (le proclitique seul) en tête, avant j'ai/j'aime. Idem c', qu', d', n', s', t', m', l'.

Code. daemon/predictord.cpp completePrefix (classement des candidats exacts).

Approche. Rétrograder (petit ÷K, pas d'exclusion) les candidats qui sont un proclitique d'élision nu (forme = proclitique + apostrophe finale, rien après) quand le préfixe vaut ce proclitique — ils sont rarement le mot final voulu. Liste des proclitiques déjà connue (cf determiner/extract_elisions.py).

Test. {"context":[],"prefix":"j'"}j'ai/j'aime remontent au-dessus de j' nu.

C — Espacement typographique français (priorité moyenne)

Problème. frenchSpacing=false : pas d'espace fine insécable (U+202F) avant ; : ! ? ni à l'intérieur des guillemets « … ». Convention typographique FR.

Code. engine/predict.cpp — il existe déjà un usage de U+202F (ic->commitString("\xE2\x80\xAF")) et le réglage frenchSpacing (config). À câbler : sur insertion de ; : ! ? », préfixer d'une fine insécable ; après «, en ajouter une.

Approche. Implémenter le comportement derrière frenchSpacing (défaut à décider — proposer true mais réglable ; certaines apps/contextes code n'en veulent pas). Gérer le cas où l'utilisateur a déjà tapé l'espace.

Test. e2e kwrite : taper bonjour !bonjour ! avec fine insécable (U+202F) avant !.

D — Majuscule automatique (priorité moyenne, petit)

Problème. autoCapitalize=false : pas de capitale en début de champ ni après . ! ?.

Code. engine/predict.cppcapFirst/applyCase existent déjà ; commitWord capitalise en début de phrase uniquement quand state->ctx est vide. Étendre : détecter une fin de phrase (. ! ?) dans le contexte/SurroundingText et capitaliser le mot suivant.

Approche. Derrière autoCapitalize (réglable) : si le dernier token de contexte non vide se termine par . ! ? (ou contexte vide), capitaliser la 1ʳᵉ lettre du mot committé. Ne pas toucher aux mots déjà capitalisés ni aux sigles.

Test. bonjour. commentComment capitalisé.


Notes transverses

  • Tous les boosts/pénalités restent multiplicatifs, jamais de filtre dur (cohérent avec l'accord et la langue stricte ; ne jamais renvoyer 0 sauf exclusion de langue explicite).
  • Réglages exposés dans config.json (hot-reload) + documentés au README, comme langBoost/agreeBoost.
  • Suivi annexe (hors de ce ticket) : ajouter des tests committés dans daemon/test_predict.py pour accord/élisions/langue (jusqu'ici vérifiés via socket/e2e ad-hoc) ; et l'edge « les petits cha → chaque » (déterminant singulier non pénalisé).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions