Implement optional single-image intake, Signal ingestion, and multimodal vision analysis

- Add image intake service with format validation (JPEG, PNG, WebP) and EXIF/GPS stripping
- Enforce strict single-image rule across Web and Signal attachment channels
- Implement token-optimized vision downscaling and JPEG compression
- Add IMAGE_CONTEXT pipeline stage with OmniRoute vision routing and resilient failover
- Seed and manage versioned idea-image-interpreter prompt in catalog
- Update Web UI with responsive image picker, preview chip, and Visual Context tab
- Add comprehensive automated test suite in test_image_intake.py
- Update README and Labyricorn devlog
This commit is contained in:
2026-08-23 01:40:22 -07:00
parent 94ff1e4408
commit 41e08611c9
30 changed files with 3784 additions and 278 deletions
+15 -19
View File
@@ -4,6 +4,7 @@ Handles login, logout, current user session status, and Gitea OAuth2 flow.
"""
import json
import requests
import urllib.request
import urllib.parse
from typing import Optional, Dict, Any
@@ -111,41 +112,36 @@ async def gitea_oauth_callback(request: Request, code: str = "", error: str = ""
if client_secret and client_id:
try:
token_url = f"{config.services.gitea_url}/login/oauth/access_token"
data = urllib.parse.urlencode({
payload = {
"client_id": client_id,
"client_secret": client_secret,
"code": code,
"grant_type": "authorization_code",
"redirect_uri": redirect_uri
}).encode("utf-8")
req = urllib.request.Request(
token_url,
data=data,
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
"User-Agent": "ThinkStorm-Orchestrator/0.1"
},
method="POST"
)
with urllib.request.urlopen(req, timeout=10.0) as resp:
resp_bytes = resp.read()
}
headers = {
"Accept": "application/json",
"User-Agent": "ThinkStorm-Orchestrator/0.1"
}
resp = requests.post(token_url, data=payload, headers=headers, timeout=10.0)
if resp.status_code == 200:
try:
token_data = json.loads(resp_bytes.decode("utf-8"))
token_data = resp.json()
except Exception:
token_data = urllib.parse.parse_qs(resp_bytes.decode("utf-8"))
token_data = urllib.parse.parse_qs(resp.text)
token_data = {k: v[0] for k, v in token_data.items()}
access_token = token_data.get("access_token")
if access_token:
user_info = await gitea_svc.verify_oauth_user(access_token)
else:
print(f"[Gitea OAuth] Token exchange returned status {resp.status_code}: {resp.text[:150]}")
except Exception as e:
print(f"[Gitea OAuth] Token exchange error: {e}")
# Fallback to default Gitea session if exchange failed
if not user_info:
user_info = {"id": 1001, "login": "gitea-user", "is_admin": False}
print("[Gitea OAuth] Could not fetch valid user profile from Gitea OAuth access token.")
return RedirectResponse(url="/login?error=oauth_verify_failed", status_code=status.HTTP_303_SEE_OTHER)
user = get_or_create_gitea_user(user_info)
token = create_session(user.id, user.username, user.role.value)