@senad-d/observme 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.env.example +57 -0
  2. package/CHANGELOG.md +52 -0
  3. package/LICENSE +21 -0
  4. package/ObservMe-Production-Docs/00-README.md +79 -0
  5. package/ObservMe-Production-Docs/01-requirements-and-scope.md +207 -0
  6. package/ObservMe-Production-Docs/02-reference-architecture.md +306 -0
  7. package/ObservMe-Production-Docs/03-pi-event-and-session-model.md +266 -0
  8. package/ObservMe-Production-Docs/04-telemetry-semantic-conventions.md +722 -0
  9. package/ObservMe-Production-Docs/05-otel-pipeline-and-collector.md +355 -0
  10. package/ObservMe-Production-Docs/06-security-privacy-redaction.md +294 -0
  11. package/ObservMe-Production-Docs/07-extension-implementation-blueprint.md +447 -0
  12. package/ObservMe-Production-Docs/08-query-grafana-integration.md +276 -0
  13. package/ObservMe-Production-Docs/09-dashboards-alerts-slos.md +640 -0
  14. package/ObservMe-Production-Docs/10-testing-release-operations.md +319 -0
  15. package/ObservMe-Production-Docs/11-deployment-runbooks.md +203 -0
  16. package/ObservMe-Production-Docs/12-configuration-reference.md +337 -0
  17. package/ObservMe-Production-Docs/13-source-notes.md +33 -0
  18. package/ObservMe-Production-Docs/pi-session-format.md +427 -0
  19. package/README.md +356 -0
  20. package/SECURITY.md +45 -0
  21. package/dashboards/observme-agent-node-graphs.json +250 -0
  22. package/dashboards/observme-agents.json +1880 -0
  23. package/dashboards/observme-alerts.yaml +113 -0
  24. package/dashboards/observme-branches-compactions.json +1042 -0
  25. package/dashboards/observme-cost.json +1254 -0
  26. package/dashboards/observme-errors.json +1659 -0
  27. package/dashboards/observme-export-health.json +1802 -0
  28. package/dashboards/observme-latency.json +1494 -0
  29. package/dashboards/observme-llm-conversations.json +730 -0
  30. package/dashboards/observme-logs-llm.json +644 -0
  31. package/dashboards/observme-models.json +933 -0
  32. package/dashboards/observme-overview.json +2129 -0
  33. package/dashboards/observme-slo-health.json +737 -0
  34. package/dashboards/observme-slos.yaml +56 -0
  35. package/dashboards/observme-tools.json +902 -0
  36. package/dashboards/observme-trace-journey.json +1793 -0
  37. package/docs/STRUCTURE.md +49 -0
  38. package/docs/agent-subagent-observability-requirements.md +997 -0
  39. package/docs/compatibility-matrix.md +39 -0
  40. package/docs/configuration-tui-design-standard.md +767 -0
  41. package/docs/configuration.md +38 -0
  42. package/docs/review-validation.md +115 -0
  43. package/docs/validation-flow.md +117 -0
  44. package/examples/collector.yaml +123 -0
  45. package/examples/observme.yaml +131 -0
  46. package/img/demo.gif +0 -0
  47. package/img/icon.svg +47 -0
  48. package/package.json +103 -0
  49. package/src/commands/obs-agents-runtime.ts +150 -0
  50. package/src/commands/obs-agents.ts +491 -0
  51. package/src/commands/obs-args.ts +63 -0
  52. package/src/commands/obs-backfill.ts +1334 -0
  53. package/src/commands/obs-command-support.ts +43 -0
  54. package/src/commands/obs-cost.ts +228 -0
  55. package/src/commands/obs-diagnostics.ts +22 -0
  56. package/src/commands/obs-errors.ts +156 -0
  57. package/src/commands/obs-health.ts +301 -0
  58. package/src/commands/obs-link.ts +90 -0
  59. package/src/commands/obs-logs.ts +194 -0
  60. package/src/commands/obs-loki-summary.ts +184 -0
  61. package/src/commands/obs-session.ts +259 -0
  62. package/src/commands/obs-status.ts +359 -0
  63. package/src/commands/obs-tools.ts +274 -0
  64. package/src/commands/obs-trace.ts +411 -0
  65. package/src/commands/obs.ts +211 -0
  66. package/src/config/bootstrap-project-config.ts +300 -0
  67. package/src/config/defaults.ts +143 -0
  68. package/src/config/load-config.ts +631 -0
  69. package/src/config/project-paths.ts +61 -0
  70. package/src/config/schema.ts +405 -0
  71. package/src/config/validate.ts +456 -0
  72. package/src/constants.ts +4 -0
  73. package/src/diagnostics/sanitize.ts +6 -0
  74. package/src/extension.ts +38 -0
  75. package/src/otel/logs.ts +160 -0
  76. package/src/otel/metrics.ts +165 -0
  77. package/src/otel/otlp-endpoint.ts +10 -0
  78. package/src/otel/sdk.ts +114 -0
  79. package/src/otel/shutdown.ts +102 -0
  80. package/src/otel/traces.ts +166 -0
  81. package/src/pi/agent-lineage.ts +378 -0
  82. package/src/pi/agent-tree-tracker.ts +258 -0
  83. package/src/pi/event-handlers/agent-turn.ts +155 -0
  84. package/src/pi/event-handlers/lifecycle.ts +642 -0
  85. package/src/pi/event-handlers/llm.ts +115 -0
  86. package/src/pi/event-handlers/session-events.ts +159 -0
  87. package/src/pi/event-handlers/tool-bash.ts +275 -0
  88. package/src/pi/handler-internals.ts +2154 -0
  89. package/src/pi/handler-runtime.ts +633 -0
  90. package/src/pi/handler-types.ts +261 -0
  91. package/src/pi/handlers.ts +75 -0
  92. package/src/pi/subagent-spawn.ts +975 -0
  93. package/src/pi/subagent-types.ts +29 -0
  94. package/src/privacy/content-capture.ts +104 -0
  95. package/src/privacy/hash.ts +93 -0
  96. package/src/privacy/redact.ts +619 -0
  97. package/src/privacy/secret-patterns.ts +185 -0
  98. package/src/privacy/truncate.ts +69 -0
  99. package/src/query/grafana-readiness.ts +164 -0
  100. package/src/query/grafana-transport.ts +481 -0
  101. package/src/query/grafana.ts +371 -0
  102. package/src/query/loki.ts +332 -0
  103. package/src/query/prometheus.ts +388 -0
  104. package/src/query/tempo.ts +332 -0
  105. package/src/safety/sensitive-input.ts +208 -0
  106. package/src/semconv/attributes.ts +279 -0
  107. package/src/semconv/metrics.ts +146 -0
  108. package/src/semconv/spans.ts +19 -0
  109. package/src/semconv/values.ts +13 -0
  110. package/src/util/bounded-map.ts +97 -0
  111. package/tsconfig.json +15 -0
@@ -0,0 +1,933 @@
1
+ {
2
+ "__requires": [
3
+ {
4
+ "id": "grafana",
5
+ "name": "Grafana",
6
+ "type": "grafana",
7
+ "version": "10.0.0"
8
+ },
9
+ {
10
+ "id": "prometheus",
11
+ "name": "Prometheus",
12
+ "type": "datasource",
13
+ "version": "1.0.0"
14
+ },
15
+ {
16
+ "id": "loki",
17
+ "name": "Loki",
18
+ "type": "datasource",
19
+ "version": "1.0.0"
20
+ },
21
+ {
22
+ "id": "row",
23
+ "name": "Row",
24
+ "type": "panel",
25
+ "version": ""
26
+ },
27
+ {
28
+ "id": "timeseries",
29
+ "name": "Time series",
30
+ "type": "panel",
31
+ "version": ""
32
+ },
33
+ {
34
+ "id": "stat",
35
+ "name": "Stat",
36
+ "type": "panel",
37
+ "version": ""
38
+ },
39
+ {
40
+ "id": "bargauge",
41
+ "name": "Bar gauge",
42
+ "type": "panel",
43
+ "version": ""
44
+ }
45
+ ],
46
+ "annotations": {
47
+ "list": [
48
+ {
49
+ "datasource": {
50
+ "type": "prometheus",
51
+ "uid": "prometheus"
52
+ },
53
+ "enable": true,
54
+ "hide": false,
55
+ "iconColor": "rgba(31, 120, 193, 1)",
56
+ "name": "Model changes",
57
+ "expr": "sum(increase(observme_model_changes_total[$__interval])) by (provider, model) > 0",
58
+ "titleFormat": "Model change",
59
+ "textFormat": "{{provider}} / {{model}}"
60
+ },
61
+ {
62
+ "datasource": {
63
+ "type": "prometheus",
64
+ "uid": "prometheus"
65
+ },
66
+ "enable": true,
67
+ "hide": false,
68
+ "iconColor": "rgba(245, 183, 0, 1)",
69
+ "name": "Thinking level changes",
70
+ "expr": "sum(increase(observme_thinking_level_changes_total[$__interval])) by (environment) > 0",
71
+ "titleFormat": "Thinking level change",
72
+ "textFormat": "{{environment}}"
73
+ }
74
+ ]
75
+ },
76
+ "editable": true,
77
+ "fiscalYearStartMonth": 0,
78
+ "graphTooltip": 1,
79
+ "id": null,
80
+ "links": [
81
+ {
82
+ "icon": "external link",
83
+ "includeVars": true,
84
+ "keepTime": true,
85
+ "targetBlank": false,
86
+ "title": "Cost",
87
+ "tooltip": "Open spend, token, cache, and budget context.",
88
+ "type": "link",
89
+ "url": "/d/observme-cost/observme-cost?${__url_time_range}"
90
+ },
91
+ {
92
+ "icon": "external link",
93
+ "includeVars": true,
94
+ "keepTime": true,
95
+ "targetBlank": false,
96
+ "title": "Latency",
97
+ "tooltip": "Open cross-stage latency percentiles and volume context.",
98
+ "type": "link",
99
+ "url": "/d/observme-latency/observme-latency?${__url_time_range}"
100
+ },
101
+ {
102
+ "icon": "external link",
103
+ "includeVars": true,
104
+ "keepTime": true,
105
+ "targetBlank": false,
106
+ "title": "Errors",
107
+ "tooltip": "Open aggregate failure pressure and failure-log drill-downs.",
108
+ "type": "link",
109
+ "url": "/d/observme-errors/observme-errors?${__url_time_range}"
110
+ },
111
+ {
112
+ "icon": "external link",
113
+ "includeVars": true,
114
+ "keepTime": true,
115
+ "targetBlank": false,
116
+ "title": "LLM lifecycle logs",
117
+ "tooltip": "Open privacy-safe LLM lifecycle logs and token/size trends.",
118
+ "type": "link",
119
+ "url": "/d/observme-logs-llm/observme-logs-llm?${__url_time_range}"
120
+ },
121
+ {
122
+ "icon": "external link",
123
+ "includeVars": true,
124
+ "keepTime": true,
125
+ "targetBlank": false,
126
+ "title": "LLM Conversations",
127
+ "tooltip": "Open optional redacted prompt, response, and thinking capture.",
128
+ "type": "link",
129
+ "url": "/d/observme-llm-conversations/observme-llm-conversations?${__url_time_range}"
130
+ },
131
+ {
132
+ "icon": "external link",
133
+ "includeVars": true,
134
+ "keepTime": true,
135
+ "targetBlank": false,
136
+ "title": "Trace Journey",
137
+ "tooltip": "Open execution logs and Tempo traces for representative requests.",
138
+ "type": "link",
139
+ "url": "/d/observme-trace-journey/observme-trace-journey?${__url_time_range}"
140
+ }
141
+ ],
142
+ "liveNow": false,
143
+ "panels": [
144
+ {
145
+ "collapsed": false,
146
+ "gridPos": {
147
+ "h": 1,
148
+ "w": 24,
149
+ "x": 0,
150
+ "y": 0
151
+ },
152
+ "id": 100,
153
+ "panels": [],
154
+ "title": "Traffic and reliability",
155
+ "type": "row"
156
+ },
157
+ {
158
+ "datasource": {
159
+ "type": "prometheus",
160
+ "uid": "prometheus"
161
+ },
162
+ "description": "Top 10 LLM request-rate series grouped by the bounded provider and model labels. No data can mean an idle range or missing request instrumentation; use Export Health before treating an empty panel as healthy.",
163
+ "fieldConfig": {
164
+ "defaults": {
165
+ "color": {
166
+ "mode": "palette-classic"
167
+ },
168
+ "mappings": [],
169
+ "min": 0,
170
+ "thresholds": {
171
+ "mode": "absolute",
172
+ "steps": [
173
+ {
174
+ "color": "blue",
175
+ "value": null
176
+ }
177
+ ]
178
+ },
179
+ "unit": "ops"
180
+ },
181
+ "overrides": []
182
+ },
183
+ "gridPos": {
184
+ "h": 8,
185
+ "w": 12,
186
+ "x": 0,
187
+ "y": 1
188
+ },
189
+ "id": 1,
190
+ "links": [
191
+ {
192
+ "targetBlank": false,
193
+ "title": "Open LLM lifecycle logs",
194
+ "url": "/d/observme-logs-llm/observme-logs-llm?${__url_time_range}"
195
+ },
196
+ {
197
+ "targetBlank": false,
198
+ "title": "Open LLM Conversations",
199
+ "url": "/d/observme-llm-conversations/observme-llm-conversations?${__url_time_range}"
200
+ }
201
+ ],
202
+ "options": {
203
+ "legend": {
204
+ "calcs": [
205
+ "lastNotNull",
206
+ "max"
207
+ ],
208
+ "displayMode": "table",
209
+ "placement": "bottom",
210
+ "showLegend": true
211
+ },
212
+ "tooltip": {
213
+ "mode": "multi",
214
+ "sort": "desc"
215
+ }
216
+ },
217
+ "pluginVersion": "11.1.0",
218
+ "targets": [
219
+ {
220
+ "datasource": {
221
+ "type": "prometheus",
222
+ "uid": "prometheus"
223
+ },
224
+ "editorMode": "code",
225
+ "expr": "topk(10, sum by (provider, model) (rate(observme_llm_requests_total[$__rate_interval])))",
226
+ "legendFormat": "{{provider}} / {{model}}",
227
+ "range": true,
228
+ "refId": "A"
229
+ }
230
+ ],
231
+ "title": "Requests by provider/model",
232
+ "type": "timeseries"
233
+ },
234
+ {
235
+ "datasource": {
236
+ "type": "prometheus",
237
+ "uid": "prometheus"
238
+ },
239
+ "description": "Top 10 LLM failure ratios by provider/model. Active request series with no matching failure series render as zero without losing labels. Yellow is a 2% review level; red matches the default 5% over 10m ObservMeHighLlmErrorRate alert. No request series remains no data rather than false green health.",
240
+ "fieldConfig": {
241
+ "defaults": {
242
+ "color": {
243
+ "mode": "palette-classic"
244
+ },
245
+ "mappings": [],
246
+ "max": 1,
247
+ "min": 0,
248
+ "thresholds": {
249
+ "mode": "absolute",
250
+ "steps": [
251
+ {
252
+ "color": "green",
253
+ "value": null
254
+ },
255
+ {
256
+ "color": "yellow",
257
+ "value": 0.02
258
+ },
259
+ {
260
+ "color": "red",
261
+ "value": 0.05
262
+ }
263
+ ]
264
+ },
265
+ "unit": "percentunit"
266
+ },
267
+ "overrides": []
268
+ },
269
+ "gridPos": {
270
+ "h": 8,
271
+ "w": 12,
272
+ "x": 12,
273
+ "y": 1
274
+ },
275
+ "id": 2,
276
+ "links": [
277
+ {
278
+ "targetBlank": false,
279
+ "title": "Open Errors",
280
+ "url": "/d/observme-errors/observme-errors?${__url_time_range}"
281
+ },
282
+ {
283
+ "targetBlank": false,
284
+ "title": "Open LLM lifecycle logs",
285
+ "url": "/d/observme-logs-llm/observme-logs-llm?${__url_time_range}"
286
+ }
287
+ ],
288
+ "options": {
289
+ "legend": {
290
+ "calcs": [
291
+ "lastNotNull",
292
+ "max"
293
+ ],
294
+ "displayMode": "table",
295
+ "placement": "bottom",
296
+ "showLegend": true
297
+ },
298
+ "tooltip": {
299
+ "mode": "multi",
300
+ "sort": "desc"
301
+ }
302
+ },
303
+ "pluginVersion": "11.1.0",
304
+ "targets": [
305
+ {
306
+ "datasource": {
307
+ "type": "prometheus",
308
+ "uid": "prometheus"
309
+ },
310
+ "editorMode": "code",
311
+ "expr": "topk(10, clamp_max(((sum by (provider, model) (rate(observme_llm_errors_total[$__rate_interval])) or on (provider, model) (0 * sum by (provider, model) (rate(observme_llm_requests_total[$__rate_interval])))) / on (provider, model) clamp_min(sum by (provider, model) (rate(observme_llm_requests_total[$__rate_interval])), 1e-9)), 1))",
312
+ "legendFormat": "{{provider}} / {{model}}",
313
+ "range": true,
314
+ "refId": "A"
315
+ }
316
+ ],
317
+ "title": "Error rate by provider/model",
318
+ "type": "timeseries"
319
+ },
320
+ {
321
+ "datasource": {
322
+ "type": "prometheus",
323
+ "uid": "prometheus"
324
+ },
325
+ "description": "Top 10 provider/model shares of selected-range LLM requests. The denominator is the aggregate LLM request count for the same range. An empty panel means no request series exists; it is not proof of a healthy telemetry path.",
326
+ "fieldConfig": {
327
+ "defaults": {
328
+ "color": {
329
+ "mode": "palette-classic"
330
+ },
331
+ "mappings": [],
332
+ "max": 1,
333
+ "min": 0,
334
+ "thresholds": {
335
+ "mode": "absolute",
336
+ "steps": [
337
+ {
338
+ "color": "blue",
339
+ "value": null
340
+ }
341
+ ]
342
+ },
343
+ "unit": "percentunit"
344
+ },
345
+ "overrides": []
346
+ },
347
+ "gridPos": {
348
+ "h": 7,
349
+ "w": 12,
350
+ "x": 0,
351
+ "y": 9
352
+ },
353
+ "id": 7,
354
+ "links": [
355
+ {
356
+ "targetBlank": false,
357
+ "title": "Open Cost",
358
+ "url": "/d/observme-cost/observme-cost?${__url_time_range}"
359
+ }
360
+ ],
361
+ "options": {
362
+ "displayMode": "gradient",
363
+ "maxVizHeight": 300,
364
+ "minVizHeight": 16,
365
+ "minVizWidth": 8,
366
+ "namePlacement": "auto",
367
+ "orientation": "horizontal",
368
+ "reduceOptions": {
369
+ "calcs": [
370
+ "lastNotNull"
371
+ ],
372
+ "fields": "",
373
+ "values": false
374
+ },
375
+ "showUnfilled": true,
376
+ "sizing": "auto",
377
+ "valueMode": "color"
378
+ },
379
+ "pluginVersion": "11.1.0",
380
+ "targets": [
381
+ {
382
+ "datasource": {
383
+ "type": "prometheus",
384
+ "uid": "prometheus"
385
+ },
386
+ "editorMode": "code",
387
+ "expr": "topk(10, sum by (provider, model) (increase(observme_llm_requests_total[$__range])) / scalar(clamp_min((sum(increase(observme_llm_requests_total[$__range])) or vector(0)), 1)))",
388
+ "instant": true,
389
+ "legendFormat": "{{provider}} / {{model}}",
390
+ "range": false,
391
+ "refId": "A"
392
+ }
393
+ ],
394
+ "title": "Request share by provider/model",
395
+ "type": "bargauge"
396
+ },
397
+ {
398
+ "datasource": {
399
+ "type": "prometheus",
400
+ "uid": "prometheus"
401
+ },
402
+ "description": "Top 10 selected-range LLM failure totals by provider/model, shown beside failure ratios and request traffic. Active request series with no matching failure series render as labeled zeros. No request telemetry remains no data.",
403
+ "fieldConfig": {
404
+ "defaults": {
405
+ "color": {
406
+ "mode": "thresholds"
407
+ },
408
+ "mappings": [],
409
+ "min": 0,
410
+ "thresholds": {
411
+ "mode": "absolute",
412
+ "steps": [
413
+ {
414
+ "color": "green",
415
+ "value": null
416
+ },
417
+ {
418
+ "color": "red",
419
+ "value": 1e-9
420
+ }
421
+ ]
422
+ },
423
+ "unit": "short"
424
+ },
425
+ "overrides": []
426
+ },
427
+ "gridPos": {
428
+ "h": 7,
429
+ "w": 12,
430
+ "x": 12,
431
+ "y": 9
432
+ },
433
+ "id": 8,
434
+ "links": [
435
+ {
436
+ "targetBlank": false,
437
+ "title": "Open Errors",
438
+ "url": "/d/observme-errors/observme-errors?${__url_time_range}"
439
+ },
440
+ {
441
+ "targetBlank": false,
442
+ "title": "Open Trace Journey",
443
+ "url": "/d/observme-trace-journey/observme-trace-journey?${__url_time_range}"
444
+ }
445
+ ],
446
+ "options": {
447
+ "displayMode": "gradient",
448
+ "maxVizHeight": 300,
449
+ "minVizHeight": 16,
450
+ "minVizWidth": 8,
451
+ "namePlacement": "auto",
452
+ "orientation": "horizontal",
453
+ "reduceOptions": {
454
+ "calcs": [
455
+ "lastNotNull"
456
+ ],
457
+ "fields": "",
458
+ "values": false
459
+ },
460
+ "showUnfilled": true,
461
+ "sizing": "auto",
462
+ "valueMode": "color"
463
+ },
464
+ "pluginVersion": "11.1.0",
465
+ "targets": [
466
+ {
467
+ "datasource": {
468
+ "type": "prometheus",
469
+ "uid": "prometheus"
470
+ },
471
+ "editorMode": "code",
472
+ "expr": "topk(10, (sum by (provider, model) (increase(observme_llm_errors_total[$__range])) or on (provider, model) (0 * sum by (provider, model) (increase(observme_llm_requests_total[$__range])))))",
473
+ "instant": true,
474
+ "legendFormat": "{{provider}} / {{model}}",
475
+ "range": false,
476
+ "refId": "A"
477
+ }
478
+ ],
479
+ "title": "Selected-range errors by provider/model",
480
+ "type": "bargauge"
481
+ },
482
+ {
483
+ "collapsed": false,
484
+ "gridPos": {
485
+ "h": 1,
486
+ "w": 24,
487
+ "x": 0,
488
+ "y": 16
489
+ },
490
+ "id": 101,
491
+ "panels": [],
492
+ "title": "Latency",
493
+ "type": "row"
494
+ },
495
+ {
496
+ "datasource": {
497
+ "type": "prometheus",
498
+ "uid": "prometheus"
499
+ },
500
+ "description": "Top 10 p95 LLM request-duration trends grouped by provider/model. The histogram keeps le, provider, and model during aggregation. Sparse request volume can make p95 jump or disappear; compare the adjacent selected-range request volume before treating a model as a slow offender.",
501
+ "fieldConfig": {
502
+ "defaults": {
503
+ "color": {
504
+ "mode": "palette-classic"
505
+ },
506
+ "mappings": [],
507
+ "min": 0,
508
+ "thresholds": {
509
+ "mode": "absolute",
510
+ "steps": [
511
+ {
512
+ "color": "blue",
513
+ "value": null
514
+ }
515
+ ]
516
+ },
517
+ "unit": "ms"
518
+ },
519
+ "overrides": []
520
+ },
521
+ "gridPos": {
522
+ "h": 8,
523
+ "w": 16,
524
+ "x": 0,
525
+ "y": 17
526
+ },
527
+ "id": 3,
528
+ "links": [
529
+ {
530
+ "targetBlank": false,
531
+ "title": "Open Latency",
532
+ "url": "/d/observme-latency/observme-latency?${__url_time_range}"
533
+ },
534
+ {
535
+ "targetBlank": false,
536
+ "title": "Open Trace Journey",
537
+ "url": "/d/observme-trace-journey/observme-trace-journey?${__url_time_range}"
538
+ }
539
+ ],
540
+ "options": {
541
+ "legend": {
542
+ "calcs": [
543
+ "lastNotNull",
544
+ "max"
545
+ ],
546
+ "displayMode": "table",
547
+ "placement": "bottom",
548
+ "showLegend": true
549
+ },
550
+ "tooltip": {
551
+ "mode": "multi",
552
+ "sort": "desc"
553
+ }
554
+ },
555
+ "pluginVersion": "11.1.0",
556
+ "targets": [
557
+ {
558
+ "datasource": {
559
+ "type": "prometheus",
560
+ "uid": "prometheus"
561
+ },
562
+ "editorMode": "code",
563
+ "expr": "topk(10, histogram_quantile(0.95, sum by (provider, model, le) (rate(observme_llm_request_duration_ms_bucket[$__rate_interval]))))",
564
+ "legendFormat": "{{provider}} / {{model}}",
565
+ "range": true,
566
+ "refId": "A"
567
+ }
568
+ ],
569
+ "title": "p95 latency by provider/model",
570
+ "type": "timeseries"
571
+ },
572
+ {
573
+ "datasource": {
574
+ "type": "prometheus",
575
+ "uid": "prometheus"
576
+ },
577
+ "description": "Top 10 selected-range LLM request totals for interpreting the adjacent p95 panel. Low volume makes percentile comparisons statistically sparse. No data means no request counter series exists in the selected range.",
578
+ "fieldConfig": {
579
+ "defaults": {
580
+ "color": {
581
+ "mode": "palette-classic"
582
+ },
583
+ "mappings": [],
584
+ "min": 0,
585
+ "thresholds": {
586
+ "mode": "absolute",
587
+ "steps": [
588
+ {
589
+ "color": "blue",
590
+ "value": null
591
+ }
592
+ ]
593
+ },
594
+ "unit": "short"
595
+ },
596
+ "overrides": []
597
+ },
598
+ "gridPos": {
599
+ "h": 8,
600
+ "w": 8,
601
+ "x": 16,
602
+ "y": 17
603
+ },
604
+ "id": 9,
605
+ "links": [
606
+ {
607
+ "targetBlank": false,
608
+ "title": "Open Latency",
609
+ "url": "/d/observme-latency/observme-latency?${__url_time_range}"
610
+ }
611
+ ],
612
+ "options": {
613
+ "displayMode": "gradient",
614
+ "maxVizHeight": 300,
615
+ "minVizHeight": 16,
616
+ "minVizWidth": 8,
617
+ "namePlacement": "auto",
618
+ "orientation": "horizontal",
619
+ "reduceOptions": {
620
+ "calcs": [
621
+ "lastNotNull"
622
+ ],
623
+ "fields": "",
624
+ "values": false
625
+ },
626
+ "showUnfilled": true,
627
+ "sizing": "auto",
628
+ "valueMode": "color"
629
+ },
630
+ "pluginVersion": "11.1.0",
631
+ "targets": [
632
+ {
633
+ "datasource": {
634
+ "type": "prometheus",
635
+ "uid": "prometheus"
636
+ },
637
+ "editorMode": "code",
638
+ "expr": "topk(10, sum by (provider, model) (increase(observme_llm_requests_total[$__range])))",
639
+ "instant": true,
640
+ "legendFormat": "{{provider}} / {{model}}",
641
+ "range": false,
642
+ "refId": "A"
643
+ }
644
+ ],
645
+ "title": "Latency request volume",
646
+ "type": "bargauge"
647
+ },
648
+ {
649
+ "collapsed": false,
650
+ "gridPos": {
651
+ "h": 1,
652
+ "w": 24,
653
+ "x": 0,
654
+ "y": 25
655
+ },
656
+ "id": 102,
657
+ "panels": [],
658
+ "title": "Stop reasons",
659
+ "type": "row"
660
+ },
661
+ {
662
+ "datasource": {
663
+ "type": "loki",
664
+ "uid": "loki"
665
+ },
666
+ "description": "Selected-range totals from completed LLM request logs, grouped by the provisioned pi_llm_stop_reason label. This is an instant query over $__range. Empty means no matching completed requests with a non-empty stop reason were observed; inspect LLM lifecycle logs and Export Health if requests were expected.",
667
+ "fieldConfig": {
668
+ "defaults": {
669
+ "color": {
670
+ "mode": "palette-classic"
671
+ },
672
+ "mappings": [],
673
+ "min": 0,
674
+ "thresholds": {
675
+ "mode": "absolute",
676
+ "steps": [
677
+ {
678
+ "color": "blue",
679
+ "value": null
680
+ }
681
+ ]
682
+ },
683
+ "unit": "short"
684
+ },
685
+ "overrides": []
686
+ },
687
+ "gridPos": {
688
+ "h": 7,
689
+ "w": 24,
690
+ "x": 0,
691
+ "y": 26
692
+ },
693
+ "id": 4,
694
+ "links": [
695
+ {
696
+ "targetBlank": false,
697
+ "title": "Open LLM lifecycle logs",
698
+ "url": "/d/observme-logs-llm/observme-logs-llm?${__url_time_range}"
699
+ },
700
+ {
701
+ "targetBlank": false,
702
+ "title": "Open LLM Conversations",
703
+ "url": "/d/observme-llm-conversations/observme-llm-conversations?${__url_time_range}"
704
+ },
705
+ {
706
+ "targetBlank": false,
707
+ "title": "Open Trace Journey",
708
+ "url": "/d/observme-trace-journey/observme-trace-journey?${__url_time_range}"
709
+ }
710
+ ],
711
+ "options": {
712
+ "displayMode": "gradient",
713
+ "maxVizHeight": 300,
714
+ "minVizHeight": 16,
715
+ "minVizWidth": 8,
716
+ "namePlacement": "auto",
717
+ "orientation": "horizontal",
718
+ "reduceOptions": {
719
+ "calcs": [
720
+ "lastNotNull"
721
+ ],
722
+ "fields": "",
723
+ "values": false
724
+ },
725
+ "showUnfilled": true,
726
+ "sizing": "auto",
727
+ "valueMode": "color"
728
+ },
729
+ "pluginVersion": "11.1.0",
730
+ "targets": [
731
+ {
732
+ "datasource": {
733
+ "type": "loki",
734
+ "uid": "loki"
735
+ },
736
+ "editorMode": "code",
737
+ "expr": "sum by (pi_llm_stop_reason) (count_over_time({service_name=\"observme-pi-extension\", event_name=\"llm.request.completed\", pi_llm_stop_reason!=\"\"}[$__range]))",
738
+ "instant": true,
739
+ "legendFormat": "{{pi_llm_stop_reason}}",
740
+ "queryType": "instant",
741
+ "range": false,
742
+ "refId": "A"
743
+ }
744
+ ],
745
+ "title": "Stop reason distribution",
746
+ "type": "bargauge"
747
+ },
748
+ {
749
+ "collapsed": false,
750
+ "gridPos": {
751
+ "h": 1,
752
+ "w": 24,
753
+ "x": 0,
754
+ "y": 33
755
+ },
756
+ "id": 103,
757
+ "panels": [],
758
+ "title": "Efficiency",
759
+ "type": "row"
760
+ },
761
+ {
762
+ "datasource": {
763
+ "type": "prometheus",
764
+ "uid": "prometheus"
765
+ },
766
+ "description": "Top 10 rolling provider/model LLM costs per 1,000 LLM requests. Cost and request vectors match explicitly on provider and model; this panel never divides model cost by the global turn counter. Active request series with no cost series render as zero, which can also mean provider cost accounting is unsupported or incomplete rather than free usage.",
767
+ "fieldConfig": {
768
+ "defaults": {
769
+ "color": {
770
+ "mode": "palette-classic"
771
+ },
772
+ "mappings": [],
773
+ "min": 0,
774
+ "thresholds": {
775
+ "mode": "absolute",
776
+ "steps": [
777
+ {
778
+ "color": "blue",
779
+ "value": null
780
+ }
781
+ ]
782
+ },
783
+ "unit": "currencyUSD"
784
+ },
785
+ "overrides": []
786
+ },
787
+ "gridPos": {
788
+ "h": 8,
789
+ "w": 16,
790
+ "x": 0,
791
+ "y": 34
792
+ },
793
+ "id": 5,
794
+ "links": [
795
+ {
796
+ "targetBlank": false,
797
+ "title": "Open Cost",
798
+ "url": "/d/observme-cost/observme-cost?${__url_time_range}"
799
+ },
800
+ {
801
+ "targetBlank": false,
802
+ "title": "Open LLM lifecycle logs",
803
+ "url": "/d/observme-logs-llm/observme-logs-llm?${__url_time_range}"
804
+ }
805
+ ],
806
+ "options": {
807
+ "legend": {
808
+ "calcs": [
809
+ "lastNotNull",
810
+ "max"
811
+ ],
812
+ "displayMode": "table",
813
+ "placement": "bottom",
814
+ "showLegend": true
815
+ },
816
+ "tooltip": {
817
+ "mode": "multi",
818
+ "sort": "desc"
819
+ }
820
+ },
821
+ "pluginVersion": "11.1.0",
822
+ "targets": [
823
+ {
824
+ "datasource": {
825
+ "type": "prometheus",
826
+ "uid": "prometheus"
827
+ },
828
+ "editorMode": "code",
829
+ "expr": "topk(10, 1000 * ((sum by (provider, model) (rate(observme_llm_cost_usd_total[$__rate_interval])) or on (provider, model) (0 * sum by (provider, model) (rate(observme_llm_requests_total[$__rate_interval])))) / on (provider, model) clamp_min(sum by (provider, model) (rate(observme_llm_requests_total[$__rate_interval])), 1e-9)))",
830
+ "legendFormat": "{{provider}} / {{model}}",
831
+ "range": true,
832
+ "refId": "A"
833
+ }
834
+ ],
835
+ "title": "Cost per 1k LLM requests",
836
+ "type": "timeseries"
837
+ },
838
+ {
839
+ "datasource": {
840
+ "type": "prometheus",
841
+ "uid": "prometheus"
842
+ },
843
+ "description": "Selected-range aggregate LLM cost divided by completed turns. This is intentionally overall because turn counters do not carry provider/model labels. Zero can mean idle workload or absent provider cost accounting; use Cost for the tunable $50/hour budget reference rather than treating this neutral efficiency value as an alert.",
844
+ "fieldConfig": {
845
+ "defaults": {
846
+ "color": {
847
+ "mode": "thresholds"
848
+ },
849
+ "mappings": [],
850
+ "min": 0,
851
+ "thresholds": {
852
+ "mode": "absolute",
853
+ "steps": [
854
+ {
855
+ "color": "blue",
856
+ "value": null
857
+ }
858
+ ]
859
+ },
860
+ "unit": "currencyUSD"
861
+ },
862
+ "overrides": []
863
+ },
864
+ "gridPos": {
865
+ "h": 8,
866
+ "w": 8,
867
+ "x": 16,
868
+ "y": 34
869
+ },
870
+ "id": 6,
871
+ "links": [
872
+ {
873
+ "targetBlank": false,
874
+ "title": "Open Cost",
875
+ "url": "/d/observme-cost/observme-cost?${__url_time_range}"
876
+ }
877
+ ],
878
+ "options": {
879
+ "colorMode": "value",
880
+ "graphMode": "area",
881
+ "justifyMode": "auto",
882
+ "orientation": "auto",
883
+ "reduceOptions": {
884
+ "calcs": [
885
+ "lastNotNull"
886
+ ],
887
+ "fields": "",
888
+ "values": false
889
+ },
890
+ "showPercentChange": false,
891
+ "textMode": "auto",
892
+ "wideLayout": true
893
+ },
894
+ "pluginVersion": "11.1.0",
895
+ "targets": [
896
+ {
897
+ "datasource": {
898
+ "type": "prometheus",
899
+ "uid": "prometheus"
900
+ },
901
+ "editorMode": "code",
902
+ "expr": "(sum(increase(observme_llm_cost_usd_total[$__range])) or vector(0)) / clamp_min((sum(increase(observme_turns_completed_total[$__range])) or vector(0)), 1)",
903
+ "instant": true,
904
+ "range": false,
905
+ "refId": "A"
906
+ }
907
+ ],
908
+ "title": "Overall cost per completed turn",
909
+ "type": "stat"
910
+ }
911
+ ],
912
+ "refresh": "30s",
913
+ "schemaVersion": 39,
914
+ "style": "dark",
915
+ "tags": [
916
+ "observme",
917
+ "models",
918
+ "llm"
919
+ ],
920
+ "templating": {
921
+ "list": []
922
+ },
923
+ "time": {
924
+ "from": "now-6h",
925
+ "to": "now"
926
+ },
927
+ "timepicker": {},
928
+ "timezone": "browser",
929
+ "title": "ObservMe Models",
930
+ "uid": "observme-models",
931
+ "version": 2,
932
+ "weekStart": ""
933
+ }