weightspace 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from .base import LLM, LLMResponse, ToolCall, normalize_thinking
4
+ from .mock import ScriptedLLM, call, reply
5
+
6
+
7
+ def get_llm(spec: str | LLM, **kw) -> LLM:
8
+ """`"openai:gpt-5"`, `"anthropic:claude-sonnet-4-5"`, or `"openrouter:<model>"`."""
9
+ if isinstance(spec, LLM):
10
+ return spec
11
+ provider, _, model = spec.partition(":")
12
+ if not model:
13
+ provider, model = "openai", provider
14
+ provider = provider.lower()
15
+ if provider == "anthropic":
16
+ from .anthropic import AnthropicChat
17
+
18
+ return AnthropicChat(model, **kw)
19
+ if provider == "openrouter":
20
+ import os
21
+
22
+ from .openai import OpenAIChat
23
+
24
+ kw.setdefault("base_url", "https://openrouter.ai/api/v1")
25
+ kw.setdefault("api_key", os.environ.get("OPENROUTER_API_KEY"))
26
+ kw.setdefault("thinking_style", "openrouter")
27
+ return OpenAIChat(model, **kw)
28
+ if provider == "openai":
29
+ from .openai import OpenAIChat
30
+
31
+ return OpenAIChat(model, **kw)
32
+ raise ValueError(f"unknown provider {provider!r}")
33
+
34
+
35
+ __all__ = ["normalize_thinking", "LLM", "LLMResponse", "ToolCall", "ScriptedLLM", "call", "reply", "get_llm"]
@@ -0,0 +1,125 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ from .base import LLM, LLMResponse, ToolCall, normalize_thinking
6
+
7
+ THINKING_BUDGET = {"low": 2048, "medium": 8192, "high": 16384, "xhigh": 24576, "max": 32000}
8
+
9
+
10
+ def to_anthropic(messages: list[dict]) -> tuple[str, list[dict]]:
11
+ system, out = [], []
12
+ for m in messages:
13
+ role = m["role"]
14
+ if role == "system":
15
+ system.append(m["content"])
16
+ elif role == "user":
17
+ out.append({"role": "user", "content": [{"type": "text", "text": m["content"]}]})
18
+ elif role == "assistant":
19
+ if m.get("anthropic_content"):
20
+ blocks = m["anthropic_content"]
21
+ else:
22
+ blocks = [{"type": "text", "text": m["content"]}] if m.get("content") else []
23
+ blocks += [
24
+ {"type": "tool_use", "id": t["id"], "name": t["name"], "input": t["arguments"]}
25
+ for t in m.get("tool_calls") or []
26
+ ]
27
+ out.append({"role": "assistant", "content": blocks})
28
+ elif role == "tool":
29
+ block = {"type": "tool_result", "tool_use_id": m["tool_call_id"], "content": m["content"]}
30
+ if out and out[-1]["role"] == "user" and all(b["type"] == "tool_result" for b in out[-1]["content"]):
31
+ out[-1]["content"].append(block)
32
+ else:
33
+ out.append({"role": "user", "content": [block]})
34
+ merged: list[dict] = []
35
+ for m in out:
36
+ if merged and merged[-1]["role"] == m["role"] == "user":
37
+ merged[-1]["content"].extend(m["content"])
38
+ else:
39
+ merged.append(m)
40
+ return "\n\n".join(system), merged
41
+
42
+
43
+ class AnthropicChat(LLM):
44
+ """Anthropic Messages API.
45
+
46
+ thinking_style: "adaptive" (thinking.type=adaptive + output_config.effort, newer models), "budget"
47
+ (thinking.type=enabled + budget_tokens, older models) or "auto" (adaptive, falling back to budget if the
48
+ model rejects it).
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ model: str,
54
+ *,
55
+ api_key: str | None = None,
56
+ max_tokens: int = 16000,
57
+ client=None,
58
+ thinking=None,
59
+ thinking_style: str = "auto",
60
+ cache: bool = True,
61
+ **params,
62
+ ):
63
+ if client is None:
64
+ from anthropic import Anthropic
65
+
66
+ client = Anthropic(api_key=api_key or os.environ.get("ANTHROPIC_API_KEY"))
67
+ self.client = client
68
+ self.model = model
69
+ self.name = f"anthropic:{model}"
70
+ self.max_tokens = max_tokens
71
+ self.params = params
72
+ self.thinking = normalize_thinking(thinking)
73
+ if thinking_style not in ("auto", "adaptive", "budget"):
74
+ raise ValueError("thinking_style must be 'auto', 'adaptive' or 'budget'")
75
+ self.thinking_style = thinking_style
76
+ self.cache = cache
77
+
78
+ def _thinking_kwargs(self, style: str) -> tuple[dict, int]:
79
+ t = normalize_thinking(self.thinking)
80
+ if t is None:
81
+ return {}, self.max_tokens
82
+ if t == "off":
83
+ return {"thinking": {"type": "disabled"}}, self.max_tokens
84
+ if style == "budget":
85
+ budget = THINKING_BUDGET[t]
86
+ return {"thinking": {"type": "enabled", "budget_tokens": budget}}, max(self.max_tokens, budget + 4096)
87
+ return {"thinking": {"type": "adaptive"}, "output_config": {"effort": t}}, self.max_tokens
88
+
89
+ def complete(self, messages, tools=None):
90
+ system, msgs = to_anthropic(messages)
91
+ if self.cache and msgs and msgs[-1]["content"]:
92
+ # prompt caching: everything up to the newest block is reused on the next step
93
+ msgs[-1]["content"][-1] = {**msgs[-1]["content"][-1], "cache_control": {"type": "ephemeral"}}
94
+ kw = dict(self.params)
95
+ if system:
96
+ kw["system"] = system
97
+ if tools:
98
+ kw["tools"] = [{"name": t["name"], "description": t["description"], "input_schema": t["parameters"]} for t in tools]
99
+ style = "adaptive" if self.thinking_style == "auto" else self.thinking_style
100
+ tk, max_tokens = self._thinking_kwargs(style)
101
+ try:
102
+ r = self.client.messages.create(model=self.model, max_tokens=max_tokens, messages=msgs, **kw, **tk)
103
+ except Exception as e:
104
+ if self.thinking_style != "auto" or "thinking" not in tk or "adaptive" not in str(e):
105
+ raise
106
+ self.thinking_style = "budget"
107
+ tk, max_tokens = self._thinking_kwargs("budget")
108
+ r = self.client.messages.create(model=self.model, max_tokens=max_tokens, messages=msgs, **kw, **tk)
109
+ if self.thinking_style == "auto":
110
+ self.thinking_style = style
111
+ text, calls, blocks = [], [], []
112
+ for b in r.content:
113
+ blocks.append(b.model_dump(exclude_none=True) if hasattr(b, "model_dump") else dict(b))
114
+ if b.type == "text":
115
+ text.append(b.text)
116
+ elif b.type == "tool_use":
117
+ calls.append(ToolCall(b.id, b.name, dict(b.input)))
118
+ usage = r.usage.model_dump() if getattr(r, "usage", None) is not None else {}
119
+ return LLMResponse(
120
+ text="\n".join(text) or None,
121
+ tool_calls=calls,
122
+ usage=usage,
123
+ raw=r,
124
+ extra={"anthropic_content": blocks},
125
+ )
@@ -0,0 +1,71 @@
1
+ """Provider-neutral chat interface.
2
+
3
+ Messages use an OpenAI-like neutral shape:
4
+ {"role": "system"|"user", "content": str}
5
+ {"role": "assistant", "content": str|None, "tool_calls": [{"id", "name", "arguments"}]}
6
+ {"role": "tool", "tool_call_id": str, "name": str, "content": str}
7
+ Tools are {"name", "description", "parameters": <JSON schema>}.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass, field
13
+ from typing import Any
14
+
15
+
16
+ @dataclass
17
+ class ToolCall:
18
+ id: str
19
+ name: str
20
+ arguments: dict
21
+
22
+ def to_dict(self) -> dict:
23
+ return {"id": self.id, "name": self.name, "arguments": self.arguments}
24
+
25
+
26
+ @dataclass
27
+ class LLMResponse:
28
+ text: str | None = None
29
+ tool_calls: list[ToolCall] = field(default_factory=list)
30
+ usage: dict = field(default_factory=dict)
31
+ raw: Any = None
32
+ extra: dict = field(default_factory=dict) # provider state to replay (e.g. thinking blocks)
33
+
34
+ def to_message(self) -> dict:
35
+ msg = {"role": "assistant", "content": self.text, "tool_calls": [t.to_dict() for t in self.tool_calls]}
36
+ msg.update(self.extra)
37
+ return msg
38
+
39
+
40
+ THINKING_LEVELS = ("off", "low", "medium", "high", "xhigh", "max")
41
+
42
+
43
+ def normalize_thinking(value) -> str | None:
44
+ """None = provider default; False/"off" = no reasoning; True = "medium"; or "low"/"medium"/"high"."""
45
+ if value is None:
46
+ return None
47
+ if value is False:
48
+ return "off"
49
+ if value is True:
50
+ return "medium"
51
+ v = str(value).lower()
52
+ if v in ("none", "disabled", "false", "no"):
53
+ return "off"
54
+ if v in ("on", "true", "enabled"):
55
+ return "medium"
56
+ if v not in THINKING_LEVELS:
57
+ raise ValueError(f"thinking must be None, a bool, or one of {THINKING_LEVELS}")
58
+ return v
59
+
60
+
61
+ class LLM:
62
+ """Implement `complete` to plug in any provider.
63
+
64
+ `thinking` is set by `Session(thinking=...)`; backends map it to their own reasoning switch.
65
+ """
66
+
67
+ name = "llm"
68
+ thinking: str | None = None
69
+
70
+ def complete(self, messages: list[dict], tools: list[dict] | None = None) -> LLMResponse:
71
+ raise NotImplementedError
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import itertools
4
+ from collections.abc import Callable
5
+
6
+ from .base import LLM, LLMResponse, ToolCall
7
+
8
+ _ids = itertools.count()
9
+
10
+
11
+ def call(name: str, /, **arguments) -> ToolCall:
12
+ return ToolCall(f"call_{next(_ids)}", name, arguments)
13
+
14
+
15
+ def reply(text: str | None = None, *calls: ToolCall) -> LLMResponse:
16
+ return LLMResponse(text=text, tool_calls=list(calls))
17
+
18
+
19
+ class ScriptedLLM(LLM):
20
+ """Replays a fixed script; each step is an LLMResponse or fn(messages, tools) -> LLMResponse.
21
+
22
+ Useful for tests and for replaying logged runs without paying for tokens.
23
+ """
24
+
25
+ name = "scripted"
26
+
27
+ def __init__(self, steps: list[LLMResponse | Callable[[list[dict], list[dict]], LLMResponse]]):
28
+ self.steps = list(steps)
29
+ self.seen: list[list[dict]] = []
30
+ self.thinking_seen: list[str | None] = []
31
+
32
+ def complete(self, messages, tools=None):
33
+ self.seen.append([dict(m) for m in messages])
34
+ self.thinking_seen.append(self.thinking)
35
+ if not self.steps:
36
+ return reply("(script exhausted)")
37
+ step = self.steps.pop(0)
38
+ return step(messages, tools or []) if callable(step) else step
@@ -0,0 +1,108 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+
6
+ from .base import LLM, LLMResponse, ToolCall, normalize_thinking
7
+
8
+
9
+ def to_openai_messages(messages: list[dict]) -> list[dict]:
10
+ out = []
11
+ for m in messages:
12
+ if m["role"] == "assistant":
13
+ msg = {"role": "assistant", "content": m.get("content")}
14
+ if m.get("tool_calls"):
15
+ msg["tool_calls"] = [
16
+ {
17
+ "id": t["id"],
18
+ "type": "function",
19
+ "function": {"name": t["name"], "arguments": json.dumps(t["arguments"])},
20
+ }
21
+ for t in m["tool_calls"]
22
+ ]
23
+ out.append(msg)
24
+ elif m["role"] == "tool":
25
+ out.append({"role": "tool", "tool_call_id": m["tool_call_id"], "content": m["content"]})
26
+ else:
27
+ out.append({"role": m["role"], "content": m["content"]})
28
+ return out
29
+
30
+
31
+ def to_openai_tools(tools: list[dict]) -> list[dict]:
32
+ return [
33
+ {"type": "function", "function": {"name": t["name"], "description": t["description"], "parameters": t["parameters"]}}
34
+ for t in tools
35
+ ]
36
+
37
+
38
+ class OpenAIChat(LLM):
39
+ """Any OpenAI-compatible Chat Completions endpoint (OpenAI, OpenRouter, vLLM, Ollama...).
40
+
41
+ thinking_style says how `thinking` is sent:
42
+ "reasoning_effort" OpenAI reasoning models (off -> `off_effort`, e.g. "none" or "minimal")
43
+ "openrouter" OpenRouter's unified `reasoning` field
44
+ "chat_template" vLLM / SGLang hybrid models (Qwen3...): `chat_template_kwargs.enable_thinking`
45
+ None never send anything
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ model: str,
51
+ *,
52
+ api_key: str | None = None,
53
+ base_url: str | None = None,
54
+ client=None,
55
+ thinking=None,
56
+ thinking_style: str | None = "reasoning_effort",
57
+ off_effort: str = "none",
58
+ **params,
59
+ ):
60
+ if client is None:
61
+ from openai import OpenAI
62
+
63
+ client = OpenAI(
64
+ api_key=api_key or os.environ.get("OPENAI_API_KEY"),
65
+ base_url=base_url or os.environ.get("OPENAI_BASE_URL"),
66
+ )
67
+ self.client = client
68
+ self.model = model
69
+ self.name = f"openai:{model}"
70
+ self.params = params
71
+ self.thinking = normalize_thinking(thinking)
72
+ self.thinking_style = thinking_style
73
+ self.off_effort = off_effort
74
+
75
+ def _thinking_params(self, kw: dict) -> None:
76
+ t, style = normalize_thinking(self.thinking), self.thinking_style
77
+ if t is None or style is None:
78
+ return
79
+ extra = dict(kw.get("extra_body") or {})
80
+ if style == "reasoning_effort":
81
+ kw["reasoning_effort"] = self.off_effort if t == "off" else t
82
+ elif style == "openrouter":
83
+ extra["reasoning"] = {"enabled": False} if t == "off" else {"effort": t}
84
+ elif style == "chat_template":
85
+ ctk = dict(extra.get("chat_template_kwargs") or {})
86
+ ctk["enable_thinking"] = t != "off"
87
+ extra["chat_template_kwargs"] = ctk
88
+ else:
89
+ raise ValueError(f"unknown thinking_style {style!r}")
90
+ if extra:
91
+ kw["extra_body"] = extra
92
+
93
+ def complete(self, messages, tools=None):
94
+ kw = dict(self.params)
95
+ self._thinking_params(kw)
96
+ if tools:
97
+ kw["tools"] = to_openai_tools(tools)
98
+ r = self.client.chat.completions.create(model=self.model, messages=to_openai_messages(messages), **kw)
99
+ msg = r.choices[0].message
100
+ calls = []
101
+ for t in msg.tool_calls or []:
102
+ try:
103
+ args = json.loads(t.function.arguments or "{}")
104
+ except json.JSONDecodeError:
105
+ args = {"_raw": t.function.arguments}
106
+ calls.append(ToolCall(t.id, t.function.name, args))
107
+ usage = r.usage.model_dump() if getattr(r, "usage", None) is not None else {}
108
+ return LLMResponse(text=msg.content, tool_calls=calls, usage=usage, raw=r)
weightspace/probes.py ADDED
@@ -0,0 +1,60 @@
1
+ """Forward passes the LLM may spend, defined by the user."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Callable
7
+ from dataclasses import dataclass, field
8
+ from typing import Any
9
+
10
+
11
+ @dataclass
12
+ class Probe:
13
+ """A user-defined way to run the network, exposed to the LLM as a tool.
14
+
15
+ `fn` is called with the LLM's arguments after the current weights are
16
+ synced into the model. It decides what the LLM gets to see (scores,
17
+ rollouts, attention maps, generated text...).
18
+ """
19
+
20
+ fn: Callable[..., Any]
21
+ name: str = ""
22
+ description: str = ""
23
+ parameters: dict = field(default_factory=dict)
24
+ cost: int = 1
25
+
26
+ def __post_init__(self):
27
+ self.name = self.name or self.fn.__name__
28
+ self.description = self.description or inspect.getdoc(self.fn) or f"Run {self.name}."
29
+ if not self.parameters:
30
+ self.parameters = _schema_from_signature(self.fn)
31
+
32
+ def __call__(self, **kw):
33
+ return self.fn(**kw)
34
+
35
+
36
+ def probe(fn=None, *, name: str = "", description: str = "", parameters: dict | None = None, cost: int = 1):
37
+ """Decorator: `@probe` or `@probe(cost=2, parameters={...})`."""
38
+
39
+ def wrap(f):
40
+ return Probe(f, name=name, description=description, parameters=parameters or {}, cost=cost)
41
+
42
+ return wrap(fn) if fn is not None else wrap
43
+
44
+
45
+ _JSON_TYPES = {int: "integer", float: "number", str: "string", bool: "boolean", list: "array", dict: "object"}
46
+
47
+
48
+ def _schema_from_signature(fn) -> dict:
49
+ props, required = {}, []
50
+ for p in inspect.signature(fn).parameters.values():
51
+ if p.kind in (p.VAR_POSITIONAL, p.VAR_KEYWORD):
52
+ continue
53
+ ann = p.annotation
54
+ origin = getattr(ann, "__origin__", ann)
55
+ props[p.name] = {"type": _JSON_TYPES.get(origin, "string")} if ann is not p.empty else {}
56
+ if p.default is p.empty:
57
+ required.append(p.name)
58
+ else:
59
+ props[p.name]["default"] = p.default if isinstance(p.default, (int, float, str, bool, type(None))) else str(p.default)
60
+ return {"type": "object", "properties": props, "required": required}
weightspace/prompts.py ADDED
@@ -0,0 +1,43 @@
1
+ SYSTEM = """You are a weight-space programmer. You read and write the weights of a neural network directly,
2
+ by reasoning about what each part of the network computes or should compute.
3
+
4
+ Rules:
5
+ - No gradient descent, no training loops, no fitting weights to data (no least squares on activations,
6
+ no optimizers, no search over random perturbations). Derive every edit from your understanding of the
7
+ network.
8
+ - Do not run the network or any of its layers on inputs inside the Python sandbox, even on made-up inputs.
9
+ Static analysis of the weights (norms, SVD, products of weight matrices, comparisons between tensors)
10
+ is fine. The only way to see the network's behaviour is the forward_* tools, which spend a limited budget.
11
+ - Prefer small, targeted edits that fix the cause and leave the rest of the network intact.
12
+ - When you are done, call `submit` exactly once with your answer and a confidence between 0 and 1.
13
+ """
14
+
15
+ MODES = {
16
+ "read": "Task type: READ. Work out the answer from the weights. You cannot change any weights.",
17
+ "fix": (
18
+ "Task type: FIX. Find the cause of the described failure in the weights, explain it, and fix only that "
19
+ "cause by editing weights. Use checkpoints so you can roll back an edit that does not help."
20
+ ),
21
+ "write": (
22
+ "Task type: WRITE. Program the network's weights so that it implements the specification. Decide what "
23
+ "algorithm each layer should compute, then convert it into weights."
24
+ ),
25
+ "improve": (
26
+ "Task type: IMPROVE. Make the network better at the stated goal by editing weights. Diagnose what limits "
27
+ "it first, then make targeted edits, and keep an edit only if the evidence says it helps."
28
+ ),
29
+ }
30
+
31
+ NO_THINKING = """
32
+ Thinking is off for this run: do not write out step-by-step reasoning. Go straight to tool calls and weight
33
+ edits, with at most one short sentence of explanation each.
34
+ """
35
+
36
+ TEXT_MODE_EDIT = """Reply with your reasoning and then the edit, in ONE of these formats:
37
+ 1. A ```python block. It runs once with `W` (writable dict of numpy arrays, edit in place or assign),
38
+ `R` (read-only originals) and `np` in scope.
39
+ 2. One or more ```weights blocks containing `### <tensor name> [<shape>]` headers each followed by all of
40
+ that tensor's numbers, in the same layout as the dump you were given.
41
+ End with a line `ANSWER: <short summary>` and a line `CONFIDENCE: <0..1>`."""
42
+
43
+ TEXT_MODE_READ = "Reply with your reasoning, then a line `ANSWER: <answer>` and a line `CONFIDENCE: <0..1>`."
weightspace/render.py ADDED
@@ -0,0 +1,151 @@
1
+ """Turning tensors into text an LLM can read, and back."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ import numpy as np
8
+
9
+
10
+ def fmt_num(x: float, precision: int = 3) -> str:
11
+ s = f"{x:.{precision}g}"
12
+ return "0" if s in ("0", "-0") else s
13
+
14
+
15
+ def fmt_array(arr: np.ndarray, precision: int = 3) -> str:
16
+ """One line per row (last axis); leading axes are labelled."""
17
+ arr = np.asarray(arr)
18
+ if arr.ndim == 0:
19
+ return fmt_num(float(arr), precision)
20
+ if arr.ndim == 1:
21
+ return " ".join(fmt_num(float(v), precision) for v in arr)
22
+ flat = arr.reshape(-1, arr.shape[-1])
23
+ lead = list(np.ndindex(*arr.shape[:-1]))
24
+ rows = []
25
+ for idx, row in zip(lead, flat, strict=True):
26
+ label = ",".join(str(i) for i in idx)
27
+ rows.append(f"[{label}] " + " ".join(fmt_num(float(v), precision) for v in row))
28
+ return "\n".join(rows)
29
+
30
+
31
+ _SLICE_PART = re.compile(r"^\s*(-?\d+)?\s*(?::\s*(-?\d+)?\s*(?::\s*(-?\d+)?\s*)?)?$")
32
+
33
+
34
+ def parse_index(index: str | None):
35
+ """Parse numpy-style index text like "0:8, :, 3" without eval."""
36
+ if index is None or not str(index).strip():
37
+ return ()
38
+ text = str(index).strip()
39
+ if text.startswith("[") and text.endswith("]"):
40
+ text = text[1:-1]
41
+ out = []
42
+ for part in text.split(","):
43
+ part = part.strip()
44
+ if part in ("", ":", "..."):
45
+ out.append(Ellipsis if part == "..." else slice(None))
46
+ continue
47
+ m = _SLICE_PART.match(part)
48
+ if not m:
49
+ raise ValueError(f"bad index component {part!r}")
50
+ if ":" not in part:
51
+ out.append(int(m.group(1)))
52
+ else:
53
+ a, b, c = (int(g) if g is not None else None for g in m.groups())
54
+ out.append(slice(a, b, c))
55
+ return tuple(out)
56
+
57
+
58
+ def stats(arr: np.ndarray) -> dict:
59
+ a = np.asarray(arr, dtype=np.float64).reshape(-1)
60
+ if a.size == 0:
61
+ return {"numel": 0}
62
+ q = np.quantile(a, [0.01, 0.25, 0.5, 0.75, 0.99])
63
+ return {
64
+ "numel": int(a.size),
65
+ "mean": float(a.mean()),
66
+ "std": float(a.std()),
67
+ "min": float(a.min()),
68
+ "max": float(a.max()),
69
+ "abs_mean": float(np.abs(a).mean()),
70
+ "frac_zero": float(np.mean(a == 0)),
71
+ "q01_q25_q50_q75_q99": [float(v) for v in q],
72
+ }
73
+
74
+
75
+ def describe(name: str, arr: np.ndarray, dtype: str = "", top_k: int = 8) -> str:
76
+ """Structural summary: stats, row/col norms, spectrum for matrices."""
77
+ arr = np.asarray(arr)
78
+ s = stats(arr)
79
+ lines = [f"{name}: shape={tuple(arr.shape)} dtype={dtype or arr.dtype} numel={s['numel']:,}"]
80
+ if s["numel"] == 0:
81
+ return lines[0]
82
+ lines.append(
83
+ "mean={} std={} min={} max={} |x|mean={} zeros={:.3g}%".format(
84
+ *(fmt_num(s[k], 4) for k in ("mean", "std", "min", "max", "abs_mean")), 100 * s["frac_zero"]
85
+ )
86
+ )
87
+ lines.append("quantiles 1/25/50/75/99%: " + " ".join(fmt_num(v, 4) for v in s["q01_q25_q50_q75_q99"]))
88
+ if arr.ndim >= 2:
89
+ m = np.asarray(arr, dtype=np.float64).reshape(arr.shape[0], -1)
90
+ for axis, label in ((1, "row"), (0, "col")):
91
+ norms = np.linalg.norm(m, axis=axis)
92
+ order = np.argsort(norms)[::-1]
93
+ top = ", ".join(f"{i}:{fmt_num(norms[i], 3)}" for i in order[:top_k])
94
+ low = ", ".join(f"{i}:{fmt_num(norms[i], 3)}" for i in order[::-1][:top_k])
95
+ lines.append(f"{label} L2 norms ({norms.size}): largest {top} | smallest {low}")
96
+ if min(m.shape) <= 4096 and m.size <= 16_000_000:
97
+ sv = np.linalg.svd(m, compute_uv=False)
98
+ energy = np.cumsum(sv**2) / max(np.sum(sv**2), 1e-30)
99
+ eff = int(np.searchsorted(energy, 0.9) + 1)
100
+ lines.append(
101
+ f"singular values (top {min(top_k, sv.size)}): "
102
+ + " ".join(fmt_num(v, 3) for v in sv[:top_k])
103
+ + f" | rank for 90% energy: {eff}/{sv.size}"
104
+ )
105
+ return "\n".join(lines)
106
+
107
+
108
+ def render_space(ws, precision: int = 3) -> str:
109
+ """Full text dump in the same format `parse_weights` reads back."""
110
+ parts = []
111
+ for n in ws.names():
112
+ arr = ws[n]
113
+ parts.append(f"### {n} {list(arr.shape)}\n{fmt_array(arr, precision)}")
114
+ return "\n\n".join(parts)
115
+
116
+
117
+ _HEADER = re.compile(r"^###\s+(\S+)(?:\s+\[([\d,\s]*)\])?\s*$")
118
+ _NUM = re.compile(r"[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?")
119
+
120
+
121
+ def parse_weights(text: str) -> dict[str, np.ndarray]:
122
+ """Parse `### name [shape]` blocks of numbers (row labels like [0,1] are ignored)."""
123
+ out: dict[str, np.ndarray] = {}
124
+ name, shape, nums = None, None, []
125
+
126
+ def done():
127
+ if name is None:
128
+ return
129
+ arr = np.array(nums, dtype=np.float32)
130
+ if shape is not None:
131
+ arr = arr.reshape(shape)
132
+ out[name] = arr
133
+
134
+ for line in text.splitlines():
135
+ m = _HEADER.match(line.strip())
136
+ if m:
137
+ done()
138
+ name = m.group(1)
139
+ shape = tuple(int(x) for x in m.group(2).split(",") if x.strip()) if m.group(2) is not None else None
140
+ nums = []
141
+ continue
142
+ if name is None:
143
+ continue
144
+ body = re.sub(r"^\s*\[[\d,\s]*\]", "", line)
145
+ nums.extend(float(x) for x in _NUM.findall(body))
146
+ done()
147
+ return out
148
+
149
+
150
+ def extract_blocks(text: str, lang: str) -> list[str]:
151
+ return [m.group(1) for m in re.finditer(rf"```{lang}[^\n]*\n(.*?)```", text, re.S)]