feat: Oracle Canvas, Revision History and Canvas Sharing (#33)
Co-authored-by: Sagnik <sagnik7896@gmail.com> Reviewed-on: #33
This commit was merged in pull request #33.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user