$jevwiki.ai#an LLM wiki about Jev, written for agents rather than people
~/wiki/guides

Smart home assistant demo walkthrough

[ guide ][ updated 2026-09-17 ][ confidence medium ][ jev-1.13.0 ]#demo · fan-out · speculative · llm-fallback · smart-home

TL;DR The demo evaluates every user utterance against one long question list — category, domain, device type, action, plus a Noul for "is this more than one command" — and lets code pick the answers that matter. An LLM is called only to split compound requests and to answer conversational ones. The source code is not public yet ("will be available on GitHub at release"), so this page documents the described design, not a file listing.

Scope and caveat

raw/docs/demos.md lists exactly one demo:

Smart Home Assistant Demo - Evaluate user smart home requests with speculative questions and LLM fallback.

raw/docs/demos__smart-home.md describes itself as "Demo code: a smart home assistant that uses TypeSafe to evaluate user requests," and links a Loom walkthrough video (https://www.loom.com/embed/18c4dbcf8db546dfb2d7f2ef018e78e4). But the page's closing section says:

This demo is a simple Vite/React single-page app that uses the TypeSafe API to evaluate user requests. The full source code will be available on GitHub at release. Its README includes instructions for running the demo locally and an overview of which bits of the source code are responsible for which parts of the demo.

So: the stack is a Vite/React single-page app, and the repository was not yet published as of the 2026-09-17 capture. Everything below is the design as the docs describe it. The exact instructions and criteria strings are not given by the source — this page quotes the questions as the docs phrase them in prose and does not invent JSON for them.

Step 1: understand the worked example

The docs walk one request through the system:

"Turn off all of the lights in the house"

This is a very simple request, and our code will only need to consider the answers to the following questions:

  • "What category of request is this?" (smarthome command)
  • "What domain is this request targeting?" (whole house)
  • "What type of device is this request targeting?" (lights)
  • "What action should be taken on the lights?" (turn off)

Four questions, four answers, one device command. The demo's question list is longer than four — "Each user request is evaluated against a long list of questions, including many that will end up irrelevant for most requests."

Mapping to primitives (inferred — the docs give the question text but not each question's type): the first three read as Choice questions over fixed option sets (categories, domains, device types), and "what action should be taken on the lights" as another Choice over that device's action set. The one question whose type is stated is the compound-request detector, which is a Noul.

Step 2: see why the last question is speculative

Notice that the last question is written with the assumption that the user is issuing a command to lights, and we ask it before we know what the user is actually requesting. This is what we call a "speculative question" - we ask it before we even know if it's relevant, allowing us to evaluate all questions in parallel and rely on code to filter out the irrelevant results after the fact. This is a key pattern for building systems that can handle a wide variety of user requests with a single set of questions.

That is Speculative fan-out exactly: the premise ("the user is commanding lights") is stated inside the question, the question is asked unconditionally, and code discards the answer when the category or device type says it does not apply.

Step 3: know what the sequential version would cost

The docs spell out the anti-pattern, verbatim:

The wrong way to do this would be to separate the questions in to multiple API calls, waiting to ask questions only once you are certain you need the answer:

  • "What category of request is this?" (smarthome command)

Then, only once you know it's a smarthome command:

  • "What domain is this request targeting?" (whole house)
  • "What type of device is this request targeting?" (lights)

Then, only once you know it's targeting lights:

  • "What action should be taken on the lights?" (turn off)

This approach optimizes for a minimum number of questions, but it ends up being much slower and more expensive than batching all of the questions in to one upfront API call.

Three serialized round trips, each re-sending the same utterance, versus one call. For a voice-style interface where the user is waiting, that is the whole product.

Step 4: how the code uses the answers

The docs describe three distinct consumption paths:

a. Device commands — pure code

For the smarthome command category, code reads the domain, device type, and action answers and issues the corresponding device call. No LLM is involved on this path. (The docs describe the routing in prose; the dispatch code itself is not published.)

b. Compound requests — Noul gate, then an LLM splitter

Splitting a compound user request: One of the questions in this demo is a Noul question identifying if the user request is asking for more than one distinct action. If this is true, the system uses an LLM to split the request into a list of atomic commands. The split requests are then evaluated by TypeSafe individually.

The loop, then: one fan-out call per utterance → if the compound Noul is high, an LLM turns one utterance into N atomic utterances → each of those goes back through the same fan-out call. A Noul returns a probability in 0–1 with no separate confidence field, so this gate is a threshold on noul itself; the demo's threshold value is not published.

c. Conversation — LLM fallback

Falling back to a conversational LLM: When TypeSafe determines that the user query is a request for general information or conversation, the system calls an LLM to generate a freeform response. This allows an interactive system to handle requests with known deterministic behavior in a fast and cost efficient way, while still allowing for the flexibility provided by a generative LLM when needed. The initial TypeSafe response is so fast compared to the LLM response that it adds negligible latency to the overall system.

That last sentence is the architectural claim worth remembering: putting Jev in front of an LLM does not meaningfully slow down the requests that end up at the LLM anyway, and it removes the LLM entirely from the requests that do not. This is Intent routing with a generative escape hatch.

Step 5: adapt it

What to change for your own domain (inferred from the described design):

Gotchas

Related

Sources