- TypeScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
vibe 3.0.2 (mistralai_vibe_local_harness/vibe/adapters/mistral.py) sends
images in tool messages as chunked content with the correct format:
{"type": "image_url", "image_url": {"url": "data:..."}}
Two bugs fixed:
1. user messages: image_url was a plain string, now an object {"url": ...}
2. tool messages: images were flattened to text (wrong). They are now sent
as chunked content (list of blocks) with the correct snake_case
image_url object format — matching vibe 3.x behavior. When the model
does not support images, content falls back to a plain string with a
placeholder.
The previous 422 error was caused by camelCase "imageUrl" (API expects
snake_case "image_url"), not by array content on tool messages.
|
||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| index.ts | ||
| package.json | ||
| README.md | ||
| stream.ts | ||
| vibe-config.ts | ||
| wire.ts | ||
mistral-vibe (pi provider)
Provider pi mistral-vibe qui parle à l'API Mistral exactement comme le
harness mistral-vibe : mêmes headers HTTP, même mécanisme de reasoning,
mêmes modèles — de sorte que, vu du backend Mistral, les requêtes sont
indiscernables de celles émises par vibe.
Installation
Aucune : l'extension est auto-découverte depuis ~/.pi/agent/extensions/
(dossier git versionné). Après un changement, recharger avec /reload (dans
pi) — le fichier ~/.vibe/config.toml et ~/.vibe/.env sont relus à chaque
chargement.
Utilisation
pi -m mistral-vibe/mistral-vibe-cli-latest # défaut harness (= mistral-medium-3.5)
pi -m mistral-vibe/mistral-large-4-0 # Mistral Large 4
pi -m mistral-vibe/zai-glm-5-2 # modèle de ta config vibe
pi -m mistral-vibe/zai-glm-5-3 # modèle de ta config vibe
# NB: les providers d'extensions n'apparaissent pas dans `pi --list-models`;
# vérifier l'enregistrement via le sélecteur /model ou /mistral-vibe-status.
Dans l'UI : /model puis sélection, ou Ctrl+P. Statut du provider :
/mistral-vibe-status
Sources de configuration (lues au chargement)
| Donnée | Source |
|---|---|
| Clé API | env MISTRAL_API_KEY, puis ~/.vibe/.env (là où vibe persiste sa clé) |
api_base |
~/.vibe/config.toml → [[providers]] mistral (défaut https://api.mistral.ai/v1) |
| Modèles | ~/.vibe/config.toml → [[models]] dont le provider tourne sur le backend mistral + le modèle par défaut du harness mistral-vibe-cli-latest (= mistral-medium-3.5) |
| Prix | USD officiels (mistral.ai/pricing) : hardcodés pour mistral-vibe-cli-latest ($1.5 / $0.15 cache / $7.5), lus du config.toml pour les modèles de la config (GLM 5.2/5.3 : $1.4 / $0.14 cache / $4.4) |
| Version UA | paquet installé (uv/tools/mistral-vibe/.../vibe/__init__.py), fallback constant |
Seuls les modèles du backend mistral sont enregistrés (les modèles deepseek/llamacpp de la config sont ignorés). Les entrées de la config remportent sur les défauts du harness en cas de doublon d'id.
Config locale de l'extension (config.local.json)
Fichier gitignoré à côté de l'extension
(~/.pi/agent/extensions/mistral-vibe/config.local.json), relu au
/reload. JSON strict (pas de commentaires). Options :
| Clé | Défaut | Effet |
|---|---|---|
sendTelemetryMetadata |
true |
envoie le bloc de télémétrie metadata dans chaque requête. Parité wire — vibe l'envoie toujours, même avec enable_telemetry = false (ce réglage ne coupe que le canal d'événements séparé) — mais le bloc décrit le CLI vibe (agent_entrypoint: "cli", client_name: "vibe_cli") alors que pi n'est pas ce client. false = moins de fausse télémétrie, au prix d'une divergence wire visible (champ absent d'une requête vibe). |
Override ponctuel sans toucher au fichier : MISTRAL_VIBE_METADATA=off|on
(prime sur le fichier). Le réglage effectif est visible dans
/mistral-vibe-status (ligne telemetry block).
Configurer un modèle dans Mistral Vibe (~/.vibe/config.toml)
Pour qu'un modèle soit proposé par le plugin, il doit avoir une entrée
[[models]] dans ~/.vibe/config.toml dont le provider tourne sur le backend
mistral. Exemple complet :
# Provider mistral — optionnel : ces valeurs sont les défauts du plugin.
# À ne déclarer que pour surcharger api_base ou la variable de clé.
[[providers]]
name = "mistral"
api_base = "https://api.mistral.ai/v1"
api_key_env_var = "MISTRAL_API_KEY"
backend = "mistral"
[[models]]
name = "zai-glm-5-3" # id envoyé sur le wire (champ `model`) et id pi
alias = "zai-glm-5-3" # nom affiché (optionnel, défaut : name)
provider = "mistral" # ⚠️ doit être sur le backend mistral
thinking = "high" # off | minimal | low | medium | high | xhigh | max
temperature = 1.0
input_price = 1.4 # $/Mtok — utilisés pour le cost tracking pi
output_price = 4.4
cached_input_price = 0.14
supports_images = false
auto_compact_threshold = 300000 # seuil de compaction vibe, PAS la fenêtre réelle
Règles de reconnaissance :
- Backend mistral obligatoire : seules les entrées
[[models]]dont le provider estmistral(ou un[[providers]]avecbackend = "mistral") sont enregistrées. Les modèles sur un backendgeneric(deepseek, scaleway, opencode-zen…) sont ignorés. name= identifiant : c'est l'id pi (pi -m mistral-vibe/<name>) et la valeurmodelenvoyée à l'API Mistral.aliasne sert qu'à l'affichage.- Doublons : une entrée
[[models]]de mêmenamequ'un modèle intégré du harness (ex.mistral-vibe-cli-latest) remplace celui-ci ; entre deux entrées de la config, la première gagne. - Clé API : résolue comme vibe — variable d'env (par défaut
MISTRAL_API_KEY, cf.api_key_env_vardu provider), puis~/.vibe/.env. - Prix :
input_price/output_price/cached_input_priceen USD par Mtok ; à défaut, 0. - Fenêtre de contexte :
auto_compact_thresholdn'est pas la fenêtre réelle — la vraie valeur vient deKNOWN_MODEL_SPECS(voir section suivante), sinon repli surauto_compact_thresholdou 200k. Surchargeable viamodels.jsonde pi. - Après toute modification du toml :
/reloaddans pi (la config est relue à chaque chargement de l'extension) et vérifier avec/mistral-vibe-status.
Contrat wire (ce que le provider envoie)
Parité cible : vibe 3.0.2 agent turns (mistralai_vibe_local_harness/vibe/ adapters/mistral.py + SDK mistralai 2.6.0).
Headers, par requête chat streaming :
POST {origin}/v1/chat/completions
authorization: Bearer <key>
content-type: application/json
accept: text/event-stream
user-agent: speakeasy-sdk/python 2.6.0 2.884.13 1.0.0 mistralai
x-affinity: <session_id>
User-Agent : vibe 3.x envoie le User-Agent par défaut du SDK Mistral sur les appels agent (pas le UA vibe CLI). Le UA vibe (
mistral-client-python/Mistral-Vibe/<version>) n'est envoyé par vibe que sur les completions utilitaires (titres, narration, probe).
Body (mirroir de la sérialisation SDK mistralai 2.6.0 / vibe 3.0.2) :
model,messages(ThinkChunk rejoués pour le multi-tour ; champs en snake_case :tool_calls,tool_call_id,image_url— fetch brut sans SDK),stream: truereasoning_effort:none(minimal/low) /high(medium/high/xhigh/max) ; champ omis si thinkingoff(vibe 3.x n'envoie"none"pouroffque quand lesthinking_levelsdu modèle sont curatés pour inclure"off"; pi ne connaît pas cette curation, donc omission — comme vibe sur un modèle non curaté)temperature: toujours envoyée avec la valeur de la config vibe ([[models]] temperature) ; repli : options pi explicites, sinon défautModelConfig0.2metadata: bloc de télémétrie vibe (os, arch, os_version, version, session_id, call_typemain_call, call_sourcevibe_code, host_kindlocal, agent_entrypointcli, client_namevibe_cli…) — désactivable viaconfig.local.json(sendTelemetryMetadata: false, divergence documentée ; vibe l'envoie inconditionnellement, même avecenable_telemetry = false)max_tokens: la valeur pi (clampée à la fenêtre de contexte) — dérive volontaire : vibe 3.x calcule un cap dynamique et omet le champ quand il n'y en a pas, mais pi s'en sert pour protéger la fenêtre de contextetools: tableau quand il y a des outils,parallel_tool_calls: truetoujours envoyé avec (vibe 3.x) ; champ omis quand il n'y a pas d'outils (vibe 3.x omet les champs unset, contrairement à vibe 2.26 qui envoyaitnullexplicite)top_p: envoyé si configuré viasamplingParamsde pi (models.jsonou options de requête)reasoning_effort,tools,max_tokens: champs omis quand unset (vibe 3.x ne passe le kwarg au SDK que quand il a une valeur ; le SDK omet les champsUNSETdu JSON)- pas de
prompt_cache_key(vibe ne l'envoie pas)
Différences volontaires avec le provider mistral natif de pi :
mistral (pi natif) |
mistral-vibe |
|
|---|---|---|
reasoning_effort |
seulement pour 3 ids de modèles, sinon prompt_mode |
tous les modèles (none/high, omis si off) |
temperature |
non gérée | valeur de la config vibe |
metadata |
non envoyé | bloc télémétrie vibe |
prompt_cache_key |
envoyé si cache retention | jamais |
x-affinity |
si cache retention | toujours (comme vibe) |
| User-Agent | SDK pi | SDK Mistral (speakeasy-sdk/python …) |
parallel_tool_calls |
non envoyé | true quand tools présents |
Vérification de la parité
/mistral-vibe-status→ base URL, User-Agent, modèles, source de clé.- Debug wire :
MISTRAL_VIBE_DEBUG=1 pi …(ou dans une session interactive) → une ligne JSON par requête dans/tmp/mistral-vibe-debug.log(MISTRAL_VIBE_DEBUG_LOGpour changer le chemin) : request (url, headers,reasoning_effort, température, x-affinity,prompt_cache_key), premier delta thinking, usage/cost et stopReason audone. Vérifie reasoning, cache et pricing sans toucher au code. - Comparer le body/headers émis : hooks pi
before_provider_request/before_provider_headers. - Test réel :
pi -p "…" -m mistral-vibe/zai-glm-5-2ouzai-glm-5-3(consomme du crédit).
Fenêtre de contexte des modèles
La config vibe (auto_compact_threshold) est un seuil d'auto-compaction, pas
la fenêtre réelle du modèle. L'extension utilise donc des specs connues
(KNOWN_MODEL_SPECS, d'après docs.mistral.ai), avec repli sur
auto_compact_threshold :
| Modèle | Context window | Max output |
|---|---|---|
| zai-glm-5-2 | 1 000 000 | 128 000 |
| zai-glm-5-3 | 1 000 000 | 128 000 |
| mistral-large-4-0 | 1 048 576 | 262 144 |
| mistral-vibe-cli-latest (medium-3.5) | 262 144 | 16 384 |
Toute valeur reste surchargeable via models.json de pi
(mistral-vibe/<id>.contextWindow / maxTokens).
| Modèle | Context window | Max output |
|---|---|---|
| zai-glm-5-2 | 1 000 000 | 128 000 |
| zai-glm-5-3 | 1 000 000 | 128 000 |
| mistral-large-4-0 | 1 048 576 | 262 144 |
| mistral-vibe-cli-latest (medium-3.5) | 262 144 | 16 384 |
Thinking
- Niveau par défaut pi :
medium→reasoning_effort: "high"envoyé (comme la config vibethinking = "high"de zai).:low→"none",:off→ champ omis (vibe 3.x ometreasoning_effortquand le niveau estoffet que les niveaux du modèle ne sont pas curatés). - La température envoyée est celle de la config vibe (1.0 pour zai/large-4) — vibe 3.x ne force pas la température.
- Le modèle peut légitimement ne pas émettre de thinking sur une question
triviale (GLM 5.2 décide) ;
contentTypesdu log montre["text","thinking","text"]quand il raisonne.
Dépannage
- Le modèle ne voit ni le system prompt ni les outils (il répond comme un
chatbot nu, sans contexte pi) : régression de compatibilité avec pi 0.86.0,
qui est passé d'un
Context(systemPrompt/tools) à unTranscriptContextnormalisé. Le provider lit désormais le prompt et les outils viacollapseSystemMessages()/getCurrentSystemPrompt()/getCurrentTools()(voirstream.ts). Vérifier avecMISTRAL_VIBE_DEBUG=1:systemPromptCharsettoolCountdoivent être > 0 dans le logrequest. No API key ...: la clé doit être dans l'env ou~/.vibe/.env(variableMISTRAL_API_KEY— leapi_key_env_vardu provider config est respecté si tu l'as surchargé).- Modèle absent : il n'est pas sur le backend mistral dans
~/.vibe/config.toml(ex. les modèles deepseek) → normal. - Overflow de contexte : normalisé vers
context_length_exceededpour déclencher l'auto-compaction de pi (handlermessage_endscoped au provider). - Changements de config vibe :
/reloaddans pi.
Développement
Repo git autonome : ~/.pi/agent/extensions/mistral-vibe/.
wire.ts— helpers wire purs (aucun import pi-ai) : headers, mapping reasoning, transform de messages, parsing SSE. Testable isolément.vibe-config.ts— lecture de~/.vibe(config.toml, .env, version).stream.ts—streamSimple(fetch brut + parsing SSE + events pi). Reçoit unTranscriptContextpi >= 0.86 et en extrait prompt système et outils avant construction de la requête.index.ts— chargement,registerProvider, commande de statut, handler d'overflow.
Pas de dépendances npm (parser TOML minimal embarqué).
Compatibilité pi
| pi | Contexte provider | État |
|---|---|---|
| < 0.86 | Context avec systemPrompt / tools |
historique |
| >= 0.86 | TranscriptContext (prompt + outils dans les messages système) |
supporté |