# Dream Weaver API v2 — iOS Integration Guide (Dynamic Keywords) **Version:** 2.0-FINAL | **Updated:** 2026-03-09 | **Server:** `54.172.172.2` | **Port:** `8082` > This document is for **Sayan** (iOS / Swift). > Dream Weaver API v2 introduces a **Dynamic Keyword to Local LLM Prompt Expansion** system. > The app no longer relies on 5 hardcoded styles. Users can pick ANY keywords, and a local LLM (Qwen 3.5 27B via Ollama) will generate a photorealistic interior design prompt based on the room type without sending data to the cloud. > [!CAUTION] > **PORT 8080 IS DEAD.** Do not use port 8080 anymore. The old gateway process has been completely killed. If you try to send `POST /dream-weaver` or `/docs` to port 8080 you will get a 404. You MUST change your `AppConfig.baseURL` parameter to use port **`8082`**. --- ## 1. Architecture Overview (API v2) ``` ┌────────────────────┐ HTTP/S ┌──────────────────────────────┐ │ │ ── keywords ────► │ Dream Weaver Gateway v2 │ │ iPad App (Swift) │ │ FastAPI port 8082 │ │ │ ◄── PNG result ── │ dw_gateway_v2.py │ └────────────────────┘ └─────────────┬────────────────┘ │ LLM Prompt Expansion │ (Local Ollama: Qwen 3.5 27B) ▼ ┌─────────────────────────┐ │ ComfyUI Engine │ │ port 8188 │ │ RealVisXL V5.0 Ltng │ └─────────────────────────┘ ``` **Key changes in v2:** 1. The API now runs on port **`8082`** to avoid conflicts. 2. The [style](file:///F:/Workin%20In%20Progress/DESINEURON/GITLAB/Project_Velocity/comfy_engine/scripts/dreamweaver_batch_processor.py#394-414) parameter is deprecated in favor of `keywords` (array of strings) and [room_type](file:///F:/Workin%20In%20Progress/DESINEURON/GITLAB/Project_Velocity/comfy_engine/scripts/dw_gateway_v2.py#171-186). --- ## 2. Dynamic Keyword Expansion Flow Instead of injecting keywords into a rigid template, the new backend reads the `keywords` and [room_type](file:///F:/Workin%20In%20Progress/DESINEURON/GITLAB/Project_Velocity/comfy_engine/scripts/dw_gateway_v2.py#171-186), and asks a local LLM (Qwen 3.5 27B) to act as an interior designer: 1. **User input:** `keywords: ["blue marble", "gold veins", "renaissance"]`, `room_type: "bathroom"` 2. **Backend LLM Expansion:** The LLM knows that a "bathroom" cannot have beds and needs wet-area materials. It creates a rich positive prompt: *"renaissance revival luxury interior design, blue veined marble flooring, gold brass fixtures..."* 3. **ComfyUI Generation:** The expanded prompt is sent to ComfyUI for generation. **Supported Room Types:** `bedroom`, `living_room`, `bathroom`, `kitchen`, `dining_room`, `home_office`, `hallway`, `balcony`. --- ## 3. API Reference — New v2 Endpoints ### BASE URL ``` http://54.172.172.2:8082 ``` ### 3.1 `GET /health` — Liveness Check Call this on app launch to confirm the v2 server is up. **Response:** ```json { "status": "ok", "comfyui": true, "gpu": "4x NVIDIA L4 (96GB VRAM)", "model": "RealVisXL V5.0 Lightning", "llm_expansion": true, "version": "2.0.0" } ``` ### 3.2 `GET /room-types` Returns all supported room types and their required design context (useful if you want to build UI tooltips). ```json { "room_types": { "bedroom": { "description": "a private sleeping space", "key_elements": ["bed", "bedside tables", "wardrobe", "soft lighting", "textiles", "headboard"] }, ... } } ``` ### 3.3 `POST /dream-weaver/expand` (Preview Prompt) Use this if you want the user to **preview** the LLM's generated prompt before committing to a generation. **Request (JSON):** ```json { "keywords": ["blue marble", "gold veins", "renaissance"], "room_type": "bathroom" } ``` **Response:** ```json { "style_name": "Renaissance Luxury", "positive_prompt": "renaissance revival luxury interior design, blue veined marble flooring...", "negative_prompt": "(worst quality, low quality...), extra windows...", "cfg": 7.5, "denoise": 0.72, "steps": 30, "source": "ollama_local" } ``` ### 3.4 `POST /dream-weaver` (Submit Generation) Use this for the main generation flow. **Request:** `multipart/form-data` | Field | Type | Required | Description | |---|---|---|---| | [image](file:///F:/Workin%20In%20Progress/DESINEURON/GITLAB/Project_Velocity/comfy_engine/scripts/a100_deployment_executor.py#306-322) | File | ✅ | The room photo (JPEG/PNG) | | `keywords` | String | ✅ | Comma-separated user keywords e.g. `"gold, marble, luxury"` | | [room_type](file:///F:/Workin%20In%20Progress/DESINEURON/GITLAB/Project_Velocity/comfy_engine/scripts/dw_gateway_v2.py#171-186) | String | ✅ | e.g. `"living_room"`, `"bedroom"` | | `additional_notes` | String | ➕ | (Optional) e.g. `"make it feel like a luxury hotel"` | | `denoise` | Float | ➕ | (Optional) 0.5–0.85. If omitted, LLM decides. | **Response:** ```json { "job_id": "a1b2c3d4-...", "status": "processing", "prompt_preview": "renaissance revival luxury interior design...", "poll_url": "/dream-weaver/status/a1b2c3d4-...", "result_url": "/dream-weaver/result/a1b2c3d4-..." } ``` --- ## 4. Polling & Downloading (Unchanged from v1) **Poll Job Status:** `GET /dream-weaver/status/{job_id}` every 2 seconds until `ready == true`. **Download Result:** `GET /dream-weaver/result/{job_id}` returns the raw PNG stream. --- ## 5. Updated Swift Example (v2) ```swift func submitGenerationV2(image: UIImage, roomType: String, keywords: [String]) async throws -> GenerationJob { let url = URL(string: "\(AppConfig.baseURL)/dream-weaver")! var request = URLRequest(url: url) request.httpMethod = "POST" let boundary = UUID().uuidString request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") var body = Data() // 1. Image let imageData = image.jpegData(compressionQuality: 0.85)! body.appendMultipartForm(boundary: boundary, name: "image", filename: "room.jpg", contentType: "image/jpeg", data: imageData) // 2. Room Type body.appendMultipartForm(boundary: boundary, name: "room_type", value: roomType) // 3. Keywords let kwString = keywords.joined(separator: ", ") body.appendMultipartForm(boundary: boundary, name: "keywords", value: kwString) body.append("--\(boundary)--\r\n".data(using: .utf8)!) request.httpBody = body let (data, _) = try await URLSession.shared.data(for: request) return try JSONDecoder().decode(GenerationJob.self, from: data) } // Helper extension for building multipart forms cleanly extension Data { mutating func appendMultipartForm(boundary: String, name: String, value: String) { self.append("--\(boundary)\r\n".data(using: .utf8)!) self.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n".data(using: .utf8)!) self.append("\(value)\r\n".data(using: .utf8)!) } mutating func appendMultipartForm(boundary: String, name: String, filename: String, contentType: String, data: Data) { self.append("--\(boundary)\r\n".data(using: .utf8)!) self.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(filename)\"\r\n".data(using: .utf8)!) self.append("Content-Type: \(contentType)\r\n\r\n".data(using: .utf8)!) self.append(data) self.append("\r\n".data(using: .utf8)!) } } ``` --- ## 6. Sayan's Action Checklist (v2) - [ ] Change `AppConfig.baseURL` port to `8082` (e.g., `http://54.172.172.2:8082`). - [ ] Add a UI element for the user to select the **Room Type** (`bedroom`, `living_room`, `bathroom`, etc.). - [ ] Change the `POST /dream-weaver` payload from `{style}` to `{keywords, room_type}`. - [ ] (Optional) Use the new `GET /dream-weaver/expand` endpoint to let the user preview and edit the AI-generated prompt before generating.