bots-garden/mini-mepublic Fork
⑂ main
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. eca9ec1 · on main · k33g · 7d ago
README.fr.md · 123 lines · 7.3 KBmarkdown
Blame HistoryOpen raw

mini-me depuis Go : le paquet minime

English version

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

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.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 dit mm 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_user n'est pas proposé.

Tests

cd demo.acp.sdk/go
go test ./...         # 40 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. 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.

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 mm avec, pour chaque plateforme.
  • session/load / session/resume (sessions enregistrées), images et fichiers joints dans les prompts, formulaires ask_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
# mini-me depuis Go : le paquet `minime`

*[English version](README.en.md)*

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](../nodejs/README.fr.md). 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`](../README.fr.md) pour les messages ACP qui se cachent derrière.

```go
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

```bash
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
```

`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é :

```bash
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.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 dit `mm 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_user` n'est pas proposé.

## Tests

```bash
cd demo.acp.sdk/go
go test ./...         # 40 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. `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. |

## 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 `mm` avec, pour chaque plateforme.
- `session/load` / `session/resume` (sessions enregistrées), images et fichiers joints dans les prompts, formulaires `ask_user` : non exposés.