
Le 12 octobre, mon Mac a saturé. La charge système oscillait entre 100 et 260, et les outils commençaient à échouer avec ENFILE : le système refusait d'ouvrir un fichier de plus. Plusieurs sessions d'agents travaillaient en parallèle, chacune avec ses builds natifs, ses conteneurs Docker ou son simulateur mobile. Tout ça sur un MacBook Pro M1 Max avec 64 Go de mémoire.
Ce Mac est le premier serveur d'Orazaka, le moteur d'IA que je construis. L'idée d'Orazaka est simple : faire tourner l'IA sur une machine que vous contrôlez. Pour moi, cette machine, c'est d'abord mon portable. Voici ce qu'il m'a appris.
Une plateforme d'IA, c'est beaucoup de monde dans une petite cuisine
Même sans IA, une plateforme a besoin de plusieurs choses à la fois : une base de données, un courtier de messages, les services Java eux-mêmes. Ajoutez un serveur de modèles de langage, un moteur d'images, un worker pour la vidéo. Tout ce monde partage la même mémoire. Quand deux gros plats cuisent en même temps, le four sature.
Ma réponse du 12 octobre a été très simple, presque bête : un verrou. Avant une tâche lourde (un build natif, une pile Docker, une génération), une session crée un dossier .heavy.lock. Si le dossier existe déjà, elle attend une minute et réessaie. Comme la clé unique des toilettes d'un café : une personne à la fois, les autres patientent. Ça ralentit un peu tout le monde, mais plus rien ne s'effondre.
Les services Java ont aussi leur part. Orazaka compte jusqu'à onze services Spring Boot. Une proposition ouverte (orazaka#132) estime 4 à 6 Go au repos pour l'ensemble et propose un profil de développement avec une seule JVM. Ce sont des estimations, pas des mesures, et ce n'est pas encore livré.
Le modèle vidéo qui ne rentre pas
J'ai voulu générer de la vidéo en local avec Wan 2.2 TI2V 5B, en format vertical 480×832 et 25 images. Le modèle a manqué de mémoire au moment de décoder : il demandait 26 Gio alors que 62 Gio étaient déjà occupés sur une machine de 64 Go. En 704×1280, même résultat. Quand ça avançait, chaque étape prenait environ 32 secondes, d'après mes notes de travail.
La leçon est claire : la mémoire unifiée du Mac est généreuse pour l'image, pas pour la vidéo. On y reviendra.
Spring Boot 4, Spring AI 2, et les modèles qui deviennent des données
En juin, j'ai migré la plateforme vers Spring Boot 4.0 et Spring AI 2.0 (ADR-030). Ça impose Spring Framework 7 et Jackson 3, qui déplace com.fasterxml.jackson vers tools.jackson. Le changement de version m'intéressait moins que ce qu'il permettait.
Avant, le choix du modèle vivait dans la configuration : un bloc spring.ai.* dans application.yml, et des starters qui configurent les clients à votre place. Changer de modèle voulait dire redéployer. J'ai retiré tout ça. Aujourd'hui, une table ai_providers contient l'adresse et la clé de chaque fournisseur, et une table orazaka_models relie un nom de modèle à son fournisseur. Un DynamicModelRouter lit ces lignes à chaque appel et garde le client en cache.
Pensez à un répertoire téléphonique. Avant, chaque numéro était gravé dans le mur. Maintenant, il est écrit dans le répertoire : on change une ligne, et le prochain appel part au bon endroit, sans travaux. Un administrateur modifie une ligne en base, et la plateforme suit.
Un détail rend ça possible : Ollama et LocalAI parlent le même protocole qu'OpenAI. Un seul client compatible OpenAI, pointé vers une adresse lue en base, suffit pour tous les fournisseurs locaux.
La ClassCastException qui a coupé le chat
Évidemment, ça a cassé. Tout passe par le client compatible OpenAI, qui attend des options de type OpenAiChatOptions. Mais une étape plus haut dans le pipeline étiquetait les fournisseurs locaux avec des OllamaChatOptions. Résultat : une ClassCastException, et plus de chat du tout.
Le correctif, dans RoutedChatModel, convertit les options avant l'appel :
static Prompt toOpenAiPrompt(Prompt prompt) {
ChatOptions options = prompt.getOptions();
if (options == null || options instanceof OpenAiChatOptions) {
return prompt;
}
OpenAiChatOptions.Builder builder =
OpenAiChatOptions.builder()
.model(options.getModel())
.temperature(options.getTemperature())
.maxTokens(options.getMaxTokens());
// ...tool callbacks, when present
return new Prompt(prompt.getInstructions(), builder.build());
}
Il y a eu un deuxième piège, plus discret. Une étape qui demandait qwen2.5:7b partait parfois vers l'inférence hébergée, parce que le nom du modèle était ignoré sauf pour deux fournisseurs. Depuis orazaka-ai-engine#13, le routage suit le lien du catalogue entre le modèle et son fournisseur. Et un modèle dont le fournisseur ne répond pas échoue avec une erreur qui nomme le modèle et le fournisseur, au lieu de partir ailleurs en silence. Pour une IA souveraine, ce silence-là est le pire bug possible.
Le disque qui oublie et le tunnel qui coupe
Deux autres problèmes sont venus du mélange entre le Mac et le cloud. La plateforme de dev tourne sur DigitalOcean, et le Mac la rejoint par un tunnel, à travers un relais.
Le premier : les médias générés étaient écrits sur le disque du conteneur, effacé à chaque déploiement. Les lignes en base survivaient, les fichiers disparaissaient. On passe maintenant par un stockage durable, avec un adaptateur Bunny (orazaka-ai-engine#21).
Le second : chaque déploiement redémarre le relais et coupe le tunnel. Un clip en cours de calcul était perdu, puis refait de zéro. Le worker se reconnecte désormais et ne traite jamais deux fois la même tâche (orazaka-worker-media#24).
Ce que le Mac fait bien
Pour tout le reste, le Mac est un très bon point de départ. Il fait le chat avec Ollama, la voix avec Piper, la musique avec ACE-Step et les images avec stable-diffusion.cpp. Une image FLUX.2 [klein] 4B en 1024×1024 prend 62 secondes en médiane, de la commande au fichier. Pas de facture, pas de compte cloud, pas de pilote à installer. Et tout nouveau développeur a déjà un portable.
Trois images sorties de cette machine. Sous chacune, « Comment c'est fait » donne le prompt, la graine, la durée et l'exécution Orazaka qui l'a produite.

Comment c'est fait
- Modèle
- FLUX.2 [klein] 4B (Q8_0 GGUF) · Apache-2.0
- Moteur
- stable-diffusion.cpp on Metal, 4 steps, guidance 1.0
- Machine
- MacBook Pro, Apple M1 Max, 64 GB (Metal)
- Via
- Orazaka Studio run on dev.orazaka.com
- Studio
art-miniature- Prompt
A tiny winter park in Montreal: figurines skating on a frozen pond, a small plain wooden cabin with a glowing window and no signboard, snow-covered spruce trees and warm string lights at dusk. Tilt-shift miniature diorama photograph: handcrafted figurines and props at 1:24 scale, macro lens, extremely shallow depth of field with a thin plane of focus, soft window light, ultra-detailed textures of felt, embroidered thread, painted resin and carved wood. Warm ochre, saffron and indigo palette, playful and tender. No text, no lettering, no logos.
- Graine (seed)
1643- Taille
- 1360×768
- Durée
- 75 s, telles qu'enregistrées par le run
- Coût
- aucun coût en argent (matériel à nous ; électricité non mesurée)
- Run Orazaka
- non enregistré

Comment c'est fait
- Modèle
- FLUX.2 [klein] 4B (Q8_0 GGUF) · Apache-2.0
- Moteur
- stable-diffusion.cpp on Metal, 4 steps, guidance 1.0
- Machine
- MacBook Pro, Apple M1 Max, 64 GB (Metal)
- Via
- Orazaka Studio run on dev.orazaka.com
- Studio
art-miniature- Prompt
A tiny souk alley: figurine spice merchants behind pyramids of saffron, paprika and cumin, hanging pierced-brass lanterns, woven baskets, a cat asleep on a blue doorstep. Tilt-shift miniature diorama photograph: handcrafted figurines and props at 1:24 scale, macro lens, extremely shallow depth of field with a thin plane of focus, soft window light, ultra-detailed textures of felt, embroidered thread, painted resin and carved wood. Warm ochre, saffron and indigo palette, playful and tender. No text, no lettering, no logos.
- Graine (seed)
1212- Taille
- 1024×1280
- Durée
- 100 s, telles qu'enregistrées par le run
- Coût
- aucun coût en argent (matériel à nous ; électricité non mesurée)
- Run Orazaka
- non enregistré

Comment c'est fait
- Modèle
- FLUX.2 [klein] 4B (Q8_0 GGUF) · Apache-2.0
- Moteur
- stable-diffusion.cpp on Metal, 4 steps, guidance 1.0
- Machine
- MacBook Pro, Apple M1 Max, 64 GB (Metal)
- Via
- Orazaka Studio run on dev.orazaka.com
- Studio
art-montreal-night- Prompt
A narrow back alley of the Plateau between plain brick walls with no signs, exterior spiral staircases, wooden balconies with potted plants and parked bicycles, a lone cyclist far away. Night street photograph in Montreal after rain: wet cobblestones and asphalt mirroring warm window light and a neon glow of magenta and teal, light mist, 35mm lens at f/1.8, cinematic colour grading, deep blacks, rich textures of old brick, wrought-iron spiral staircases and wet pavement. No readable signs, no lettering, no logos.
- Graine (seed)
12- Taille
- 1360×768
- Durée
- 109 s, telles qu'enregistrées par le run
- Coût
- aucun coût en argent (matériel à nous ; électricité non mesurée)
- Run Orazaka
- non enregistré
Elles sont plus grandes que 1024×1024, d'où des durées de 75 à 109 secondes.
Où le portable abandonne
La vidéo part ailleurs. En hébergé, un clip de cinq secondes de Wan 2.2 A14B en 832×480 prend 131 secondes en médiane. Sur un H100 loué, on monte en 720p. La vidéo part donc vers un GPU quand je choisis de l'y envoyer, et le reste du travail reste sur le portable. Les chiffres, les factures et les erreurs de ce choix sont dans le billet sur les coûts.
Ce que vous pouvez en retenir
Si vous démarrez une plateforme d'IA, commencez sur votre portable : c'est la machine la moins chère que vous possédez déjà. Mettez un verrou sur les tâches lourdes avant que la machine ne le fasse pour vous. Rangez le choix des modèles dans des données plutôt que dans le code. Et ne laissez jamais un appel partir vers un autre modèle sans le dire.
Ce que je n'ai pas mesuré : la mémoire et le swap du Mac sur une journée, la mémoire réelle des JVM au repos, et la vitesse du chat en local. Je les utilise tous les jours, mais je n'ai pas encore d'enregistrement chiffré pour les montrer.


