Helix AI
Helix is TeslaSync's optional AI layer. The brand mark (HelixMark) appears anywhere AI is in play — sidebar nav, chatbot avatar, AI feature badges, Settings → AI header.
This page is the user-facing reference. The implementation source of truth is:
internal/ai/features/registry.go— every featureweb/src/ai/features.ts— frontend mirror, generated bytools/aigeninternal/ai/provider/— provider adapter interface + 4 adaptersinternal/ai/{guard,redact,limit,audit,rag,tools,strategies,dispatch}/— runtime
Off by default. Always.
There is no global "AI on" switch. Each Helix feature is enabled individually from Settings → AI. Disabled features are invisible:
| Surface | Off-mode behaviour |
|---|---|
| HTTP route | Returns 404 Not Found (asserted by tools/aivet) |
| React route | Mounts but renders no AI UI — the withAiFeature HOC short-circuits |
| Background job | The dispatcher gate trips before execution |
| Push notifications | The push fan-out worker filters by kind + recipient mode |
These invariants are enforced in CI by a final-gate test suite (Phase-50) that walks every feature in the registry and asserts each off-mode contract.
Surfaces
| Surface | Route | Description |
|---|---|---|
| Helix Chat | /chatbot | Evidence-first fleet agent with cross-domain tool use, app knowledge retrieval, and visible provenance |
| AI Settings | /settings/ai | Per-feature toggles, provider config, today's usage and seven-day per-feature spend, restore panel |
| Inline widgets | various pages | 55+ AI*.tsx widgets, each gated by withAiFeature |
The 56 user features
Grouped by intent. Every feature has a stable kebab-case ID; toggle each in Settings → AI.
Narratives & summaries
| Feature ID | What it does |
|---|---|
digest-narration | LLM narration on top of the weekly digest |
yir-narration | Year-in-review narration |
period-compare-narration | "This month vs last month" prose |
tco-narration | True cost of ownership narrative |
cost-forecast-narration | Forward-looking charging cost narrative |
battery-health-forecast-narrative | Battery degradation outlook |
cabin-temperature-impact-narrative | Cabin-temp impact on range / comfort |
vampire-drain-explanation | Why your car drank kWh while parked |
Natural-language builders
| Feature ID | What it does |
|---|---|
nl-alert-builder | Type a sentence → draft an Alert Studio rule |
nl-automation-builder | Type a sentence → draft an automation |
nl-dashboard-composer | Compose a dashboard from prose |
nl-grafana-panel | Compose a Grafana panel from prose |
nl-sql-playground | Natural-language → SQL against the warehouse |
nl-drive-search-replay | "Find the drive where I went over 90" |
nl-search | Global NL search across fleet entities |
signal-explorer-nl-filter | NL filter for the signal explorer |
Predictions & ML
| Feature ID | What it does |
|---|---|
range-prediction-model | Range prediction under stated conditions |
predictive-maintenance | Maintenance prediction from telemetry trends |
smart-charge-schedule-suggestion | Schedule based on rates + departure |
preheat-precool-recommender | When to pre-condition before a drive |
charging-curve-fingerprint-clustering | Cluster sessions by curve shape |
ml-charging-curve-clustering | ML variant with embedding similarity |
learned-per-vehicle-anomaly-baselines | Per-VIN normal-range learning |
Explainers & coaching
| Feature ID | What it does |
|---|---|
drive-coaching | Per-drive efficiency coaching |
safety-setting-explainer | "Why is this safety setting on?" |
anomaly-explanations | Plain-language anomaly explanations |
mqtt-sse-inspector-explanations | What does this MQTT / SSE event mean? |
state-machine-debugger-narrator | Explain a state transition |
log-trace-summarization | Summarise a log or trace span |
software-update-changelog-summarizer | Concise summary of a Tesla changelog |
incident-timeline-summarizer | Timeline summary of an incident |
charging-diagnosis | Diagnose a slow / failed session |
speed-profile-insights | Insights from the speed distribution |
route-efficiency-suggestions | Suggest a more efficient route |
tire-pressure-trend-reasoning | Reason over tyre pressure trends |
Automation & operations
| Feature ID | What it does |
|---|---|
alert-tuning-suggestions | Suggest threshold / cooldown tweaks |
cross-rule-conflict-detection | Find rules that fight each other |
quiet-hours-suggestion | Suggest quiet-hours windows |
inbox-auto-categorization | Categorise inbox / notifications |
feedback-queue-triage | Triage the feedback queue |
data-repair-suggestions | Suggest data-repair actions |
geofence-aware-automation-suggestions | Suggest automations from geofences |
suggest-new-geofences | Suggest new geofences from clusters |
auto-name-unnamed-locations | Name locations from context |
auto-trip-naming | Name trips from route + endpoints |
alert-message-template-suggestion | Propose a notification message template for Alert Studio |
alert-pack-builder | Propose a goal-based custom Alert Pack |
Multimodal & misc
| Feature ID | What it does |
|---|---|
voice-mode | Voice-driven assistant mode |
watch-face-nl-response | NL response surface for the watch face |
trip-planner-llm-agent | LLM agent for trip planning |
trip-postcard-share-card-image-generation | Generated share-card image for a trip |
vehicle-paint-preview | Image-gen vehicle paint preview |
pii-redaction-shared-exports | LLM-assisted PII redaction for shared exports |
rag-help | RAG-backed in-app help |
lifetime-stats-qa | Q&A over your lifetime stats |
chatbot-llm | The Helix Chat backend |
Ops-only meta-features (3)
| Feature ID | What it does |
|---|---|
__usage__ | AI Usage Card — per-call audit log + spend |
__redaction_bypass__ | Per-(feature, provider) redaction-bypass report |
ai-provider-health | Diagnostic — which adapter is active and what can it do |
The __double_underscore__ IDs are not toggleable per feature; they gate on ai_mode != 'off'.
Providers
Helix talks to LLMs through a small adapter interface (internal/ai/provider/provider.go). Four production adapters ship in-tree plus a mock adapter for tests:
| Adapter | Use cases |
|---|---|
openai | OpenAI hosted models (gpt-4o, gpt-4o-mini, gpt-4.1, …) |
azure | Microsoft Foundry OpenAI v1 only ({resource}.services.ai.azure.com/openai/v1 or {resource}.openai.azure.com/openai/v1). Select Auto, Chat Completions, or Responses. No model-name routing or older API surfaces. |
anthropic | Claude models (Sonnet, Opus, Haiku, …) |
ollama | Self-hosted models via Ollama — fully local |
Set Deployment name to the exact portal deployment name. The optional Embedding deployment name is independent of the chat deployment. Both use the same Foundry v1 base URL and API key; the persisted provider key remains azure, so existing credentials and feature-provider selections are preserved. Resource-root URLs automatically receive /openai/v1; /models, deployment-path URLs, queries, and other API paths are rejected rather than routed elsewhere.
API protocol defaults to Auto: Chat Completions first, then one Responses attempt only on structured operation-not-supported / not-found errors. Chat Completions and Responses call only the selected API, without fallback. The selection is persisted as api_protocol (auto, chat_completions, or responses) and is used by validation and Helix Chat/Stream identically. Embeddings always call /openai/v1/embeddings, regardless of chat protocol.
Existing configuration migration: obsolete flavor, api_version, deployment, and embedding_deployment fields are not part of the adapter. For entries without api_protocol, the configuration-read boundary promotes a previously effective chat deployment override into model (a stale override from a Foundry-flavor entry is ignored, preserving its existing model identity). An embedding override is promoted into embedding_model. The settings form displays these effective names and removes obsolete keys on Save. Modern entries with api_protocol ignore obsolete override keys. No legacy routes or model-specific heuristics remain. Review the visible names and endpoint before saving.
Validate uses a 30-second timeout and an explicit 1,024-output-token Azure probe budget (including reasoning tokens). Normal requests keep their caller-supplied token budget.
Auto makes at most two attempts and never changes the configured API base. Authentication, rate-limit, server, and token-budget failures do not trigger fallback; if both protocols fail, both errors are retained. The dispatcher does not replay a finalized Azure failure as another Chat call. Responses requests send store: false and replay tool calls/results, encrypted reasoning items, and assistant message phases explicitly between tool turns. Opaque continuation state is kept only in memory, not serialized into saved history or client responses. Existing content redaction still applies. Responses fallback streaming is buffered, not token-by-token SSE: Helix receives text/tool chunks followed by one terminal event after completion. Failed, incomplete, refused, and empty Responses results are errors, not success. Chat Completions also rejects malformed/empty completions and unfinished streams; truncated or filtered tool calls are never executed. Cancellation closes the upstream stream, including when the downstream client stops accepting output.
Protocol references: Azure Responses API and Azure OpenAI v1 lifecycle.
Per-feature provider selection
Provider choice resolves with this precedence (highest wins):
- Per-feature override — set the provider for a specific feature (e.g. use Claude for
drive-coaching, GPT-4o-mini for everything else). - Mode default — the globally configured default for the current
ai_mode. - No provider — the feature stays off.
See ResolveProviderName in internal/ai/provider/config.go.
Settings → AI shows audited per-feature calls, input/output tokens, and estimated cost over the last seven days, ordered by cost. The daily cost cap, when set, applies to all Helix calls combined; feature toggles are opt-ins, not individual cost caps. Provider/model labels come from the latest matching call among the 50 most recent audited calls, not from the seven-day aggregates. Features without such a recent call have no provider/model attribution in this view. Usage queries are disabled while Helix is off.
Decorator chain
Every adapter call is wrapped by decorators in this order (outermost first):
- Trace — OpenTelemetry span with feature ID, provider, model
- Audit — write request / response metadata to the audit log
- Cost — compute spend from token counts × per-model price table (
internal/ai/cost/cost.go) - Rate-limit — per-provider concurrency cap; tripped requests get back pressure, not silent failure
- Redact — outbound PII redaction; bypasses are logged for the bypass report
Result: nothing reaches the network without an audit row and a redaction pass.
Provider health (Ollama)
The Ollama health prober (internal/ai/health/ollama_poll.go) polls the local endpoint periodically. If Ollama goes unreachable, the prober suspends the ollama provider in the rate-limiter so requests fail fast with a clear error instead of hanging. The ai-provider-health feature exposes the current adapter snapshot for ops debugging.
RAG (Retrieval-Augmented Generation)
Features marked NeedsRAG=true in the registry retrieve relevant chunks from the pgvector index before calling the provider. The chunk store and the embedding model are configurable; see internal/ai/rag/.
Helix Chat uses an embedded corpus of maintained TeslaSync documentation. A local BM25-style retriever ranks that corpus in process, so product questions remain grounded without provider embeddings, a populated pgvector index, or the separate rag-help feature toggle. Retrieved passages include source paths that Chat can cite in its answer.
Grounding and evidence
Every strategy runs under the shared Helix intelligence contract. It requires fleet-specific factual claims to come from tool results or context supplied in the current run, while preserving the output format requested by each feature. Helix calls out missing, stale, sparse, or conflicting evidence instead of filling gaps from model priors. It provides concise rationale, not hidden chain-of-thought.
The shared streaming UI retains a privacy-safe execution trail: tool name, running/succeeded/unavailable status, successful source count, and terminal token usage. Tool arguments and raw result payloads are deliberately excluded from this shared metadata because they can contain locations, VINs, or other fleet details.
Helix Chat can discover safe vehicle summaries and use all twelve core read-only fleet tools: vehicle state and location, battery status, drives and drive detail, charging sessions and charge detail, active and recent alerts, geofences, and period efficiency. It can chain these sources for cross-domain analysis. Questions about TeslaSync itself use the chatbot-scoped application knowledge retriever and cite only sources returned by retrieval.
Response voice and evaluations
Every language-producing Helix surface follows one voice contract: warm, observant, and precise. Responses lead with the most useful supported observation, fit their shape to the question, and stay honest about what TeslaSync knows. Grounded semantics always win over style — a pretty sentence with an invented fact fails review, and so does technically valid monotony.
Severity and surface set the tone, never the other way around:
- Informational moments may be memorable and specific to the event.
- Warnings stay sharp and restrained; critical conditions stay urgent but calm, with the condition unmistakable and a safe next step where one exists.
- Safety, compact (voice/watch), and privacy-control surfaces keep their exact meaning and length contracts; no jokes soften warnings and no desktop narrative formatting leaks into glanceable surfaces.
- Builders and other proposal surfaces put personality in the explanation only: the structured draft the user reviews stays exact.
When AI is off or a feature is disabled, the deterministic baseline remains the product: template digests, manual Alert Studio forms, typed search filters, and the existing CRUD pages. AI panels are absent from the DOM; AI routes return 404; background jobs and push fan-out stay gated.
Response quality is verified two ways. Deterministic checks (always runnable offline) assert tool sequencing, schema/placeholder validity, scope bindings, redaction, and that each goldens.yaml evaluates the production prompt it is pinned to (see TestGoldenPromptMatchesProduction in every strategy test):
go test ./internal/ai/... ./cmd/ai-eval/...
go run ./cmd/ai-eval --all # canned mock provider, zero network egress
go run ./cmd/ai-eval --feature digest-narration
go run ./tools/aivet # registry / route / guard coverageEditorial quality (grounding, relevance, tone, concision, creative variety) is scored per-feature by the judge_rubric blocks in each goldens.yaml, either by the LLM judge (ai-eval --all --judge, needs JUDGE_PROVIDER + JUDGE_API_KEY) or by human review of before/after outputs on the same fixture. Canned fixtures prove deterministic contracts only — hand-authored fixture prose is never evidence that a live model writes well.
Tools (function calling)
Features marked NeedsTools=true can invoke functions on the server. The tool registry lives at internal/ai/tools/:
- Each tool has a typed input schema (reflected from the Go DTO) and a typed output.
- The dispatcher (
internal/ai/dispatch/) validates the model's proposed arguments against the schema before running the tool — no schemaless calls reach state-mutating code. - Multiple tool calls per assistant turn are supported (OpenAI, Azure, and Anthropic all allow it). Every call is round-tripped through the conversation history so the next turn sees the result.
- Provider-specific streaming fragments are assembled into complete, schema-valid tool calls before execution. Partial JSON never reaches a tool.
Streaming
Features marked NeedsStream=true deliver tokens through SSE via the provider's streaming endpoint. Deltas are forwarded as they arrive rather than materializing a complete response and replaying it as one chunk. A stream that closes without a terminal frame is reported as incomplete. The shared streaming layer lives at internal/ai/stream/.
Audit, usage, cost
The AI Usage Card surfaces:
- Today — token counts, requests, cost, top features
- By feature — per-feature breakdown over the selected window
- Recent — last N calls with feature, provider, model, latency, tokens, cost, and redaction status
Cost is computed locally from the per-model price table; no provider call is needed to price a request.
Redaction
The redact decorator runs against every outbound payload, replacing well-known PII surfaces (VINs, emails, GPS coordinates outside the relevant operation, addresses) with stable token placeholders. Each (feature, provider) pair has a configurable allow-list for fields that legitimately need the raw value. Per-(feature, provider) bypass events are written for the __redaction_bypass__ report.
Restore
The AI Restore Panel (Settings → AI) lets you replay a past AI response from the audit log without re-paying the provider. Useful when iterating on a prompt or recovering a lost narration.
Adding a new Helix feature
The contract is:
- Add a
Featureentry tointernal/ai/features/registry.gowithDefaultOn: false, populatedRoutes, and the right capability flags. - Implement the backend strategy under
internal/ai/strategies/<feature-id>/strategy.gowith agoldens.yamlfixture set. - Wire the handler in its carved subpackage at
internal/api/ai<slug>/handler.go(one directory per feature, e.g.internal/api/aialerttune/handler.go) and mount it insideinternal/api/ai_routes.gousingg.Wrap("<feature-id>", handler). - Add the React component under
web/src/components/ai/AI<FeatureName>.tsxwrapped withwithAiFeature('<feature-id>'). - Run
make generate(orgo run ./tools/aigen) to regenerateweb/src/ai/features.ts. - CI will fail unless:
tools/aivetfinds the route is wrapped byg.Wrap.tools/aigen --checkfinds the frontend mirror is in sync.- The off-mode invariant tests pass for the new feature.
Configuration
See Configuration → AI for every environment variable. The short version: each provider needs an API key (or, for Ollama, a reachable base URL), and a default model. Per-feature overrides live in the database (Settings → AI writes them).
Where Helix is in the UI
- Sidebar — the Helix entry (purple) deep-links to
/chatbot. - Settings — Settings → AI owns toggles, providers, audit, restore.
- Inline — wherever you see an
HelixMarkglyph or a "Generate with Helix" button. Each is governed by exactly one feature ID in the registry.
Acknowledgements
Helix builds on standard provider SDKs and the open Ollama project. The runtime contract (off-by-default, audited, redacted) is described in ADR-015.