diff --git a/ai/__pycache__/local_ai_api.cpython-311.pyc b/ai/__pycache__/local_ai_api.cpython-311.pyc index ae12bda..0d88e42 100644 Binary files a/ai/__pycache__/local_ai_api.cpython-311.pyc and b/ai/__pycache__/local_ai_api.cpython-311.pyc differ diff --git a/ai/local_ai_api.py b/ai/local_ai_api.py index bcff732..17146d1 100644 --- a/ai/local_ai_api.py +++ b/ai/local_ai_api.py @@ -1,35 +1,5 @@ """ LocalAIApi — lightweight Python client for the Flatlogic AI proxy. - -Usage (inside the Django workspace): - - from ai.local_ai_api import LocalAIApi - - response = LocalAIApi.create_response({ - "input": [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Summarise this text in two sentences."}, - ], - "text": {"format": {"type": "json_object"}}, - }) - - if response.get("success"): - data = LocalAIApi.decode_json_from_response(response) - # ... - -# Typical successful payload (truncated): -# { -# "id": "resp_xxx", -# "status": "completed", -# "output": [ -# {"type": "reasoning", "summary": []}, -# {"type": "message", "content": [{"type": "output_text", "text": "Your final answer here."}]} -# ], -# "usage": { "input_tokens": 123, "output_tokens": 456 } -# } - -The helper automatically injects the project UUID header and falls back to -reading executor/.env if environment variables are missing. """ from __future__ import annotations @@ -145,6 +115,7 @@ def request(path: Optional[str], payload: Dict[str, Any], options: Optional[Dict "Content-Type": "application/json", "Accept": "application/json", cfg["project_header"]: project_uuid, + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", } extra_headers = options.get("headers") if isinstance(extra_headers, Iterable): @@ -180,6 +151,7 @@ def fetch_status(ai_request_id: Any, options: Optional[Dict[str, Any]] = None) - headers: Dict[str, str] = { "Accept": "application/json", cfg["project_header"]: project_uuid, + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", } extra_headers = options.get("headers") if isinstance(extra_headers, Iterable): @@ -294,7 +266,6 @@ def _extract_text(response: Dict[str, Any]) -> str: return payload return "" - def _config() -> Dict[str, Any]: global _CONFIG_CACHE # noqa: PLW0603 if _CONFIG_CACHE is not None: @@ -314,13 +285,12 @@ def _config() -> Dict[str, Any]: "project_id": project_id, "project_uuid": os.getenv("PROJECT_UUID"), "project_header": os.getenv("AI_PROJECT_HEADER", "project-uuid"), - "default_model": os.getenv("AI_DEFAULT_MODEL", "gpt-5-mini"), + "default_model": os.getenv("AI_DEFAULT_MODEL", "gpt-4o-mini"), "timeout": int(os.getenv("AI_TIMEOUT", "30")), "verify_tls": os.getenv("AI_VERIFY_TLS", "true").lower() not in {"0", "false", "no"}, } return _CONFIG_CACHE - def _build_url(path: str, base_url: str) -> str: trimmed = path.strip() if trimmed.startswith("http://") or trimmed.startswith("https://"): @@ -329,7 +299,6 @@ def _build_url(path: str, base_url: str) -> str: return f"{base_url}{trimmed}" return f"{base_url}/{trimmed}" - def _resolve_status_path(ai_request_id: Any, cfg: Dict[str, Any]) -> str: base_path = (cfg.get("responses_path") or "").rstrip("/") if not base_path: @@ -338,7 +307,6 @@ def _resolve_status_path(ai_request_id: Any, cfg: Dict[str, Any]) -> str: base_path = f"{base_path}/ai-request" return f"{base_path}/{ai_request_id}/status" - def _http_request(url: str, method: str, body: Optional[bytes], headers: Dict[str, str], timeout: int, verify_tls: bool) -> Dict[str, Any]: """ @@ -413,7 +381,7 @@ def _ensure_env_loaded() -> None: continue key, value = stripped.split("=", 1) key = key.strip() - value = value.strip().strip('\'"') + value = value.strip().strip('"') if key and not os.getenv(key): os.environ[key] = value except OSError: diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 8cc0d9b..8c4b496 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 218f670..984f4c3 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index af7098a..b24f06b 100644 --- a/core/models.py +++ b/core/models.py @@ -1,6 +1,8 @@ from django.db import models from django.utils.text import slugify import uuid +import random +import string class StudioConfig(models.Model): """Singleton model to store studio-wide settings and the unique admin key.""" @@ -43,7 +45,16 @@ class Project(models.Model): def save(self, *args, **kwargs): if not self.slug: - self.slug = slugify(self.title) + base_slug = slugify(self.title) + if not base_slug: + base_slug = "project" + + unique_slug = base_slug + while Project.objects.filter(slug=unique_slug).exists(): + random_string = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)) + unique_slug = f"{base_slug}-{random_string}" + + self.slug = unique_slug super().save(*args, **kwargs) def __str__(self): @@ -109,4 +120,4 @@ class CgiAsset(models.Model): assigned_artist = models.CharField(max_length=100, blank=True) def __str__(self): - return f"{self.name} ({self.get_asset_type_display()})" + return f"{self.name} ({self.get_asset_type_display()})" \ No newline at end of file diff --git a/core/views.py b/core/views.py index 76bcbb2..0f8f1ef 100644 --- a/core/views.py +++ b/core/views.py @@ -80,7 +80,7 @@ def generate_production(request): {"role": "system", "content": "You are an expert Hollywood Producer and AI Cinema Director."}, {"role": "user", "content": prompt}, ], - "response_format": {"type": "json_object"}, + "text": {"format": {"type": "json_object"}}, }) if response.get("success"): @@ -178,4 +178,4 @@ def project_detail(request, slug): "assets": project.assets.all(), "scenes": project.scenes.all(), } - return render(request, "core/project_detail.html", context) \ No newline at end of file + return render(request, "core/project_detail.html", context)