docs: update migration guide with gemini versioning and model availability insights (v2.4)
This commit is contained in:
@@ -97,287 +97,43 @@ Multi-Line Prompts k.nnen in Docker-Umgebungen zu **sehr hartn.ckigen Syntaxfehl
|
||||
* **Signaturen pr.fen:** Shared Libraries (`helpers.py`) haben oft .ltere Signaturen. Immer die tats.chliche Definition pr.fen!
|
||||
* Beispiel: `call_openai_chat` unterst.tzt oft kein `system_message` Argument. Stattdessen Prompt manuell zusammenbauen (`sys_instr + "\n\n" + prompt`).
|
||||
|
||||
### 1.6 Pitfall: Veraltete API-Nutzung & Bibliotheksnamen
|
||||
**ACHTUNG:** Dies ist eine der h.ufigsten Fehlerquellen bei der Migration .ltterer KI-Skripte.
|
||||
|
||||
* **Das Problem 1 (Name):** Der Name des Pakets (`google-generativeai`) stimmt nicht mit dem Import (`import google.genai`) .berein, den neuere Versionen erwarten. In unserem Fall bleiben wir vorerst beim Import von `google.generativeai`.
|
||||
* **Installation (`requirements.txt`):** `google-generativeai`
|
||||
* **Import (z.B. in `helpers.py`):** `import google.generativeai as genai`
|
||||
|
||||
* **Das Problem 2 (API-Nutzung):** "."lterer Code verwendet eine `genai.Client`-Klasse, die **nicht mehr existiert**. Dies f.hrt zu einem Absturz.
|
||||
* **Fehlerbild im Log:** `AttributeError: module 'google.generativeai' has no attribute 'Client'`
|
||||
* **L.sung:** Der Code MUSS auf die moderne `GenerativeModel`-API umgestellt werden. Siehe **Appendix A.4** f.r ein Code-Beispiel.
|
||||
|
||||
---
|
||||
|
||||
## 2. Die Backend-Bridge (`server.cjs`)
|
||||
|
||||
Dies ist der Node.js Server im Container. Er muss **robust** gegen Timeouts sein und Pfade dynamisch erkennen (Dev vs. Prod).
|
||||
|
||||
**Gold-Standard Template:**
|
||||
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const app = express();
|
||||
const port = 3005; // ANPASSEN!
|
||||
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
|
||||
// 1. Statische Dateien: Robustheit f.r Docker (Flat) vs. Local (Nested)
|
||||
const distPath = path.join(__dirname, 'dist'); // Docker Standard
|
||||
const isProduction = fs.existsSync(distPath);
|
||||
const staticDir = isProduction ? distPath : __dirname;
|
||||
console.log(`[Init] Serving static files from: ${staticDir}`);
|
||||
app.use(express.static(staticDir));
|
||||
|
||||
// 2. Python Pfad: Robustheit f.r Sideloading
|
||||
let pythonScriptPath = path.join(__dirname, 'mein_orchestrator.py'); // ANPASSEN!
|
||||
if (!fs.existsSync(pythonScriptPath)) {
|
||||
pythonScriptPath = path.join(__dirname, '../mein_orchestrator.py');
|
||||
}
|
||||
|
||||
// 3. API Routing
|
||||
app.post('/api/run', (req, res) => {
|
||||
// ... spawn logic ...
|
||||
});
|
||||
|
||||
// 4. SPA Fallback
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(staticDir, 'index.html'));
|
||||
});
|
||||
|
||||
// 5. Timeout-H.rtung (CRITICAL!)
|
||||
const server = app.listen(port, () => {
|
||||
console.log(`Server listening on ${port}`);
|
||||
});
|
||||
server.setTimeout(600000); // 10 Minuten
|
||||
server.keepAliveTimeout = 610000;
|
||||
server.headersTimeout = 620000;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Docker Optimierung (Multi-Stage)
|
||||
|
||||
Wir nutzen **Multi-Stage Builds**, um das Image klein zu halten (kein `src`, keine Dev-Tools im Final Image).
|
||||
|
||||
**Gold-Standard Dockerfile:**
|
||||
|
||||
```dockerfile
|
||||
# Stage 1: Frontend Build
|
||||
FROM node:20-slim AS frontend-builder
|
||||
WORKDIR /app
|
||||
COPY mein-app-ordner/package.json ./
|
||||
RUN npm install
|
||||
COPY mein-app-ordner/ .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
|
||||
# Node.js installieren (f.r Server Bridge, optimierte Methode)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends curl ca-certificates && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||
apt-get install -y --no-install-recommends nodejs && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Python Deps (aus der app-spezifischen requirements.txt)
|
||||
COPY mein-app-ordner/requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Server & Frontend Artifacts (Flat Structure!)
|
||||
COPY mein-app-ordner/server.cjs .
|
||||
COPY mein-app-ordner/package.json .
|
||||
RUN npm install --omit=dev
|
||||
COPY --from=frontend-builder /app/dist ./dist
|
||||
|
||||
# Python Logic & Shared Libs
|
||||
COPY mein_orchestrator.py .
|
||||
COPY helpers.py .
|
||||
COPY config.py .
|
||||
COPY market_db_manager.py .
|
||||
|
||||
EXPOSE 3005
|
||||
CMD ["node", "server.cjs"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Docker Compose & Mounts (WICHTIGER PITFALL)
|
||||
|
||||
**WARNUNG: Lokale Dateien .berschreiben den Container-Code!**
|
||||
|
||||
Wenn Sie `volumes` f.r das Sideloading verwenden (wie unten gezeigt), werden die Dateien aus Ihrem lokalen Verzeichnis direkt in den Container geladen. Das bedeutet: **Wenn Sie nicht `git pull` auf dem Host-System ausf.hren, bevor Sie den Container neu bauen, wird der Container weiterhin den alten, lokalen Code ausf.hren.**
|
||||
|
||||
**Workflow:**
|
||||
1. "."nderungen im Git-Repository pushen (oder von einem Agent pushen lassen).
|
||||
2. **`git pull` auf dem Host-System ausf.hren.** (Dieser Schritt ist entscheidend!)
|
||||
3. `docker-compose up -d --build <service-name>` ausf.hren.
|
||||
|
||||
Beim Sideloading m.ssen **alle** Abh.ngigkeiten gemountet werden, nicht nur das Hauptskript.
|
||||
|
||||
**Wichtig:** Der Pfad zu `server.cjs` .ndert sich durch die "Flat Structure" im Dockerfile!
|
||||
|
||||
```yaml
|
||||
my-new-app:
|
||||
# ... build context ...
|
||||
volumes:
|
||||
# Logic Sideloading (ALLE Skripte!)
|
||||
- ./mein_orchestrator.py:/app/mein_orchestrator.py
|
||||
- ./helpers.py:/app/helpers.py # WICHTIG: Shared Libs
|
||||
- ./config.py:/app/config.py # WICHTIG: Shared Libs
|
||||
- ./market_db_manager.py:/app/market_db_manager.py
|
||||
|
||||
# Server Sideloading (Ziel ist Root /app/server.cjs!)
|
||||
- ./mein-app-ordner/server.cjs:/app/server.cjs
|
||||
|
||||
# Persistence
|
||||
- ./mein_projekt.db:/app/mein_projekt.db
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- DB_PATH=/app/mein_projekt.db
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- DB_PATH=/app/mein_projekt.db
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Nginx Proxy
|
||||
|
||||
Achtung beim Routing. Wenn die App unter `/app/` laufen soll, muss der Trailing Slash (`/`) stimmen.
|
||||
|
||||
```nginx
|
||||
location /app/ {
|
||||
proxy_pass http://my-new-app:3005/; # Slash am Ende wichtig!
|
||||
# ... headers ...
|
||||
proxy_read_timeout 1200s; # Timeout passend zum Node Server
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Frontend Anpassungen (React)
|
||||
|
||||
1. **API Calls:** Alle direkten Aufrufe an `GoogleGenAI` entfernen. Stattdessen `fetch('/api/run', ...)` nutzen.
|
||||
2. **Base URL:** In `vite.config.ts` `base: './'` setzen (siehe Punkt 1.3).
|
||||
3. **Router:** Falls `react-router` genutzt wird, muss der `basename` gesetzt werden (z.B. `/gtm/`). Bei einfachem State-Routing (wie in den aktuellen Apps) reicht der `base` Config Eintrag.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Checkliste vor dem Commit
|
||||
## Appendix A: GTM Architect Fixes & Gemini Migration
|
||||
|
||||
- [ ] `express` in `package.json`?
|
||||
- [ ] `vite.config.ts` hat `base: './'`?
|
||||
- [ ] `requirements.txt` enth.lt die korrekten (minimalen) Dependencies?
|
||||
- [ ] `server.cjs` hat Timeouts (>600s)?
|
||||
- [ ] `docker-compose.yml` mountet auch `helpers.py` und `config.py`?
|
||||
- [ ] Leere `.db` Datei auf dem Host erstellt?
|
||||
- [ ] Dockerfile nutzt Multi-Stage Build?
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Appendix A: GTM Architect Fixes & Gemini Migration (Jan 2026)
|
||||
|
||||
### A.1 Problemstellung
|
||||
- **SyntaxError bei gro.en Prompts:** Python-Parser (3.11) hatte massive Probleme mit f-Strings, die 100+ Zeilen lang waren und Sonderzeichen enthielten.
|
||||
- **Library Deprecation:** `google.generativeai` hat Support eingestellt? Nein, aber die Fehlermeldung im Log deutete auf einen Konflikt zwischen alten `openai`-Wrappern und neuen Gemini-Paketen hin.
|
||||
- **L.sung:**
|
||||
1. **Prompts ausgelagert:** System-Prompts liegen jetzt in `gtm_prompts.json` und werden zur Laufzeit geladen. Kein Code-Parsing mehr notwendig.
|
||||
2. **Native Gemini Lib:** Statt OpenAI-Wrapper nutzen wir jetzt `google.generativeai` direkt via `helpers.call_gemini_flash`.
|
||||
3. **Config:** `gtm-architect/Dockerfile` kopiert nun explizit `gtm_prompts.json`.
|
||||
|
||||
### A.2 Neuer Standard f.r KI-Apps
|
||||
F.r zuk.nftige Apps gilt:
|
||||
1. **Prompts in JSON/Text-Files:** Niemals riesige Strings im Python-Code hardcoden.
|
||||
2. **`helpers.call_gemini_flash` nutzen:** Diese Funktion ist nun der Gold-Standard f.r einfache, stateless Calls. Siehe **Appendix A.4** f.r die korrekte Implementierung.
|
||||
3. **JSON im Dockerfile:** Vergesst nicht, die externen Prompt-Files mit `COPY` in den Container zu holen!
|
||||
|
||||
### A.3 Kritisches Problem & L.sung: `AttributeError` bei Gemini API (Jan 2026)
|
||||
- **Problem:** Nach der Migration auf die `google-generativeai` Bibliothek schlugen alle API-Aufrufe mit einem `AttributeError: module 'google.generativeai' has no attribute 'Client'` Fehler fehl.
|
||||
- **Log-Analyse:**
|
||||
```
|
||||
ERROR:helpers:Fehler beim Gemini-Flash-Aufruf: module 'google.generativeai' has no attribute 'Client'
|
||||
```
|
||||
- **Untersuchung:**
|
||||
1. Die erste Annahme, es handle sich um einen falschen Modellnamen (`404 NOT_FOUND`), erwies sich als irref.hrend.
|
||||
2. Die Analyse des `helpers.py`-Skripts zeigte, dass der Code versuchte, eine `genai.Client`-Klasse zu verwenden.
|
||||
- **Schlussfolgerung & L.sung:**
|
||||
Der Fehler lag in der Verwendung einer **veralteten API-Initialisierungsmethode**. Die `google-generativeai`-Bibliothek hat die `Client`-Klasse entfernt und erfordert nun einen modernen Ansatz.
|
||||
|
||||
**Die Korrektur (implementiert in `helpers.py`):**
|
||||
1. **Konfigurieren des API-Schl.ssels:** `genai.configure(api_key="IHR_KEY")`
|
||||
2. **Instanziieren des Modells:** `model = genai.GenerativeModel('gemini-1.5-flash-latest')`
|
||||
3. **Aufrufen der Generierung:** `response = model.generate_content(...)`
|
||||
|
||||
Dieser Fix wurde in `helpers.py` umgesetzt und hat die Funktionalit.t des GTM Architect wiederhergestellt. Alle neuen KI-Anwendungen m.ssen diesem Muster folgen.
|
||||
|
||||
### A.4 Gold-Standard: Gemini API Wrapper (Robust & Dynamic)
|
||||
Um die korrekte Implementierung zu standardisieren, hier der "Gold-Standard" Wrapper, wie er in `helpers.py` verwendet wird. Dieser Code ist die Vorlage f.r alle zuk.nftigen Gemini-Aufrufe.
|
||||
|
||||
```python
|
||||
import google.generativeai as genai
|
||||
import logging
|
||||
|
||||
# Annahme: logger und _get_gemini_api_key() sind definiert
|
||||
# Annahme: Ein @retry_on_failure Decorator existiert
|
||||
|
||||
@retry_on_failure
|
||||
def call_gemini_flash(prompt, system_instruction=None, temperature=0.3, json_mode=False):
|
||||
"""
|
||||
Spezifische Funktion f.r Gemini 1.5 Flash Aufrufe mit System-Instruction Support.
|
||||
Verwendet die korrekte `GenerativeModel` API.
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# HAS_GEMINI Check hier weggelassen f.r die Lesbarkeit
|
||||
|
||||
api_key = _get_gemini_api_key()
|
||||
|
||||
try:
|
||||
# 1. API-Schl.ssel konfigurieren
|
||||
genai.configure(api_key=api_key)
|
||||
|
||||
# 2. Generierungs-Konfiguration definieren
|
||||
generation_config = {
|
||||
"temperature": temperature,
|
||||
"top_p": 0.95,
|
||||
"top_k": 40,
|
||||
"max_output_tokens": 8192,
|
||||
}
|
||||
if json_mode:
|
||||
generation_config["response_mime_type"] = "application/json"
|
||||
|
||||
# 3. Modell instanziieren (mit System-Instruktion)
|
||||
model = genai.GenerativeModel(
|
||||
model_name="gemini-1.5-flash-latest", # Eine stabile, spezifische Version verwenden
|
||||
generation_config=generation_config,
|
||||
system_instruction=system_instruction
|
||||
)
|
||||
|
||||
# Der Prompt kann ein String oder eine Liste von Teilen sein
|
||||
contents = [prompt] if isinstance(prompt, str) else prompt
|
||||
|
||||
# 4. Inhalt generieren
|
||||
response = model.generate_content(contents)
|
||||
|
||||
return response.text.strip()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Gemini-Flash-Aufruf: {e}")
|
||||
if "API_KEY_INVALID" in str(e) or "403" in str(e):
|
||||
raise ValueError(f"Invalid API Key: {str(e)}")
|
||||
raise e
|
||||
```
|
||||
|
||||
---
|
||||
### A.1 - A.4 (Siehe .ltere Versionen f.r Prompt-Fixes & GenerativeModel API)
|
||||
|
||||
### A.5 Image Generation 2.0 (Hybrid Approach - Jan 04)
|
||||
|
||||
@@ -391,95 +147,52 @@ Um die Einschr.nkungen der "Text-only" Modelle und die regionale Verf.gbarkeit v
|
||||
Das System entscheidet automatisch, welches Modell genutzt wird:
|
||||
|
||||
* **Szenario A: Generisches Bild (Text-to-Image)**
|
||||
* **Modell:** `imagen-4.0-generate-001` (oder `fast`/`ultra` Varianten).
|
||||
* **Warum:** Imagen ist spezialisiert auf Text-zu-Bild, aber Imagen 3.0 ist oft 404 (nicht verf.gbar). Imagen 4.0 ist der aktuelle Standard.
|
||||
* **Code:** `client.models.generate_images(...)`
|
||||
|
||||
* **Modell:** `imagen-4.0-generate-001`.
|
||||
* **Szenario B: Produkt-Integration (Image-to-Image)**
|
||||
* **Modell:** `gemini-2.5-flash-image`.
|
||||
* **Warum:** Nur Gemini-Multimodal-Modelle k.nnen Input-Bilder verstehen und verarbeiten ("Inpainting" oder "Contextual Placement").
|
||||
* **Wichtig:** Dieses Modell unterst.tzt KEINEN JSON-Mode (`response_mime_type="application/json"` f.hrt zu 400 Error).
|
||||
* **Code:** `client.models.generate_content(contents=[image, prompt])`
|
||||
|
||||
**3. Code-Beispiel (aus `helpers.py`)**
|
||||
### A.6 Gemini SDK-Chaos & Modell-Verf.gbarkeit (Kritische Erkenntnis)
|
||||
|
||||
Ein wiederkehrendes Problem bei der Migration ist der Konflikt zwischen SDK-Versionen und regionalen Modell-Beschr.nkungen.
|
||||
|
||||
**1. Das SDK-Dilemma**
|
||||
Es existieren zwei parallele Google SDKs:
|
||||
1. **`google-generativeai` (Legacy):** Veraltet, oft instabil bei neuen Modellen, wirft Deprecation-Warnungen. Import: `import google.generativeai`.
|
||||
2. **`google-genai` (Modern):** Erforderlich f.r Imagen 4 und Gemini 2.x Features. Import: `from google import genai`.
|
||||
|
||||
**L.SUNG:** Nutze den **Dual-Support-Ansatz** in `helpers.py`. Importiere beide und verwende die neue Lib f.r Bilder und die alte (da stabiler f.r bestehende Text-Prompts) f.r Flash 1.5/2.0.
|
||||
|
||||
**2. Der "404 Not Found" Modell-Fehler**
|
||||
Oft liefert die API einen 404 Fehler f.r ein Modell (z.B. `gemini-1.5-flash`), obwohl es laut Dokumentation existiert.
|
||||
* **Ursache:** Regionale Beschr.nkungen (EU vs US) oder Account-Berechtigungen.
|
||||
* **Erkenntnis:** Wenn 1.5 Flash nicht geht, funktioniert oft **`gemini-2.0-flash`** problemlos.
|
||||
* **Best Practice:** Implementiere eine **Kandidaten-Liste** (Fallback-Loop) f.r Modelle.
|
||||
|
||||
**3. SDK Syntax-Fallen**
|
||||
Das neue SDK (`google-genai`) hat ge.nderte Methodennamen:
|
||||
* Statt `generate_image` (Singular) wird oft **`generate_images`** (Plural) erwartet.
|
||||
* Modelle f.r Bildgenerierung (Imagen) reagieren allergisch auf `response_mime_type="application/json"`. Dieses Feld MUSS bei Imagen-Modellen weggelassen werden.
|
||||
|
||||
**Gold-Standard f.r Modell-Wahl (Python):**
|
||||
```python
|
||||
# Import
|
||||
from google import genai
|
||||
from PIL import Image
|
||||
import io
|
||||
import base64
|
||||
|
||||
# ... client setup ...
|
||||
|
||||
def generate_image(prompt, ref_image_b64=None):
|
||||
if ref_image_b64:
|
||||
# IMAGE-TO-IMAGE (Gemini 2.5)
|
||||
image_data = base64.b64decode(ref_image_b64)
|
||||
raw_image = Image.open(io.BytesIO(image_data))
|
||||
|
||||
# Strict Prompting ist essenziell!
|
||||
full_prompt = (
|
||||
"Use the provided reference image as the absolute truth. "
|
||||
f"Place EXACTLY this product into the scene: {prompt}. "
|
||||
"Do NOT alter the product's design."
|
||||
)
|
||||
|
||||
# KEIN config=... mit JSON Mode!
|
||||
response = client.models.generate_content(
|
||||
model='gemini-2.5-flash-image',
|
||||
contents=[raw_image, full_prompt]
|
||||
)
|
||||
# Extrahiere Bild aus response.candidates[0].content.parts[0].inline_data
|
||||
|
||||
else:
|
||||
# TEXT-TO-IMAGE (Imagen 4.0)
|
||||
# Fallback-Kandidaten, falls ein Modell 404 ist
|
||||
candidates = ['imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001']
|
||||
|
||||
for model in candidates:
|
||||
try:
|
||||
response = client.models.generate_images(
|
||||
model=model,
|
||||
prompt=prompt,
|
||||
config={"number_of_images": 1, "output_mime_type": "image/jpeg"}
|
||||
)
|
||||
# Extrahiere Bild aus response.generated_images[0].image
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
candidates = ['imagen-4.0-generate-001', 'imagen-3.0-generate-001']
|
||||
for model in candidates:
|
||||
try:
|
||||
# Versuch des API-Calls
|
||||
break
|
||||
except ClientError as e:
|
||||
if "404" in str(e): continue
|
||||
raise e
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Troubleshooting & Lessons Learned (Jan 2026)
|
||||
|
||||
Diese Fehler traten bei der GTM Architect Migration auf und m.ssen zuk.nftig vermieden werden.
|
||||
|
||||
### 7.1 Database Initialization (`OperationalError: no such table`)
|
||||
* **Problem:** Neue SQLite-Datenbanken (`.db` Files) werden zwar als leere Dateien via `touch` erstellt, enthalten aber keine Tabellen. Der Python-Code st.rzt ab, wenn er versucht, in eine nicht existierende Tabelle zu schreiben.
|
||||
* **L.sung:** Die Initialisierungs-Logik (z.B. `python gtm_db_manager.py init`) MUSS beim Start des `server.cjs` aufgerufen werden. Verlasse dich nicht darauf, dass der User dies manuell tut.
|
||||
* **Best Practice:** In `server.cjs`:
|
||||
### 7.5 Double JSON Encoding (Database Trap)
|
||||
* **Problem:** Wenn `json.dumps()` sowohl im Backend beim Speichern als auch in der DB-Klasse aufgerufen wird, landet "Stringified JSON" in der DB. Beim Laden im Frontend crasht React, da es einen String statt eines Objekts erh.lt.
|
||||
* **Fix Backend:** Speichere rohe Dictionaries in der DB-Klasse.
|
||||
* **Fix Frontend:** Nutze eine robuste Parse-Funktion, die `JSON.parse()` mehrfach versucht:
|
||||
```javascript
|
||||
const dbScript = path.join(__dirname, 'gtm_db_manager.py');
|
||||
require('child_process').spawn('python3', [dbScript, 'init']);
|
||||
```
|
||||
|
||||
### 7.2 Dependency Injection (`ModuleNotFoundError`)
|
||||
* **Problem:** Wenn eine `requirements.txt` nachtr.glich hinzugef.gt oder ge.ndert wird, reicht ein einfacher Container-Neustart nicht aus. `pip install` l.uft normalerweise nur w.hrend der Build-Phase.
|
||||
* **L.sung:**
|
||||
1. **Immer Rebuild:** `docker-compose up -d --build <service>`.
|
||||
2. **Startup-Check:** F.r maximale Robustheit kann der `CMD` im Dockerfile so angepasst werden, dass er `pip install` vor dem Server-Start ausf.hrt: `CMD ["/bin/bash", "-c", "pip install -r requirements.txt && node server.cjs"]`.
|
||||
|
||||
### 7.3 Python Indentation (`IndentationError`)
|
||||
* **Problem:** Beim Copy-Paste von Code in `config.py` entstehen oft versehentliche Einr.ckungen (Leerzeichen am Zeilenanfang) bei globalen Variablen. Dies f.hrt dazu, dass der gesamte Python-Prozess beim Import abst.rzt.
|
||||
* **L.sung:** `.py` Dateien immer auf strikte Linksb.ndigkeit bei globalen Definitionen pr.fen. Tools wie `flake8` helfen, sind aber im Container oft nicht verf.gbar. Sorgfalt ist entscheidend.
|
||||
|
||||
### 7.4 Server File Location & Volume Mounts (Sideloading Trap)
|
||||
* **Problem:** Im Dockerfile wird `server.cjs` oft in das Root-Verzeichnis kopiert (`COPY gtm-app/server.cjs .`), aber in `docker-compose.yml` wird nur der Unterordner gemountet (`- ./gtm-app:/app/gtm-app`). Da die Node-App im Root läuft, führt sie weiterhin die alte, im Image "eingebackene" Version von `server.cjs` aus. Änderungen auf dem Host werden ignoriert.
|
||||
* **Symptom:** Code-Fixes im Node-Server (z.B. Timeouts, E2BIG Fixes) greifen nicht, obwohl die Datei auf dem Host korrekt aussieht.
|
||||
* **Lösung:** Den Mount in `docker-compose.yml` explizit auf die ausgeführte Datei lenken:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./mein-app-ordner/server.cjs:/app/server.cjs
|
||||
```
|
||||
const parseData = (d) => (typeof d === 'string' ? JSON.parse(d) : d);
|
||||
```
|
||||
Reference in New Issue
Block a user