Voice asks which device you mean, LLM agents can ask Jev, and 1.16.0 - #31
Merged
Merged
Conversation
…ly budget The voice agent only checked whether the budget was already spent, so the last command of the day went through in full and could end it over budget. It now estimates the command from its request size, as contexts and AI Task do, holds the estimate while the call runs, and reports the size to the tracker so the estimate learns from voice requests too.
Each command adds one agent detail to the conversation trace: the decision, the latency and tokens, and every answer with its probabilities. The debug view then shows why a command picked a device or fell back, without a diagnostics download.
When the action is clear and the entity answer is split between two exposed devices, the agent asks "Do you mean A or B?" and keeps the microphone open. The reply gets one small question about those two devices, and the first command then runs on the one it picked. A reply that picks neither is handled as a new command. The question lasts as long as the chat session.
… budget jev.noul, jev.choice, jev.score and jev.ask never checked the budget. A script that looped on an action spent without a limit. They now make the same check as contexts, AI Task and voice: estimate the request, refuse it with a message that names the estimate, what is left and the budget, and hold the estimate while the call runs.
…ost and the Bayesian sensor
A 138 byte action was estimated at 70 tokens and billed 278. The estimate was bytes over a ratio, with no fixed part, and the ratio came from the last call alone. After that small call, a 6 KB request was estimated at 14,327 tokens, billed 3,277 one call earlier, and refused on every try. The estimate is now 250 tokens plus the body over the ratio, and only a call whose body was billed at least 250 tokens updates the ratio.
Jev ran the action through the intent layer, which says nothing, and the Assist dialog showed no reply at all. Home Assistant's own agent picks a sentence from home-assistant-intents by what the command targeted. Jev now reads the same key off the slots it sent and renders that sentence, and says "Done." where the package has none, as for a toggle.
The everything option read "The whole house, with no room or device named". For "turn off all the lights" the model put 0.44 to 0.52 on it, below the 0.6 floor, and the command fell back. With the option naming every device of one kind, "all the lights", "every light" and "the lights" acted 6 of 6 times live. Commands naming an exposed device or a room still named it, 8 of 8, and "turn on all the lights" was still refused by the whole-house gate.
…n error The check refuses a command whose estimate does not fit, which happens with tokens left. The line said the budget "is spent". It now says not enough is left, in all 13 languages. With no fallback agent, the reply was an action_done response. The Assist dialog and a satellite read that as a command that went through. It is now an error, no_intent_match for a sentence not understood and failed_to_handle for the budget, a rejected key and no answer, as the default agent answers.
# Conflicts: # tests/test_conversation.py
chat_log.async_trace() writes to the conversation trace, and nothing in Home Assistant 2026.9.3 reads that: pipeline_debug/get showed only run-start, intent-start, intent-end and run-end. The agent now sends the record to the chat log's delta listener as thinking_content. The pipeline stores it as an intent-progress event, and the Assist dialog appends thinking_content under the reply. On a development instance pipeline_debug/get held the event for an action and for a refusal, with the decision, the slots, the top three options of each answer, the model, the input tokens and the latency. The delta goes to the listener only and is not added to the chat log, so a fallback agent reading the same conversation does not see it.
# Conflicts: # custom_components/jev/conversation.py
On a development instance with two lights both called "Lamp", "turn on the lamp" came back as one of them at 1.00, every time. The entity answer never split, so the ask-back never asked, and the agent acted on a guess that looked sure. The candidates now come from the exposed names of the chosen device's kind. The whole name said beats a name that only shares a word with the command, so "the lamp" is Lamp beside a Desk lamp, and "the desk lamp" is the Desk lamp. Two names that fit equally well get the question. More than two fall back. A room the model is sure of narrows the list first, so "the lamp in the office" acts. When no name fits the words at all, the model's answer stands as before. A reply that picks a device reads the command again without the check, so it does not ask the same question twice.
A test window with the door open for two minutes returned hours_true 0.0 beside times_true 1, which read as the never-true refusal failing to fire. Both hour counts now keep two places, so the same window reads 0.03.
The ask-back section described the probability split. It now describes the name comparison. It also restores the "What it costs" heading that the section had replaced. The diagnostics section named the debug view as the place for the agent detail. The note now shows in the Assist dialog and as an intent-progress event in the pipeline debug view. The refusal row and the budget sensor now say what the budget actually refuses: a command whose estimate does not fit, with tokens left.
Home Assistant's own agent prefers the satellite's area when a name fits devices in several rooms. Jev asked instead. It now reads the satellite entity's area, then its device's area, the same lookup, and asks only when that room does not hold exactly one of the devices.
With an unexposed "Desk lamp" and an exposed "Lamp", "turn on the desk lamp" came back from the model as Lamp and turned it on, 3 of 3 times on a development instance. The model can only pick from what it was shown, and Lamp was the closest option. The snapshot now keeps the names of entities the model is not shown: not exposed, a lock or entrance cover, or past the cap. They stay in Home Assistant. When the command says one of them in more words than it says the chosen device, the agent falls back and records "named a device that is not exposed".
A voice command is refused before it is sent if it would pass the daily budget
The Assist debug view shows what Jev answered for each command
Voice asks which device you mean when two fit the name
A hidden device named in full is not swapped for an exposed one
Actions are refused before they are sent if they would pass the daily budget
Other LLM agents can ask Jev yes/no and pick-one questions, when the options allow it
# Conflicts: # tests/test_services.py
# Conflicts: # custom_components/jev/conversation.py # tests/test_conversation.py
A new action finds the threshold that fits a yes/no sensor, from its recorded history
The budget estimate counts the part of the bill every request pays
A voice command that acts says what it did
"Turn off all the lights" counts as every device of one kind
Docs for calibration, the LLM tools, the ask-back and a month of cost
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 1.16.0.
manifest.jsoncarries 1.16.0 and pinsjevclient==1.2.0, which is on PyPI. HACS reads the version from the release tag, so the merge commit needs the tag1.16.0.hacs.jsonkeeps Home Assistant 2026.9.0 as the minimum. The config entry stays at version 1.2, so there is no migration.What changes
jev.calibratefinds the threshold that fits a yes/no sensor from its recorded history. It costs no tokens.Gate
Run on the branch tip:
CI passed on each phase PR, #20 to #30.
Live check
I installed the merged code on a test instance that runs Home Assistant 2026.9.3, on its existing entry and usage storage.
jev.calibratereturned a threshold.ai_task.generate_dataanswered.Known
"Toggle the lamp in the office" split 0.53 toggle against 0.47 turn_on and went to the fallback. That is below the 0.6 floor, and this release does not change it.