AdaTrack v1.5: An AI Brain Wired Directly Into Your IoT Platform

  • May 11, 2026
  • AdaTrack Team
AdaTrack v1.5: An AI Brain Wired Directly Into Your IoT Platform

AdaTrack v1.5: An AI Brain Wired Directly Into Your IoT Platform

Published: | Category: Engineering

Every IoT platform eventually accumulates the same problem: the data is all there, the devices are online, the alerts are firing — but answering a specific operational question still requires navigating three pages, running a stats query, cross-referencing alert history, and calling someone who knows which geofence was configured last quarter. The interface is structured for power users, not for the field engineer who just needs to know whether the temperature in warehouse 3 has been stable for the last 48 hours. AdaTrack v1.5 ships the ai_assistant plugin — a compiled extension that embeds an LLM-powered chat interface directly into the platform with native access to live device data, telemetry, alerts, geofences, workflows, and statistics. It is not a bolted-on chatbot. It is an agentic reasoning engine that can query your fleet and act on it, built on the same plugin infrastructure that ships the MQTT broker and the Telegram bot.

Teaching the Plugin System to Render Everywhere

The AI assistant is the first plugin in the platform's history that needs to render UI globally — not just on its own dedicated route, but on every authenticated page simultaneously. The floating action button that opens the chat drawer must be present whether the user is looking at device profiles, live map positions, alert rules, or the workflow builder. Achieving this required a minimal, non-breaking extension to the plugin manifest system.

The PluginCapabilities struct gained a new FloatingWidgets field alongside the existing NavItems, DashboardWidgets, and WorkflowNodes declarations. A FloatingWidgetDecl carries an ID, a label, and a position — currently bottom-right, though the type is extensible. On the frontend, pluginStore derives a getFloatingWidgets() selector and MainLayout maps the registered components directly into the app shell, after AppShell.Main. The result is that any future plugin can claim a persistent corner of the authenticated UI by declaring a single manifest field — no changes to the core layout required.

The AI assistant registers its floating widget as ai_fab. The AIAssistantFloatingWidget component it mounts is a 543-line drawer that manages session history, streams tokens from the SSE endpoint, renders markdown with a live cursor, and tracks daily quota usage — all while surviving route transitions because it is mounted at the root, not within any route boundary.

The Agentic Tool Loop: Eight Platform Tools, Zero Extra Queries

The chat endpoint at POST /api/v1/plugins/ai_assistant/sessions/{id}/chat does not simply forward the user's message to an LLM and stream the response back. It runs a multi-hop reasoning loop. The LLM receives the user's message alongside a structured set of eight platform tools with JSON Schema definitions. It can invoke any subset of those tools, receive the results, and use them to reason further before producing a final response. The default maximum depth is five hops, configurable up to ten.

The eight tools expose the platform's operational surface area:

  • list_devices — all user devices with online status and last-seen timestamp
  • get_device_telemetry — latest readings for a specific device, raw decoded payload included
  • get_device_location — last known GPS coordinates
  • list_alerts — recent alert events, filterable by device ID and severity
  • list_geofences — all geofences with type, name, and boundary metadata
  • query_stats — time-series aggregation (avg/min/max) with configurable time bucketing by minute, hour, or day, backed directly by the TimescaleDB hypertable
  • trigger_workflow — execute a named workflow with an optional JSON payload
  • get_system_summary — total device count, online count, and active alerts in the last 24 hours

When the LLM invokes multiple tools in a single hop, they are executed concurrently via goroutines coordinated with a sync.WaitGroup. Ownership is verified on every call — a tool invocation for a device the user does not own is rejected at the repository layer before any data is fetched. Results are serialised back into the conversation as tool-role messages and the loop continues until the model produces a final stop response.

On the wire, this translates into a Server-Sent Events stream. The client receives token events as the LLM streams output, tool_call events when a tool is invoked (so the UI can show a loading indicator), tool_result events when execution completes, and a done event carrying total token usage. The SSE write deadline is cleared during the streaming session to prevent timeout disconnects mid-loop — a subtle but important detail when a single request can span multiple LLM calls separated by real database queries.

Controlling LLM Costs Across a Multi-Tenant Fleet

Embedding an LLM provider in a multi-tenant SaaS platform creates a cost exposure that does not exist with deterministic platform features. A single user who issues a complex multi-hop query against a fleet of hundreds of devices can consume thousands of tokens in seconds. Without enforcement at multiple layers, a platform operator's API bill is entirely at the mercy of user behaviour.

The AI assistant implements three independent enforcement layers, checked in order of increasing overhead:

Layer 1 — Requests Per Minute (RPM): A token-bucket algorithm in memory, refilled over time. Default is three requests per user per minute. This check is fast — no database involved — and rejects runaway request patterns before a single LLM call is made.

Layer 2 — Daily Token Quota: Checked against an append-only plugin_ai_assistant_token_usage ledger. Every completed request writes a row with the user ID, input tokens, output tokens, and timestamp. The daily check sums rows from midnight UTC. The append-only design is deliberate: when a user deletes a chat session, the messages are gone but the token consumption they incurred is not. This ensures that the quota accurately reflects cost rather than persisted storage.

Layer 3 — Monthly Token Quota: The same ledger, summed over the current calendar month. Default is -1 (unlimited), intended for tier-gated enforcement where the platform operator assigns a budget per subscription tier.

When any layer rejects a request, the SSE stream immediately emits an error event with a machine-readable code (rpm_exceeded, daily_quota_exceeded, or monthly_quota_exceeded), a human-readable message, and a retry_after value in seconds. The floating widget surfaces daily quota consumption as a progress bar at the bottom of the session rail, turning red when consumption exceeds eighty percent of the configured limit.

Context Injection and Provider Abstraction

One of the design constraints for the AI assistant was that responses should be relevant to what the user is currently looking at, without requiring the user to explain their context in every message. The solution is a lightweight AIContext payload sent with each chat request: a page string identifying the current UI route and an optional entity_ids array carrying the device or resource IDs in scope. The backend injects this as a prefix on the user's message before it reaches the LLM, and the system prompt is constructed to reference it. A user asking "what's wrong with this device?" from the device details panel receives an answer scoped to that device, not a generic fleet-wide query.

The dashboard widget variant of the assistant goes further: it is configured with a specific list of device IDs at widget-creation time and injects them as context into every message, making an embedded AI panel in a "Cold Chain Dashboard" automatically aware of the relevant refrigerated-truck devices without the user doing anything.

The LLM provider is abstracted behind a Provider interface with streaming and single-turn methods. The Anthropic implementation uses claude-sonnet-4-6 by default, invoking the streaming messages API for chat and a synchronous call for the workflow node variant (which does not need SSE). The OpenAI implementation follows the same interface. Switching providers is an admin configuration change — no restart, no code change, no migration.

Two Integrations That Share No Code

The AI assistant integrates with two other parts of the platform — the workflow engine and the Telegram/Slack chatbot plugin — without importing either of them at the package level. Both integrations use the platform's EventBus as the only coupling surface, which means removing the AI assistant plugin does not break the chatbot, and the chatbot does not need to know whether the AI assistant is installed.

The workflow integration is the cleaner of the two. The AI assistant plugin declares a WorkflowNodeDecl in its manifest — type ai_analysis, category transform. The plugin registry makes this node type available in the workflow builder's node library automatically, the same way MQTT's session_cleanup cron job or the chatbot's alert dispatcher appear in their respective extension points. The node accepts a prompt_template with {{field}} interpolation placeholders and an output_format of text or json. It executes as a single-turn, non-streaming LLM call and emits response, structured, and tokens output fields for downstream nodes to consume.

The chatbot integration is an EventBus routing pattern. When the Telegram or Slack dispatcher receives a message that does not match any registered command, instead of returning the localised unknownCommand string, it publishes a plugin.chatbot.ai_request event carrying the user ID, platform identifier, and raw message text. The AI assistant subscribes to this event type and processes the message as a single-turn call against a stable per-user "Chatbot Session" — preserving conversation continuity across separate Telegram messages. The result is published back as plugin.ai_assistant.result, which the chatbot plugin picks up and forwards to the user. The two plugins start, stop, and fail independently; the only shared contract is the event payload schema.

v1.5 Is Available Now

The AI Assistant plugin is available on Standard tier and above. Enable it from Admin > Plugins > AI Assistant, enter your Anthropic or OpenAI API key, and the floating chat button appears on every page immediately. The dashboard widget, workflow node, and Telegram/Slack brain activate once the plugin is enabled — no additional configuration required for those surfaces. See the full list of platform features it plugs into.

Get Started

Key Highlights

  • Real-time Telemetry Ingestion via UDP
  • High Performance Time Series Storage
  • Advanced WebGL Powered Geospatial Visualization
  • Intelligent Geofencing and Alerting Engine

We are committed to providing cutting-edge solutions that help businesses run robust, scalable, and secure IoT systems.