tt-hous's picture
Add files using upload-large-folder tool
be3ecc8 verified
Raw History Blame Contribute Delete
29.4 kB
import asyncio
import hashlib
import hmac
import inspect
import logging
import os
import queue
import threading
import time
import uuid
from collections import OrderedDict
from concurrent.futures import Future
from contextlib import asynccontextmanager
from dataclasses import dataclass, field
from pathlib import Path
import torch
from fastapi import FastAPI, HTTPException, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from models.autoports.cloudflare_clef.tt import encode as clef_encode
from models.autoports.cloudflare_clef.tt import head as clef_head
from models.autoports.cloudflare_clef.tt import precision_defaults
from models.autoports.cloudflare_clef.tt.api import SystemOneRequest, api_request, decode_media, to_record
DEFAULT_REPO = "Cloudflare/clef"
DEFAULT_REVISION = "2f3de3dd85f379784083b0814d997ab627200f0c"
DEFAULT_MODEL_NAMES = "clef"
HIDDEN = 5120
ALIGN = 128
TP = 2
L1_SMALL_SIZE = 24576
COMMAND_QUEUES = 2
TRACE_REGION = 1 << 30
ENCODE_MAX_LENGTH = 1 << 31
API_KEY = os.environ.get("CLEF_API_KEY")
OPEN_PATHS = ("/health", "/v1/health")
PARENTS = {"1x4": ("FABRIC_1D", (1, 4)), "2x2": ("FABRIC_2D", (2, 2))}
MESH_DEVICE_SHAPES = {"P150X2": (1, 2), "P300": (1, 2), "P150X4": (1, 4), "P300X2": (1, 4), "QB2": (2, 2)}
log = logging.getLogger("clef.tt.server")
if not log.handlers:
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s"))
log.addHandler(handler)
log.setLevel(logging.INFO)
WARMUP = {
"model": "clef",
"state": "Checkout has been failing for every customer for the last hour.",
"questions": {
"urgent": {"type": "noul", "instructions": "Is this support request urgent?"},
"team": {
"type": "choice",
"instructions": "Which team should handle this request?",
"criteria": {
"billing": "Payments, invoices, and refunds",
"technical": "Outages, errors, and configuration",
"sales": "Plans and upgrades",
},
},
"severity": {
"type": "score",
"instructions": "How severe is the customer impact?",
"criteria": ["No impact", "Minor", "Major", "Critical"],
},
},
}
def flag(name, default="0"):
return os.environ.get(name, default) == "1"
def parse_mesh_shape(shape, mesh_device):
if shape:
r, c = shape.lower().split("x")
return int(r), int(c)
return MESH_DEVICE_SHAPES.get((mesh_device or "").upper(), (1, 2))
def parse_offset(text):
r, c = (text or "0,0").split(",")
return int(r), int(c)
def resolve_model(value, revision=None):
if os.path.isdir(value):
return str(Path(value).resolve())
from huggingface_hub import snapshot_download
repo, _, at_revision = value.partition("@")
offline = os.environ.get("HF_HUB_OFFLINE") == "1"
return snapshot_download(repo, revision=at_revision or revision or None, local_files_only=offline)
@dataclass
class Settings:
model: str
revision: str
snapshot: str
model_names: tuple
mesh_shape: tuple
parent_mesh: str
submesh_offset: tuple
fake: bool
fake_workers: int
trace_region: int
prefix_cache: int
max_state: int
max_tail: int
truncate: bool
traced: bool
planner: bool = True
allow_remote_images: bool = True
warmup: bool = True
extra: dict = field(default_factory=dict)
@classmethod
def from_env(cls):
model = os.environ.get("CLEF_MODEL") or os.environ.get("HF_MODEL") or DEFAULT_REPO
revision = os.environ.get("CLEF_REVISION", DEFAULT_REVISION)
snapshot = resolve_model(model, revision)
os.environ["CLEF_MODEL"] = snapshot
traced = flag("CLEF_TRACED", "0")
return cls(
model=model,
revision=revision,
snapshot=snapshot,
model_names=tuple(
n.strip() for n in os.environ.get("CLEF_MODEL_NAMES", DEFAULT_MODEL_NAMES).split(",") if n.strip()
),
mesh_shape=parse_mesh_shape(os.environ.get("CLEF_MESH_SHAPE"), os.environ.get("MESH_DEVICE")),
parent_mesh=os.environ.get("CLEF_PARENT_MESH", ""),
submesh_offset=parse_offset(os.environ.get("CLEF_SUBMESH_OFFSET")),
fake=flag("CLEF_FAKE_ENGINE"),
fake_workers=int(os.environ.get("CLEF_FAKE_WORKERS", "1")),
trace_region=int(os.environ.get("CLEF_TRACE_REGION", str(TRACE_REGION if traced else 0))),
prefix_cache=int(os.environ.get("CLEF_PREFIX_CACHE", "4")),
max_state=int(os.environ.get("CLEF_MAX_STATE", "16384")),
max_tail=int(os.environ.get("CLEF_MAX_TAIL", "4096")),
truncate=flag("CLEF_TRUNCATE_STATES"),
traced=traced,
planner=flag("CLEF_PLANNER", "1"),
allow_remote_images=flag("CLEF_ALLOW_REMOTE_IMAGES", "1"),
warmup=flag("CLEF_WARMUP", "1"),
)
def mesh_plan(mesh_shape, visible, parent="", offset=(0, 0)):
r, c = mesh_shape
if (r, c) == (1, 2):
if parent or visible > 2:
name = parent or "1x4"
if name not in PARENTS:
raise ValueError(f"CLEF_PARENT_MESH={name!r}; expected one of {sorted(PARENTS)}")
fabric, shape = PARENTS[name]
return {"fabric": fabric, "open": shape, "submeshes": [((1, 2), tuple(offset))], "parent": name}
return {"fabric": "FABRIC_1D", "open": (1, 2), "submeshes": [], "parent": None}
if (r, c) == (1, 4):
if parent:
raise ValueError("CLEF_PARENT_MESH is only supported for CLEF_MESH_SHAPE=1x2")
return {
"fabric": "FABRIC_1D",
"open": (1, 4),
"submeshes": [((1, 2), (0, 0)), ((1, 2), (0, 2))],
"parent": None,
}
if (r, c) == (2, 2):
if parent:
raise ValueError("CLEF_PARENT_MESH is only supported for CLEF_MESH_SHAPE=1x2")
return {
"fabric": "FABRIC_2D",
"open": (2, 2),
"submeshes": [((1, 2), (0, 0)), ((1, 2), (1, 0))],
"parent": None,
}
raise ValueError(f"unsupported mesh shape {r}x{c}; Clef runs TP={TP} groups on 1x2, 1x4 or 2x2")
def visible_chips():
import ttnn
return len(ttnn.get_device_ids())
def open_devices(settings):
import ttnn
visible = visible_chips()
plan = mesh_plan(settings.mesh_shape, visible, settings.parent_mesh, settings.submesh_offset)
ttnn.set_fabric_config(getattr(ttnn.FabricConfig, plan["fabric"]))
parent = ttnn.open_mesh_device(
mesh_shape=ttnn.MeshShape(*plan["open"]),
l1_small_size=L1_SMALL_SIZE,
num_command_queues=COMMAND_QUEUES,
trace_region_size=settings.trace_region,
)
groups = []
for shape, offset in plan["submeshes"]:
groups.append(parent.create_submesh(ttnn.MeshShape(*shape), offset=ttnn.MeshCoordinate(*offset)))
if not groups:
groups = [parent]
for group in groups:
group.enable_program_cache()
log.info(
"mesh: %d chip(s) visible, %s parent %s chips %s, %d TP group(s) %s",
visible,
plan["fabric"],
list(parent.shape),
list(parent.get_device_ids()),
len(groups),
[list(g.get_device_ids()) for g in groups],
)
def close():
for group in groups:
ttnn.synchronize_device(group)
for child in parent.get_submeshes():
ttnn.close_mesh_device(child)
ttnn.close_mesh_device(parent)
ttnn.set_fabric_config(ttnn.FabricConfig.DISABLED)
return parent, groups, close, plan
def accepted_kwargs(fn, candidates):
params = inspect.signature(fn).parameters
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values()):
return dict(candidates)
return {k: v for k, v in candidates.items() if k in params}
def build_engine(mesh, settings):
from models.autoports.cloudflare_clef.tt.engine import ClefEngine
candidates = dict(
max_state_len=settings.max_state,
max_tail_len=settings.max_tail,
snapshot_slots=max(1, settings.prefix_cache),
traced=settings.traced,
planner=settings.planner,
)
return ClefEngine(mesh, **accepted_kwargs(ClefEngine.__init__, candidates))
def build_fake_engine(settings):
from models.autoports.cloudflare_clef.tt.fake_engine import FakeEngine
return FakeEngine(
max_state_len=settings.max_state, max_tail_len=settings.max_tail, snapshot_slots=max(1, settings.prefix_cache)
)
def media_key(ids, media_digest):
key = clef_encode.cache_key(ids)
if media_digest is None:
return key
return hashlib.sha1(f"{key}:{media_digest}".encode()).hexdigest()
@dataclass
class Job:
ids: list
state_ids: list
tail_ids: list
encoded: object
media: dict
key: str
questions: dict
def supports_media(engine):
try:
return "media" in inspect.signature(engine.prefill_state).parameters
except (TypeError, ValueError):
return bool(getattr(engine, "supports_media", False))
def supports_tail_len(engine):
try:
return "tail_len" in inspect.signature(engine.prefill_state).parameters
except (TypeError, ValueError):
return False
class Worker:
def __init__(self, wid, build, head, lm_rows, cache_size):
self.id = wid
self.build = build
self.head = head
self.lm_rows = lm_rows
self.cache_size = cache_size
self.queue = queue.Queue()
self.cache = OrderedDict()
self.free_slots = []
self.hits = self.misses = self.requests = 0
self.inflight = 0
self.counter = threading.Lock()
self.ready = threading.Event()
self.error = None
self.engine = None
self.media_ok = False
self.tail_len_ok = False
self.traced = False
self.warm_grids = None
self.gdn_conv = None
self.planner = None
self.thread = threading.Thread(target=self._run, name=f"clef-worker-{wid}", daemon=True)
self.thread.start()
def load(self):
return self.queue.qsize() + self.inflight
def submit(self, job):
done = Future()
with self.counter:
self.inflight += 1
self.queue.put((job, done))
return done
def stop(self):
self.queue.put(None)
self.thread.join()
def _run(self):
try:
self.engine = self.build()
slots = getattr(self.engine, "snapshot_slots", 1)
self.cache_size = min(self.cache_size, slots)
self.free_slots = list(range(self.cache_size))
self.media_ok = supports_media(self.engine)
self.tail_len_ok = supports_tail_len(self.engine)
self.traced = bool(getattr(self.engine, "traced", False))
self.gdn_conv = getattr(self.engine, "gdn_conv_impl", None)
self.planner = getattr(self.engine, "planner", None)
if self.traced:
self.warm_grids = sorted(tuple(g) for g, _ in getattr(self.engine, "vision_warmed_grids", []))
except Exception as error:
self.error = error
self.ready.set()
return
self.ready.set()
while True:
item = self.queue.get()
if item is None:
return
job, done = item
try:
done.set_result(self._serve(job))
except Exception as error:
done.set_exception(error)
finally:
with self.counter:
self.inflight -= 1
def _media_kwargs(self, job):
if job.media is None:
return {}
if not self.media_ok:
raise HTTPException(422, "this server build does not accept images or videos")
return {"media": job.media}
def _serve(self, job):
t0 = time.perf_counter()
ids = torch.tensor([job.ids], dtype=torch.long)
hit = False
if self.cache_size == 0:
hidden = self.engine.prefill_hidden(ids, slot=0, **self._media_kwargs(job))
else:
entry = self.cache.get(job.key)
hit = entry is not None
if hit:
self.cache.move_to_end(job.key)
self.hits += 1
handle, slot = entry
else:
self.misses += 1
slot = self.free_slots.pop() if self.free_slots else self.cache.popitem(last=False)[1][1]
hint = {"tail_len": len(job.tail_ids)} if self.tail_len_ok else {}
try:
handle = self.engine.prefill_state(
torch.tensor([job.state_ids], dtype=torch.long),
slot=slot,
key=job.key,
**self._media_kwargs(job),
**hint,
)
except ValueError as error:
self.free_slots.append(slot)
raise HTTPException(422, str(error))
except Exception:
self.free_slots.append(slot)
raise
self.cache[job.key] = (handle, slot)
tail = self.engine.schema_hidden(handle, torch.tensor([job.tail_ids], dtype=torch.long))
hidden = torch.cat([self.engine.prefix_hidden[slot], tail], dim=0)
probs = clef_head.probs_for_record(self.head, hidden, ids[0], job.encoded, self.lm_rows)
self.requests += 1
return probs, hit, round((time.perf_counter() - t0) * 1000, 1)
class Server:
def __init__(self, settings):
self.settings = settings
self.snapshot = settings.snapshot
self.release = clef_encode.release_module(self.snapshot)
self.tok = clef_encode.load_tokenizer(self.snapshot)
self.processor = None
self.head = clef_head.load_head(self.snapshot)
self.parent = None
self.close_devices = lambda: None
self.plan = None
self.lock = threading.Lock()
self.workers = []
self.stopping = threading.Event()
self.device_name = "none"
self.started = time.time()
@classmethod
def start(cls, settings):
server = cls(settings)
server.open()
if settings.warmup:
server.warmup()
return server
def processor_for_media(self):
if self.processor is None:
with self.lock:
if self.processor is None:
self.processor = clef_encode.load_processor(self.snapshot)
return self.processor
def _lm_rows(self):
from models.autoports.cloudflare_clef.tt.loader import LmHeadRows
return LmHeadRows(self.snapshot)
def open(self):
s = self.settings
if s.fake:
builds = [(f"fake{i}", lambda: build_fake_engine(s)) for i in range(max(1, s.fake_workers))]
self.device_name = f"fake x{len(builds)}"
else:
self.parent, groups, self.close_devices, self.plan = open_devices(s)
builds = [(str(list(g.get_device_ids())), lambda g=g: build_engine(g, s)) for g in groups]
self.device_name = f"ttnn {s.mesh_shape[0]}x{s.mesh_shape[1]}: {len(groups)} TP={TP} worker(s)"
self.workers = [
Worker(i, build, self.head, self._lm_rows(), s.prefix_cache) for i, (_, build) in enumerate(builds)
]
for w, (name, _) in zip(self.workers, builds):
w.ready.wait()
if w.error is not None:
self.close()
raise RuntimeError(f"worker {w.id} ({name}) failed to load") from w.error
log.info(
"worker %d ready on %s, prefix cache %d state(s), media=%s, mode=%s, gdn_conv=%s, planner=%s, "
"warm_grids=%s",
w.id,
name,
w.cache_size,
w.media_ok,
"traced" if w.traced else "eager",
w.gdn_conv,
w.planner,
w.warm_grids,
)
def warmup(self):
req = SystemOneRequest.model_validate(WARMUP)
job, _ = self.encode(req)
for w in self.workers:
probs, _, ms = w.submit(job).result()
log.info("warmup worker=%d questions=%d latency_ms=%.1f", w.id, len(probs), ms)
def close(self):
if self.stopping.is_set():
return
self.stopping.set()
for w in self.workers:
if w.ready.is_set() and w.error is None:
w.stop()
self.close_devices()
def _encode_record(self, record, processor, max_state_tokens=None):
try:
encoded = clef_encode.encode(
self.tok, record, processor=processor, max_length=ENCODE_MAX_LENGTH, max_state_tokens=max_state_tokens
)
except ValueError as error:
raise HTTPException(422, str(error))
try:
state_ids, tail_ids, _ = clef_encode.split_for_cache(encoded, self.tok, record)
except ValueError as error:
raise HTTPException(500, f"prefix split failed: {error}")
return encoded, state_ids, tail_ids
def encode(self, req):
s = self.settings
try:
images, videos, media_digest = decode_media(req)
except ValueError as error:
raise HTTPException(422, str(error))
record = to_record(req, images, videos)
processor = self.processor_for_media() if (images or videos) else None
encoded, state_ids, tail_ids = self._encode_record(record, processor)
if encoded.media:
self.check_warm_grids(encoded.media)
S = len(state_ids)
stats = {"state_tokens": S, "state_tokens_used": S}
if S > s.max_state:
n_state = len(self.release._tokens(self.tok, self.release.render(req.state)))
fixed = S - n_state
keep = s.max_state - fixed
if not s.truncate or keep < 0:
raise HTTPException(422, overflow_message(S, fixed, s.max_state))
encoded, state_ids, tail_ids = self._encode_record(record, processor, max_state_tokens=keep)
stats["state_tokens_used"] = len(state_ids)
if len(tail_ids) > s.max_tail:
raise HTTPException(
422,
f"schema is {len(tail_ids):,} tokens, over the {s.max_tail:,}-token limit: "
"use fewer questions or shorter criteria, or split them across requests",
)
key = media_key(state_ids, media_digest)
job = Job(list(encoded.input_ids), state_ids, tail_ids, encoded, encoded.media, key, req.question_dicts())
stats["tokens"] = len(encoded.input_ids)
stats["images"] = len(images)
stats["videos"] = len(videos)
return job, stats
def warm_grids(self):
lists = [w.warm_grids for w in self.workers if w.warm_grids is not None]
if not lists:
return None
return sorted({grid for grids in lists for grid in grids})
def check_warm_grids(self, media):
warm = self.warm_grids()
if warm is None:
return
rows = []
for name in ("image_grid_thw", "video_grid_thw"):
grids = media.get(name)
if grids is not None:
rows.extend(torch.as_tensor(grids).reshape(-1, 3).tolist())
if len(rows) > 1:
raise HTTPException(422, single_grid_message(len(rows)))
for grid in rows:
if tuple(grid) not in warm:
raise HTTPException(422, warm_grid_message(tuple(grid), warm))
def pick_worker(self, key):
with self.lock:
for w in self.workers:
if key in w.cache:
return w
return min(self.workers, key=lambda w: (w.load(), w.id))
def submit(self, req):
if self.stopping.is_set():
raise HTTPException(503, "the server is stopping")
job, stats = self.encode(req)
worker = self.pick_worker(job.key)
done = Future()
inner = worker.submit(job)
def finish(f):
if f.exception() is not None:
done.set_exception(f.exception())
return
probs, hit, ms = f.result()
stats.update(worker=worker.id, latency_ms=ms, prefix_cache_hit=hit)
log.info(
"worker=%d S=%d tail=%d questions=%d images=%d videos=%d latency_ms=%.1f cache_hit=%s",
worker.id,
stats["state_tokens_used"],
len(job.tail_ids),
len(probs),
stats["images"],
stats["videos"],
ms,
hit,
)
done.set_result((probs, stats, job))
inner.add_done_callback(finish)
return done
def as_request(self, rec):
return rec if isinstance(rec, SystemOneRequest) else SystemOneRequest.model_validate(api_request(rec))
def predict(self, rec):
probs, stats, _ = self.submit(self.as_request(rec)).result()
return probs, stats
def answer(self, req):
return self.body(req, *self.submit(req).result())
async def answer_async(self, req):
return self.body(req, *await asyncio.wrap_future(self.submit(req)))
def body(self, req, probs, stats, job):
answers = {qid: self.release.systemone_answer(job.questions[qid], dist) for qid, dist in probs.items()}
body = {
"model": req.model,
"answers": answers,
"usage": {"input_tokens": stats["tokens"], "output_tokens": 0},
"latency_ms": stats["latency_ms"],
}
if self.settings.truncate:
body["usage"].update(state_tokens=stats["state_tokens"], state_tokens_used=stats["state_tokens_used"])
body["truncated"] = stats["state_tokens"] > stats["state_tokens_used"]
return body
def card(self):
s = self.settings
workers = [
{
"id": w.id,
"queued": w.load(),
"cached_states": len(w.cache),
"cache_size": w.cache_size,
"hits": w.hits,
"misses": w.misses,
"requests": w.requests,
"media": w.media_ok,
}
for w in self.workers
]
backend = "fake engine" if s.fake else "ttnn, TP=2"
warm = self.warm_grids()
traced_media = None
if warm is not None:
traced_media = {
"warm_grids": [list(grid) for grid in warm],
"rule": "one image or video grid per request, and a grid (t, h, w) outside this list is refused "
"with 422; eager (CLEF_TRACED=0) accepts any grid and several images or videos per request",
}
return {
"description": f"Cloudflare/clef joint schema decisions on Tenstorrent ({backend})",
"weights": {"repo": DEFAULT_REPO, "revision": s.revision, "snapshot": self.snapshot},
"device": self.device_name,
"mesh_shape": f"{s.mesh_shape[0]}x{s.mesh_shape[1]}",
"mesh_plan": self.plan,
"backend": "fake" if s.fake else "ttnn",
"precision": precision_defaults.active(),
"traced": s.traced,
"mode": "traced" if s.traced else "eager",
"gdn_conv": next((w.gdn_conv for w in self.workers if w.gdn_conv), None),
"prefix_planner": s.planner,
"traced_media": traced_media,
"max_state_tokens": s.max_state,
"max_schema_tokens": s.max_tail,
"truncate_states": s.truncate,
"remote_images": s.allow_remote_images,
"prefix_cache": {
"size": s.prefix_cache,
"hits": sum(w.hits for w in self.workers),
"misses": sum(w.misses for w in self.workers),
"cached_states": sum(len(w.cache) for w in self.workers),
},
"workers": workers,
"requests": sum(w.requests for w in self.workers),
"uptime_s": round(time.time() - self.started, 1),
}
def overflow_message(S, fixed, max_state):
return (
f"state is {S:,} tokens ({fixed} of them the system prompt and media placeholders), "
f"over the {max_state:,}-token limit: shorten the state or split it across requests; "
f"or start the server with CLEF_TRUNCATE_STATES=1 to read only its first {max_state:,} tokens "
"(responses then say truncated: true)"
)
def warm_grid_message(grid, warm):
grids = "; ".join(",".join(str(x) for x in g) for g in warm)
return (
f"image grid {grid} (t, h, w patches) is not in this traced server's warm list [{grids}]: "
"resize the image to a warmed grid, add the grid to CLEF_VISION_WARM_GRID at startup, "
"or serve with CLEF_TRACED=0 (the default), which accepts any grid"
)
def single_grid_message(n_grids):
return (
f"this request carries {n_grids} image or video grids; a traced server (CLEF_TRACED=1) takes one grid "
"per request, because the tower joins several images with a concat program compiled per image count "
"and no vision program may compile while traces are live: send one image (or one video) per request, "
"or serve with CLEF_TRACED=0 (the default), which accepts several"
)
@asynccontextmanager
async def lifespan(app):
settings = Settings.from_env()
log.info(
"starting: model=%s snapshot=%s fake=%s mesh=%s parent=%s offset=%s prefix_cache=%d max_state=%d "
"max_tail=%d truncate=%s traced=%s planner=%s trace_region=%d",
settings.model,
settings.snapshot,
settings.fake,
settings.mesh_shape,
settings.parent_mesh or "auto",
settings.submesh_offset,
settings.prefix_cache,
settings.max_state,
settings.max_tail,
settings.truncate,
settings.traced,
settings.planner,
settings.trace_region,
)
app.state.server = await asyncio.to_thread(Server.start, settings)
server_ = app.state.server
log.info(
"ready: %d worker(s) on %s, mode=%s, gdn_conv=%s, planner=%s, warm_grids=%s",
len(server_.workers),
server_.device_name,
"traced" if settings.traced else "eager",
next((w.gdn_conv for w in server_.workers if w.gdn_conv), None),
settings.planner,
server_.warm_grids(),
)
try:
yield
finally:
app.state.server.close()
app = FastAPI(title="clef-tt", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["x-typesafe-request-id", "server-timing"],
)
def validation_detail(error):
errors = error.errors()
if not errors:
return "invalid request"
first = errors[0]
msg = str(first.get("msg", "invalid request"))
if msg.startswith("Value error, "):
return msg[len("Value error, ") :]
loc = ".".join(str(p) for p in first.get("loc", ()) if p != "body")
return f"{loc}: {msg}" if loc else msg
@app.exception_handler(RequestValidationError)
async def on_validation_error(request: Request, error: RequestValidationError):
return JSONResponse({"detail": validation_detail(error)}, 422)
@app.middleware("http")
async def typesafe(request, call_next):
started = time.perf_counter()
path = request.url.path
if (
API_KEY
and path.startswith("/v1")
and path not in OPEN_PATHS
and not hmac.compare_digest(request.headers.get("authorization", ""), f"Bearer {API_KEY}")
):
resp = JSONResponse(
{"detail": "missing or invalid API key; send Authorization: Bearer <CLEF_API_KEY>"},
401,
{"www-authenticate": "Bearer"},
)
else:
resp = await call_next(request)
resp.headers["x-typesafe-request-id"] = request.headers.get("x-typesafe-request-id") or uuid.uuid4().hex
resp.headers["server-timing"] = f"app;dur={(time.perf_counter() - started) * 1000:.1f}"
return resp
def server() -> Server:
return app.state.server
@app.post("/v1/systemone")
async def systemone(req: SystemOneRequest):
return await server().answer_async(req)
@app.get("/v1/models")
def models():
card = server().card()
return {"models": [{"name": name, **card} for name in server().settings.model_names]}
@app.get("/health")
@app.get("/v1/health")
def health():
s = server()
return {"status": "ok", "workers": len(s.workers), "queued": sum(w.load() for w in s.workers)}
def main():
import argparse
import uvicorn
ap = argparse.ArgumentParser()
ap.add_argument("--host", default="127.0.0.1")
ap.add_argument("--port", type=int, default=8008)
a = ap.parse_args()
uvicorn.run(app, host=a.host, port=a.port, lifespan="on")
if __name__ == "__main__":
main()