Téléverser les fichiers vers "/"

fichier recap et configuration d'images docker, de template
This commit is contained in:
2026-02-23 13:24:24 +00:00
parent 9dc27ded53
commit 1cd8eb5f42
5 changed files with 847 additions and 0 deletions

44
202602-feuilleroute.txt Normal file
View File

@ -0,0 +1,44 @@
faire un abonnement vast.ai
installer un serveur ollama
faire tourner plusieirs modeles successivement.
faire tourner plusieurs modeles en meme temps.
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDw6NFpHs1rqxwoi4Dmy9eQX4wJdQ1EOprD49qO8ltql nicolas b@DESKTOP-KPPH6RI
ssh -p 13777 root@ssh2.vast.ai -L 8080:localhost:8080
ssh -i "C:\Users\Nicolas B\.ssh\id_vast_demo" -p 17725 root@ssh2.vast.ai -L 8080:localhost:8080
ssh -i "C:\Users\Nicolas B\.ssh\id_vast_demo" -p 18271 root@142.170.89.112 -L 8080:localhost:8080
jsson QWEN:
{
"models": [
{
"title": "Qwen 3 480B (Vast.ai)",
"provider": "openai",
"model": "qwen3-480b",
"apiKey": "EMPTY",
"baseUrl": "http://localhost:8000/v1",
"contextLength": 32768,
"completionOptions": {
"temperature": 0.2,
"maxTokens": 4096
}
}
],
"tabAutocompleteModel": {
"title": "Qwen 3 Autocomplete",
"provider": "openai",
"model": "qwen3-480b",
"apiKey": "EMPTY",
"baseUrl": "http://localhost:8000/v1"
},
"allowAnonymousTelemetry": false
}
a lancer dans l'ide
ssh -L 8000:localhost:8000 root@[IP_DE_L_INSTANCE] -p [PORT_SSH_VAST]

210
ARCHITECTURE-STUDIO-AI.md Normal file
View File

@ -0,0 +1,210 @@
# 🎙️ Studio IA Multimodal — Architecture & Spécifications
## Contexte du projet
Studio IA accessible à des utilisateurs novices via navigateur web.
Interface unifiée pour la génération audio, image, et code, pilotée par un chatbot Mistral.
---
## 1. Architecture générale
```
[Utilisateur navigateur]
[Open WebUI — port 3000]
[Ollama — Mistral Medium]
↓ (Tools/Functions)
┌────┴────┬──────────┐
[AudioCraft] [ComfyUI] [Code]
port 7860 port 8188
```
---
## 2. Pile logicielle
| Couche | Technologie | Rôle | Port |
|---|---|---|---|
| Interface | Open WebUI | Chat unifié, gestion users | 3000 |
| LLM | Ollama + Mistral | Chat + orchestration outils | 11434 |
| Audio | AudioCraft (Meta) | MusicGen + AudioGen | 7860 |
| Image | ComfyUI | Stable Diffusion / Flux.1 | 8188 |
| Code | Ollama + CodeLlama | Génération de code | 11434 |
| Reverse proxy | Caddy | Routage + HTTPS | 80/443 |
---
## 3. Image Docker de base
**Nom cible :** `nicoboy/studio-ai:latest`
### Contenu de l'image (sans les modèles)
```dockerfile
FROM nvidia/cuda:12.1.1-cudnn8-devel-ubuntu22.04
# Python 3.11
RUN add-apt-repository ppa:deadsnakes/ppa && \
apt-get install -y python3.11 python3.11-venv python3.11-distutils
# Dépendances système
RUN apt-get install -y \
ffmpeg libsndfile1 sox \
git wget curl nano screen \
nodejs npm
# pip pour Python 3.11
RUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
# Frameworks IA
RUN python3.11 -m pip install \
torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# AudioCraft
RUN python3.11 -m pip install \
git+https://github.com/facebookresearch/audiocraft.git \
gradio scipy soundfile
# Open WebUI
RUN python3.11 -m pip install open-webui
# Ollama
RUN curl -fsSL https://ollama.ai/install.sh | sh
```
### Ce qui N'est PAS dans l'image (téléchargé au 1er lancement)
- Modèles Ollama (Mistral, CodeLlama) → ~8 Go
- Modèles AudioCraft (MusicGen, AudioGen) → ~4 Go
- Modèles ComfyUI (SDXL, Flux.1) → ~7 Go
- **Total modèles : ~20 Go**
---
## 4. Taille estimée
| Élément | Taille |
|---|---|
| Image Docker (frameworks seuls) | ~15-20 Go |
| Modèles (téléchargés au lancement) | ~20 Go |
| Données utilisateurs | ~5 Go |
| **Total disque recommandé** | **50-60 Go minimum** |
> ⚠️ 130 Go de disque = confortable pour tout faire tourner.
---
## 5. Template Vast.ai cible
| Paramètre | Valeur |
|---|---|
| Image | `nicoboy/studio-ai:latest` |
| GPU | RTX A4000 / RTX 4060 Ti (16 Go VRAM) |
| Disk | 130 Go |
| Ports | 3000, 7860, 8188, 11434 |
| Launch mode | `Jupyter + SSH` |
| PROVISIONING_SCRIPT | URL Gitea → télécharge les modèles |
| PORTAL_CONFIG | OpenWebUI:3000, AudioCraft:7860, ComfyUI:8188 |
---
## 6. Provisioning Script (logique au 1er lancement)
```bash
# 1. Télécharger les modèles Ollama
ollama pull mistral
ollama pull codellama
# 2. Télécharger les modèles AudioCraft
# (fait automatiquement au 1er appel Python)
# 3. Installer ComfyUI + modèles SD/Flux
git clone https://github.com/comfyanonymous/ComfyUI /root/ComfyUI
cd /root/ComfyUI && pip install -r requirements.txt
wget -P /root/ComfyUI/models/checkpoints/ [URL_MODELE_SDXL]
# 4. Lancer les services
screen -dmS ollama ollama serve
screen -dmS openwebui open-webui serve --port 3000
screen -dmS audiocraft python /root/audiocraft-api/server.py
screen -dmS comfyui python /root/ComfyUI/main.py --listen 0.0.0.0 --port 8188
```
---
## 7. Configuration Open WebUI (partie délicate — à faire ensemble)
> ⚠️ Cette section sera configurée manuellement après déploiement.
### 7.1 Connexion Ollama
- URL : `http://localhost:11434`
- Modèles : Mistral (chat), CodeLlama (code)
### 7.2 Tools à créer (Functions Open WebUI)
- `generate_audio(prompt, duration, type)` → appelle AudioCraft port 7860
- `generate_image(prompt, steps, width, height)` → appelle ComfyUI port 8188
- `generate_code(prompt, language)` → appelle CodeLlama via Ollama
### 7.3 System Prompt de l'assistant
```
Tu es un assistant créatif expert.
Tu as accès aux outils suivants :
- generate_audio : pour créer musique et bruitages
- generate_image : pour créer des images
- generate_code : pour écrire du code
Quand un utilisateur fait une demande créative,
tu l'aides à préciser sa demande puis tu utilises
l'outil approprié.
```
### 7.4 Modèle JSON Open WebUI (à importer)
> À générer une fois l'instance stable.
---
## 8. Étapes de construction (ordre)
- [x] Définir l'architecture
- [ ] **Construire l'image Docker** ← Gemini
- [ ] Pousser sur DockerHub (`nicoboy/studio-ai`)
- [ ] Mettre à jour le template Vast.ai avec la nouvelle image
- [ ] Écrire le provisioning script final
- [ ] **Configurer Open WebUI** ← Claude
- [ ] **Créer les Tools AudioCraft + ComfyUI** ← Claude
- [ ] **Rédiger les System Prompts** ← Claude
- [ ] Tests utilisateur
---
## 9. Commandes utiles
```bash
# Vérifier les services qui tournent
screen -ls
# Voir les logs d'un service
screen -r openwebui
screen -r audiocraft
screen -r comfyui
# Vérifier GPU
nvidia-smi
# Vérifier les modèles Ollama
ollama list
# Tester AudioCraft API
curl http://localhost:7860
```
---
## 10. Points d'attention pour Gemini
1. **Python 3.11 obligatoire** — Open WebUI refuse Python 3.10
2. **Ne pas inclure les modèles dans l'image** — trop lourds, dans le provisioning script
3. **L'image doit être sur DockerHub public** pour que Vast.ai puisse la télécharger
4. **CUDA 12.1** — compatible A4000 et RTX 4060 Ti
5. **Tester le build** avec `docker build -t nicoboy/studio-ai .` avant de pusher

View File

@ -0,0 +1,53 @@
🎙️ Studio-AI : Récapitulatif de Configuration
Version : 1.0 (2026-02-23)
Image Docker : nicoboy/studio-ai:latest
Base : NVIDIA PyTorch NGC (Ubuntu 22.04 + CUDA 13)
🛠 1. Contenu du Dockerfile
Voici le code final qui a permis de construire l'image avec succès, en évitant les conflits de dépendances audio et les erreurs de décompression.
Dockerfile
FROM nvcr.io/nvidia/pytorch:26.01-py3
ENV DEBIAN_FRONTEND=noninteractive
# 1. Installation des outils système et librairies de développement audio
RUN apt-get update && apt-get install -y \
ffmpeg \
pkg-config \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libavdevice-dev \
libavfilter-dev \
libsndfile1 \
sox \
zstd \
screen \
nano \
git \
wget \
curl \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# 2. Installation de Ollama (Moteur LLM)
RUN curl -fsSL https://ollama.ai/install.sh | sh
# 3. Installation de Open WebUI (Interface Chat)
RUN pip install --no-cache-dir open-webui
# 4. Installation de l'Audio (Audiocraft)
# Note : --no-deps est utilisé pour éviter que Audiocraft n'écrase Torch 2.6
RUN pip install --no-cache-dir gradio scipy soundfile
RUN pip install --no-cache-dir --no-deps git+https://github.com/facebookresearch/audiocraft.git
# 5. Installation de ComfyUI (Génération d'images)
WORKDIR /root
RUN git clone https://github.com/comfyanonymous/ComfyUI.git && \
cd ComfyUI && \
pip install --no-cache-dir -r requirements.txt
# 6. Configuration réseau et environnement
EXPOSE 3000 7860 8188 11434
WORKDIR /root
CMD ["/bin/bash"]

60
dockerfile.txt Normal file
View File

@ -0,0 +1,60 @@
FROM nvcr.io/nvidia/pytorch:26.01-py3
ENV DEBIAN_FRONTEND=noninteractive
# 1. Dépendances système et audio
RUN apt-get update && apt-get install -y \
ffmpeg pkg-config libavcodec-dev libavformat-dev \
libavutil-dev libswscale-dev libavdevice-dev libavfilter-dev \
libsndfile1 sox zstd screen nano git wget curl \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# 2. Installation d'Ollama
RUN curl -fsSL https://ollama.ai/install.sh | sh
# 3. Installation des dépendances Python (Validées par nos tests manuels)
# On installe les briques manquantes AVANT Audiocraft
RUN pip install --no-cache-dir \
open-webui \
gradio \
scipy \
soundfile \
julius \
omegaconf \
hydra-core \
hydra-colorlog \
flashy \
num2words \
torchmetrics \
encodec \
xformers \
einops \
spacy==3.7.6 \
numpy==1.26.4
# 4. Installation d'Audiocraft (Isolée pour ne pas casser PyTorch)
RUN pip install --no-cache-dir --no-deps git+https://github.com/facebookresearch/audiocraft.git
# 5. Installation ComfyUI
WORKDIR /root
RUN git clone https://github.com/comfyanonymous/ComfyUI.git && \
cd ComfyUI && \
pip install --no-cache-dir -r requirements.txt
# 6. Intégration de ton script de serveur Audio
RUN mkdir -p /root/audiocraft-api
COPY server.py /root/audiocraft-api/server.py
# 7. Script d'auto-lancement (Auto-Boot) ultra-robuste
RUN echo '#!/bin/bash\n\
ollama serve > /root/ollama.log 2>&1 & \n\
open-webui serve --port 3000 > /root/webui.log 2>&1 & \n\
python3 /root/ComfyUI/main.py --listen 0.0.0.0 --port 8188 > /root/comfy.log 2>&1 & \n\
python3 /root/audiocraft-api/server.py > /root/audio.log 2>&1 & \n\
echo "--- TOUS LES SERVICES SONT EN COURS DE LANCEMENT ---"\n\
wait' > /start.sh && chmod +x /start.sh
# 8. Configuration finale
EXPOSE 3000 7860 8188 11434
WORKDIR /root
# On force l'utilisation de bash pour éviter les problèmes de droits
ENTRYPOINT ["/bin/bash", "/start.sh"]

480
game.js Normal file
View File

@ -0,0 +1,480 @@
// Variables globales
let game;
let canvas;
let ctx;
class Game {
constructor() {
this.canvas = document.getElementById('gameCanvas');
this.ctx = this.canvas.getContext('2d');
this.width = this.canvas.width;
this.height = this.canvas.height;
// État du jeu
this.score = 0;
this.lives = 3;
this.level = 1;
this.gameRunning = false;
this.paused = false;
// Entités du jeu
this.player = null;
this.enemies = [];
this.bubbles = [];
this.platforms = [];
// Contrôles
this.keys = {};
}
init() {
// Créer le joueur
this.player = new Player(100, this.height - 100, this);
// Créer quelques plateformes
this.createPlatforms();
// Créer quelques ennemis
this.createEnemies();
// Écouter les événements clavier
this.setupControls();
// Démarrer la boucle du jeu
this.gameRunning = true;
this.gameLoop();
}
createPlatforms() {
this.platforms = [];
// Plateforme du sol
this.platforms.push({
x: 0,
y: this.height - 40,
width: this.width,
height: 40
});
// Plateformes flottantes
this.platforms.push({
x: 100,
y: this.height - 150,
width: 200,
height: 20
});
this.platforms.push({
x: 400,
y: this.height - 250,
width: 200,
height: 20
});
this.platforms.push({
x: 200,
y: this.height - 350,
width: 200,
height: 20
});
}
createEnemies() {
this.enemies = [];
// Créer quelques ennemis
this.enemies.push(new Enemy(300, this.height - 100, this));
this.enemies.push(new Enemy(500, this.height - 200, this));
}
setupControls() {
document.addEventListener('keydown', (e) => {
this.keys[e.key] = true;
// Sauter avec espace
if (e.key === ' ' && this.gameRunning && !this.paused) {
if (this.player) {
this.player.jump();
}
}
// Tirer une bulle avec Ctrl
if ((e.key === 'Control' || e.key === 'ControlLeft') && this.gameRunning && !this.paused) {
if (this.player) {
this.player.shootBubble();
}
}
});
document.addEventListener('keyup', (e) => {
this.keys[e.key] = false;
});
}
update() {
if (!this.gameRunning || this.paused) return;
// Mettre à jour le joueur
if (this.player) {
this.player.update();
}
// Mettre à jour les ennemis
for (let i = 0; i < this.enemies.length; i++) {
this.enemies[i].update();
// Vérifier si l'ennemi est hors jeu
if (this.enemies[i].y > this.height) {
this.enemies.splice(i, 1);
i--;
}
}
// Mettre à jour les bulles
for (let i = 0; i < this.bubbles.length; i++) {
this.bubbles[i].update();
// Vérifier si la bulle est hors jeu
if (this.bubbles[i].y < -50 || this.bubbles[i].x > this.width + 50 || this.bubbles[i].x < -50) {
this.bubbles.splice(i, 1);
i--;
continue;
}
// Vérifier les collisions entre bulles et ennemis
for (let j = 0; j < this.enemies.length; j++) {
if (j < this.enemies.length && this.checkCollision(this.bubbles[i], this.enemies[j])) {
// Ennemi capturé dans une bulle
this.score += 100;
this.updateUI();
this.bubbles.splice(i, 1);
this.enemies.splice(j, 1);
i--;
break;
}
}
}
// Vérifier les collisions entre joueur et ennemis
if (this.player) {
for (let i = 0; i < this.enemies.length; i++) {
if (this.checkCollision(this.player, this.enemies[i])) {
this.playerHit();
break;
}
}
}
// Vérifier si tous les ennemis sont éliminés
if (this.enemies.length === 0) {
this.nextLevel();
}
}
draw() {
// Effacer le canvas
this.ctx.clearRect(0, 0, this.width, this.height);
// Dessiner le fond
this.ctx.fillStyle = '#00008B';
this.ctx.fillRect(0, 0, this.width, this.height);
// Dessiner les plateformes
this.ctx.fillStyle = '#8B4513';
for (let platform of this.platforms) {
this.ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
}
// Dessiner le joueur
if (this.player) {
this.player.draw();
}
// Dessiner les ennemis
for (let enemy of this.enemies) {
enemy.draw();
}
// Dessiner les bulles
for (let bubble of this.bubbles) {
bubble.draw();
}
}
checkCollision(obj1, obj2) {
return obj1.x < obj2.x + obj2.width &&
obj1.x + obj1.width > obj2.x &&
obj1.y < obj2.y + obj2.height &&
obj1.y + obj1.height > obj2.y;
}
playerHit() {
this.lives--;
this.updateUI();
if (this.lives <= 0) {
this.gameOver();
} else {
// Réinitialiser la position du joueur
if (this.player) {
this.player.x = 100;
this.player.y = this.height - 100;
this.player.velocityY = 0;
this.player.velocityX = 0;
}
}
}
nextLevel() {
this.level++;
this.updateUI();
// Réinitialiser les ennemis
this.createEnemies();
// Réinitialiser le joueur
if (this.player) {
this.player.x = 100;
this.player.y = this.height - 100;
this.player.velocityY = 0;
this.player.velocityX = 0;
}
}
gameOver() {
this.gameRunning = false;
setTimeout(() => {
alert(`Game Over! Score final: ${this.score}`);
}, 100);
}
updateUI() {
document.getElementById('score').textContent = this.score;
document.getElementById('lives').textContent = this.lives;
document.getElementById('level').textContent = this.level;
}
gameLoop() {
this.update();
this.draw();
if (this.gameRunning) {
requestAnimationFrame(() => this.gameLoop());
}
}
}
class Player {
constructor(x, y, game) {
this.x = x;
this.y = y;
this.width = 30;
this.height = 30;
this.velocityX = 0;
this.velocityY = 0;
this.speed = 5;
this.jumpPower = 12;
this.gravity = 0.5;
this.onGround = false;
this.game = game;
}
update() {
// Gestion des contrôles
this.velocityX = 0;
if (this.game.keys['ArrowLeft']) {
this.velocityX = -this.speed;
}
if (this.game.keys['ArrowRight']) {
this.velocityX = this.speed;
}
// Appliquer la gravité
this.velocityY += this.gravity;
// Mettre à jour la position
this.x += this.velocityX;
this.y += this.velocityY;
// Empêcher le joueur de sortir de l'écran
if (this.x < 0) this.x = 0;
if (this.x + this.width > this.game.width) this.x = this.game.width - this.width;
// Vérifier les collisions avec les plateformes
this.onGround = false;
for (let platform of this.game.platforms) {
if (this.x < platform.x + platform.width &&
this.x + this.width > platform.x &&
this.y + this.height <= platform.y + 10 &&
this.y + this.height + this.velocityY >= platform.y) {
this.y = platform.y - this.height;
this.velocityY = 0;
this.onGround = true;
}
}
// Empêcher le joueur de tomber sous l'écran
if (this.y > this.game.height) {
this.game.playerHit();
}
}
jump() {
if (this.onGround) {
this.velocityY = -this.jumpPower;
this.onGround = false;
}
}
shootBubble() {
// Créer une nouvelle bulle
let direction = 1;
if (this.game.keys['ArrowLeft']) direction = -1;
this.game.bubbles.push(new Bubble(this.x + this.width/2, this.y + this.height/2, direction, this.game));
}
draw() {
// Dessiner le joueur (cercle vert)
this.game.ctx.fillStyle = '#00FF00';
this.game.ctx.beginPath();
this.game.ctx.arc(this.x + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
this.game.ctx.fill();
// Dessiner les yeux
this.game.ctx.fillStyle = '#000000';
let eyeOffset = this.game.keys['ArrowLeft'] ? -5 : (this.game.keys['ArrowRight'] ? 5 : 0);
this.game.ctx.beginPath();
this.game.ctx.arc(this.x + this.width/2 - 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
this.game.ctx.arc(this.x + this.width/2 + 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
this.game.ctx.fill();
}
}
class Enemy {
constructor(x, y, game) {
this.x = x;
this.y = y;
this.width = 25;
this.height = 25;
this.velocityX = 2;
this.velocityY = 0;
this.gravity = 0.5;
this.game = game;
}
update() {
// Déplacement horizontal
this.x += this.velocityX;
// Appliquer la gravité
this.velocityY += this.gravity;
this.y += this.velocityY;
// Vérifier les collisions avec les plateformes
let onPlatform = false;
for (let platform of this.game.platforms) {
if (this.x < platform.x + platform.width &&
this.x + this.width > platform.x &&
this.y + this.height <= platform.y + 10 &&
this.y + this.height + this.velocityY >= platform.y) {
this.y = platform.y - this.height;
this.velocityY = 0;
onPlatform = true;
// Changer de direction si on arrive au bord de la plateforme
if (this.x <= platform.x || this.x + this.width >= platform.x + platform.width) {
this.velocityX *= -1;
}
}
}
// Rebondir sur les murs
if (this.x <= 0 || this.x + this.width >= this.game.width) {
this.velocityX *= -1;
}
}
draw() {
// Dessiner l'ennemi (cercle rouge)
this.game.ctx.fillStyle = '#FF0000';
this.game.ctx.beginPath();
this.game.ctx.arc(this.x + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
this.game.ctx.fill();
// Dessiner les yeux
this.game.ctx.fillStyle = '#000000';
let eyeOffset = this.velocityX < 0 ? -5 : 5;
this.game.ctx.beginPath();
this.game.ctx.arc(this.x + this.width/2 - 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
this.game.ctx.arc(this.x + this.width/2 + 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
this.game.ctx.fill();
}
}
class Bubble {
constructor(x, y, direction, game) {
this.x = x;
this.y = y;
this.radius = 10;
this.velocityX = direction * 4;
this.velocityY = -3;
this.game = game;
}
update() {
this.x += this.velocityX;
this.y += this.velocityY;
// La bulle monte légèrement
this.velocityY -= 0.1;
// Limiter la vitesse verticale
if (this.velocityY < -5) this.velocityY = -5;
}
draw() {
// Dessiner la bulle (cercle transparent avec bordure)
this.game.ctx.strokeStyle = '#FFFFFF';
this.game.ctx.lineWidth = 2;
this.game.ctx.beginPath();
this.game.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
this.game.ctx.stroke();
// Ajouter un reflet
this.game.ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
this.game.ctx.beginPath();
this.game.ctx.arc(this.x - this.radius/3, this.y - this.radius/3, this.radius/4, 0, Math.PI * 2);
this.game.ctx.fill();
}
}
// Fonctions globales
function startGame() {
if (game) {
// Réinitialiser le jeu
game.score = 0;
game.lives = 3;
game.level = 1;
game.updateUI();
game.createPlatforms();
game.createEnemies();
if (game.player) {
game.player.x = 100;
game.player.y = game.height - 100;
game.player.velocityY = 0;
game.player.velocityX = 0;
}
game.bubbles = [];
game.gameRunning = true;
game.gameLoop();
} else {
// Créer un nouveau jeu
game = new Game();
game.init();
}
game.gameRunning = true;
game.paused = false;
}
function pauseGame() {
if (game && game.gameRunning) {
game.paused = !game.paused;
}