Router / infer.py
CompactAI's picture
Upload 11 files
35700de verified
Raw
History Blame Contribute Delete
4.15 kB
"""glint-router-1m: route prompts, edit the policy, teach it new categories.
python infer.py "write a python function that reverses a linked list"
python infer.py --file prompts.txt
python infer.py --policy my_policy.json "summarize this thread"
python infer.py --add-category refunds examples.txt
python infer.py --demo
everything runs on cpu. one forward pass per prompt, around 0.2 ms.
"""
from __future__ import annotations
import sys
import time
from pathlib import Path
import torch
from modeling import encode_batch, load_router
from policy import Policy, Prototypes, decide
HERE = Path(__file__).parent
PROTOTYPES = HERE / "prototypes.json"
def route(prompts: list[str], policy_path: Path | None = None,
prototypes_path: Path | None = None, device: str = "cpu"):
model, tokenizer = load_router(HERE, device)
policy = Policy.load(policy_path or HERE / "policy.json")
prototypes = Prototypes.load(prototypes_path)
tokens = encode_batch(tokenizer, prompts, model.config.max_len).to(device)
return [decide(model, tokens[i:i + 1], prompts[i], policy, prototypes)
for i in range(len(prompts))]
def add_category(name: str, examples: list[str], device: str = "cpu") -> None:
"""teach it a label it was never trained on. one forward pass, no optimizer."""
model, tokenizer = load_router(HERE, device)
tokens = encode_batch(tokenizer, examples, model.config.max_len).to(device)
with torch.no_grad():
projections = model(tokens)["proj"]
prototypes = Prototypes.load(PROTOTYPES)
prototypes.add(name, projections)
prototypes.save(PROTOTYPES)
print(f"added category {name!r} from {len(examples)} examples -> {PROTOTYPES}")
def show(decisions, prompts) -> None:
for decision, prompt in zip(decisions, prompts, strict=True):
flags = [k for k, v in decision.signals.items() if v]
head = prompt if len(prompt) <= 58 else prompt[:55] + "..."
print(f"{decision.tier:<9} {decision.domain:<20} c={decision.complexity} "
f"d={decision.difficulty:.2f} {head}")
print(f" {decision.reason}"
+ (f" | signals={flags}" if flags else "")
+ (f" | category={decision.category}" if decision.category else ""))
DEMO = [
"What's the capital of France?",
"Write a short poem about a Python snake in the garden.",
"Write a Python function that reverses a linked list in place.",
"A train leaves at 3pm going 60mph, another at 4pm going 80mph, when do they meet?",
"Summarize this email thread in two sentences.",
"What are the latest news headlines today?",
"Explain the difference between a mutex and a semaphore, with an example.",
]
def main() -> int:
args = sys.argv[1:]
if not args or args[0] in ("-h", "--help"):
print(__doc__)
return 0
if args[0] == "--demo":
started = time.perf_counter()
decisions = route(DEMO)
elapsed = time.perf_counter() - started
show(decisions, DEMO)
print(f"\n{len(DEMO)} prompts in {elapsed * 1000:.0f} ms "
f"({elapsed * 1000 / len(DEMO):.2f} ms each, cpu, includes model load)")
return 0
if args[0] == "--add-category":
if len(args) < 3:
print("usage: python infer.py --add-category <name> <examples.txt>")
return 1
examples = [line.strip() for line in Path(args[2]).read_text().splitlines()
if line.strip()]
add_category(args[1], examples)
return 0
policy_path = None
if args[0] == "--policy":
policy_path, args = Path(args[1]), args[2:]
if args and args[0] == "--file":
prompts = [line.strip() for line in Path(args[1]).read_text().splitlines()
if line.strip()]
else:
prompts = [" ".join(args)]
decisions = route(prompts, policy_path,
PROTOTYPES if PROTOTYPES.is_file() else None)
show(decisions, prompts)
if len(prompts) == 1:
print()
print(decisions[0].as_line())
return 0
if __name__ == "__main__":
raise SystemExit(main())