| from __future__ import annotations |
| from fastapi import FastAPI, HTTPException |
| from pydantic import BaseModel, Field |
| from typing import Any, Optional |
| from uuid import uuid4 |
| from datetime import datetime, timezone |
| from pathlib import Path |
| import hashlib |
|
|
| app = FastAPI(title="JackAILocal SaaS Builder", version="0.1.0") |
| BUILDS: dict[str, dict[str, Any]] = {} |
| ROOT = Path(__file__).resolve().parents[2] |
|
|
| class HardwareProfile(BaseModel): |
| product: str = "JackAILocal" |
| customer_email: str = "" |
| os: str = "windows" |
| cpu_name: Optional[str] = None |
| cpu_threads: int = 4 |
| ram_gb: float = 8 |
| vram_gb: float = 0 |
| gpus: list[Any] = Field(default_factory=list) |
| whichllm: Optional[Any] = None |
|
|
| MODEL_RULES = [ |
| {"profile":"lite", "label":"Fast", "min_ram_gb":6, "min_vram_gb":0, "ollama":"qwen3.5:2b"}, |
| {"profile":"balanced", "label":"Balanced", "min_ram_gb":8, "min_vram_gb":0, "ollama":"qwen3.5:4b"}, |
| {"profile":"smart", "label":"Smart", "min_ram_gb":16, "min_vram_gb":6, "ollama":"qwen3.5:9b"}, |
| {"profile":"vision", "label":"Vision", "min_ram_gb":12, "min_vram_gb":4, "ollama":"qwen3-vl:4b"}, |
| ] |
|
|
| def plan(profile: HardwareProfile) -> dict[str, Any]: |
| |
| allowed = [r for r in MODEL_RULES if profile.ram_gb >= r["min_ram_gb"] * 0.97 and profile.vram_gb >= r["min_vram_gb"] * 0.97] |
| if not allowed: |
| allowed = [MODEL_RULES[0]] |
| default = allowed[-1] |
| return { |
| "default_profile": default["profile"], |
| "profiles": allowed, |
| "ollama_models": sorted(set(r["ollama"] for r in allowed)), |
| "backends": ["ollama"], |
| "notes": ["Models are installed during target build or factory preload, not on every runtime launch."] |
| } |
|
|
| def asset_entry(relative: str) -> dict[str, Any] | None: |
| path = ROOT / relative |
| if not path.is_file(): |
| return None |
| return { |
| "path": relative.replace("\\", "/"), |
| "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), |
| "bytes": path.stat().st_size, |
| } |
|
|
| @app.post("/api/v1/hardware/submit") |
| def submit(profile: HardwareProfile): |
| build_id = str(uuid4()) |
| build_plan = plan(profile) |
| BUILDS[build_id] = { |
| "build_id": build_id, |
| "product": "JackAILocal", |
| "created_at": datetime.now(timezone.utc).isoformat(), |
| "hardware": profile.model_dump(), |
| "plan": build_plan, |
| } |
| return {"build_id": build_id, "manifest_url": f"/api/v1/builds/{build_id}/manifest", "plan": build_plan} |
|
|
| @app.get("/api/v1/builds/{build_id}/manifest") |
| def manifest(build_id: str): |
| b = BUILDS.get(build_id) |
| if not b: |
| raise HTTPException(404, "build not found") |
| plan = b["plan"] |
| assets = [ |
| item for item in [ |
| asset_entry("backends/ollama/windows/ollama.exe"), |
| asset_entry("bin/jackailocald.exe"), |
| asset_entry("config/voice-assets.json"), |
| asset_entry("content/field-manual/cards.json"), |
| ] if item is not None |
| ] |
| return { |
| "product":"JackAILocal", |
| "build_id":build_id, |
| "created_at":b["created_at"], |
| "default_profile":plan["default_profile"], |
| "ollama_models":plan["ollama_models"], |
| "assets": assets, |
| "security":{"signed_updates":True,"local_only":True,"no_admin_normal_use":True}, |
| "notes":"Use the platform builder package to install these assets and selected models into the target." |
| } |
|
|