ai_tracker 0.1.0
ai_tracker: ^0.1.0 copied to clipboard
On-device observability (cost, latency, tokens, errors) for any AI/LLM call from a Flutter app. Local-first & zero-config by default. Optional opt-in sync to your own backend. Pure-Dart core, works on [...]
ai_tracker #
On-device observability for every AI/LLM call in your Flutter app — cost, latency, tokens, errors. Local-first & zero-config by default. Optional sync only to your own backend. Pure-Dart core: Android, iOS, web, desktop, no platform channels for tracking.
Build & run (this repo) #
This package uses drift code generation. Generate once before compiling:
flutter pub get
dart run build_runner build --delete-conflicting-outputs
Run the demo (no API keys needed — it simulates calls):
cd example
flutter create --platforms=android,ios,web . # generate native host projects
flutter pub get
flutter run # mobile/desktop
To run the demo on web, fetch drift's WASM assets first:
bash ../platform_setup/web/fetch_web_assets.sh web
flutter run -d chrome
Run tests:
flutter test
Platform setup for sync + web: local-only tracking needs no platform config anywhere. Enabling sync (Android/iOS background) or running on web requires the steps in
PLATFORM_SETUP.md.
Version constraints in
pubspec.yamlare conservative. Ifpub getcomplains, runflutter pub upgrade --major-versionsand re-runbuild_runner.
(a) Fully local, zero config — under 60 seconds #
import 'package:ai_tracker/ai_tracker.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await AiTracker.init(); // 1. one line
runApp(const MyApp());
}
// 2. wrap any AI call — result is returned unchanged
final reply = await AiTracker.track(
provider: 'openai',
model: 'gpt-4o',
operation: () => myOpenAiCall(prompt),
extractUsage: (r) => AiUsage(
inputTokens: r.promptTokens, outputTokens: r.completionTokens),
);
// 3. see it
Navigator.push(context, MaterialPageRoute(
builder: (_) => const Scaffold(body: AiTrackerDashboard())));
No server. No INTERNET permission required. Data never leaves the device.
Streaming calls #
final stream = AiTracker.trackStream(
provider: 'openai',
model: 'gpt-4o',
operation: () => myOpenAiStream(prompt), // Stream<Chunk>
extractUsage: (chunks) => AiUsage(
inputTokens: chunks.last.promptTokens ?? 0,
outputTokens: chunks.last.completionTokens ?? 0),
);
await for (final chunk in stream) { /* your UI */ }
// captures time-to-first-token AND total latency automatically
On-device inference (TFLite / ML Kit / Gemini Nano) #
Same call — you just supply the token count, since there's no HTTP response
to parse. These calls make no network request, so proxy-based tools can't
see them; ai_tracker can.
await AiTracker.track(
provider: 'gemini-nano',
model: 'nano-2',
operationType: AiOperationType.onDeviceInference,
operation: () => geminiNano.generate(prompt),
extractUsage: (r) => AiUsage(outputTokens: r.tokenCount),
);
(b) Optional: aggregate across all your users — 3 lines #
Point the SDK at your own backend. Off unless you turn it on.
await AiTracker.init(
syncEnabled: true,
syncEndpoint: 'https://myapp.com/api/ai-events', // your server, nobody else's
);
Events batch (N events or T minutes, whichever first), upload in the
background (survive app kill), retry with exponential backoff when offline,
and carry no prompt or response text — metrics only. A ready-to-host
FastAPI receiver is in reference_backend/.
Platform note: background uploads use workmanager on Android/iOS
(Android minimum period is 15 min; iOS runs opportunistically via
BGTaskScheduler). Web and desktop have no OS scheduler, so there sync runs via
the in-process timer while the app is alive. Full config in
PLATFORM_SETUP.md.
Export a shareable HTML report (no backend, ever) #
dart run ai_tracker:report # writes ai_report.html
dart run ai_tracker:report <db-path> # if the db isn't at the default location
Configuration #
AiTracker.init(...) |
default | meaning |
|---|---|---|
syncEnabled |
false |
master switch for upload — off by default |
syncEndpoint |
null |
your server URL (required when syncEnabled) |
syncIntervalMinutes |
15 |
flush cadence |
syncBatchSize |
50 |
flush early once this many pile up |
pricing |
built-ins | Map<String, ModelPrice> overrides |
License #
MIT (add a LICENSE file before publishing).