Insane that Fable doesn't write to cache after the initial system prompts. Notice that it wrote 4.9k tokens to cache when the session began and never again. I feel rugged! This planning session cost me 8.9k sats. ๐Ÿ˜ญ Deepseek V4 Pro caches everything and costs much less, proportionally. (see image below) ps: this is routstrd top that shows cache r/w data image

Replies (21)

Okay, I think I figured it out. Unlike most other modern LLMs, that write to cache incrementally, Anthropic models only seem write to cache initially and never again?
.'s avatar
. 1 month ago
Have youn tried out Fugu or Fugu Ultra? I am impressed so far. I set up a direct api call to Sakana AI on Routstr with Private Provider.
Insanely expensive and ended abruptly. Got: ` Unexpected token 'd', "data: {"id"... is not valid JSON` Weird. Okay, I think your node isn't taking care of cache pricing as it's a custom upstream. I see that the response contains the cache read data in it. The current main fixes this for some direct upstreams like deepseek, maybe it also works for custom upstreams. \ @Thefux In the usage chunk i got: ``` \"usage\": {\"prompt_tokens\": 45763, \"completion_tokens\": 193, \"total_tokens\": 45956, \"prompt_tokens_details\": {\"cached_tokens\": 43520, \"orchestration_input_tokens\": 0, \"orchestration_input_cached_tokens\": 0}, \"completion_tokens_details\": {\"reasoning_tokens\": 77, \"orchestration_output_tokens\": 0} ``` But i was charged full prompt token price here. image
.'s avatar
. 1 month ago
Hhhm. Okay. Do you suggest I update the node? The model should also be available from me and others too if you wanted to see the outcomes.
I just wanted to try it out and it was definitely NOT worth it lol. Both the Fable + GPT 5.5 combo and Deepseek V4 Pro ended up doing the exact same thing. Maybe I should try again with a more difficult task. Will report back. image
fugu-ultra is available from a lot of upstream (through Openrouter i think) but fugu is available only from you. idk the difference. will get back to you on updating the node. I'll wait for @Thefux to get back to me first.
.'s avatar
. 1 month ago
I do offer both Fugu and Fugu Ultra from Sakana but since PPQ also offers Fugu Ultra I don't think both show up separately based on my testing. Since Openrouter and PPQ don't offer just Fugu I think my custom provider option shows. I have two upstream provider enabled now. Fugu is the standard use, it is an llm that orchestrates across a pool of Claude, GPT and Gemini. Fugu Ultra is exrtra high reasoning like Mythos. The api is $5/$30 per million
Okay! that makes sense. We should handle cache read data in responses for custom upstreams so that your users don't get billed full prompt prices vs 90% discount cache read prices. Will verify this asap!
I understand your angle here. I mostly use GLM 5.2 these days. But I still think it's important to understand the gap between the closed source frontier and the open source frontier. People seem to like Fable a lot and i don't think it's bad to use better tools if available. It is strictly better to use Deepseek V4 Pro for medium tier tasks. It costs 100x cheaper. Also, if there's a better tool than Routstr but it doesn't fully follow my principles, I'd use that. But Routstr is the best tool for my needs right now. :)
This is what Fugu said about the unexpected error you saw. That error is a classic streaming vs. non-streaming mismatch, not a Routstr-vs-Fugu incompatibility. Hereโ€™s whatโ€™s happening in plain terms. ## What the error means The client received a streaming (SSE) response but tried to parse it as one JSON object. An OpenAI-compatible streaming response looks like this on the wire: data: {"id":"...","choices":[{"delta":{"content":"Hel"}}]} data: {"id":"...","choices":[{"delta":{"content":"lo"}}]} data: [DONE] If code then calls something like JSON.parse(body) or response.json(), it hits the literal text data: {"id"... and fails with exactly: Unexpected token 'd', "data: {"id"... is not valid JSON So the response is almost certainly fine โ€” the client just isnโ€™t reading it as a stream. ## The 3 likely causes (most to least common) 1. Client asked for a stream but parses it as JSON. The request included "stream": true, but the code does await response.json() instead of reading the SSE stream line-by-line. This is the #1 cause. 2. The request/tool defaults to streaming. Some UIs, SDKs, or curl copy-paste snippets set stream: true by default. The user may not realize it. 3. Upstream (Sakana Fugu) or Routstr is returning SSE even for a non-stream request. Less likely, but worth ruling out โ€” if Fugu always streams, Routstr passes that through, and a client expecting a single JSON blob breaks. ## How to confirm quickly (no secrets in output) Two safe tests. Use placeholder values; never paste your real key. Test A โ€” force non-streaming and expect one JSON object: curl -s https://<your-routstr-node>/v1/chat/completions \ -H "Authorization: Bearer <cashu-or-api-token>" \ -H "Content-Type: application/json" \ -d '{"model":"fugu","messages":[{"role":"user","content":"hi"}],"stream":false}' - If this returns a normal JSON object โ†’ your node works; the client just needs stream: false or proper stream handling. - If this still returns data: {...} lines โ†’ cause #3 (upstream/Routstr is forcing streaming), which needs a config/handling fix. Test B โ€” explicitly stream and watch the SSE frames: curl -N https://<your-routstr-node>/v1/chat/completions \ -H "Authorization: Bearer <cashu-or-api-token>" \ -H "Content-Type: application/json" \ -d '{"model":"fugu","messages":[{"role":"user","content":"hi"}],"stream":true}' - You should see multiple data: {...} lines ending in data: [DONE]. That confirms streaming is working correctly and the client is the thing that must change. ## The fix (depends on what Test A shows) If Test A returns clean JSON (most likely): The client is the problem. Two options: - Easiest: send "stream": false and keep using response.json(). - Or handle the stream properly. Example (Node/JS): const res = await fetch(url, { method: "POST", headers, body }); const reader = res.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); for (const line of buffer.split("\n")) { const s = line.trim(); if (!s.startsWith("data:")) continue; const payload = s.slice(5).trim(); if (payload === "[DONE]") break; const json = JSON.parse(payload); // parse each frame, not the whole body process.stdout.write(json.choices?.[0]?.delta?.content ?? ""); } } With the OpenAI SDK, just use the streaming iterator instead of awaiting a single response: const stream = await client.chat.completions.create({ model: "fugu", messages, stream: true }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); } If Test A still streams (cause #3): Then the node/upstream is forcing SSE. In that case Iโ€™d want to check how your Routstr node is configured and how Fugu is being called, because Routstr should honor the clientโ€™s stream flag rather than always streaming. ## Question so I can pin it down Can you tell me: 1. What is the client? (a website/chat UI, the OpenAI SDK, curl, LangChain, something else?) 2. Was stream: true set in the request (or is it a UI you donโ€™t control)? 3. Can you run Test A above and tell me whether it returns clean JSON or data: {...} lines? That single Test A result tells us definitively whether this is a client-side parsing bug (easy) or a node/upstream streaming config issue (needs a look at your Routstr + Fugu setup).
Given that the client is probably routstrd + Pi Agent, I would treat this as a client/ daemon streaming compatibility problem, not necessarily a bad Fugu provider setup. ## Simple diagnosis The error: Unexpected token 'd', "data: {"id"... is not valid JSON means something received this kind of response: data: {"id":"..."} data: {"id":"..."} data: [DONE] โ€ฆbut tried to read it as normal JSON. That data: format is Server-Sent Events streaming, which Routstr documents as the expected format when stream: true is used. Routstrโ€™s non-streaming default is stream: false; when stream: true, responses are sent as data: ... lines ending with data: [DONE]. (docs.routstr.com (https://docs.routstr.com/api/endpoints/)) So the likely chain is: Pi Agent โ†’ routstrd โ†’ your Routstr node โ†’ Sakana Fugu โ†’ returns streaming data Then either Pi Agent or routstrd tries to parse the whole streaming response as one JSON object and crashes. ## Why routstrd is suspicious here Routstrd is meant to sit locally between agents like Pi Agent and Routstr providers. Routstrโ€™s own page says routstrd can onboard Pi Agent, discover Routstr nodes, and auto-route to providers. (routstr.com (https://routstr.com/routstrd)) The routstrd README says its daemon API accepts a body with: { "model": "model-id", "messages": [], "stream": false } and shows a normal JSON response, not an SSE stream. (github.com (https://github.com/Routstr/routstrd)) That does not prove routstrd cannot stream, but it supports the practical guess: routstrd/Pi may be taking a streaming response and handling it as non-streaming JSON. ## Most likely cause Most likely: > Pi Agent is requesting streaming, or routstrd is forwarding a streaming request, but routstrd/Pi is not correctly consuming the streaming response from your Fugu-backed Routstr node. Your Routstr node may be doing the right thing by returning data: {...} lines if the request included stream: true.
Thank you for the diagnosis. Will look into this further. Actually, Routstrd's default mode is streaming mode. Most of Routstr's requests go through routstrd, and fugu model worked for 8 requests, in streaming mode, before it broke. Will try to reproduce this and see where the issue is.
Same model will not show up twice for two different provider .. The requests will be always forwarded to the first ("cheapest") upstream and fallback to the other if failed ..
โ†‘