Repository navigation
v0.1.4
TiinyBench 0.1.4
Problem. TiinyBench could tell you what a model costs and never let you talk to one. Its sibling on the same hardware, AINode Pocket, has a chat tab that reports every reply's cost under the reply and the device beside the conversation, and asked for it here. The two apps also derived the same numbers from the same two gateway blocks in two places, which is the arrangement that drifts the first time the gateway renames a field and then has a chat bar and a saved benchmark disagreeing about the same request.
Fix. A Chat page, Pocket's three columns adapted to one Tiiny. Under every reply: time to first token, decode tok/s, total wall time, tokens in with the cache hits, tokens out, which device and model answered, and why generation stopped, all read off the gateway's own timings and usage on the stream's final chunk plus this machine's clock; the numbers are saved with the message. Left: a model card for whatever is selected (type, params, size, NPU units, input and output, where it is loaded, the vendor's description, a catalogue link) and the last twenty conversations, kept in the browser. Right: every model loaded on the box with its units and status and an Unload button, the NPU budget bar, and a Load panel that refuses in a sentence when a model is not installed, cannot chat, or would not fit, because the device accepts a load that does not fit and rolls it back without saying so. Replies render as markdown with highlighted code blocks and a Copy button; a reasoning model's chain of thought sits in a collapsible block behind the Thinking toggle; temperature, max tokens and a system prompt are controls. New routes in serve.py: POST /api/chat (streamed, ending with an event: stats before [DONE]), GET /api/model_card, GET /api/instances, POST /api/instances/load and /unload. All of it goes through this release's own transport negotiation, so the chat reaches the gateway on whichever port and vhost the firmware put it behind rather than assuming one. derive_stats and chat_stats are copied verbatim into bench.py with a comment naming their origin, and bench.chat now reads that derivation instead of its own second copy of the same arithmetic, so the page, a saved run and Pocket all compute one set of numbers. The Models page's own load goes through the same guard, minus the chat requirement, so both doors onto the device's start give the same refusals and neither can drift into letting a silent rollback through.
Tests. tests/test_chat.py and tests/test_manage.py, sixty tests on stdlib unittest against a fake device in tests/fake_device.py, offline, beside the discovery tests this release already had: a hundred in total, all passing on 3.9 and 3.14 and through the invocation CI uses. They hold the derivation against a completion measured on real hardware, the SSE framing (a blank line after every frame, event: stats then [DONE], one of each), the three load refusals word for word, a load that goes loading to running, the device taking an oversized load and rolling it back in silence, a stream that fails part way reporting nothing rather than zeroes, the model card's fields, enable_thinking arriving on the wire where the runtime reads it, and the page carrying no external script, stylesheet or font. Verified against a real Tiiny as well: chats to Ornith-1.0-35B reconcile with the gateway's raw blocks to the rounding, and the rail lists Ornith at 50 units, the embedder at 1 and the TTS at 7 against a 58 of 100 budget. The browser check that fails if the page does nothing now loads the Chat page too, at 2000, 1440 and 390 wide, with no page errors and nothing off the right edge.
Also. The Models page's catalogue is read off the box on every call and nothing about it is pinned, but you could not tell that from the page, and when the model store did not answer the read came back as an empty list and the page printed "0 in the catalogue" as though the store were empty. bench.online now raises instead, /api/manage carries online_error and online_read_at beside online, and the page says "catalogue: 50 entries, read from the box at 07:49" with a Refresh beside it, or "the box did not answer for the catalogue: ..." in red when it did not. Neither place prints a count it does not have.
Four transition: all rules now name what they animate, the two full-height rules are 100dvh so a mobile browser's shrinking chrome does not make the sidebar jump, and the two glyph verdicts on Can I Run It read as words, because the colour was already saying fits or does not fit. The dead-page bug this branch also carried a fix for is not listed: it never shipped, and the fix for it landed upstream first and better, along with the phone-width grid track.
Riding along in the same release is the field work that landed while this was being built: firmware 1.0 moved the gateway, so the app now finds the box over USB, LAN and the TiinyOS proxy names and negotiates the port and vhost per service instead of assuming one; discovery works on Windows, which it did not, so a tester with a Tiiny on the same network is no longer told there is none; every text read and write names its encoding, which is what made that Windows failure hard to see; the footer says which box it is before a key is entered; the connection tiles fit the values they hold, including a full serial and a vhost name; and CI loads the page in a real browser on every push and fails if the controls are dead.