Resolved multiple issues preventing the 'competitor-analysis' app from running and serving its frontend:
1. **Fixed Python SyntaxError in Prompts:** Corrected unterminated string literals and ensure proper multi-line string formatting (using .format() instead of f-strings for complex prompts) in .
2. **Addressed Python SDK Compatibility (google-generativeai==0.3.0):**
* Removed for and by adapting the orchestrator to pass JSON schemas as direct Python dictionaries, as required by the older SDK version.
* Updated with detailed guidance on handling / imports and dictionary-based schema definitions for older SDKs.
3. **Corrected Frontend Build Dependencies:** Moved critical build dependencies (like , , ) from to in .
* Updated to include this pitfall, ensuring frontend build tools are installed in Docker.
4. **Updated Documentation:**
* : Added comprehensive lessons learned regarding dependencies, Python SDK versioning (specifically and imports for ), and robust multi-line prompt handling.
* : Integrated specific details of the encountered errors and their solutions, making the migration report a more complete historical record and guide.
These changes collectively fix the 404 error by ensuring the Python backend starts correctly and serves the frontend assets after a successful build.
206 lines
10 KiB
Markdown
206 lines
10 KiB
Markdown
# Migration Guide: Google AI Builder Apps -> Local Docker Stack
|
|
|
|
> **WICHTIGER HINWEIS:** Der Gemini-Agent f"."hrt Code **innerhalb** dieses Docker-Containers aus. Er hat keinen Zugriff auf den Docker-Daemon des Host-Systems. Daher kann und wird der Agent **NIEMALS** in der Lage sein, Befehle wie `docker build`, `docker-compose up` oder andere Docker-Management-Aufgaben auszuf.hren. Diese Befehle m.ssen immer vom Benutzer auf dem Host-System ausgef.hrt werden.
|
|
|
|
**Ziel:** Standardisierter Prozess, um eine von Google AI Studio generierte React-App schnell, robust und fehlerfrei in die lokale Docker/Python-Architektur zu integrieren.
|
|
|
|
**Grundsatz:** "Minimalset & Robustheit". Wir bauen keine aufgebl.hten Container, und wir verhindern Timeouts und fehlende Abh.ngigkeiten proaktiv.
|
|
|
|
---
|
|
|
|
## 1. Vorbereitung & Abh.ngigkeiten (Common Pitfalls)
|
|
|
|
Bevor Code kopiert wird, m.ssen die Grundlagen stimmen.
|
|
|
|
### 1.1 Package.json Check (Frontend Build-Falle)
|
|
Generierte Apps haben oft kein `express`, da sie keinen Server erwarten. Noch wichtiger ist, dass kritische Build-Tools oft fälschlicherweise in `devDependencies` deklariert werden.
|
|
|
|
* **Aktion:** Öffne `package.json` der App.
|
|
* **Prüfung 1 (Backend):** Steht `express` unter `dependencies`?
|
|
* **Fix 1:**
|
|
```json
|
|
"dependencies": {
|
|
...
|
|
"express": "^4.18.2",
|
|
"cors": "^2.8.5"
|
|
}
|
|
```
|
|
* **Prüfung 2 (Frontend Build):** Stehen Build-Tools wie `vite`, `@vitejs/plugin-react` oder `typescript` unter `devDependencies`?
|
|
* **Fix 2 (KRITISCH):** Verschiebe **alle** `devDependencies` in die `dependencies`. Der `npm install`-Befehl im `Dockerfile` installiert `devDependencies` standardmäßig nicht, was zu einem fehlgeschlagenen `npm run build` führt.
|
|
|
|
### 1.2 Datenbank-Datei
|
|
Docker kann keine einzelne Datei mounten, wenn sie auf dem Host nicht existiert.
|
|
* **Fehler:** "Bind mount failed: ... does not exist"
|
|
* **Fix:** VOR dem ersten Start die Datei anlegen:
|
|
```bash
|
|
touch mein_neues_projekt.db
|
|
```
|
|
|
|
### 1.3 Vite Base Path (White Screen Fix)
|
|
Wenn die App unter einem Unterverzeichnis (z.B. `/gtm/`) l.uft, findet sie ihre JS/CSS-Dateien nicht, wenn Vite Standard-Pfade (`/`) nutzt.
|
|
* **Datei:** `vite.config.ts`
|
|
* **Fix:** `base` auf `./` setzen.
|
|
```typescript
|
|
export default defineConfig({
|
|
base: './', // WICHTIG f.r Sub-Pfad Deployment
|
|
// ...
|
|
});
|
|
```
|
|
|
|
### 1.4 Python Dependencies & Shared Libraries (Critical Pitfall)
|
|
Das Projekt nutzt ein zentrales `helpers.py`, das von mehreren Services geteilt wird. Dies f.hrt oft zu `ModuleNotFoundError`, da eine kleine App (wie `gtm-architect`) nicht alle Bibliotheken ben.tigt, die in `helpers.py` importiert werden (z.B. `gspread`, `pandas`).
|
|
|
|
* **Fehler:** `ModuleNotFoundError: No module named 'gspread'`
|
|
* **Ursache:** Die `gtm-architect/requirements.txt` enth.lt `gspread` nicht, aber `helpers.py` versucht es zu importieren.
|
|
* **Fix (in `helpers.py`):** Machen Sie "exotische" Importe optional. Dies ist die robusteste Methode, um die Kompatibilit.t zu wahren, ohne die `requirements.txt` kleiner Apps aufzubl.hen.
|
|
|
|
```python
|
|
# Beispiel in helpers.py
|
|
try:
|
|
import gspread
|
|
GSPREAD_AVAILABLE = True
|
|
except ImportError:
|
|
GSPREAD_AVAILABLE = False
|
|
gspread = None # Wichtig, damit Referenzen nicht fehlschlagen
|
|
```
|
|
* **Fix (in `requirements.txt`):** Stellen Sie sicher, dass die f.r die App **unmittelbar** ben.tigten Bibliotheken vorhanden sind. F.r `gtm-architect` sind das:
|
|
```text
|
|
google-generativeai
|
|
google-genai
|
|
Pillow
|
|
requests
|
|
beautifulsoup4
|
|
```
|
|
|
|
### 1.5 Python Syntax & F-Strings
|
|
Multi-Line Prompts k.nnen in Docker-Umgebungen zu **sehr hartn.ckigen Syntaxfehlern** f.hren, selbst wenn sie lokal korrekt aussehen.
|
|
|
|
* **Das Problem:** Der Python-Parser (insbesondere bei `f-strings` in Kombination mit Zahlen/Punkten am Zeilenanfang oder verschachtelten Klammern) kann Multi-Line-Strings (`f"""..."""`) falsch interpretieren, was zu Fehlern wie `SyntaxError: invalid decimal literal` oder `unmatched ')'` f.hrt, auch wenn der Code scheinbar korrekt ist.
|
|
|
|
* **ULTIMATIVE L.SUNG (Maximale Robustheit):**
|
|
1. **Vermeide `f"""` komplett f.r komplexe Multi-Line-Prompts.** Definiere stattdessen den Prompt als **Liste von einzelnen String-Zeilen** und f.ge sie mit `"\n".join(prompt_parts)` zusammen.
|
|
2. **Nutze die `.format()` Methode oder f-Strings in EINZEILIGEN Strings** zur Variablen-Injektion. Dies trennt die String-Definition komplett von der Variablen-Interpolation und ist die robusteste Methode.
|
|
|
|
```python
|
|
# Beispiel: Maximal robust
|
|
prompt_template_parts = [
|
|
"1) Mache dies: {variable_1}",
|
|
"2) Mache das: {variable_2}",
|
|
]
|
|
prompt_template = "\n".join(prompt_template_parts)
|
|
prompt = prompt_template.format(variable_1=wert1, variable_2=wert2)
|
|
# System-Instruktion muss immer noch vorangestellt werden:
|
|
full_prompt = sys_instr + "\n\n" + prompt
|
|
```
|
|
|
|
* **Versionierung f.r Debugging:** Um sicherzustellen, dass die korrekte Version des Codes l.uft, f.ge Versionsnummern in die Start-Logs des Node.js Servers (`server.cjs`) und des Python Orchestrators (`gtm_architect_orchestrator.py`) ein.
|
|
* `server.cjs`: `console.log(`... (Version: ${VERSION})`);`
|
|
* `gtm_architect_orchestrator.py`: `print(f"DEBUG: Orchestrator v{__version__} loaded ...")`
|
|
|
|
* **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`).
|
|
|
|
---
|
|
|
|
## 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).
|
|
|
|
---
|
|
|
|
## 3. Docker Optimierung (Multi-Stage)
|
|
|
|
Wir nutzen **Multi-Stage Builds**, um das Image klein zu halten (kein `src`, keine Dev-Tools im Final Image).
|
|
|
|
---
|
|
|
|
## 4. Docker Compose & Mounts (WICHTIGER PITFALL)
|
|
|
|
**WARNUNG: Lokale Dateien .berschreiben den Container-Code!**
|
|
|
|
---
|
|
|
|
## 5. Nginx Proxy
|
|
|
|
Achtung beim Routing. Wenn die App unter `/app/` laufen soll, muss der Trailing Slash (`/`) stimmen.
|
|
|
|
---
|
|
|
|
## 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).
|
|
|
|
---
|
|
|
|
|
|
## Appendix A: GTM Architect Fixes & Gemini Migration
|
|
|
|
### A.1 - A.4 (Siehe .ltere Versionen f.r Prompt-Fixes & GenerativeModel API)
|
|
|
|
### A.5 Image Generation 2.0 (Hybrid Approach - Jan 04)
|
|
|
|
Um die Einschr.nkungen der "Text-only" Modelle und die regionale Verf.gbarkeit von Imagen 3 zu umgehen, nutzen wir einen hybriden Ansatz.
|
|
|
|
**1. Anforderungen**
|
|
* **Bibliothek:** `google-genai` (v1.x) MUSS installiert sein (`pip install google-genai`). `google-generativeai` (v0.x) ist veraltet.
|
|
* **Bildverarbeitung:** `Pillow` muss installiert sein (`pip install Pillow`).
|
|
|
|
**2. Die Logik (Text vs. Bild)**
|
|
Das System entscheidet automatisch, welches Modell genutzt wird:
|
|
|
|
* **Szenario A: Generisches Bild (Text-to-Image)**
|
|
* **Modell:** `imagen-4.0-generate-001`.
|
|
* **Szenario B: Produkt-Integration (Image-to-Image)**
|
|
* **Modell:** `gemini-2.5-flash-image`.
|
|
|
|
### 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, sowie die schnelle Evolution der API-Schnittstellen.
|
|
|
|
**1. Das SDK-Dilemma**
|
|
Es existieren zwei parallele Google SDKs, und selbst innerhalb von `google-generativeai` ändern sich die Modulstrukturen schnell:
|
|
1. **`google-generativeai` (Legacy):** Versionen wie `0.3.0` verhalten sich anders als neuere. 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`.
|
|
|
|
**KRITISCHES PROBLEM: `ImportError` für `Schema` und `Content` mit `google-generativeai==0.3.0`**
|
|
* **Fehler:** `ImportError: cannot import name 'Schema' from 'google.generativeai.types'` oder `ImportError: cannot import name 'Content' from 'google.generativeai.types'`.
|
|
* **Ursache:** In älteren Versionen des `google-generativeai`-SDK (z.B. `0.3.0`, wie in diesem Projekt verwendet) existierten diese Klassen (`Schema`, `Content`) nicht an den gleichen Importpfaden wie in neueren Versionen oder wurden gar nicht als separate Klassen verwendet.
|
|
* **LÖSUNG (für `google-generativeai==0.3.0`):**
|
|
* Entferne **alle** Importe für `Schema` und `Content` (z.B. `from google.generativeai.types import HarmCategory, HarmBlockThreshold`).
|
|
* Ersetze **alle** Instanziierungen von `Schema(...)` durch einfache Python-Dictionaries, die direkt das JSON-Schema repräsentieren. Die `generation_config` akzeptiert in dieser Version direkt das Dictionary.
|
|
|
|
**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
|
|
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)
|
|
|
|
### 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 parseData = (d) => (typeof d === 'string' ? JSON.parse(d) : d);
|
|
``` |