198 lines
8.7 KiB
Markdown
198 lines
8.7 KiB
Markdown
# 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.
|