Docs Outils

Bulles de dialogue et modèles locaux

Faites parler n’importe quelle unité du jeu, sous n’importe quelle identité, dans une bulle au-dessus de sa tête. Branchez un LLM local : une phrase entre, une réponse s’affiche au-dessus de l’unité.

Les bulles sont une couche destinée au spectacle : elles n’influent pas sur l’issue de la partie, et se prêtent au streaming, au commentaire et au débogage.

  • n’importe quelle unité peut parler, sous n’importe quelle identité ; plusieurs unités peuvent parler en même temps ;
  • taille de police, couleur, largeur, pointe, transparence et vitesse de frappe se règlent bulle par bulle ;
  • branchement direct sur un LLM local (LM Studio), avec sortie en streaming : la bulle se met à jour au fil de la génération.

Depuis un Bot

Le plus simple est le say fourni avec le SDK :

g.say(hero, "Avec moi, chargez !", seconds=4)

Démarrage et interface

Le plus simple : le « Centre de contrôle », page d’accueil de Farsight — cliquez d’abord sur « LLM local → Démarrer et charger le modèle » (serveur local LM Studio + chargement du modèle configuré en mémoire vidéo), puis sur « Bulles de dialogue → Démarrer ». Les cartes permettent aussi de consulter les journaux, d’arrêter et de redémarrer.

L’interface se trouve dans la page « Bulles de dialogue », à gauche dans Farsight : faire parler les unités (choisir une unité, écrire le texte, régler le style, dialoguer avec le modèle), pause-café des paysans, dialogues à la caméra, déclenchement par la partie, réglages du modèle ; tout s’applique à l’instance choisie dans la barre du haut.

Également en ligne de commande :

python speech/speak_launch.py              # lance le serveur de modèle local + charge et préchauffe le modèle + lance l'API des bulles
python speech/speak_launch.py --restart    # relance l'API après une modification du code
python speech/speak_launch.py --stop       # arrête l'API et décharge le modèle de la mémoire vidéo

Chaque étape est ignorée si elle est déjà faite : relancer la commande n’a aucun effet de bord.

API HTTP

Adresse par défaut : http://127.0.0.1:8872/ (le port est défini par ports.speech dans openwar3.json) ; n’importe quel programme peut l’appeler.

Faire parler une unité POST /api/say

{
  "inst": 16,
  "bubbles": [
    { "unit": "0x14A12614", "name": "Roi de la montagne", "text": "Avec moi, chargez !" },
    { "unit": "0x14A12924", "name": "Archimage", "text": "Je m'occupe du Blizzard.",
      "style": { "font_px": 26, "text_color": "#FFE080", "bg_color": "#C0102040" } },
    { "screen": [960, 110], "key": 1, "name": "Narrateur", "text": "Première vague d'orcs dans 30 secondes.",
      "style": { "tail": false, "type_ms": 0 } },
    { "world": [-4684, 2644], "key": 2, "text": "Point de ralliement", "style": { "font_px": 16 } }
  ]
}
ChampDescription
unit / world / screenUn seul des trois : suivre une unité (collée juste au-dessus de sa barre de vie si elle en a une) / coordonnées sur la carte / pixels à l’écran (pour la narration)
nameNom de l’orateur affiché en première ligne ; libre, pas forcément celui de l’unité
textTexte principal, avec retour à la ligne automatique
duration_msDurée d’affichage ; 0 = automatique, 3 à 5 secondes
keyIdentifiant d’une bulle monde / écran : un nouveau message avec la même key remplace l’ancien
updateSi la même bulle existe déjà, ne remplace que le texte, sans réinitialiser la minuterie (pour le streaming)
stylefont_px, max_width_px, text_color, bg_color, border_color, tail, side_px, opacity, type_ms, font…

32 bulles au maximum en même temps ; coût moyen d’environ 0.1 à 0.2 ms par frame.

Dialoguer avec le modèle local POST /api/chat

{
  "inst": 16, "unit": "0x14A12614", "name": "Roi de la montagne",
  "persona": "Vous incarnez Muradin, le Roi de la montagne de Warcraft : jovial, amateur de bière. Une ou deux phrases parlées, pas plus de 40 caractères.",
  "message": "Il y a une bande d'ogres devant, on charge ou pas ?",
  "stream": true
}

Renvoie {"reply": "...", "first_token_ms": 283, "total_ms": 342} ; au même moment, la réponse s’affiche déjà au-dessus de l’unité. Chaque unité garde en mémoire ses 6 derniers échanges.

Autres

InterfaceDescription
GET /api/instancesParties en cours
GET /api/units?inst=16&mine=true&heroes=trueListe des unités (avec nom chinois, coordonnées, points de vie)
POST /api/clearEfface une bulle ou toutes les bulles
GET /api/llm, POST /api/llmConsulter / modifier la configuration du modèle (base_url, model, max_tokens, temperature)
POST /api/banterPause-café des paysans : les ouvriers de la base râlent à tour de rôle selon leur personnage, et annoncent le début de la partie (toutes les infos de jeu sont réelles)
POST /api/camtalkDialogues à la caméra : les héros et leur escorte visibles à l’écran dialoguent selon leur rôle
POST /api/eventsDéclenché par la partie : début de combat, fin de combat, mort d’un héros, montée de tier, bâtiment détruit… on ne parle que lorsqu’il se passe quelque chose

Choisir un modèle local

Mesuré sur une RTX 5090 (5 répliques de jeu) :

ModèleVRAMVitesseUne réponseVerdict
Qwen3.6-35B-A3B (MoE, seulement 3B actifs à chaque pas), Q4, réflexion désactivée20.6 GBEnviron 142 token/sEnviron 0.3 seconde (premier token vers 0.27 seconde)Recommandé : rapide, jeu de rôle naturel en chinois
gpt-oss-20b (MXFP4), raisonnement low11.3 GBEnviron 280 token/s0.3 à 0.8 secondeQuand la VRAM est juste ; chinois un peu plat
Qwen3.6-27B (dense), Q417.2 GBEnviron 39 token/sToujours en réflexion après 5.5 secondesInadapté au dialogue en temps réel
  • La vitesse dépend des « paramètres actifs à chaque pas », pas du total : le MoE de 35B n’en active que 3B et va 3 à 4 fois plus vite que le 27B dense.
  • Désactivez impérativement la « réflexion » : sinon tous les tokens partent dans la réflexion, et pas un mot de réponse ne sort.
  • La bulle s’écrit caractère par caractère, à environ 22 caractères par seconde : la vitesse de génération n’est plus le goulot d’étranglement. Ce qui compte vraiment pour l’expérience, c’est la latence du premier token.

Des répliques qui sonnent « vrai »

Ne donnez au modèle que des données réelles sur la partie (nombre de parties, victoires et défaites, effectifs, stocks), et précisez explicitement « n’utilisez que ces faits ». En test, sans cette contrainte, le modèle invente des combats qui n’ont jamais eu lieu.