mini-me depuis Go : le paquet minime
Un client Go qui utilise mini-me comme bibliothèque en pilotant mm -acp par ACP (ticket 168). C'est le jumeau Go du client Node.js. C'est un module à part (minime), construit sur le SDK ACP que mm utilise lui-même, github.com/coder/acp-go-sdk v0.13.5, qui n'a lui-même aucune dépendance. Voir ../README.fr.md pour les messages ACP qui se cachent derrière.
agent, err := minime.Start(ctx, minime.Options{Config: "agent.yaml"})
if err != nil {
log.Fatal(err)
}
defer agent.Close()
res, err := agent.Complete(ctx, "Explain this repository") // sans stream
for ev, err := range agent.Stream(ctx, "And the tests?") { // en stream
if err != nil {
log.Fatal(err)
}
if ev.Kind == minime.KindText {
fmt.Print(ev.Text)
}
}
ctx, cancel := context.WithTimeout(ctx, 3*time.Second) // annulé au bout de 3 s
defer cancel()
res, err = agent.Complete(ctx, "A long task") // res.StopReason == "cancelled"
Lancer les exemples
llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
llmman serve
cd demo.acp.sdk/go
go run ./examples/complete # sans stream
go run ./examples/stream # en stream, avec les appels d'outils
go run ./examples/cancel # annulé au bout de 3 secondes
go run ./examples/permission # une commande refusée, puis autorisée
go run ./examples/clienttools # une liste de tâches dans ce programme, proposée à l'agent sous forme de deux outils
mm doit être dans le PATH, ou désigné par MM_BIN. Les exemples utilisent agent.yaml (Qwen3.5 4B sur llmman, bash seulement, yolo: false) ; tout fichier d'agent fonctionne. Go 1.24 ou plus récent (range-over-func).
Pour utiliser le paquet depuis un autre module, tant qu'il n'est pas publié :
go mod edit -require=minime@v0.0.0 -replace=minime=../chemin/vers/demo.acp.sdk/go
go mod tidy
L'API
minime.Start(ctx, minime.Options{...}) (*Agent, error) |
lance mm -acp -config <Config> (rendu absolu) dans Cwd, puis initialize et session/new {cwd}, en 60 s maximum. |
Options.Config |
le fichier d'agent. Obligatoire. |
Options.Cwd |
le répertoire de travail de la session, là où les outils s'exécutent. Par défaut : le répertoire courant. |
Options.MM |
le binaire mm. Par défaut : $MM_BIN, puis mm dans le PATH. |
Options.OnPermission func(ctx, ToolCall) Decision |
appelé quand l'agent veut lancer un outil qui demande une confirmation (yolo: false). Sans lui, chacun de ces appels est refusé. |
Options.Tools []Tool |
des fonctions Go proposées à l'agent comme outils (les outils du client ACP de mm, acp.clientTools, activés par défaut). Le modèle voit client_<Name> ; chaque appel exécute Handle. Start refuse un outil sans Name, sans Handle, un nom en double, ou une Method qui ne commence pas par _. |
Options.ToolDiscovery |
minime.DiscoveryMeta (par défaut : les outils voyagent dans initialize) ou minime.DiscoveryRequest (seul true voyage ; la liste est donnée en réponse à _org.mini-me/tools/list). Doit correspondre à acp.clientTools.discovery dans le fichier d'agent. |
Options.OnStderr func(line string) |
le stderr de mm, ligne par ligne (bannière, avertissements, lignes [acp]). Sinon il est lu puis jeté ; sa dernière ligne est ajoutée aux erreurs. |
agent.Complete(ctx, prompt) (Result, error) |
un tour ; renvoie Result{Text, StopReason, Usage} une fois terminé. |
agent.Stream(ctx, prompt) iter.Seq2[Event, error] |
un tour sous forme de séquence range-over-func ; le dernier événement est KindDone. Sortir de la boucle avant la fin annule le tour. |
annuler ctx |
envoie session/cancel ; le tour se termine avec StopReason: "cancelled", le texte déjà reçu et une erreur nil. Si mm ne répond pas sous 30 s, le tour se termine par une erreur. |
agent.Reset(ctx) error |
une nouvelle session sur le même processus : la conversation repart de zéro. |
agent.Close() error |
ferme le stdin de mm et attend qu'il se termine (le tue au bout de 5 s). L'appeler deux fois est sans effet. |
agent.SessionID() string |
l'identifiant de session ACP. |
minime.ErrBusy |
renvoyée par un tour ou un Reset pendant qu'un autre tour tourne sur le même Agent. |
Decision, la réponse de OnPermission :
minime.Allow |
exécuter l'outil cette fois (allow_once) |
minime.Reject, ou la valeur zéro Decision{} |
le refuser cette fois (reject_once) |
minime.ChooseOption(id) |
une des options proposées par mm, par son id : "allow", "allow_always" (cet outil, pour la session), "reject", "never" (cet outil, pour la session). Un id que mm n'a pas proposé vaut Reject. |
Un callback qui panique vaut Reject. Son ctx est celui du tour.
Les événements (Event.Kind) :
| Kind | Champs remplis |
|---|---|
KindText |
Text : un morceau de la réponse |
KindThinking |
Text : un morceau de la réflexion d'un modèle qui « réfléchit » |
KindToolCall |
Call : ID, Title (par ex. bash: ls), Kind (execute, read, edit…), Input (les arguments) |
KindToolResult |
Call.ID, Status (completed ou failed), Output |
KindDone |
StopReason, Usage (nil quand mm n'en a pas envoyé), Text : toute la réponse |
Les règles à connaître :
- Un Agent = un processus mm = une conversation. Chaque appel la continue, comme dans un éditeur. Un seul tour à la fois par Agent (
ErrBusy) ; lancez plusieurs Agents pour travailler en parallèle. - Un tour que mm termine par une erreur la renvoie, avec les mots de mm et la dernière ligne de son stderr, par exemple
minime: turn failed: Internal error (-32603): the model repeated the same bash call 6 times…. Si mm a planté, l'erreur ditmm exited: exit status N. - mm retire de la conversation une question annulée : la question suivante n'en sait rien.
- Le client n'annonce aucune capacité fichiers, terminal ou formulaire : mm n'en a pas besoin, et son outil
ask_usern'est pas proposé. Sa seule capacité est_meta["org.mini-me/tools"], quandOptions.Toolsen contient.
Tool, une entrée de Options.Tools :
| Champ | |
|---|---|
Name |
obligatoire, unique ; le modèle voit client_<Name>. |
Description |
ce que le modèle lit pour décider quand l'appeler. |
Method |
la méthode d'extension ACP qu'appelle mm ; doit commencer par _. Par défaut : _minime.go/tools/<Name>. |
InputSchema map[string]any |
schéma JSON des arguments (type: object) ; optionnel. |
Handle func(ctx, ToolCallRequest) (string, error) |
obligatoire. ToolCallRequest{SessionID, Arguments}. Le texte retourne au modèle ; une erreur (ou un panic) retourne comme un appel en échec (isError: true) et le client continue. Les appels peuvent s'exécuter en parallèle. |
Avec yolo: false dans le fichier d'agent, chaque appel passe d'abord par OnPermission, avec le titre client: <Name>.
Tests
cd demo.acp.sdk/go
go test ./... # 47 tests et sous-tests, sans modèle ni mm, ~1 s
Le binaire de test joue un faux mm (fakemm_test.go) : relancé avec MINIME_FAKE_MM=1, il répond en ACP avec les formats de messages de mm (quatre options de permission, rawOutput.output, usage dans la réponse au prompt) : des réponses, une réflexion, un appel d'outil avec sa permission, un tour lent à annuler, un tour sourd à l'annulation, une erreur, un plantage ; et, comme mm, il découvre les outils du client (dans initialize, ou par _org.mini-me/tools/list à session/new) et en appelle un. go test -race demande cgo et un compilateur C.
Mesures (2026-11-01, llmman sur la machine de l'auteur, mm v1.0.3 compilé depuis l'arbre de travail)
| Exemple | Résultat |
|---|---|
complete |
réponse en une phrase, end_turn, 663 tokens, 8,3 s (modèle à froid) |
stream |
find examples -name "*.go" | wc -l autorisé par le callback (lecture seule), résultat 4, réponse streamée morceau par morceau, end_turn |
cancel |
histoire coupée au bout de 3,0 s (cancelled, 394 caractères gardés) ; la question suivante sur le même Agent a eu sa réponse aussitôt, sans connaître l'histoire, que mm a retirée |
permission |
pwd refusé : le modèle a inventé une réponse (/root) ; autorisé : le vrai répertoire. Un refus n'empêche pas un petit modèle de deviner. |
Ajouté le 2026-11-10 (même machine et même modèle, mm compilé depuis la branche feature/acp-client-tools) :
| Exemple | Résultat |
|---|---|
clienttools |
client_add_todo appelé deux fois (buy milk, write the blog post), client_list_todos deux fois ; la réponse liste les deux éléments, et la liste du programme les contient. Même résultat avec ToolDiscovery: DiscoveryRequest et acp.clientTools.discovery: request. |
Quand le choisir, et quand ne pas le choisir
- Ce client a besoin du binaire
mm, mais il marche dès aujourd'hui sans rien changer à mm, et il isole chaque agent dans son propre processus (l'état global de mm ne pose aucun problème). - Le ticket 139 (un SDK en mémoire) n'aurait pas besoin du binaire, mais il faudrait d'abord faire du code de mm une bibliothèque importable.
Pas fait
- Pas de module Go publié : il faudrait livrer le binaire
mmavec, pour chaque plateforme. session/load/session/resume(sessions enregistrées), images et fichiers joints dans les prompts, formulairesask_user: non exposés.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
|