"""Local research demo showing model provenance, sampling and live run metrics.""" from http.server import BaseHTTPRequestHandler, HTTPServer import json from pathlib import Path from .inference import load, continue_text, infill from .train import amp from .hybrid import generate as generate_causal from .editor import rewrite_span PAGE = r''' DiffuThink — From Scratch Lab
FROM RANDOM WEIGHTS TO TEXT

DiffuThink Research Lab

Un modèle hybride entraîné depuis zéro : continuation autorégressive et reconstruction par débruitage. Aucun poids externe préentraîné.

…paramètres
…récits d'entraînement
…tokens de contexte
…mises à jour

Expérimenter

Continuation : jusqu'à 32 tokens supplémentaires pour terminer la phrase. Une fin de phrase ne garantit pas une histoire achevée.

Température 0 : résultat déterministe. 0,5 : variations modérées. La continuation recommandée prédit un token après l'autre. Le modèle reste spécialisé dans les récits anglais simples.

Texte généré

Le résultat apparaîtra ici.

Voir les étapes de génération

Deux objectifs, un même réseau

En continuation, l'attention causale interdit de voir les tokens futurs. Pour compléter des trous, l'attention bidirectionnelle utilise les deux côtés. RoPE, RMSNorm et SwiGLU restent communs. La continuation autorégressive n'est pas présentée comme de la diffusion.

Données : TinyStories, récits synthétiques générés par GPT-3.5/4. Poids appris dans ce projet. Développement assisté par IA. Des erreurs et répétitions restent possibles ; ce modèle n'est pas un assistant généraliste.

''' def serve(args): import torch torch.set_num_threads(getattr(args,"threads",4)) model, tokenizer = load(args.model, args.device) info = json.loads((Path(args.model) / "training_info.json").read_text()) phase = info.get("previous_phase") total_steps = info["step"] while phase: total_steps += phase["step"] phase = phase.get("previous_phase") info = {**info, "phase_step": info["step"], "step": total_steps} # Warm the kernels before reporting interactive latency to the viewer. with torch.inference_mode(), amp(args.device, "bf16" if args.device == "cuda" else "fp32"): warm = torch.tensor([[1, 3, 2]], device=args.device) model(warm, torch.tensor([0.5], device=args.device)) class Handler(BaseHTTPRequestHandler): def respond(self, status, body, mime="application/json; charset=utf-8"): raw = body.encode() self.send_response(status) self.send_header("Content-Type",mime) self.send_header("Content-Length",str(len(raw))) self.end_headers() self.wfile.write(raw) def do_GET(self): if self.path == "/": self.respond(200,(Path(__file__).parent/"workshop.html").read_text(encoding="utf-8"),"text/html; charset=utf-8") elif self.path == "/lab": self.respond(200,PAGE,"text/html; charset=utf-8") elif self.path == "/api/info": self.respond(200,json.dumps(info)) else: self.respond(404,'{"error":"Not found"}') def do_POST(self): if self.path != "/api/generate": self.respond(404,'{"error":"Not found"}') return try: size=int(self.headers.get("Content-Length",0)) if not 0 < size <= 16384: raise ValueError("Invalid request size") data=json.loads(self.rfile.read(size)) if not isinstance(data,dict) or not isinstance(data.get("prompt"),str): raise ValueError("prompt must be a string") options=dict(steps=int(data.get("steps",12)),temperature=float(data.get("temperature",0.7)),seed=int(data.get("seed",42)),policy=data.get("policy","confidence"),precision="bf16" if args.device=="cuda" else "fp32") if not 1<=options["steps"]<=32 or not 0<=options["temperature"]<=1.5: raise ValueError("Invalid sampling range") if data.get("mode")=="rewrite": result=rewrite_span(model,tokenizer,data["prompt"],data.get("start"),data.get("end"),seed=options["seed"],precision=options["precision"]) elif data.get("mode")=="infill": if not isinstance(data.get("suffix",""),str): raise ValueError("suffix must be a string") result=infill(model,tokenizer,data["prompt"],data.get("suffix",""),int(data.get("missing_tokens",2)),**options) elif data.get("mode") in ("continue", "diffusion"): length=int(data.get("max_new_tokens",64)) maximum = 256 if data["mode"] == "continue" else 128 if not 1<=length<=maximum: raise ValueError(f"Length must be 1..{maximum}") if data["mode"] == "continue": if info.get("continuation_mode") != "autoregressive": raise ValueError("This checkpoint has no trained causal continuation mode") result=generate_causal(model,tokenizer,data["prompt"],max_new_tokens=length,finish_sentence_tokens=32,**options) else: result=continue_text(model,tokenizer,data["prompt"],max_new_tokens=length,**options) else: raise ValueError("Unknown task") self.respond(200,json.dumps(result,ensure_ascii=False)) except (ValueError,TypeError,OverflowError) as exc: self.respond(400,json.dumps({"error":str(exc)})) server=HTTPServer((getattr(args,"host","127.0.0.1"),args.port),Handler) print(f"DiffuThink v2: http://127.0.0.1:{args.port}",flush=True) try: server.serve_forever() except KeyboardInterrupt: pass finally: server.server_close()