feat: Oracle Canvas, Revision History and Canvas Sharing (#33)

Co-authored-by: Sagnik <sagnik7896@gmail.com>
Reviewed-on: sagnik/Project_Velocity#33
This commit is contained in:
2026-04-23 01:20:21 +05:30
parent e519339cc9
commit 6cdc366718
58 changed files with 3187 additions and 705 deletions

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env python3
"""
Dream Weaver — Local LLM Prompt Expander
========================================
Converts user keywords + room type into a photorealistic interior design prompt
using a local Ollama model (default: qwen3.5:27b).
Cloud API calls (Gemini, OpenAI) have been completely removed for data privacy
and local inference requirements.
"""
Dream Weaver — Shared Runtime Prompt Expander
============================================
Converts user keywords + room type into a photorealistic interior design prompt
using the shared OpenAI-compatible Desineuron runtime (default: SGLang-hosted
Qwen 3.6 35B A3B).
Cloud API calls (Gemini, OpenAI SaaS) have been removed in favor of the routed
Desineuron inference path.
Usage:
from prompt_expander import expand_prompt
@@ -18,7 +19,7 @@ Usage:
import os
import json
import logging
import requests
import requests
import re
logger = logging.getLogger(__name__)
@@ -126,26 +127,44 @@ class ExpandedPrompt:
self.source = source
def _call_ollama(user_message: str) -> str:
ollama_url = os.environ.get("OLLAMA_URL", "http://localhost:11434")
# Using Qwen 3.5 27B as requested
model = os.environ.get("OLLAMA_MODEL", "qwen3.5:27b")
full_prompt = f"{SYSTEM_PROMPT}\n\nUSER REQUEST:\n{user_message}\n\nReturn JSON ONLY. No markdown wrapping."
r = requests.post(
f"{ollama_url}/api/generate",
json={
"model": model,
"prompt": full_prompt,
"stream": False,
"format": "json",
"options": {"temperature": 0.5}
},
timeout=180 # Large models take time
)
r.raise_for_status()
resp_json = r.json()
return resp_json["response"]
def _call_runtime(user_message: str) -> str:
runtime_base = os.environ.get(
"SGLANG_BASE_URL",
os.environ.get(
"LLM_BASE_URL",
os.environ.get("OLLAMA_URL", "https://llm.desineuron.in"),
),
).rstrip("/")
chat_url = os.environ.get("SGLANG_CHAT_URL", f"{runtime_base}/v1/chat/completions")
model = os.environ.get(
"SGLANG_MODEL",
os.environ.get("OLLAMA_MODEL", "qwen3.6:35b-a3b"),
)
api_token = os.environ.get("SGLANG_API_TOKEN", "")
full_prompt = (
f"{SYSTEM_PROMPT}\n\nUSER REQUEST:\n{user_message}\n\nReturn JSON ONLY. No markdown wrapping."
)
headers = {"Content-Type": "application/json"}
if api_token:
headers["Authorization"] = f"Bearer {api_token}"
r = requests.post(
chat_url,
json={
"model": model,
"messages": [{"role": "user", "content": full_prompt}],
"temperature": 0.5,
"response_format": {"type": "json_object"},
"max_tokens": 1200,
},
headers=headers,
timeout=180,
)
r.raise_for_status()
resp_json = r.json()
message = ((resp_json.get("choices") or [{}])[0].get("message") or {}).get("content", "")
return message if isinstance(message, str) else json.dumps(message)
def expand_prompt(keywords: list[str], room_type: str = "living_room", additional_notes: str = "") -> ExpandedPrompt:
@@ -164,16 +183,16 @@ AVOID: {ctx['avoid']}
{f'NOTES: {additional_notes}' if additional_notes else ''}"""
try:
logger.info("Calling local Ollama LLM...")
raw = _call_ollama(user_message).strip()
logger.info("Calling shared Desineuron runtime LLM...")
raw = _call_runtime(user_message).strip()
# Log the raw response for debugging
logger.info(f"Raw Ollama response length: {len(raw)}")
# Handle empty response
if not raw:
logger.error("Empty response from Ollama")
raise ValueError("Ollama returned an empty response")
logger.error("Empty response from shared runtime")
raise ValueError("Shared runtime returned an empty response")
# Clean string of common junk (control characters, leading/trailing non-bracket junk)
raw_cleaned = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', raw)
@@ -215,7 +234,7 @@ AVOID: {ctx['avoid']}
source="ollama_local"
)
except Exception as e:
logger.error(f"Ollama LLM expansion failed: {e}")
logger.error(f"Shared runtime LLM expansion failed: {e}")
import traceback
traceback.print_exc()
# Full fallback if anything goes wrong