Skip to content

Instantly share code, notes, and snippets.

@thexa4
Created June 11, 2026 17:33
Show Gist options
  • Select an option

  • Save thexa4/7796bb5d26a1f5884bfe356d80258882 to your computer and use it in GitHub Desktop.

Select an option

Save thexa4/7796bb5d26a1f5884bfe356d80258882 to your computer and use it in GitHub Desktop.
Cat llm
#!/usr/bin/env python3
# Copyright (c) 2026 Max Maton
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""OpenAI-compatible chat/completions server that simulates a cat walking on a keyboard.
Two paws walk a 2D QWERTY grid with gait and momentum. Printable keys produce text;
Enter ends the message; F-keys invoke tools from the request (F1 = first tool, extra
rows F13+ when there are more than 12 tools). String tool arguments use the same walk.
Binds to 127.0.0.1:8013 by default. No third-party dependencies.
Run:
python cat_llm_server.py
Point the editor LLM base URL at http://127.0.0.1:8013/v1
Smoke tests:
# Non-streaming text
curl -s http://127.0.0.1:8013/v1/chat/completions \\
-H 'Content-Type: application/json' \\
-d '{"model":"cat","messages":[{"role":"user","content":"hi"}],"stream":false}'
# Tool call
curl -s http://127.0.0.1:8013/v1/chat/completions \\
-H 'Content-Type: application/json' \\
-d '{"model":"cat","messages":[{"role":"user","content":"hi"}],"tools":[{"type":"function","function":{"name":"grep","parameters":{"type":"object","properties":{"pattern":{"type":"string"},"case_sensitive":{"type":"boolean","default":false}},"required":["pattern"]}}}],"stream":false}'
# Model list (llama.cpp uses GET /models; OpenAI clients use GET /v1/models)
curl -s http://127.0.0.1:8013/models
# Tokenize (llama.cpp POST /tokenize)
curl -s http://127.0.0.1:8013/tokenize \\
-H 'Content-Type: application/json' \\
-d '{"content": "hello", "with_pieces": true}'
# Apply chat template (llama.cpp POST /apply-template)
curl -s http://127.0.0.1:8013/apply-template \\
-H 'Content-Type: application/json' \\
-d '{"messages": [{"role": "user", "content": "Hello!"}]}'
# Streaming
curl -N http://127.0.0.1:8013/v1/chat/completions \\
-H 'Content-Type: application/json' \\
-d '{"model":"cat","messages":[{"role":"user","content":"hi"}],"stream":true}'
Optional: --seed N for reproducibility; request header X-Cat-Seed overrides per call.
"""
from __future__ import annotations
import argparse
import json
import random
import secrets
import sys
import time
from dataclasses import dataclass, field
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Any, Iterator, Literal
from urllib.parse import urlparse
LISTEN_HOST = "127.0.0.1"
LISTEN_PORT = 8013
FKEYS_PER_ROW = 12
MAX_PAW_DISTANCE = 4
MOMENTUM_CONTINUE_PROB = 0.75
SAME_PAW_TWICE_PROB = 0.15
TRAILING_PRESS_PROB = 0.30
DEFAULT_MAX_KEY_EVENTS = 512
DEFAULT_MODEL = "cat-on-keyboard"
BOS_TOKEN_ID = 1
BOS_TOKEN_PIECE = "<s>"
# Fake vocab: one token per Unicode code point (stable, no real tokenizer).
_TOKEN_ID_BASE = 256
_IM_START = "<|im_start|>"
_IM_END = ""
# US QWERTY rows (F-row prepended / extended at runtime). None = empty cell.
_BASE_CHAR_ROWS: list[list[str | None]] = [
list("`1234567890-="),
list("qwertyuiop[]\\"),
list("asdfghjkl;'"),
list("zxcvbnm,./"),
[None, None, " ", " ", " ", " ", " ", " ", " ", None, None, "Enter"],
]
_DIRECTIONS: tuple[tuple[int, int], ...] = (
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, -1),
(1, 0),
(1, 1),
)
@dataclass(frozen=True)
class KeyCell:
kind: Literal["empty", "char", "enter", "fkey"]
value: str | int = ""
@staticmethod
def from_label(label: str | None) -> KeyCell:
if label is None:
return KeyCell("empty")
if label == "Enter":
return KeyCell("enter")
if label.startswith("F") and label[1:].isdigit():
return KeyCell("fkey", int(label[1:]))
return KeyCell("char", label)
@dataclass
class ToolSpec:
name: str
parameters: dict[str, Any]
fkey: int
@dataclass
class KeyPressEvent:
kind: Literal["char", "tool", "enter"]
value: str | ToolSpec = ""
@dataclass
class KeyboardGrid:
rows: list[list[KeyCell]] = field(default_factory=list)
@classmethod
def build(cls, num_tools: int) -> KeyboardGrid:
extra_f_rows = max(0, (num_tools - 1) // FKEYS_PER_ROW)
grid_rows: list[list[KeyCell]] = []
for row_idx in range(extra_f_rows, 0, -1):
start = row_idx * FKEYS_PER_ROW + 1
labels = [f"F{i}" for i in range(start, start + FKEYS_PER_ROW)]
grid_rows.append([KeyCell.from_label(l) for l in labels])
f_labels = [f"F{i}" for i in range(1, FKEYS_PER_ROW + 1)]
grid_rows.append([KeyCell.from_label(l) for l in f_labels])
for char_row in _BASE_CHAR_ROWS:
grid_rows.append([KeyCell.from_label(c) for c in char_row])
return cls(rows=grid_rows)
@property
def height(self) -> int:
return len(self.rows)
@property
def width(self) -> int:
return max((len(r) for r in self.rows), default=0)
def cell(self, row: int, col: int) -> KeyCell:
if row < 0 or row >= self.height:
return KeyCell("empty")
r = self.rows[row]
if col < 0 or col >= len(r):
return KeyCell("empty")
return r[col]
def letter_rows(self) -> range:
"""Row indices for the main typing area (after F-key rows)."""
f_row_count = 0
for row in self.rows:
if row and row[0].kind == "fkey":
f_row_count += 1
else:
break
return range(f_row_count, self.height)
def _manhattan(a: tuple[int, int], b: tuple[int, int]) -> int:
return abs(a[0] - b[0]) + abs(a[1] - b[1])
def _clamp_step(row: int, col: int, dr: int, dc: int, grid: KeyboardGrid) -> tuple[int, int]:
nr = max(0, min(grid.height - 1, row + dr))
nc = max(0, min(grid.width - 1, col + dc))
return nr, nc
def _nearest_non_empty(grid: KeyboardGrid, row: int, col: int) -> tuple[int, int]:
if grid.cell(row, col).kind != "empty":
return row, col
for dist in range(1, 6):
for dr in range(-dist, dist + 1):
for dc in range(-dist, dist + 1):
if abs(dr) + abs(dc) != dist:
continue
r, c = row + dr, col + dc
if grid.cell(r, c).kind != "empty":
return r, c
return row, col
def _step_toward(src: tuple[int, int], dst: tuple[int, int]) -> tuple[int, int]:
sr, sc = src
dr = dst[0] - sr
dc = dst[1] - sc
if dr == 0 and dc == 0:
return src
step_r = 0 if dr == 0 else (1 if dr > 0 else -1)
step_c = 0 if dc == 0 else (1 if dc > 0 else -1)
if abs(dr) >= abs(dc):
return sr + step_r, sc
return sr, sc + step_c
class CatWalkSimulator:
def __init__(
self,
rng: random.Random,
grid: KeyboardGrid,
tool_by_fkey: dict[int, ToolSpec],
allowed_fkeys: set[int] | None = None,
) -> None:
self.rng = rng
self.grid = grid
self.tool_by_fkey = tool_by_fkey
self.allowed_fkeys = allowed_fkeys
self.left_paw: tuple[int, int] = (0, 0)
self.right_paw: tuple[int, int] = (0, 0)
self.leading_paw: Literal["left", "right"] = "left"
self.heading: tuple[int, int] = (0, 1)
def type_until_enter(self, max_events: int = DEFAULT_MAX_KEY_EVENTS) -> str:
chars: list[str] = []
for ev in self.walk_until_enter(max_events):
if ev.kind == "char":
chars.append(str(ev.value))
return "".join(chars)
def walk_until_enter(self, max_events: int = DEFAULT_MAX_KEY_EVENTS) -> list[KeyPressEvent]:
self._init_paws()
events: list[KeyPressEvent] = []
count = 0
while count < max_events:
for ev in self._step():
if ev.kind == "enter":
return events
events.append(ev)
count += 1
if count >= max_events:
return events
return events
def simulate_message(
self,
max_events: int = DEFAULT_MAX_KEY_EVENTS,
) -> tuple[str | None, list[dict[str, Any]]]:
raw = self.walk_until_enter(max_events)
content_parts: list[str] = []
tool_calls: list[dict[str, Any]] = []
for ev in raw:
if ev.kind == "char":
content_parts.append(str(ev.value))
elif ev.kind == "tool":
spec = ev.value
assert isinstance(spec, ToolSpec)
args = generate_args_from_schema(spec.parameters, self.rng, self.grid, self.tool_by_fkey)
tool_calls.append(
{
"id": f"call_{secrets.token_hex(8)}",
"type": "function",
"function": {
"name": spec.name,
"arguments": json.dumps(args, ensure_ascii=False),
},
}
)
content = "".join(content_parts)
return (content if content else None), tool_calls
def _init_paws(self) -> None:
letter = list(self.grid.letter_rows())
if not letter:
letter = [0]
row = self.rng.choice(letter)
col = self.rng.randrange(self.grid.width)
left = _nearest_non_empty(self.grid, row, col)
offset_c = self.rng.choice([-2, -1, 1, 2])
offset_r = self.rng.choice([0, 0, -1, 1])
right = _nearest_non_empty(self.grid, row + offset_r, col + offset_c)
if _manhattan(left, right) > MAX_PAW_DISTANCE:
right = _nearest_non_empty(self.grid, row, col + self.rng.choice([-1, 1]))
self.left_paw = left
self.right_paw = right
self.leading_paw = self.rng.choice(["left", "right"])
self.heading = self.rng.choice(_DIRECTIONS)
def _pick_direction(self) -> tuple[int, int]:
if self.rng.random() < MOMENTUM_CONTINUE_PROB and self.heading != (0, 0):
return self.heading
# Bias toward letter rows (away from top F-rows).
f_rows = sum(1 for r in self.grid.rows if r and r[0].kind == "fkey")
candidates = list(_DIRECTIONS)
weights = []
for dr, dc in candidates:
w = 1.0
paw = self.left_paw if self.leading_paw == "left" else self.right_paw
nr, nc = _clamp_step(paw[0], paw[1], dr, dc, self.grid)
if nr >= f_rows + 1:
w += 1.5
if nr < f_rows:
w *= 0.5
weights.append(w)
return self.rng.choices(candidates, weights=weights, k=1)[0]
def _resolve_press(self, row: int, col: int) -> KeyPressEvent | None:
cell = self.grid.cell(row, col)
if cell.kind == "empty":
return None
if cell.kind == "enter":
return KeyPressEvent("enter")
if cell.kind == "char":
return KeyPressEvent("char", cell.value)
if cell.kind == "fkey":
fnum = int(cell.value)
if self.allowed_fkeys is not None and fnum not in self.allowed_fkeys:
return None
spec = self.tool_by_fkey.get(fnum)
if spec is None:
return None
return KeyPressEvent("tool", spec)
return None
def _step(self) -> list[KeyPressEvent]:
if self.rng.random() >= SAME_PAW_TWICE_PROB:
self.leading_paw = "right" if self.leading_paw == "left" else "left"
direction = self._pick_direction()
self.heading = direction
dr, dc = direction
if self.leading_paw == "left":
step_paw, trail_paw = "left", "right"
step_pos = self.left_paw
else:
step_paw, trail_paw = "right", "left"
step_pos = self.right_paw
sr, sc = step_pos
new_step = _clamp_step(sr, sc, dr, dc, self.grid)
if step_paw == "left":
self.left_paw = new_step
else:
self.right_paw = new_step
trail_pos = self.left_paw if trail_paw == "left" else self.right_paw
step_pos = self.left_paw if step_paw == "left" else self.right_paw
if _manhattan(step_pos, trail_pos) > MAX_PAW_DISTANCE:
shuffled = _step_toward(trail_pos, step_pos)
shuffled = (
max(0, min(self.grid.height - 1, shuffled[0])),
max(0, min(self.grid.width - 1, shuffled[1])),
)
if trail_paw == "left":
self.left_paw = shuffled
else:
self.right_paw = shuffled
events: list[KeyPressEvent] = []
press = self._resolve_press(step_pos[0], step_pos[1])
if press is not None:
if press.kind == "enter":
return [press]
events.append(press)
trail_pos = self.left_paw if trail_paw == "left" else self.right_paw
if self.rng.random() < TRAILING_PRESS_PROB:
trail_press = self._resolve_press(trail_pos[0], trail_pos[1])
if trail_press is not None:
if trail_press.kind == "enter":
return events + [trail_press]
events.append(trail_press)
return events
def generate_args_from_schema(
schema: dict[str, Any] | None,
rng: random.Random,
grid: KeyboardGrid,
tool_by_fkey: dict[int, ToolSpec],
) -> dict[str, Any]:
if not isinstance(schema, dict):
return {}
if schema.get("type") == "object" or "properties" in schema:
return _object_from_schema(schema, rng, grid, tool_by_fkey)
return {}
def _object_from_schema(
schema: dict[str, Any],
rng: random.Random,
grid: KeyboardGrid,
tool_by_fkey: dict[int, ToolSpec],
) -> dict[str, Any]:
if isinstance(schema.get("example"), dict):
return dict(schema["example"])
props = schema.get("properties")
if not isinstance(props, dict):
return {}
required_raw = schema.get("required")
required = set(required_raw) if isinstance(required_raw, list) else set()
out: dict[str, Any] = {}
for key, spec in props.items():
if not isinstance(spec, dict):
continue
if "default" in spec:
out[key] = spec["default"]
continue
if key not in required:
continue
out[key] = _value_from_property(spec, rng, grid, tool_by_fkey)
return out
def _value_from_property(
spec: dict[str, Any],
rng: random.Random,
grid: KeyboardGrid,
tool_by_fkey: dict[int, ToolSpec],
) -> Any:
if "example" in spec:
return spec["example"]
if "oneOf" in spec and isinstance(spec["oneOf"], list) and spec["oneOf"]:
branch = rng.choice(spec["oneOf"])
if isinstance(branch, dict):
return _value_from_property(branch, rng, grid, tool_by_fkey)
if "anyOf" in spec and isinstance(spec["anyOf"], list) and spec["anyOf"]:
branch = rng.choice(spec["anyOf"])
if isinstance(branch, dict):
return _value_from_property(branch, rng, grid, tool_by_fkey)
if "enum" in spec and isinstance(spec["enum"], list) and spec["enum"]:
return rng.choice(spec["enum"])
typ = spec.get("type")
if typ == "boolean":
return rng.choice([True, False])
if typ == "integer":
if "minimum" in spec and "maximum" in spec:
lo, hi = int(spec["minimum"]), int(spec["maximum"])
if lo > hi:
lo, hi = hi, lo
return rng.randint(lo, hi)
return rng.randint(0, 100)
if typ == "number":
if "minimum" in spec and "maximum" in spec:
lo, hi = float(spec["minimum"]), float(spec["maximum"])
if lo > hi:
lo, hi = hi, lo
return rng.uniform(lo, hi)
return float(rng.randint(0, 100))
if typ == "string":
sim = CatWalkSimulator(rng, grid, tool_by_fkey)
return sim.type_until_enter(max_events=128)
if typ == "object":
return _object_from_schema(spec, rng, grid, tool_by_fkey)
if typ == "array":
items = spec.get("items")
if not isinstance(items, dict):
return []
length = rng.randint(0, 2)
return [_value_from_property(items, rng, grid, tool_by_fkey) for _ in range(length)]
return ""
def parse_tools(tools_raw: Any) -> list[ToolSpec]:
if not isinstance(tools_raw, list):
return []
out: list[ToolSpec] = []
for i, t in enumerate(tools_raw):
if not isinstance(t, dict) or t.get("type") != "function":
continue
fn = t.get("function")
if not isinstance(fn, dict):
continue
name = str(fn.get("name") or "").strip()
if not name:
continue
params = fn.get("parameters")
if not isinstance(params, dict):
params = {}
out.append(ToolSpec(name=name, parameters=params, fkey=i + 1))
return out
def resolve_tool_choice(
tool_choice: Any,
tools: list[ToolSpec],
) -> tuple[dict[int, ToolSpec], set[int] | None]:
"""Return (fkey->ToolSpec map, allowed fkeys or None for all mapped)."""
if not tools:
return {}, set()
by_fkey = {t.fkey: t for t in tools}
by_name = {t.name: t for t in tools}
if tool_choice is None or tool_choice == "auto":
return by_fkey, None
if tool_choice == "none":
return by_fkey, set()
if tool_choice == "required":
return by_fkey, None
if isinstance(tool_choice, dict):
fn = tool_choice.get("function")
if isinstance(fn, dict):
name = str(fn.get("name") or "").strip()
if name in by_name:
t = by_name[name]
return by_fkey, {t.fkey}
if tool_choice.get("type") == "function" and isinstance(fn, dict):
name = str(fn.get("name") or "").strip()
if name in by_name:
t = by_name[name]
return by_fkey, {t.fkey}
return by_fkey, None
def run_simulation(
rng: random.Random,
tools: list[ToolSpec],
tool_choice: Any,
max_events: int,
) -> tuple[str | None, list[dict[str, Any]]]:
tool_by_fkey, allowed = resolve_tool_choice(tool_choice, tools)
grid = KeyboardGrid.build(len(tools))
sim = CatWalkSimulator(rng, grid, tool_by_fkey, allowed_fkeys=allowed)
return sim.simulate_message(max_events=max_events)
def _completion_id() -> str:
return f"chatcmpl-{secrets.token_hex(12)}"
def build_completion_body(
model: str,
content: str | None,
tool_calls: list[dict[str, Any]],
) -> dict[str, Any]:
completion_tokens = len(content or "") + len(tool_calls)
has_tools_only = bool(tool_calls) and not (content or "").strip()
finish_reason = "tool_calls" if has_tools_only else "stop"
message: dict[str, Any] = {"role": "assistant", "content": content}
if tool_calls:
message["tool_calls"] = tool_calls
return {
"id": _completion_id(),
"object": "chat.completion",
"created": int(time.time()),
"model": model,
"choices": [
{
"index": 0,
"message": message,
"finish_reason": finish_reason,
}
],
"usage": {
"prompt_tokens": 0,
"completion_tokens": completion_tokens,
"total_tokens": completion_tokens,
},
}
def iter_sse_chunks(
model: str,
content: str | None,
tool_calls: list[dict[str, Any]],
) -> Iterator[str]:
cid = _completion_id()
created = int(time.time())
base = {
"id": cid,
"object": "chat.completion.chunk",
"created": created,
"model": model,
}
def chunk(delta: dict[str, Any], finish: str | None = None) -> str:
choice: dict[str, Any] = {"index": 0, "delta": delta}
if finish is not None:
choice["finish_reason"] = finish
else:
choice["finish_reason"] = None
payload = {**base, "choices": [choice]}
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
yield chunk({"role": "assistant"})
for ch in content or "":
yield chunk({"content": ch})
if tool_calls:
yield chunk({"tool_calls": tool_calls})
has_tools_only = bool(tool_calls) and not (content or "").strip()
finish_reason = "tool_calls" if has_tools_only else "stop"
yield chunk({}, finish=finish_reason)
yield "data: [DONE]\n\n"
def _parse_max_tokens(body: dict[str, Any]) -> int:
mt = body.get("max_tokens")
if mt is None:
return DEFAULT_MAX_KEY_EVENTS
try:
n = int(mt)
return max(1, min(n, DEFAULT_MAX_KEY_EVENTS * 4))
except (TypeError, ValueError):
return DEFAULT_MAX_KEY_EVENTS
def _request_rng(handler: BaseHTTPRequestHandler, default_seed: int | None) -> random.Random:
hdr = handler.headers.get("X-Cat-Seed")
if hdr is not None:
try:
return random.Random(int(hdr))
except ValueError:
pass
if default_seed is not None:
return random.Random(default_seed ^ random.randrange(1 << 30))
return random.Random(random.randrange(1 << 30))
def _models_body() -> dict[str, Any]:
"""OpenAI + llama.cpp-compatible model list (GET /models or GET /v1/models)."""
return {
"object": "list",
"data": [
{
"id": DEFAULT_MODEL,
"object": "model",
"created": int(time.time()),
"owned_by": "llamacpp",
"aliases": [],
"tags": [],
}
],
}
def _send_json(handler: BaseHTTPRequestHandler, status: int, body: object) -> None:
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
handler.send_response(status)
handler.send_header("Content-Type", "application/json; charset=utf-8")
handler.send_header("Content-Length", str(len(data)))
handler.end_headers()
handler.wfile.write(data)
def _send_error_json(handler: BaseHTTPRequestHandler, status: int, message: str) -> None:
_send_json(
handler,
status,
{"error": {"message": message, "type": "invalid_request_error"}},
)
def _read_json_body(handler: BaseHTTPRequestHandler) -> tuple[dict[str, Any] | None, str | None]:
length = int(handler.headers.get("Content-Length", "0") or "0")
raw = handler.rfile.read(length) if length > 0 else b""
try:
body = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as e:
return None, f"invalid JSON body: {e}"
if not isinstance(body, dict):
return None, "request body must be a JSON object"
return body, None
def _char_token_id(ch: str) -> int:
return _TOKEN_ID_BASE + ord(ch)
def _tokenize_content(body: dict[str, Any]) -> tuple[dict[str, Any] | None, str | None]:
"""llama.cpp POST /tokenize — mock one-token-per-character encoding."""
if "content" not in body:
return None, "missing required field: content"
content = body["content"]
if content is None:
return None, "content must be a string"
if not isinstance(content, str):
return None, "content must be a string"
add_special = bool(body.get("add_special", False))
with_pieces = bool(body.get("with_pieces", False))
# parse_special is accepted for API compatibility; mock tokenizer has no special strings.
tokens: list[Any] = []
if add_special:
if with_pieces:
tokens.append({"id": BOS_TOKEN_ID, "piece": BOS_TOKEN_PIECE})
else:
tokens.append(BOS_TOKEN_ID)
for ch in content:
tid = _char_token_id(ch)
if with_pieces:
tokens.append({"id": tid, "piece": ch})
else:
tokens.append(tid)
return {"tokens": tokens}, None
def _message_text(content: Any) -> str:
if content is None:
return ""
if isinstance(content, str):
return content
if isinstance(content, list):
parts: list[str] = []
for part in content:
if isinstance(part, dict) and part.get("type") == "text":
parts.append(str(part.get("text") or ""))
elif isinstance(part, str):
parts.append(part)
return "".join(parts)
return str(content)
def _format_tool_calls(tool_calls: Any) -> str:
if not isinstance(tool_calls, list):
return ""
blocks: list[str] = []
for tc in tool_calls:
if not isinstance(tc, dict):
continue
fn = tc.get("function")
if not isinstance(fn, dict):
continue
name = str(fn.get("name") or "")
args_raw = fn.get("arguments")
if isinstance(args_raw, dict):
args_json = json.dumps(args_raw, ensure_ascii=False)
elif isinstance(args_raw, str) and args_raw.strip():
try:
args_json = json.dumps(json.loads(args_raw), ensure_ascii=False)
except json.JSONDecodeError:
args_json = json.dumps(args_raw, ensure_ascii=False)
else:
args_json = "{}"
blocks.append(f'<tool_call>\n{{"name": {json.dumps(name)}, "arguments": {args_json}}}\n</tool_call>')
return "\n".join(blocks)
def _apply_template(body: dict[str, Any]) -> tuple[dict[str, Any] | None, str | None]:
"""llama.cpp POST /apply-template — mock ChatML formatting."""
messages = body.get("messages")
if messages is None:
return None, "missing required field: messages"
if not isinstance(messages, list):
return None, "messages must be an array"
add_generation_prompt = bool(body.get("add_generation_prompt", True))
parts: list[str] = []
for msg in messages:
if not isinstance(msg, dict):
return None, "each message must be an object"
role = str(msg.get("role") or "user")
text = _message_text(msg.get("content"))
if role == "assistant":
block_parts: list[str] = []
reasoning = msg.get("reasoning_content")
if isinstance(reasoning, str) and reasoning.strip():
block_parts.append(f"\n{reasoning}\n")
if text:
block_parts.append(text)
tc_text = _format_tool_calls(msg.get("tool_calls"))
if tc_text:
block_parts.append(tc_text)
block = "\n".join(block_parts)
elif role == "tool":
call_id = str(msg.get("tool_call_id") or "")
block = f'<tool_response id={json.dumps(call_id)}>\n{text}\n</tool_response>'
else:
block = text
parts.append(f"{_IM_START}{role}\n{block}{_IM_END}\n")
prompt = "".join(parts)
if add_generation_prompt:
last_role = str(messages[-1].get("role") or "") if messages else ""
if not messages or last_role != "assistant":
prompt += f"{_IM_START}assistant\n"
return {"prompt": prompt}, None
def _handle_chat_completions(
handler: BaseHTTPRequestHandler,
body: dict[str, Any],
default_seed: int | None,
) -> None:
model = str(body.get("model") or DEFAULT_MODEL)
tools = parse_tools(body.get("tools"))
tool_choice = body.get("tool_choice", "auto")
max_events = _parse_max_tokens(body)
rng = _request_rng(handler, default_seed)
content, tool_calls = run_simulation(rng, tools, tool_choice, max_events)
if body.get("stream"):
handler.send_response(HTTPStatus.OK)
handler.send_header("Content-Type", "text/event-stream; charset=utf-8")
handler.send_header("Cache-Control", "no-cache")
handler.send_header("Connection", "keep-alive")
handler.end_headers()
try:
for part in iter_sse_chunks(model, content, tool_calls):
handler.wfile.write(part.encode("utf-8"))
handler.wfile.flush()
except BrokenPipeError:
pass
return
_send_json(handler, HTTPStatus.OK, build_completion_body(model, content, tool_calls))
def make_handler(default_seed: int | None) -> type[BaseHTTPRequestHandler]:
class CatLLMHandler(BaseHTTPRequestHandler):
server_version = "CatLLM/1.0"
def log_message(self, fmt: str, *args: object) -> None:
sys.stderr.write("%s - [%s] %s\n" % (self.address_string(), self.log_date_time_string(), fmt % args))
def do_GET(self) -> None:
path = urlparse(self.path).path.rstrip("/") or "/"
if path == "/":
_send_json(
self,
HTTPStatus.OK,
{"service": "cat-llm-server", "openai_compatible": True},
)
return
if path in ("/models", "/v1/models"):
_send_json(self, HTTPStatus.OK, _models_body())
return
_send_error_json(self, HTTPStatus.NOT_FOUND, f"unknown route: {path}")
def do_POST(self) -> None:
path = urlparse(self.path).path.rstrip("/")
body, err = _read_json_body(self)
if err is not None:
_send_error_json(self, HTTPStatus.BAD_REQUEST, err)
return
if path == "/tokenize":
result, err = _tokenize_content(body)
if err is not None:
_send_error_json(self, HTTPStatus.BAD_REQUEST, err)
return
_send_json(self, HTTPStatus.OK, result)
return
if path == "/apply-template":
result, err = _apply_template(body)
if err is not None:
_send_error_json(self, HTTPStatus.BAD_REQUEST, err)
return
_send_json(self, HTTPStatus.OK, result)
return
if path == "/v1/chat/completions":
_handle_chat_completions(self, body, default_seed)
return
_send_error_json(self, HTTPStatus.NOT_FOUND, f"unknown route: {path}")
return CatLLMHandler
def main() -> int:
p = argparse.ArgumentParser(
description="OpenAI-compatible LLM server simulating a cat walking on a keyboard.",
)
p.add_argument("--host", default=LISTEN_HOST, help=f"bind host (default: {LISTEN_HOST})")
p.add_argument("--port", type=int, default=LISTEN_PORT, help=f"bind port (default: {LISTEN_PORT})")
p.add_argument(
"--seed",
type=int,
default=None,
help="optional RNG seed base (per-request X-Cat-Seed header overrides)",
)
args = p.parse_args()
Handler = make_handler(args.seed)
httpd = ThreadingHTTPServer((args.host, args.port), Handler)
print(f"LISTENING=http://{args.host}:{args.port}", flush=True)
print(f"OPENAI_BASE=http://{args.host}:{args.port}/v1", flush=True)
if args.seed is not None:
print(f"CAT_SEED={args.seed}", flush=True)
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nShutting down.", file=sys.stderr)
return 0
finally:
httpd.server_close()
return 0
if __name__ == "__main__":
raise SystemExit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment