From 170f7f4c28086cde2269ef5b143433076394535f Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Tue, 28 Jul 2026 00:23:15 +0200 Subject: [PATCH 1/2] perf(admin): slim audit payloads and gzip admin responses A 25-row audit list page weighed ~27 MB for agent traffic: every entry shipped its full request body, response body, per-attempt error bodies, and one rewritten body per request revision, uncompressed. The Interactions drawer then requested up to 120 such entries and routinely timed out behind proxies. Four changes, one per bottleneck: - /admin/audit/log entries are slimmed server-side: bodies, attempt error payloads, and revision bodies are stripped; scalar metadata, attempt pips data, and revision savings survive. New bodies_omitted and conversation_payload fields tell the dashboard to lazy-load /admin/audit/detail on row expand (that fetch path already existed) and preserve the Interactions-drawer eligibility signal it previously sniffed from the bodies. - /admin/audit/conversation entries drop attempts, request revisions, and header maps; the drawer builds its transcript solely from id/timestamp/request_body/response_body/error_message. - Admin API responses are gzipped (the live-log SSE stream is exempt, it must flush per event). - New LOGGING_LOG_REVISION_BODIES (logging.log_revision_bodies, default true) lets operators keep revision metadata but skip storing the full rewritten body per compressed request, roughly halving audit storage growth under GoModel Pro token compression. Co-Authored-By: Claude Fable 5 --- cmd/gomodel/docs/docs.go | 10 + config/config.example.yaml | 1 + config/config.go | 1 + config/config_test.go | 2 +- config/logging.go | 10 + docs/advanced/configuration.mdx | 11 + docs/openapi.json | 10 + internal/admin/audit_projection.go | 146 ++++++++++ internal/admin/audit_projection_test.go | 256 ++++++++++++++++++ .../{index-TxnKYqqn.js => index-Dy9UJDJP.js} | 4 +- .../admin/dashboard/static/dist/index.html | 2 +- internal/admin/handler.go | 11 + internal/admin/handler_audit.go | 15 + internal/admin/handler_test.go | 8 +- internal/auditlog/auditlog.go | 5 + internal/auditlog/factory.go | 1 + internal/server/http.go | 14 +- internal/server/request_rewrite.go | 2 +- internal/server/request_rewrite_test.go | 29 +- .../pages/audit-logs/conversation-helpers.js | 4 + .../src/pages/audit-logs/live-logs-logic.js | 8 +- 21 files changed, 536 insertions(+), 14 deletions(-) create mode 100644 internal/admin/audit_projection.go create mode 100644 internal/admin/audit_projection_test.go rename internal/admin/dashboard/static/dist/assets/{index-TxnKYqqn.js => index-Dy9UJDJP.js} (98%) diff --git a/cmd/gomodel/docs/docs.go b/cmd/gomodel/docs/docs.go index f01cc2a8c..ac4d461f0 100644 --- a/cmd/gomodel/docs/docs.go +++ b/cmd/gomodel/docs/docs.go @@ -18,6 +18,7 @@ const docTemplate = `{ "paths": { "/admin/audit/conversation": { "get": { + "description": "Thread entries carry the request/response bodies the\ntranscript is built from; attempts, request revisions, and\nheader maps are omitted.", "produces": [ "application/json" ], @@ -127,6 +128,7 @@ const docTemplate = `{ }, "/admin/audit/log": { "get": { + "description": "Entries are slimmed for the wire: request/response bodies,\nper-attempt error bodies, and rewritten revision bodies are\nomitted (bodies_omitted=true). Fetch /admin/audit/detail for\nthe full payload of one entry.", "produces": [ "application/json" ], @@ -6654,12 +6656,20 @@ const docTemplate = `{ "auth_method": { "type": "string" }, + "bodies_omitted": { + "description": "BodiesOmitted marks a list entry whose request/response bodies (and\nother heavy payloads) were stripped server-side; the full entry is\navailable from GET /admin/audit/detail. It also tells the dashboard\nthe entry is persisted — a slim entry is never an in-flight request.", + "type": "boolean" + }, "cache_type": { "type": "string" }, "client_ip": { "type": "string" }, + "conversation_payload": { + "description": "ConversationPayload reports that the entry's (stripped) bodies were\nshaped like a conversation, preserving the Interactions-drawer\neligibility signal the dashboard otherwise sniffs from the bodies.", + "type": "boolean" + }, "data": { "description": "Data contains flexible request/response information as JSON", "allOf": [ diff --git a/config/config.example.yaml b/config/config.example.yaml index 400f1e58d..405c2aa01 100644 --- a/config/config.example.yaml +++ b/config/config.example.yaml @@ -144,6 +144,7 @@ storage: logging: enabled: true log_bodies: true # WARNING: may contain sensitive data + log_revision_bodies: true # store rewritten bodies from request rewriters (needs log_bodies) log_headers: true buffer_size: 1000 flush_interval: 5 # seconds diff --git a/config/config.go b/config/config.go index e12421f87..6ef79c647 100644 --- a/config/config.go +++ b/config/config.go @@ -102,6 +102,7 @@ func buildDefaultConfig() *Config { Logging: LogConfig{ Enabled: true, LogBodies: true, + LogRevisionBodies: true, LogHeaders: true, BufferSize: 1000, FlushInterval: 5, diff --git a/config/config_test.go b/config/config_test.go index f9d243823..b062d55dc 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -56,7 +56,7 @@ func clearAllConfigEnvVars(t *testing.T) { "STORAGE_TYPE", "SQLITE_PATH", "POSTGRES_URL", "POSTGRES_MAX_CONNS", "MONGODB_URL", "MONGODB_DATABASE", "METRICS_ENABLED", "METRICS_ENDPOINT", - "LOGGING_ENABLED", "LOGGING_LOG_BODIES", "LOGGING_LOG_HEADERS", + "LOGGING_ENABLED", "LOGGING_LOG_BODIES", "LOGGING_LOG_REVISION_BODIES", "LOGGING_LOG_HEADERS", "LOGGING_ONLY_MODEL_INTERACTIONS", "LOGGING_BUFFER_SIZE", "LOGGING_FLUSH_INTERVAL", "LOGGING_RETENTION_DAYS", "USAGE_ENABLED", "ENFORCE_RETURNING_USAGE_DATA", diff --git a/config/logging.go b/config/logging.go index c54350326..2a7043a84 100644 --- a/config/logging.go +++ b/config/logging.go @@ -21,6 +21,16 @@ type LogConfig struct { // Default: false LogAudioBodies bool `yaml:"log_audio_bodies" env:"LOGGING_LOG_AUDIO_BODIES"` + // LogRevisionBodies refines LogBodies for the request-revision chain: + // when both are enabled, every request rewriter that changed the body + // (for example GoModel Pro token compression) stores the full rewritten + // body alongside the original in the audit entry. Requires LogBodies. + // Disabling it keeps the revision metadata (rewriter name, sizes, tokens + // saved, change detail) but drops the rewritten body copy — roughly + // halving audit storage per compressed request. + // Default: true + LogRevisionBodies bool `yaml:"log_revision_bodies" env:"LOGGING_LOG_REVISION_BODIES"` + // LogHeaders enables logging of request/response headers // Sensitive headers (Authorization, Cookie, etc.) are auto-redacted // Default: true diff --git a/docs/advanced/configuration.mdx b/docs/advanced/configuration.mdx index 466d8e0f5..95518e168 100644 --- a/docs/advanced/configuration.mdx +++ b/docs/advanced/configuration.mdx @@ -137,6 +137,7 @@ Storage is shared by audit logging, usage tracking, and future features like IAM | --------------------------------- | ------------------------------------------ | ------- | | `LOGGING_ENABLED` | Enable audit logging | `true` | | `LOGGING_LOG_BODIES` | Log request/response bodies | `true` | +| `LOGGING_LOG_REVISION_BODIES` | Log rewritten bodies from request rewriters | `true` | | `LOGGING_LOG_AUDIO_BODIES` | Log audio endpoint inputs/outputs | `false` | | `LOGGING_LOG_HEADERS` | Log headers (sensitive ones auto-redacted) | `true` | | `LOGGING_ONLY_MODEL_INTERACTIONS` | Only log AI model endpoints | `true` | @@ -156,6 +157,16 @@ to `LOGGING_FLUSH_INTERVAL` seconds to appear through the stored-log API. prompts. + + `LOGGING_LOG_REVISION_BODIES` refines `LOGGING_LOG_BODIES` for the ingress + request-rewrite chain — it has no effect unless body logging is enabled. With + both on, every rewriter that changed a request (for example GoModel Pro token + compression) stores the full rewritten body alongside the original client + body in the audit entry. Disabling it keeps the revision metadata (rewriter + name, byte sizes, tokens saved, change detail) but drops the extra body copy, + roughly halving audit storage per rewritten request. + + `LOGGING_LOG_AUDIO_BODIES` refines `LOGGING_LOG_BODIES` for audio endpoints — it has no effect unless body logging is enabled. With both on, `/v1/audio/speech` diff --git a/docs/openapi.json b/docs/openapi.json index ae21f8348..1c89b337b 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -9,6 +9,7 @@ "paths": { "/admin/audit/conversation": { "get": { + "description": "Thread entries carry the request/response bodies the\ntranscript is built from; attempts, request revisions, and\nheader maps are omitted.", "tags": [ "admin" ], @@ -160,6 +161,7 @@ }, "/admin/audit/log": { "get": { + "description": "Entries are slimmed for the wire: request/response bodies,\nper-attempt error bodies, and rewritten revision bodies are\nomitted (bodies_omitted=true). Fetch /admin/audit/detail for\nthe full payload of one entry.", "tags": [ "admin" ], @@ -9747,12 +9749,20 @@ "auth_method": { "type": "string" }, + "bodies_omitted": { + "description": "BodiesOmitted marks a list entry whose request/response bodies (and\nother heavy payloads) were stripped server-side; the full entry is\navailable from GET /admin/audit/detail. It also tells the dashboard\nthe entry is persisted — a slim entry is never an in-flight request.", + "type": "boolean" + }, "cache_type": { "type": "string" }, "client_ip": { "type": "string" }, + "conversation_payload": { + "description": "ConversationPayload reports that the entry's (stripped) bodies were\nshaped like a conversation, preserving the Interactions-drawer\neligibility signal the dashboard otherwise sniffs from the bodies.", + "type": "boolean" + }, "data": { "description": "Data contains flexible request/response information as JSON", "allOf": [ diff --git a/internal/admin/audit_projection.go b/internal/admin/audit_projection.go new file mode 100644 index 000000000..5e0558282 --- /dev/null +++ b/internal/admin/audit_projection.go @@ -0,0 +1,146 @@ +package admin + +import "github.com/enterpilot/gomodel/internal/auditlog" + +// Audit list projection. +// +// A list page ships up to 100 entries, and a full entry carries the original +// request body, the response body, per-attempt upstream error bodies, and one +// rewritten body per request revision — megabytes per row for agent traffic. +// The dashboard's collapsed row renders none of that: it needs scalar +// metadata, the attempt pips, and two derived signals (can this entry open +// the Interactions drawer, and has the request settled). The expanded row +// lazy-loads the full entry from /admin/audit/detail. +// +// slimAuditListEntry therefore strips the heavy payloads from a list entry +// and records what the client needs to compensate: +// - BodiesOmitted tells the dashboard to fetch the detail endpoint on +// expand (and that the entry is persisted, i.e. not in-flight). +// - ConversationPayload preserves the drawer-eligibility signal that the +// client would otherwise sniff from the removed bodies. +// +// The detail and conversation endpoints are not slimmed this way: detail is +// the designated source of full payloads, and the conversation thread is +// built client-side from request/response bodies (see slimConversationEntry +// for what it drops instead). +func slimAuditListEntry(resp *auditLogEntryResponse) { + d := resp.Data + if d == nil { + return + } + + stripped := false + slim := *d + + if slim.RequestBody != nil || slim.ResponseBody != nil { + resp.ConversationPayload = hasConversationPayload(slim.RequestBody, slim.ResponseBody) + } + + if slim.RequestBody != nil { + slim.RequestBody = nil + stripped = true + } + if slim.ResponseBody != nil { + slim.ResponseBody = nil + stripped = true + } + + if len(slim.Attempts) > 0 { + attempts := make([]auditlog.AttemptSnapshot, len(slim.Attempts)) + copy(attempts, slim.Attempts) + for i := range attempts { + if attempts[i].ResponseBody != nil || attempts[i].ResponseHeaders != nil { + attempts[i].ResponseBody = nil + attempts[i].ResponseHeaders = nil + stripped = true + } + } + slim.Attempts = attempts + } + + if len(slim.RequestRevisions) > 0 { + revisions := make([]auditlog.RequestRevisionSnapshot, len(slim.RequestRevisions)) + copy(revisions, slim.RequestRevisions) + for i := range revisions { + if revisions[i].Body != nil { + revisions[i].Body = nil + stripped = true + } + } + slim.RequestRevisions = revisions + } + + if !stripped { + return + } + resp.Data = &slim + resp.BodiesOmitted = true +} + +// hasConversationPayload mirrors the dashboard's body sniff for entries whose +// path alone does not qualify them for the Interactions drawer (for example +// /v1/messages or provider passthrough): a request shaped like a conversation +// or a response shaped like model output. +func hasConversationPayload(requestBody, responseBody any) bool { + if req, ok := requestBody.(map[string]any); ok { + if _, ok := req["messages"].([]any); ok { + return true + } + if _, ok := req["input"]; ok { + return true + } + if _, ok := req["instructions"].(string); ok { + return true + } + if _, ok := req["previous_response_id"].(string); ok { + return true + } + } + if resp, ok := responseBody.(map[string]any); ok { + if _, ok := resp["choices"].([]any); ok { + return true + } + if looksLikeResponsesOutput(resp["output"]) { + return true + } + } + return false +} + +// looksLikeResponsesOutput reports whether v is a Responses-API output array: +// a non-empty array of typed items. +func looksLikeResponsesOutput(v any) bool { + items, ok := v.([]any) + if !ok || len(items) == 0 { + return false + } + first, ok := items[0].(map[string]any) + if !ok { + return false + } + _, hasType := first["type"] + _, hasContent := first["content"] + return hasType || hasContent +} + +// slimConversationEntry strips the fields the Interactions drawer never +// reads from a conversation-thread entry. The drawer builds its transcript +// from id, timestamp, request_body, response_body, and error_message; +// attempts, request revisions (each carrying a full rewritten body), and +// header maps only inflate the response — for agent traffic they roughly +// double it. +func slimConversationEntry(entry *auditlog.LogEntry) { + d := entry.Data + if d == nil { + return + } + if d.Attempts == nil && d.RequestRevisions == nil && d.RequestHeaders == nil && d.ResponseHeaders == nil { + return + } + slim := *d + slim.Attempts = nil + slim.RequestRevisions = nil + slim.RequestHeaders = nil + slim.ResponseHeaders = nil + entry.Data = &slim +} diff --git a/internal/admin/audit_projection_test.go b/internal/admin/audit_projection_test.go new file mode 100644 index 000000000..e76fdc51c --- /dev/null +++ b/internal/admin/audit_projection_test.go @@ -0,0 +1,256 @@ +package admin + +import ( + "encoding/json" + "net/http" + "testing" + "time" + + "github.com/enterpilot/gomodel/internal/auditlog" +) + +// fullAuditEntry builds an entry carrying every heavy payload the list +// projection is expected to strip. +func fullAuditEntry(id string) auditlog.LogEntry { + return auditlog.LogEntry{ + ID: id, + Timestamp: time.Now().UTC(), + RequestedModel: "gpt-4o", + Provider: "openai", + StatusCode: 200, + RequestID: "req-" + id, + Method: http.MethodPost, + Path: "/v1/messages", + Data: &auditlog.LogData{ + ErrorMessage: "boom", + RequestHeaders: map[string]string{"content-type": "application/json"}, + ResponseHeaders: map[string]string{"content-type": "application/json"}, + RequestBody: map[string]any{ + "model": "gpt-4o", + "messages": []any{map[string]any{"role": "user", "content": "hello"}}, + }, + ResponseBody: map[string]any{ + "choices": []any{map[string]any{"message": map[string]any{"content": "hi"}}}, + }, + Attempts: []auditlog.AttemptSnapshot{ + { + Seq: 1, + Kind: "provider", + ProviderName: "primary-openai", + StatusCode: 500, + ErrorMessage: "upstream error", + ResponseBody: map[string]any{"error": "rate limited"}, + ResponseHeaders: map[string]string{"retry-after": "1"}, + }, + {Seq: 2, Kind: "provider", ProviderName: "primary-openai", StatusCode: 200, Success: true}, + }, + RequestRevisions: []auditlog.RequestRevisionSnapshot{ + { + Seq: 1, + Rewriter: "pro-token-compression", + BytesBefore: 2000, + BytesAfter: 1000, + TokensSaved: 250, + Body: map[string]any{"model": "gpt-4o", "messages": []any{}}, + Detail: map[string]any{"blocks_replaced": float64(3)}, + }, + }, + }, + } +} + +func TestAuditLogSlimsListEntries(t *testing.T) { + reader := &mockAuditReader{ + logResult: &auditlog.LogListResult{ + Entries: []auditlog.LogEntry{fullAuditEntry("log-1")}, + Total: 1, Limit: 25, + }, + } + h := NewHandler(nil, nil, WithAuditReader(reader)) + c, rec := newHandlerContext("/admin/audit/log?days=7") + + if err := h.AuditLog(c); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", rec.Code) + } + + var result struct { + Entries []struct { + auditlog.LogEntry + BodiesOmitted bool `json:"bodies_omitted"` + ConversationPayload bool `json:"conversation_payload"` + } `json:"entries"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + if len(result.Entries) != 1 { + t.Fatalf("expected 1 entry, got %d", len(result.Entries)) + } + entry := result.Entries[0] + d := entry.Data + if d == nil { + t.Fatal("entry data must survive slimming") + } + if d.RequestBody != nil || d.ResponseBody != nil { + t.Error("list entry bodies must be stripped") + } + if len(d.Attempts) != 2 { + t.Fatalf("attempt metadata must survive, got %d attempts", len(d.Attempts)) + } + if d.Attempts[0].ResponseBody != nil || d.Attempts[0].ResponseHeaders != nil { + t.Error("attempt error payloads must be stripped") + } + if d.Attempts[0].ErrorMessage != "upstream error" || d.Attempts[0].StatusCode != 500 { + t.Error("attempt scalar fields must survive (they drive the pips)") + } + if len(d.RequestRevisions) != 1 { + t.Fatalf("revision metadata must survive, got %d revisions", len(d.RequestRevisions)) + } + rev := d.RequestRevisions[0] + if rev.Body != nil { + t.Error("revision body must be stripped") + } + if rev.TokensSaved != 250 || rev.BytesBefore != 2000 || rev.Detail == nil { + t.Errorf("revision metadata must survive, got %+v", rev) + } + if d.ErrorMessage != "boom" || d.RequestHeaders == nil || d.ResponseHeaders == nil { + t.Error("small fields must survive slimming") + } + if !entry.BodiesOmitted { + t.Error("bodies_omitted must mark the slim entry") + } + // Path is /v1/messages: the dashboard cannot sniff drawer eligibility + // from the (removed) bodies, so the server-computed flag must carry it. + if !entry.ConversationPayload { + t.Error("conversation_payload must be set for a conversation-shaped body") + } +} + +// A body-less entry (LOGGING_LOG_BODIES=false) has nothing to strip; it must +// not claim a detail fetch is worthwhile. +func TestAuditLogLeavesBodylessEntriesUnmarked(t *testing.T) { + entry := fullAuditEntry("log-1") + entry.Data = &auditlog.LogData{ErrorMessage: "boom"} + reader := &mockAuditReader{ + logResult: &auditlog.LogListResult{Entries: []auditlog.LogEntry{entry}, Total: 1, Limit: 25}, + } + h := NewHandler(nil, nil, WithAuditReader(reader)) + c, rec := newHandlerContext("/admin/audit/log?days=7") + + if err := h.AuditLog(c); err != nil { + t.Fatalf("unexpected error: %v", err) + } + var result struct { + Entries []struct { + BodiesOmitted bool `json:"bodies_omitted"` + } `json:"entries"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + if result.Entries[0].BodiesOmitted { + t.Error("an entry with no captured payload must not be marked bodies_omitted") + } +} + +// The detail endpoint is the designated source of full payloads and must not +// be slimmed. +func TestAuditLogDetailKeepsFullPayload(t *testing.T) { + entry := fullAuditEntry("log-1") + reader := &mockAuditReader{logByID: &entry} + h := NewHandler(nil, nil, WithAuditReader(reader)) + c, rec := newHandlerContext("/admin/audit/detail?log_id=log-1") + + if err := h.AuditLogDetail(c); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", rec.Code) + } + + var got struct { + auditlog.LogEntry + BodiesOmitted bool `json:"bodies_omitted"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + d := got.Data + if d == nil || d.RequestBody == nil || d.ResponseBody == nil { + t.Fatal("detail must keep request/response bodies") + } + if d.Attempts[0].ResponseBody == nil { + t.Error("detail must keep attempt error payloads") + } + if d.RequestRevisions[0].Body == nil { + t.Error("detail must keep revision bodies") + } + if got.BodiesOmitted { + t.Error("detail entries must not be marked bodies_omitted") + } +} + +func TestAuditConversationSlimsEntries(t *testing.T) { + reader := &mockAuditReader{ + conversationResult: &auditlog.ConversationResult{ + AnchorID: "log-1", + Entries: []auditlog.LogEntry{fullAuditEntry("log-1")}, + }, + } + h := NewHandler(nil, nil, WithAuditReader(reader)) + c, rec := newHandlerContext("/admin/audit/conversation?log_id=log-1") + + if err := h.AuditConversation(c); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if rec.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", rec.Code) + } + + var result auditlog.ConversationResult + if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + d := result.Entries[0].Data + if d == nil { + t.Fatal("entry data must survive") + } + if d.RequestBody == nil || d.ResponseBody == nil { + t.Error("the transcript is built from the bodies; they must survive") + } + if d.ErrorMessage != "boom" { + t.Error("error_message feeds the drawer's error rendering; it must survive") + } + if d.Attempts != nil || d.RequestRevisions != nil || d.RequestHeaders != nil || d.ResponseHeaders != nil { + t.Errorf("attempts/revisions/headers must be stripped from conversation entries, got %+v", d) + } +} + +func TestHasConversationPayload(t *testing.T) { + tests := []struct { + name string + request any + response any + want bool + }{ + {"chat messages", map[string]any{"messages": []any{}}, nil, true}, + {"responses input", map[string]any{"input": "hello"}, nil, true}, + {"responses instructions", map[string]any{"instructions": "be brief"}, nil, true}, + {"responses chaining", map[string]any{"previous_response_id": "resp_1"}, nil, true}, + {"chat choices", nil, map[string]any{"choices": []any{}}, true}, + {"responses output", nil, map[string]any{"output": []any{map[string]any{"type": "message"}}}, true}, + {"embeddings", map[string]any{"model": "text-embedding-3-small", "dimensions": float64(256)}, map[string]any{"data": []any{}}, false}, + {"nil bodies", nil, nil, false}, + {"non-object bodies", "raw", "raw", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := hasConversationPayload(tt.request, tt.response); got != tt.want { + t.Errorf("hasConversationPayload() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/admin/dashboard/static/dist/assets/index-TxnKYqqn.js b/internal/admin/dashboard/static/dist/assets/index-Dy9UJDJP.js similarity index 98% rename from internal/admin/dashboard/static/dist/assets/index-TxnKYqqn.js rename to internal/admin/dashboard/static/dist/assets/index-Dy9UJDJP.js index df2a45ccb..97e4f0a9c 100644 --- a/internal/admin/dashboard/static/dist/assets/index-TxnKYqqn.js +++ b/internal/admin/dashboard/static/dist/assets/index-Dy9UJDJP.js @@ -10,7 +10,7 @@ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],en `)}function yX(e){let t=e&&e.request_health;return t&&typeof t==`object`?t:null}function bX(e){let t=yX(e);return t?String(t.circuit_state||``).trim():``}function xX(e){let t=bX(e);return t?t.charAt(0).toUpperCase()+t.slice(1):``}function SX(e){let t=bX(e);return t===`open`?`is-unhealthy`:t===`half-open`?`is-degraded`:`is-healthy`}function CX(e){let t=yX(e);if(!t)return``;let n=Number(t.requests||0),r=Number(t.errors||0),i=Math.round(Number(t.window_seconds||0)/60),a=i>0?`last `+i+` min`:`recent`;return String(n)+` request`+(n===1?``:`s`)+` · `+String(r)+` error`+(r===1?``:`s`)+` (`+a+`)`}function wX(e){let t=yX(e);return t&&Array.isArray(t.models)?t.models:[]}function TX(e){return e?String(Number(e.errors||0))+`/`+String(Number(e.requests||0))+` failed`:``}function EX(e){let t=e&&e.last_error;return!t||!t.message?``:(t.status_code?`HTTP `+String(t.status_code)+`: `:``)+t.message}function DX(){return{name:``,slug:``,url:``,transport:`http`,description:``,enabled:!0,headers:[],allowed_tools:``,disallowed_tools:``,user_paths:``,tool_timeout_seconds:``}}function OX(){return{server:``,status:``,instructions:``,tools:[],prompts:[],resources:[],templates:[]}}function kX(e){return String(e&&(e.slug||e.name)||``).trim()}function AX(e){return String(e&&e.status||``).trim()||`connecting`}function jX(e){switch(AX(e)){case`connected`:return`status-success`;case`degraded`:return String(e&&e.last_error||``).trim()?`status-error`:`status-warning`;case`connecting`:return`status-neutral`;default:return`status-unknown`}}function MX(e,t){let n=AX(e),r=String(e&&e.last_error||``).trim();return r&&n!==`connected`?r:n===`connected`&&e&&e.connected_at?`Connected since `+(typeof t==`function`?t:String)(e.connected_at):``}function NX(e){return String(e&&e.transport||``)===`stdio`?`local command`:String(e&&e.url||``).trim()||`—`}function PX(e){let t=Number(e&&e.prompt_count||0),n=Number(e&&e.resource_count||0);return t+` prompts · `+n+` resources`}function FX(e){let t=String(e||``).normalize(`NFKD`).toLowerCase(),n=t.replace(/[\u0300-\u036f]/g,``).replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64).replace(/-+$/g,``);if(n)return n;let r=2166136261;for(let e of t)r=Math.imul((r^e.codePointAt(0))>>>0,16777619)>>>0;return`mcp-`+r.toString(16).padStart(8,`0`)}function IX(e){return String(e||``).split(` `).map(e=>e.trim()).filter(e=>e)}function LX(e){return!e||typeof e!=`object`||Array.isArray(e)?[]:Object.keys(e).sort().map(t=>({name:t,value:String(e[t]||``)}))}function RX(e){let t={};return(Array.isArray(e)?e:[]).forEach(e=>{let n=String(e&&e.name||``).trim();n&&(t[n]=String(e&&e.value||``))}),t}function zX(e,t){let n=Array.isArray(e)?e:[];if(!t)return n;let r=String(t).toLowerCase();return n.filter(e=>[e.name,e.slug,e.url,e.transport,e.description,e.status].some(e=>String(e||``).toLowerCase().includes(r)))}function BX(e){return{name:String(e.name||``).trim(),slug:kX(e),url:String(e.url||``).trim(),transport:e.transport===`sse`?`sse`:`http`,description:String(e.description||``).trim(),enabled:e.enabled!==!1,headers:LX(e.headers),allowed_tools:(Array.isArray(e.allowed_tools)?e.allowed_tools:[]).join(`, `),disallowed_tools:(Array.isArray(e.disallowed_tools)?e.disallowed_tools:[]).join(`, `),user_paths:(Array.isArray(e.user_paths)?e.user_paths:[]).join(` `),tool_timeout_seconds:e.tool_timeout_seconds?String(e.tool_timeout_seconds):``}}function VX(e,t,n){let r=String(e.name||``).trim(),i=String(e.slug||FX(r)).trim().toLowerCase(),a=String(e.url||``).trim(),o=e.transport===`sse`?`sse`:`http`;if(!r)return{error:`Name is required.`};if(!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(i))return{error:`Slug must use 1–64 lowercase ASCII letters, numbers, hyphens, or underscores.`};if(t===`create`&&(n||[]).some(e=>kX(e)===i))return{error:`Slug "`+i+`" is already in use.`};if(!a)return{error:`URL is required.`};let s,c=String(e.tool_timeout_seconds||``).trim();if(c!==``){let e=Number(c);if(!Number.isSafeInteger(e)||e<0)return{error:`Tool timeout must be a non-negative whole number of seconds.`};s=e}return{payload:{name:r,slug:i,url:a,transport:o,headers:RX(e.headers),description:String(e.description||``).trim(),enabled:!!e.enabled,allowed_tools:zL(e.allowed_tools),disallowed_tools:zL(e.disallowed_tools),user_paths:IX(e.user_paths),tool_timeout_seconds:s}}}function HX(e,t){let n=t&&typeof t==`object`&&!Array.isArray(t)?t:{},r=e=>(Array.isArray(e)?e:[]).filter(e=>e&&typeof e==`object`);return{server:String(n.server||e||``).trim(),status:String(n.status||``).trim(),instructions:String(n.instructions||``).trim(),tools:r(n.tools),prompts:r(n.prompts),resources:r(n.resources),templates:r(n.templates)}}function UX(e,t){return String(e&&e.server||``)+`_`+String(t||``)}function WX(e){let t=e||OX(),n=(e,t)=>{let n=String(e||``).trim(),r=String(t||``).trim();return n&&r?n+` — `+r:r||n},r=e=>n=>({key:e+`:`+String(n.name||``),name:String(n.name||``),aggregated:UX(t,n.name),description:String(n.description||``).trim()});return[{key:`tools`,title:`Tools`,items:(t.tools||[]).map(r(`tool`))},{key:`prompts`,title:`Prompts`,items:(t.prompts||[]).map(r(`prompt`))},{key:`resources`,title:`Resources`,items:(t.resources||[]).map(e=>({key:`resource:`+String(e.uri||``),name:String(e.uri||``),aggregated:``,description:n(e.name,e.description)}))},{key:`templates`,title:`Resource templates`,items:(t.templates||[]).map(e=>({key:`template:`+String(e.uri_template||``),name:String(e.uri_template||``),aggregated:``,description:n(e.name,e.description)}))}].filter(e=>e.items.length>0)}function GX(e){return WX(e).length===0}function KX(e){return(e||[]).length}function qX(e){return(e||[]).filter(e=>AX(e)===`connected`).length}function JX(e){return(e||[]).filter(e=>e&&e.enabled!==!1&&AX(e)===`degraded`).length}function YX(e,t){return!!e&&KX(t)>0}function XX(e){return String(qX(e))+`/`+String(KX(e))}function ZX(e){return JX(e)>0?`is-degraded`:`is-healthy`}function QX(e){let t=JX(e);if(t>0)return String(t)+` server`+(t===1?``:`s`)+` need`+(t===1?`s`:``)+` attention`;let n=KX(e),r=qX(e);return n>0&&r===n?`All MCP servers connected`:String(r)+` of `+String(n)+` server`+(n===1?``:`s`)+` connected`}function $X(){return{interval:`day`,buckets:[],summary:{requests:0},provider_latency:[]}}function eZ(e){let t=e&&typeof e==`object`?e:{};return{interval:t.interval===`hour`?`hour`:`day`,buckets:Array.isArray(t.buckets)?t.buckets:[],summary:t.summary&&typeof t.summary==`object`?t.summary:{requests:0},provider_latency:Array.isArray(t.provider_latency)?t.provider_latency:[]}}function tZ(e){return Number(e&&e.summary&&e.summary.requests||0)>0}function nZ(e){return(e&&Array.isArray(e.provider_latency)?e.provider_latency:[]).length>0}function rZ(e){let t=e&&e.summary?e.summary.success_rate:null;return t==null?`—`:(Math.round(Number(t)*1e3)/10).toFixed(1)+`%`}function iZ(e,t){return Number(e&&e.summary&&e.summary[t]||0)}function aZ(e){let t=Number(e);return Number.isFinite(t)?t>=6e4?(t/6e4).toFixed(1)+` min`:t>=1e3?(t/1e3).toFixed(2)+` s`:Math.round(t)+` ms`:`-`}function oZ(e){let t=e&&e.summary?e.summary.avg_duration_ms:null;return t==null?`—`:aZ(Number(t))}function sZ(e,t){try{let n={};return new Intl.DateTimeFormat(`en-US`,{timeZone:t,year:`numeric`,month:`short`,day:`numeric`,hour:`2-digit`,hourCycle:`h23`}).formatToParts(e).forEach(e=>{n[e.type]=e.value}),{year:n.year,month:n.month,day:n.day,hour:Number(n.hour)}}catch{return{year:String(e.getFullYear()),month:[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`][e.getMonth()],day:String(e.getDate()),hour:e.getHours()}}}function cZ(e,t,n){let r=new Date(e.start);if(Number.isNaN(r.getTime()))return String(e.start||``);let i=sZ(r,n),a=i.month+` `+i.day;return t!==`hour`||i.hour===0?a:String(i.hour).padStart(2,`0`)+`:00`}function lZ(e,t,n,r){let i=new Date(e.start);if(Number.isNaN(i.getTime()))return String(e.start||``);if(t===`hour`)return r(e.start);let a=sZ(i,n);return a.month+` `+a.day+`, `+a.year}function uZ(e){return{ok:e(`var(--success)`),clientError:e(`var(--warning)`),serverError:e(`var(--danger)`),other:e(`color-mix(in srgb, var(--text-muted) 55%, transparent)`)}}function dZ(e,t,n={}){let r=n.interval===`hour`?`hour`:`day`,i=n.zone,a=n.resolve||(e=>e),o=n.formatTimestamp||(e=>String(e)),s=t.map(e=>cZ(e,r,i)),c=uZ(a),l=a(`var(--bg-surface)`),u=e=>Number(e)||0,d=(e,t,n)=>({label:e,data:t,backgroundColor:n,borderColor:l,borderWidth:1,borderSkipped:!1,borderRadius:2,maxBarThickness:28}),f=[d(`2xx`,t.map(e=>u(e.status_2xx)),c.ok),d(`4xx`,t.map(e=>u(e.status_4xx)),c.clientError),d(`5xx`,t.map(e=>u(e.status_5xx)),c.serverError)];return t.some(e=>u(e.status_other)>0)&&f.push(d(`Other`,t.map(e=>u(e.status_other)),c.other)),{type:`bar`,data:{labels:s,datasets:f},options:{responsive:!0,maintainAspectRatio:!1,animation:{duration:0},interaction:{mode:`index`,intersect:!1},plugins:{legend:{labels:{color:e.text,font:{size:12}}},tooltip:nY(e,{title:e=>e.length?lZ(t[e[0].dataIndex],r,i,o):``,label:e=>e.dataset.label+`: `+e.parsed.y.toLocaleString(),footer:e=>{let t=0;return e.forEach(e=>{t+=Number(e.parsed.y)||0}),`Total: `+t.toLocaleString()}})},scales:{x:{stacked:!0,grid:{display:!1},border:{display:!1},ticks:{color:e.text,font:tY(),maxRotation:0,autoSkip:!0,maxTicksLimit:12}},y:{stacked:!0,beginAtZero:!0,grid:{color:e.grid},border:{display:!1},ticks:{color:e.text,font:tY(),precision:0,callback:e=>WL(e)}}}}}}function fZ(e=aY()){let t={};return function(n){return n in t||(t[n]=e[Object.keys(t).length%e.length]),t[n]}}function pZ(e,t,n,r={}){let i=r.interval===`hour`?`hour`:`day`,a=r.zone,o=r.formatTimestamp||(e=>String(e)),s=r.providerColor||fZ();return{type:`line`,data:{labels:t.map(e=>cZ(e,i,a)),datasets:n.map(e=>({label:e.provider,data:(e.avg_duration_ms||[]).map(e=>e==null?null:Number(e)),borderColor:s(e.provider),backgroundColor:s(e.provider),fill:!1,tension:.3,borderWidth:2,pointRadius:0,pointHoverRadius:4,spanGaps:i===`hour`&&2}))},options:{responsive:!0,maintainAspectRatio:!1,animation:{duration:0},interaction:{mode:`index`,intersect:!1},plugins:{legend:{labels:{color:e.text,font:{size:12}}},tooltip:nY(e,{title:e=>e.length?lZ(t[e[0].dataIndex],i,a,o):``,label:e=>{let t=(n[e.datasetIndex]&&n[e.datasetIndex].requests||[])[e.dataIndex],r=Number(t)||0;return e.dataset.label+`: `+aZ(e.parsed.y)+(r>0?` (`+r.toLocaleString()+` req)`:``)}})},scales:{x:{grid:{color:e.grid},border:{display:!1},ticks:{color:e.text,font:tY(),maxRotation:0,autoSkip:!0,maxTicksLimit:12}},y:{beginAtZero:!0,grid:{color:e.grid},border:{display:!1},ticks:{color:e.text,font:tY(),callback:e=>aZ(e)}}}}}}var mZ=class{#e=A(fn($Y()));get status(){return F(this.#e)}set status(e){j(this.#e,e,!0)}#t=A(!1);get loading(){return F(this.#t)}set loading(e){j(this.#t,e,!0)}#n=A(!1);get loadedOnce(){return F(this.#n)}set loadedOnce(e){j(this.#n,e,!0)}#r=A(!1);get detailsExpanded(){return F(this.#r)}set detailsExpanded(e){j(this.#r,e,!0)}#i=A(fn({}));get cardOverrides(){return F(this.#i)}set cardOverrides(e){j(this.#i,e,!0)}#a=null;#o=null;#s=!1;initPreferences(){if(this.#s)return;this.#s=!0;let e=eX(bI());this.detailsExpanded=e.detailsExpanded,this.cardOverrides=e.cardOverrides}cardExpanded(e){return rX(this.cardOverrides,this.detailsExpanded,e)}toggleCard(e){let t=e&&e.name?String(e.name):``;if(!t)return;let n={...this.cardOverrides};n[t]=!this.cardExpanded(e),this.cardOverrides=n,nX(bI(),this.cardOverrides)}toggleDetails(){this.detailsExpanded=!this.detailsExpanded,this.cardOverrides={},tX(bI(),this.detailsExpanded),nX(bI(),this.cardOverrides)}detailsToggleLabel(){return this.detailsExpanded?`Show Details`:`Hide Details`}async fetch(){this.initPreferences(),this.#a&&this.#a.abort();let e=new AbortController;this.#a=e,this.loading=!0;try{let t=await tL(`/admin/providers/status`,{label:`provider status`,signal:e.signal});if(t.stale||e.signal.aborted)return;if(!t.ok){this.status=$Y(),this.#l();return}let n=t.data&&typeof t.data==`object`?t.data:$Y();n.summary||=$Y().summary,Array.isArray(n.providers)||(n.providers=[]),this.status=n,this.#c()}catch(e){if(rL(e))return;console.error(`Failed to fetch provider status:`,e),this.status=$Y(),this.#l()}finally{this.#a===e&&(this.#a=null,this.loading=!1,this.loadedOnce=!0)}}#c(){this.#l(),lX(this.status.providers)&&(this.#o=setTimeout(()=>{this.#o=null,this.fetch()},XY))}#l(){this.#o&&=(clearTimeout(this.#o),null)}stopPolling(){this.#l()}},hZ=class{#e=A(fn($X()));get stats(){return F(this.#e)}set stats(e){j(this.#e,e,!0)}#t=A(!1);get loading(){return F(this.#t)}set loading(e){j(this.#t,e,!0)}#n=0;async fetch(){let e=++this.#n;this.loading=!0;try{let t=await tL(`/admin/audit/stats?`+tR.queryStr(),{label:`audit stats`});if(t.stale||e!==this.#n)return;if(!t.ok){this.stats=$X();return}this.stats=eZ(t.data)}catch(t){if(console.error(`Failed to fetch audit stats:`,t),e!==this.#n)return;this.stats=$X()}finally{e===this.#n&&(this.loading=!1)}}},gZ=class{#e=A(fn([]));get servers(){return F(this.#e)}set servers(e){j(this.#e,e,!0)}#t=A(!1);get available(){return F(this.#t)}set available(e){j(this.#t,e,!0)}#n=A(!1);get loading(){return F(this.#n)}set loading(e){j(this.#n,e,!0)}async fetch(){if(await aL.ensureLoaded(),!aL.mcpVisible()){this.available=!1,this.servers=[];return}this.loading=!0;try{let e=await tL(`/admin/mcp-servers`,{label:`mcp servers`});if(e.stale)return;if(e.status===503||e.status===404){this.available=!1,this.servers=[];return}if(this.available=!0,!e.ok){this.servers=[];return}this.servers=Array.isArray(e.data)?e.data:[]}catch(e){console.error(`Failed to fetch MCP servers:`,e),this.servers=[]}finally{this.loading=!1}}},_Z=class{#e=A(fn([]));get data(){return F(this.#e)}set data(e){j(this.#e,e,!0)}#t=A(`tokens`);get mode(){return F(this.#t)}set mode(e){j(this.#t,e,!0)}#n=A(!1);get loading(){return F(this.#n)}set loading(e){j(this.#n,e,!0)}#r=null;async fetch(){this.#r&&this.#r.abort();let e=new AbortController;this.#r=e,this.loading=!0;try{let t=await tL(`/admin/usage/daily?days=365&interval=daily`,{label:`calendar`,signal:e.signal});if(t.stale||e.signal.aborted)return;if(!t.ok){this.data=[];return}this.data=Array.isArray(t.data)?t.data:[]}catch(e){if(rL(e))return;console.error(`Failed to fetch calendar data:`,e),this.data=[]}finally{this.#r===e&&(this.#r=null,this.loading=!1)}}},vZ=new mZ,yZ=new hZ,bZ=new gZ,xZ=new _Z,SZ=L(`
Cache Hits
`),CZ=L(`
Local Cache
i + o =
`),wZ=L(``),TZ=L(` `),EZ=L(`
Provider Status
`),DZ=L(`
MCP Servers
`),OZ=L(`
Tokens
i + o =
Total Requests
Estimated Cost
Prompt Cache Rate
`);function kZ(e,t){D(t,!0);let n=k(()=>iR.summary),r=k(()=>iR.cacheOverview),i=k(()=>iR.cacheAnalyticsEnabled()),a=k(()=>vZ.status.summary);function o(){let e=document.getElementById(`provider-status-section`);e&&(e.scrollIntoView({behavior:`smooth`,block:`start`}),e.focus({preventScroll:!0}))}var s=OZ(),c=M(s),l=N(M(c),2),u=M(l),d=M(u),f=M(d,!0);E(d),We(),E(u);var p=N(u,4),m=M(p),h=M(m,!0);E(m),We(),E(p);var g=N(p,4),_=M(g,!0);E(g),E(l),E(c);var v=N(c,2),y=N(M(v),2),b=M(y,!0);E(y),E(v);var x=N(v,2),S=e=>{var t=SZ(),n=N(M(t),2),i=M(n,!0);E(n),E(t),P(e=>z(i,e),[()=>BL(F(r).summary.total_hits)]),R(e,t)};B(x,e=>{F(i)&&e(S)});var C=N(x,2),w=N(M(C),2),T=M(w,!0);E(w),E(C);var ee=N(C,2),te=e=>{var t=CZ(),n=N(M(t),2),i=M(n),a=M(i),o=M(a,!0);E(a),We(),E(i);var s=N(i,4),c=M(s),l=M(c,!0);E(c),We(),E(s);var u=N(s,4),d=M(u,!0);E(u),E(n),E(t),P((e,t,n,r,a,c)=>{U(i,`title`,e),z(o,t),U(s,`title`,n),z(l,r),U(u,`title`,a),z(d,c)},[()=>GL(`Input tokens`,F(r).summary.total_input_tokens),()=>WL(F(r).summary.total_input_tokens),()=>GL(`Output tokens`,F(r).summary.total_output_tokens),()=>WL(F(r).summary.total_output_tokens),()=>GL(`Total tokens`,MY(F(r))),()=>WL(MY(F(r)))]),R(e,t)};B(ee,e=>{F(i)&&e(te)});var ne=N(ee,2),re=N(M(ne),2),ie=M(re);YJ(ie,{build:()=>qY(UY(F(n)),rY(`var(--token-prompt)`),rY(`var(--bg-surface-hover)`))});var ae=N(ie,2),oe=M(ae,!0);E(ae),E(re),E(ne);var se=N(ne,2),ce=e=>{var t=EZ(),n=N(M(t),2),r=M(n,!0);E(n);var i=N(n,2),s=e=>{var t=wZ(),n=M(t,!0);E(t),P(e=>z(n,e),[()=>cX(F(a))]),I(`click`,t,o),R(e,t)},c=k(()=>sX(F(a))),l=e=>{var t=TZ(),n=M(t,!0);E(t),P(e=>z(n,e),[()=>cX(F(a))]),R(e,t)};B(i,e=>{F(c)?e(s):e(l,-1)}),E(t),P((e,n)=>{H(t,1,`card provider-status-flag provider-status-overview-card ${e??``}`,`svelte-6tr9cf`),z(r,n)},[()=>iX(F(a)),()=>oX(F(a))]),R(e,t)};B(se,e=>{F(a).total>0&&e(ce)});var le=N(se,2),ue=e=>{var t=DZ(),n=N(M(t),2),r=M(n,!0);E(n);var i=N(n,2),a=M(i,!0);E(i),E(t),P((e,n,i)=>{H(t,1,`card provider-status-flag mcp-servers-flag ${e??``}`,`svelte-6tr9cf`),z(r,n),z(a,i)},[()=>ZX(bZ.servers),()=>XX(bZ.servers),()=>QX(bZ.servers)]),I(`click`,i,()=>LI.navigate(`mcp-servers`)),R(e,t)},de=k(()=>YX(bZ.available,bZ.servers));B(le,e=>{F(de)&&e(ue)}),E(s),P((e,t,n,r,i,a,o,s,c,l,d)=>{U(u,`title`,e),z(f,t),U(p,`title`,n),z(h,r),U(g,`title`,i),z(_,a),U(y,`title`,o),z(b,s),z(T,c),U(re,`aria-label`,l),z(oe,d)},[()=>GL(`Input tokens`,F(n).total_input_tokens),()=>WL(F(n).total_input_tokens),()=>GL(`Output tokens`,F(n).total_output_tokens),()=>WL(F(n).total_output_tokens),()=>GL(`Total tokens`,OY(F(n))),()=>WL(OY(F(n))),()=>jY(F(n),F(r),F(i)),()=>BL(AY(F(n),F(r),F(i))),()=>VL(F(n).total_cost),()=>`Prompt cache rate `+GY(F(n)),()=>GY(F(n))]),R(e,s),O()}Ur([`click`]);var AZ=L(` `),jZ=L(`
`),MZ=L(`No usage in the selected period yet`),NZ=L(`
`),PZ=L(`

Tokens

Share of input tokens over the selected period
`);function FZ(e,t){D(t,!0);let n=k(()=>iR.cacheAnalyticsEnabled()),r=k(()=>IY(iR.summary,iR.cacheOverview,F(n))),i=k(()=>LY(iR.summary,iR.cacheOverview,F(n))),a=k(()=>FY(iR.summary,iR.cacheOverview,F(n)));var o=PZ(),s=N(M(o),2);let c;var l=M(s);V(l,17,()=>F(i),e=>e.key,(e,t)=>{var n=jZ(),r=M(n),i=e=>{var n=AZ(),r=M(n);E(n),P(()=>z(r,`${F(t).pct??``}%`)),R(e,n)};B(r,e=>{F(t).pct>=8&&e(i)}),E(n),P(e=>{Ri(n,`width: ${F(t).pct??``}%; background: var(${F(t).colorVar??``})`),U(n,`title`,e)},[()=>RY(F(t))]),R(e,n)});var u=N(l,2),d=e=>{R(e,MZ())};B(u,e=>{F(a)||e(d)}),E(s);var f=N(s,2);V(f,21,()=>F(r),e=>e.key,(e,t)=>{var n=NZ(),r=M(n),i=N(r,2),a=M(i,!0);E(i);var o=N(i,2),s=M(o);E(o);var c=N(o,2),l=M(c,!0);E(c),E(n),P((e,i)=>{U(n,`title`,e),Ri(r,`background: var(${F(t).colorVar??``})`),z(a,F(t).label),z(s,`${F(t).pct??``}%`),z(l,i)},[()=>RY(F(t)),()=>BL(F(t).tokens)]),R(e,n)}),E(f),E(o),P(e=>{c=H(s,1,`cache-meter-bar svelte-1yzecxj`,null,c,{"is-empty":!F(a)}),U(s,`aria-label`,e)},[()=>zY(F(i))]),R(e,o),O()}var IZ=L(``);function LZ(e,t){let n=ha(t,`size`,3,16),r=ha(t,`label`,3,`Loading`),i=ha(t,`class`,3,``);var a=IZ();P(()=>{H(a,1,`spinner ${i()??``}`,`svelte-b54l9o`),Ri(a,`--spinner-size: ${n()??``}px`),U(a,`aria-label`,r())}),R(e,a)}var RZ=Zr(` `),zZ=Zr(``);function BZ(e,t){let n=ha(t,`label`,3,`No data`);var r=zZ(),i=N(M(r),9),a=e=>{var t=RZ(),r=M(t,!0);E(t),P(()=>z(r,n())),R(e,t)};B(i,e=>{n()&&e(a)}),E(r),P(()=>{U(r,`role`,n()?`img`:void 0),U(r,`aria-label`,n()||void 0),U(r,`aria-hidden`,n()?void 0:`true`)}),R(e,r)}var VZ=L(`
`),HZ=L(`

`);function UZ(e,t){D(t,!0);let n=[`daily`,`weekly`,`monthly`,`yearly`];function r(e){tR.interval=e,t.onintervalchange?.()}function i(){let e=iR.daily;if(e.length===0)return null;let t=tR.rangeStart(),n=tR.rangeEnd(),r=HY(VY(e,tR.interval,t,n),VY(Array.isArray(iR.cacheOverview.daily)?iR.cacheOverview.daily:[],tR.interval,t,n));return KY(eY(),r,{cacheEnabled:iR.cacheAnalyticsEnabled(),resolve:rY})}var a=HZ(),o=M(a),s=M(o),c=M(s,!0);E(s);var l=N(s,2);{let e=k(()=>n.map(e=>({value:e,label:e.charAt(0).toUpperCase()+e.slice(1)})));QJ(l,{ariaLabel:`Usage chart interval`,get options(){return F(e)},get value(){return tR.interval},onchange:r})}E(o);var u=N(o,2),d=M(u);YJ(d,{build:i});var f=N(d,2),p=e=>{var t=VZ();LZ(M(t),{size:24,label:`Loading usage`}),E(t),R(e,t)},m=e=>{var t=VZ();BZ(M(t),{}),E(t),R(e,t)};B(f,e=>{iR.daily.length===0&&iR.loading?e(p):iR.daily.length===0&&!G.authError&&e(m,1)}),E(u),E(a),P(e=>z(c,e),[()=>tR.chartTitle()]),R(e,a),O()}var WZ=10,GZ=.7;function KZ(e){return String(e).padStart(2,`0`)}function qZ(e){if(!e)return null;let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);return t?new Date(Date.UTC(Number(t[1]),Number(t[2])-1,Number(t[3]))):null}function JZ(e){return!e||typeof e.getTime!=`function`||Number.isNaN(e.getTime())?``:e.getUTCFullYear()+`-`+KZ(e.getUTCMonth()+1)+`-`+KZ(e.getUTCDate())}function YZ(e,t){let n=qZ(e);return n?(n.setUTCDate(n.getUTCDate()+t),JZ(n)):``}function XZ(e,t){if(e<=0||t<=0)return 0;let n=(e/t)**+GZ,r=Math.ceil(n*WZ);return r<1?1:r>WZ?WZ:r}function ZZ(){let e=[];for(let t=0;t<=WZ;t++)e.push(t);return e}function QZ(e,t,n){let r={};(e||[]).forEach(e=>{r[e.date]=e});let i=qZ(YZ(n,-364)),a=i.getUTCDay();i.setUTCDate(i.getUTCDate()-a);let o=[];for(let e=new Date(i);JZ(e)<=n;e.setUTCDate(e.getUTCDate()+1)){let n=JZ(e),i=r[n],a=0;i&&(a=t===`costs`?i.total_cost==null?0:i.total_cost:i.total_tokens||0),o.push({dateStr:n,value:a,level:0,empty:!1})}let s=0;for(let e=0;es&&(s=o[e].value);for(let e=0;e0){for(;l.length<7;)l.push({dateStr:``,value:0,level:0,empty:!0});c.push(l)}return c}function $Z(e){let t=qZ(YZ(e,-364)),n=t.getUTCDay();t.setUTCDate(t.getUTCDate()-n);let r=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],i=[],a={},o=0;for(let n=new Date(t);JZ(n)<=e;n.setUTCDate(n.getUTCDate()+7),o++){let t=null;if(o===0)t=new Date(n);else for(let r=0;r<7;r++){let i=new Date(n);if(i.setUTCDate(n.getUTCDate()+r),JZ(i)>e)break;if(i.getUTCDate()===1){t=i;break}}if(!t)continue;let s=t.getUTCFullYear()+`-`+t.getUTCMonth();a[s]||(i.push({label:r[t.getUTCMonth()],col:o,key:s}),a[s]=!0)}for(let e=0;e `),rQ=L(`
`),iQ=L(`
`),aQ=L(`
`),oQ=L(`
`),sQ=L(`

Activity

Mon Wed Fri
`,1);function cQ(e,t){D(t,!0);let n=A(fn({show:!1,x:0,y:0,text:``})),r=k(()=>YI.currentDateKey()),i=k(()=>QZ(xZ.data,xZ.mode,F(r))),a=k(()=>$Z(F(r)));function o(e,t){t.empty||j(n,{show:!0,x:e.clientX,y:e.clientY,text:tQ(t,xZ.mode)},!0)}function s(){j(n,{show:!1,x:0,y:0,text:``},!0)}var c=sQ(),l=Cn(c),u=M(l),d=N(M(u),2),f=e=>{LZ(e,{size:14,label:`Loading activity`})};B(d,e=>{xZ.loading&&xZ.data.length===0&&e(f)}),QJ(N(d,2),{ariaLabel:`Activity calendar mode`,options:[{value:`tokens`,label:`Tokens`},{value:`costs`,label:`Costs`}],get value(){return xZ.mode},onchange:e=>xZ.mode=e}),E(u);var p=N(u,2),m=N(M(p),2),h=M(m);V(h,21,()=>F(a),e=>e.key,(e,t)=>{var n=nQ(),r=M(n,!0);E(n),P(()=>{Ri(n,`grid-column: ${F(t).col+1} / span ${F(t).span??``}`),z(r,F(t).label)}),R(e,n)}),E(h);var g=N(h,2);V(g,21,()=>F(i),oi,(e,t,n)=>{var r=iQ();V(r,23,()=>F(t),(e,t)=>n+`-`+t,(e,t)=>{var n=rQ();P(()=>H(n,1,`contribution-calendar-cell ${F(t).empty?`empty`:`level-`+F(t).level}`,`svelte-3hfxuq`)),Hr(`mouseenter`,n,e=>o(e,F(t))),Hr(`mouseleave`,n,s),R(e,n)}),E(r),R(e,r)}),E(g),E(m),E(p);var _=N(p,2),v=M(_),y=M(v),b=M(y,!0);E(y),E(v);var x=N(v,2);V(N(M(x),2),16,ZZ,e=>e,(e,t)=>{var n=aQ();P(()=>H(n,1,`contribution-calendar-cell level-${t??``}`,`svelte-3hfxuq`)),R(e,n)}),We(2),E(x),E(_),E(l);var S=N(l,2),C=e=>{var t=oQ(),r=M(t,!0);E(t),P(()=>{Ri(t,`left: ${F(n).x??``}px; top: ${F(n).y-40}px`),z(r,F(n).text)}),R(e,t)};B(S,e=>{F(n).show&&e(C)}),P(e=>z(b,e),[()=>eQ(xZ.data,xZ.mode)]),R(e,c),O()}var lQ=L(``),uQ=L(`

`),dQ=L(`
`);function fQ(e,t){D(t,!0);let n=ha(t,`label`,3,`help`),r=ha(t,`text`,3,``),i=ha(t,`open`,15,!1),a=ha(t,`external`,3,!1),o=k(()=>!!r()||!!t.help||a());var s=dQ(),c=M(s),l=M(c);gi(l,()=>t.title??m);var u=N(l,2),d=e=>{var r=lQ();let a;P(()=>{a=H(r,1,`inline-help-toggle svelte-y40or3`,null,a,{"is-open":i()}),U(r,`aria-label`,(i()?`Hide `:`Show `)+n()),U(r,`aria-expanded`,i()),U(r,`aria-controls`,t.copyId)}),I(`click`,r,()=>i(!i())),R(e,r)};B(u,e=>{F(o)&&e(d)}),gi(N(u,2),()=>t.extra??m),E(c);var f=N(c,2),p=e=>{var n=uQ(),i=M(n),a=e=>{var n=$r();gi(Cn(n),()=>t.help),R(e,n)},o=e=>{var t=Qr();P(()=>z(t,r())),R(e,t)};B(i,e=>{t.help?e(a):e(o,-1)}),E(n),P(()=>U(n,`id`,t.copyId)),R(e,n)};B(f,e=>{i()&&F(o)&&!a()&&e(p)}),E(s),R(e,s),O()}Ur([`click`]);var pQ=L(`

Provider Latency

`),mQ=L(`
Avg
`),hQ=L(`

Requests by Status

Success 2xx 4xx 5xx
`,1);function gQ(e,t){D(t,!0);let n=fZ(),r=k(()=>yZ.stats);function i(){return{interval:F(r).interval,zone:YI.effectiveTimezone(),resolve:rY,formatTimestamp:e=>YI.formatTimestamp(e)}}var a=$r(),o=Cn(a),s=e=>{var t=hQ(),a=Cn(t),o=M(a),s=N(M(o),2),c=M(s),l=N(M(c),2),u=M(l,!0);E(l),E(c);var d=N(c,2),f=N(M(d),4),p=M(f,!0);E(f),E(d);var m=N(d,2),h=N(M(m),4),g=M(h,!0);E(h),E(m);var _=N(m,2),v=N(M(_),4),y=M(v,!0);E(v),E(_),E(s),E(o);var b=N(o,2);YJ(M(b),{build:()=>dZ(eY(),F(r).buckets,i())}),E(b),E(a);var x=N(a,2),S=e=>{var t=mQ(),a=M(t),o=M(a);fQ(o,{copyId:`audit-latency-help-copy`,label:`provider latency help`,text:`Average duration of successful requests as measured at the gateway, per provider. Local cache hits and failed requests are excluded; streamed responses count until the stream completes.`,title:e=>{R(e,pQ())},$$slots:{title:!0}});var s=N(o,2),c=M(s),l=N(M(c),2),u=M(l,!0);E(l),E(c),E(s),E(a);var d=N(a,2);YJ(M(d),{build:()=>pZ(eY(),F(r).buckets,F(r).provider_latency,{...i(),providerColor:n})}),E(d),E(t),P(e=>z(u,e),[()=>oZ(F(r))]),R(e,t)},C=k(()=>nZ(F(r)));B(x,e=>{F(C)&&e(S)}),P((e,t,n,r)=>{z(u,e),z(p,t),z(g,n),z(y,r)},[()=>rZ(F(r)),()=>BL(iZ(F(r),`status_2xx`)),()=>BL(iZ(F(r),`status_4xx`)),()=>BL(iZ(F(r),`status_5xx`))]),R(e,t)},c=k(()=>tZ(F(r)));B(o,e=>{F(c)&&e(s)}),R(e,a),O()}var _Q=(e,t=m,n=m,r)=>{let i=kt(()=>_(r?.(),!1));var a=bQ(),o=M(a),s=M(o,!0);E(o);var c=N(o,2),l=e=>{var t=vQ(),r=M(t,!0);E(t),P(()=>z(r,n())),R(e,t)},u=e=>{var t=yQ(),r=M(t,!0);E(t),P(()=>z(r,n())),R(e,t)};B(c,e=>{F(i)?e(l):e(u,-1)}),E(a),P(()=>z(s,t())),R(e,a)},vQ=L(` `),yQ=L(` `),bQ=L(`
`),xQ=L(` `),SQ=L(``),CQ=L(`

`),wQ=L(`
Breaker State
`),TQ=L(`
`),EQ=L(`
Models (Recent Traffic)
`),DQ=L(`
`),OQ=L(`

Models Available
Last Checked

`);function kQ(e,t){D(t,!0);let n=k(()=>vZ.cardExpanded(t.provider)),r=e=>YI.formatTimestamp(e),i=k(()=>[[`Base URL`,t.provider.config?.base_url],[`API Version`,t.provider.config?.api_version]].filter(([,e])=>!!e));var a=OQ(),o=M(a),s=M(o),c=M(s),l=M(c),u=M(l,!0);E(l);var d=N(l,2),f=e=>{var n=xQ(),r=M(n);E(n),P(e=>z(r,`(${e??``})`),[()=>pX(t.provider)]),R(e,n)},p=k(()=>pX(t.provider));B(d,e=>{F(p)&&e(f)});var m=N(d,2),h=e=>{var n=SQ();P((e,t,r)=>{U(n,`href`,e),U(n,`aria-label`,t),U(n,`title`,r)},[()=>mX(t.provider),()=>`View `+(pX(t.provider)||t.provider.name)+` provider docs`,()=>`View `+(pX(t.provider)||t.provider.name)+` provider docs`]),R(e,n)},g=k(()=>mX(t.provider));B(m,e=>{F(g)&&e(h)}),E(c),E(s);var _=N(s,2),y=M(_,!0);E(_),E(o);var b=N(o,2),x=M(b),S=N(M(x),2),C=M(S,!0);E(S),E(x);var w=N(x,2),T=N(M(w),2),ee=M(T,!0);E(T),E(w),E(b);var te=N(b,2);let ne;var re=M(te),ie=M(re),ae=M(ie,!0);E(ie);var oe=N(ie,2),se=e=>{var n=CQ(),r=M(n,!0);E(n),P(()=>z(r,t.provider.last_error)),R(e,n)};B(oe,e=>{t.provider.last_error&&e(se)});var ce=N(oe,2),le=e=>{var n=DQ(),r=M(n);{let e=k(()=>CX(t.provider));_Q(r,()=>`Recent Requests`,()=>F(e))}var i=N(r,2),a=e=>{var n=wQ(),r=N(M(n),2),i=M(r),a=M(i,!0);E(i),E(r),E(n),P((e,t)=>{H(i,1,`provider-status-health-state ${e??``}`,`svelte-nopjmh`),z(a,t)},[()=>SX(t.provider),()=>xX(t.provider)]),R(e,n)},o=k(()=>bX(t.provider));B(i,e=>{F(o)&&e(a)});var s=N(i,2),c=e=>{var n=EQ(),r=N(M(n),2);V(r,21,()=>wX(t.provider),e=>e.model,(e,t)=>{var n=TQ();let r;var i=M(n),a=M(i,!0);E(i);var o=N(i,2),s=M(o,!0);E(o),E(n),P((e,i)=>{r=H(n,1,`provider-status-health-model svelte-nopjmh`,null,r,{"is-flagged":F(t).flagged}),U(n,`title`,e),z(a,F(t).model),z(s,i)},[()=>EX(F(t)),()=>TX(F(t))]),R(e,n)}),E(r),E(n),R(e,n)},l=k(()=>wX(t.provider).length>0);B(s,e=>{F(l)&&e(c)}),E(n),R(e,n)},ue=k(()=>yX(t.provider));B(ce,e=>{F(ue)&&e(le)});var de=N(ce,2),fe=M(de);V(fe,17,()=>F(i),([e,t])=>e,(e,t)=>{var n=k(()=>v(F(t),2));_Q(e,()=>F(n)[0],()=>F(n)[1],()=>!0)});var pe=N(fe,2);{let e=k(()=>_X(t.provider));_Q(pe,()=>`Configured Models`,()=>F(e))}var me=N(pe,2);{let e=k(()=>hX(t.provider));_Q(me,()=>`Retry`,()=>F(e))}var he=N(me,2);{let e=k(()=>gX(t.provider));_Q(he,()=>`Circuit Breaker`,()=>F(e))}E(de),E(re),E(te);var ge=N(te,2);let _e;W(M(ge),{name:`chevron-down`,class:`provider-status-card-toggle-icon`}),E(ge),E(a),P((e,r,i,a,o)=>{z(u,t.provider.name),H(_,1,`provider-status-pill ${e??``}`,`svelte-nopjmh`),U(_,`title`,r),z(y,t.provider.status_label),z(C,i),U(T,`title`,a),z(ee,o),ne=H(te,1,`provider-status-details svelte-nopjmh`,null,ne,{"is-expanded":F(n),"is-collapsed":!F(n)}),U(te,`aria-hidden`,!F(n)),z(ae,t.provider.status_reason),_e=H(ge,1,`provider-status-card-toggle svelte-nopjmh`,null,_e,{"is-expanded":F(n)}),U(ge,`aria-expanded`,F(n)),U(ge,`aria-label`,(F(n)?`Collapse `:`Expand `)+t.provider.name+` details`),U(ge,`title`,F(n)?`Collapse details`:`Expand details`)},[()=>aX(t.provider.status),()=>vX(t.provider),()=>BL(t.provider.runtime?.discovered_model_count),()=>fX(t.provider,r),()=>dX(t.provider,r)]),I(`click`,ge,()=>vZ.toggleCard(t.provider)),R(e,a),O()}Ur([`click`]);var AQ=L(`

Providers Overview

`),jQ=L(`
`);function MQ(e,t){D(t,!0);let n=k(()=>vZ.status.providers);var r=$r(),i=Cn(r),a=e=>{var t=AQ(),r=M(t),i=N(M(r),2),a=M(i),o=M(a,!0);E(a);var s=N(a,2);let c;E(i),E(r);var l=N(r,2);V(l,21,()=>F(n),e=>e.name,(e,t)=>{kQ(e,{get provider(){return F(t)}})}),E(l),E(t),P((e,t)=>{U(i,`aria-checked`,vZ.detailsExpanded),U(i,`title`,e),z(o,t),c=H(s,1,`provider-status-toggle-track svelte-1kx3uw4`,null,c,{"is-active":vZ.detailsExpanded})},[()=>vZ.detailsToggleLabel(),()=>vZ.detailsToggleLabel()]),I(`click`,i,()=>vZ.toggleDetails()),R(e,t)},o=e=>{var t=jQ();LZ(M(t),{size:18,label:`Loading provider status`}),E(t),R(e,t)};B(i,e=>{F(n).length>0?e(a):vZ.loading&&!vZ.loadedOnce&&e(o,1)}),R(e,r),O()}Ur([`click`]);var NQ=L(`
`);function PQ(e,t){D(t,!0);function n(){iR.fetchUsage(),iR.fetchCacheOverview(``),yZ.fetch(),vZ.fetch(),bZ.fetch(),xZ.fetch()}function r(){iR.fetchUsage(),iR.fetchCacheOverview(``),yZ.fetch()}function i(){r(),xZ.fetch()}Nn(()=>{if(G.refreshTick,LI.page===`overview`)return kr(()=>{n(),CY.start()}),()=>{CY.stop(),vZ.stopPolling()}});var a=NQ(),o=M(a);DY(o,{});var s=N(o,4);xR(M(s),{onchange:i}),E(s);var c=N(s,2);RL(c,{});var l=N(c,2);kZ(l,{});var u=N(l,2);FZ(u,{});var d=N(u,2);UZ(d,{onintervalchange:r});var f=N(d,2);cQ(f,{});var p=N(f,2);gQ(p,{}),MQ(N(p,2),{}),E(a),R(e,a),O()}var FQ=`/admin/live/logs?types=audit,usage`;function IQ(e){let t=FQ,n=Number(e||0);return Number.isFinite(n)&&n>0&&(t+=`&cursor=`+encodeURIComponent(String(n))),t}function LQ(){return{async consumeLiveLogsBody(e){let t=new TextDecoder,n=``;for(;;){let r=await e.read();if(r.done)break;n+=t.decode(r.value,{stream:!0});let i;for(;i=n.match(/\r?\n\r?\n/);){let e=i.index,t=n.slice(0,e);n=n.slice(e+i[0].length),this.handleLiveLogsFrame(t)}}n+=t.decode(),n.trim()&&this.handleLiveLogsFrame(n)},handleLiveLogsFrame(e){let t=String(e||``).split(/\r?\n/),n=[];for(let e of t)e.indexOf(`data:`)===0&&n.push(e.slice(5).trimStart());if(n.length===0)return;let r;try{r=JSON.parse(n.join(` -`))}catch{return}this.applyLiveLogEvent(r)},applyLiveLogEvent(e){if(!e||typeof e!=`object`)return;let t=Number(e.seq||0);Number.isFinite(t)&&t>this.liveLogsLastSeq&&(this.liveLogsLastSeq=t);let n=String(e.type||``).trim();if(n!==`heartbeat`){if(n===`reset`){this.reloadLiveLogSources();return}if(n===`audit.removed`){this.removeLiveAuditEntry(e.data);return}if(n.indexOf(`audit.`)===0){this.mergeLiveAuditEntry(e.data||{},n);return}n.indexOf(`usage.`)===0&&(this.mergeLiveUsageEntry(e.data||{},n),typeof this.noteLiveTokenUsage==`function`&&this.noteLiveTokenUsage(n))}},reloadLiveLogSources(){typeof this.fetchUsage==`function`&&this.fetchUsage(),this.page===`audit-logs`&&typeof this.fetchAuditLog==`function`&&this.fetchAuditLog(!0)},auditLiveInsertAllowed(){return this.auditLog&&this.auditLog.offset===0&&!this.auditSearch&&!this.auditMethod&&!this.auditStatusCode&&!this.auditStream&&!this.customStartDate&&!this.customEndDate},usageLiveInsertAllowed(){return this.usageLog&&this.usageLog.offset===0&&!this.usageLogSearch&&!this.usageFilterModel&&!this.usageFilterProvider&&!this.usageFilterLabel&&!this.usageFilterUserPath},mergeLiveAuditEntry(e,t){if(!e||typeof e!=`object`)return;let n=String(e.id||e.request_id||``).trim();if(!n)return;let r=this.auditLog&&Array.isArray(this.auditLog.entries)?this.auditLog.entries:[],i=r.findIndex(t=>String(t.id||``).trim()===n||e.request_id&&String(t.request_id||``).trim()===String(e.request_id).trim()),a=i>=0&&r[i]||{};if(t===`audit.detail`){let t={...e,_detail_loaded:!0,_response_partial:!1};if(i>=0){let e=this.mergeLiveAuditPatch(a,t);return r.splice(i,1,e),this.auditLog.entries=[...r],this.notifyLiveConversation(e),e}return this.auditLiveInsertAllowed()?(this.auditLog.entries=[this.mergeLiveAuditUsagePatch(t),...r].slice(0,this.auditLog.limit||25),this.auditLog.total=Number(this.auditLog.total||0)+1,this.auditLog.entries[0]):void 0}let o=this.liveAuditStateAfter(a._live_state,t),s=this.liveAuditEventFlushed(a._live_state)||this.liveAuditEventFlushed(o),c={...e,_live:!0,_live_state:o,_audit_flushed:s};if(s?c._live_pending=!1:c._live_pending=!0,t===`audit.stream`?c._response_partial=!0:this.liveAuditStateSettled(t)&&(c._response_partial=!1),i>=0){let e=this.mergeLiveAuditPatch(a,c);return r.splice(i,1,e),this.auditLog.entries=[...r],this.fetchExpandedAuditDetailIfReady(e),this.notifyLiveConversation(e),e}if(!this.auditLiveInsertAllowed())return;this.auditLog.entries=[this.mergeLiveAuditUsagePatch(c),...r].slice(0,this.auditLog.limit||25),this.auditLog.total=Number(this.auditLog.total||0)+1;let l=this.auditLog.entries[0];return this.fetchExpandedAuditDetailIfReady(l),this.notifyLiveConversation(l),l},mergeLiveAuditPatch(e,t){let n={...e,...t};return t.data===void 0&&e.data!==void 0?n.data=e.data:e.data&&t.data&&typeof e.data==`object`&&typeof t.data==`object`&&!Array.isArray(e.data)&&!Array.isArray(t.data)&&(n.data={...e.data,...t.data}),this.mergeLiveAuditUsagePatch(n)},mergeLiveAuditUsagePatch(e){let t=this.liveUsageEntryForAudit(e);if(!t)return e;let n=this.auditEntryWithLiveUsage(e,t);return this.removeSkippedLiveUsage(t),n},liveUsageEntryForAudit(e){let t=String(e&&e.request_id||``).trim();return t&&((this.usageLog&&Array.isArray(this.usageLog.entries)?this.usageLog.entries:[]).find(e=>String(e&&e.request_id||``).trim()===t)||this.skippedLiveUsageByRequestId&&this.skippedLiveUsageByRequestId[t])||null},notifyLiveConversation(e){e&&typeof this.refreshLiveConversation==`function`&&this.refreshLiveConversation(e)},fetchExpandedAuditDetailIfReady(e){!e||!this.isAuditEntryExpanded||!this.isAuditEntryExpanded(e)||String(e._live_state||``).trim()!==`audit.flushed`&&!e._audit_flushed||typeof this.fetchAuditEntryDetail==`function`&&this.fetchAuditEntryDetail(e)},liveAuditStateRank(e){switch(String(e||``).trim()){case`audit.started`:return 10;case`audit.updated`:case`audit.stream`:return 20;case`audit.completed`:return 30;case`audit.failed`:case`audit.flushed`:case`audit.detail`:return 40;default:return 0}},liveAuditStateAfter(e,t){let n=String(e||``).trim(),r=String(t||``).trim();return this.liveAuditStateRank(n)>this.liveAuditStateRank(r)?n:r},liveAuditStateSettled(e){return this.liveAuditStateRank(e)>=this.liveAuditStateRank(`audit.completed`)},liveAuditEventFlushed(e){let t=String(e||``).trim();return t===`audit.failed`||t===`audit.flushed`||t===`audit.detail`},removeLiveAuditEntry(e){if(!e||!this.auditLog||!Array.isArray(this.auditLog.entries))return;let t=String(e.id||``).trim(),n=String(e.request_id||``).trim();if(!t&&!n)return;let r=this.auditLog.entries.filter(e=>!(t&&String(e.id||``).trim()===t||n&&String(e.request_id||``).trim()===n)),i=this.auditLog.entries.length-r.length;i>0&&(this.auditLog.entries=r,this.auditLog.total=Math.max(0,Number(this.auditLog.total||0)-i))},mergeLiveUsageEntry(e,t){if(!e||typeof e!=`object`)return;e={...e,_live_state:t||e._live_state||`usage.completed`};let n=String(e.id||``).trim();if(!n)return;let r=this.usageLog&&Array.isArray(this.usageLog.entries)?this.usageLog.entries:[],i=r.findIndex(e=>String(e.id||``).trim()===n);if(i>=0){let t=r[i]||{},n=this.mergeLiveUsagePatch(t,e);if(this.applyLiveUsageToAudit(n),this.liveUsageShouldSkip(n)){r.splice(i,1),this.usageLog.entries=[...r],this.usageLog.total=Math.max(0,Number(this.usageLog.total||0)-1),this.storeSkippedLiveUsage(n);return}r.splice(i,1,n),this.usageLog.entries=[...r],this.removeSkippedLiveUsage(n);return}let a=this.mergeLiveUsagePatch(this.liveUsageSeedForEntry(e),e);if(this.applyLiveUsageToAudit(a),this.liveUsageShouldSkip(a)){this.storeSkippedLiveUsage(a);return}this.removeSkippedLiveUsage(a),this.usageLog.entries=[a,...r].slice(0,this.usageLog.limit||50),this.usageLog.total=Number(this.usageLog.total||0)+1},mergeLiveUsagePatch(e,t){e=e&&typeof e==`object`?e:{};let n=this.liveUsageStateAfter(e._live_state,t&&t._live_state),r=this.liveUsageEventFlushed(e)||this.liveUsageEventFlushed({...t,_live_state:n});return{...e,...t,_live:!0,_live_state:n||`usage.completed`,_live_pending:!r,_usage_flushed:r}},liveUsageShouldSkip(e){return!!(this.usageLogHideCached&&this.liveUsageEntryCached(e))||!this.usageLiveInsertAllowed()},liveUsageSeedForEntry(e){return this.skippedLiveUsageForEntry(e)||this.auditLiveUsageForEntry(e)},skippedLiveUsageForEntry(e){let t=String(e&&e.request_id||``).trim();return t&&this.skippedLiveUsageByRequestId?this.skippedLiveUsageByRequestId[t]:null},auditLiveUsageForEntry(e){let t=String(e&&e.request_id||``).trim();if(!t||!this.auditLog||!Array.isArray(this.auditLog.entries))return null;let n=this.auditLog.entries.find(e=>String(e&&e.request_id||``).trim()===t),r=n&&n.usage&&typeof n.usage==`object`&&!Array.isArray(n.usage)?n.usage:null;return r?{id:e&&e.id,request_id:t,entries:r.entries,input_tokens:r.input_tokens,uncached_input_tokens:r.uncached_input_tokens,cached_input_tokens:r.cached_input_tokens,cache_write_input_tokens:r.cache_write_input_tokens,output_tokens:r.output_tokens,total_tokens:r.total_tokens,cached_input_ratio:r.cached_input_ratio,estimated_cached_characters:r.estimated_cached_characters,_live_state:n._usage_live_state,_live_pending:n._usage_live_pending,_usage_flushed:n._usage_flushed}:null},storeSkippedLiveUsage(e){let t=String(e&&e.request_id||``).trim();t&&((!this.skippedLiveUsageByRequestId||typeof this.skippedLiveUsageByRequestId!=`object`||Array.isArray(this.skippedLiveUsageByRequestId))&&(this.skippedLiveUsageByRequestId={}),this.skippedLiveUsageByRequestId[t]=e)},removeSkippedLiveUsage(e){let t=String(e&&e.request_id||``).trim();t&&this.skippedLiveUsageByRequestId&&delete this.skippedLiveUsageByRequestId[t]},liveUsageEntryCached(e){let t=String(e&&e.cache_type||``).trim().toLowerCase();return t===`exact`||t===`semantic`||!!(e&&e.cache_hit)},liveUsageEventFlushed(e){let t=String(e&&e._live_state||``).trim();return!!(e&&e._usage_flushed)||t===`usage.failed`||t===`usage.flushed`},liveUsageStateRank(e){switch(String(e||``).trim()){case`usage.completed`:return 10;case`usage.failed`:case`usage.flushed`:return 20;default:return 0}},liveUsageStateAfter(e,t){let n=String(e||``).trim(),r=String(t||``).trim();return this.liveUsageStateRank(n)>this.liveUsageStateRank(r)?n:r},applyLiveUsageToAudit(e){let t=String(e&&e.request_id||``).trim();if(!t||!this.auditLog||!Array.isArray(this.auditLog.entries))return;let n=this.auditLog.entries.findIndex(e=>String(e.request_id||``).trim()===t);if(n<0)return;let r=this.auditLog.entries[n];this.auditLog.entries.splice(n,1,this.auditEntryWithLiveUsage(r,e)),this.auditLog.entries=[...this.auditLog.entries]},auditEntryWithLiveUsage(e,t){let n=this.liveUsageStateAfter(e._usage_live_state,t._live_state||`usage.completed`),r=this.liveUsageEventFlushed({_live_state:n,_usage_flushed:e._usage_flushed||t._usage_flushed});return{...e,usage:this.liveUsageSummary(t,e.usage),_usage_live_state:n||`usage.completed`,_usage_live_pending:!r,_usage_flushed:r}},liveUsageSummary(e,t){let n=t&&typeof t==`object`&&!Array.isArray(t)?t:{},r=this.liveNumber(e.input_tokens,this.liveNumber(n.input_tokens,0)),i=this.liveNumber(e.output_tokens,this.liveNumber(n.output_tokens,0)),a=this.liveNumber(e.uncached_input_tokens,this.liveNumber(n.uncached_input_tokens,0)),o=this.liveNumber(e.cached_input_tokens,this.liveNumber(n.cached_input_tokens,0)),s=this.liveNumber(e.cache_write_input_tokens,this.liveNumber(n.cache_write_input_tokens,0));r>0&&a+o+s===0&&(a=r);let c=a+o+s||r,l=c+i||this.liveNumber(e.total_tokens,this.liveNumber(n.total_tokens,0)),u=this.liveNumber(e.cached_input_ratio,this.liveNumber(n.cached_input_ratio,c>0?o/c:0));return{entries:Math.max(1,this.liveNumber(e.entries,this.liveNumber(n.entries,1))),input_tokens:c,uncached_input_tokens:a,cached_input_tokens:o,cache_write_input_tokens:s,output_tokens:i,total_tokens:l,cached_input_ratio:u,estimated_cached_characters:this.liveNumber(e.estimated_cached_characters,this.liveNumber(n.estimated_cached_characters,o*4))}},liveNumber(e,t){let n=Number(e);return Number.isFinite(n)?n:t},auditEntryShouldFetchDetail(e){return!e||e._detail_loading||e._detail_loaded||this.auditEntryLiveDetailPending(e)?!1:this.auditEntryNeedsPersistedLiveDetail(e)?!0:!this.auditEntryHasDetailData(e)},auditEntryLiveDetailPending(e){if(!e||!e._live)return!1;let t=String(e._live_state||``).trim();return t===`audit.failed`||!e._audit_flushed&&t!==`audit.flushed`&&t!==`audit.detail`},auditEntryNeedsPersistedLiveDetail(e){return!!(e&&e._live&&!e._detail_loaded)},auditEntryHasDetailData(e){let t=e&&e.data;return!t||typeof t!=`object`?!1:t.request_headers!==void 0||t.response_headers!==void 0||t.request_body!==void 0||t.response_body!==void 0||t.request_body_too_big_to_handle!==void 0||t.response_body_too_big_to_handle!==void 0||t.user_agent!==void 0||t.api_key_hash!==void 0||t.temperature!==void 0||t.max_tokens!==void 0||t.error_message!==void 0||t.error_code!==void 0},clearAuditDetailLoading(e){if(!e)return;let t=String(e.id||``).trim(),n=String(e.request_id||``).trim(),r=this.auditLog&&Array.isArray(this.auditLog.entries)?this.auditLog.entries:[],i=r.find(e=>t&&String(e.id||``).trim()===t?!0:!!(n&&String(e.request_id||``).trim()===n)),a=i||e;a._detail_loading=!1,i&&(this.auditLog.entries=[...r])}}}var RQ=class{#e=A(fn({entries:[],total:0,limit:25,offset:0}));get auditLog(){return F(this.#e)}set auditLog(e){j(this.#e,e,!0)}#t=A(fn({entries:[],total:0,limit:50,offset:0}));get usageLog(){return F(this.#t)}set usageLog(e){j(this.#t,e,!0)}#n=A(``);get auditSearch(){return F(this.#n)}set auditSearch(e){j(this.#n,e,!0)}#r=A(``);get auditMethod(){return F(this.#r)}set auditMethod(e){j(this.#r,e,!0)}#i=A(``);get auditStatusCode(){return F(this.#i)}set auditStatusCode(e){j(this.#i,e,!0)}#a=A(``);get auditStream(){return F(this.#a)}set auditStream(e){j(this.#a,e,!0)}#o=A(``);get usageLogSearch(){return F(this.#o)}set usageLogSearch(e){j(this.#o,e,!0)}#s=A(``);get usageFilterModel(){return F(this.#s)}set usageFilterModel(e){j(this.#s,e,!0)}#c=A(``);get usageFilterProvider(){return F(this.#c)}set usageFilterProvider(e){j(this.#c,e,!0)}#l=A(``);get usageFilterLabel(){return F(this.#l)}set usageFilterLabel(e){j(this.#l,e,!0)}#u=A(``);get usageFilterUserPath(){return F(this.#u)}set usageFilterUserPath(e){j(this.#u,e,!0)}#d=A(!1);get usageLogHideCached(){return F(this.#d)}set usageLogHideCached(e){j(this.#d,e,!0)}liveLogsLastSeq=0;liveLogsReconnectAttempts=0;liveLogsReconnectTimer=null;liveLogsController=null;skippedLiveUsageByRequestId=null;fetchUsage=null;fetchAuditLog=null;isAuditEntryExpanded=null;refreshLiveConversation=null;noteLiveTokenUsage=null;get page(){return LI.page}get customStartDate(){return tR.customStartDate}get customEndDate(){return tR.customEndDate}liveLogsEnabled(){return aL.liveLogsVisible()}async startLiveLogs(){typeof fetch!=`function`||typeof ReadableStream>`u`||(await aL.ensureLoaded(),this.liveLogsEnabled()&&(this.stopLiveLogs(),this.liveLogsController=typeof AbortController==`function`?new AbortController:null,this.readLiveLogsStream(this.liveLogsController)))}stopLiveLogs(){this.liveLogsReconnectTimer&&=(clearTimeout(this.liveLogsReconnectTimer),null),this.liveLogsController&&typeof this.liveLogsController.abort==`function`&&this.liveLogsController.abort(),this.liveLogsController=null}ensureLiveLogs(){this.liveLogsController||this.liveLogsReconnectTimer||this.startLiveLogs()}async readLiveLogsStream(e){let t={};e&&(t.signal=e.signal);let n=IQ(this.liveLogsLastSeq),r=G.generation;try{let e=await $I(n,t);if(e.status===401){if(G.handleUnauthorized(r),r{this.liveLogsReconnectTimer=null,this.startLiveLogs()},t)}async fetchAuditEntryDetail(e){if(!this.auditEntryShouldFetchDetail(e))return;let t=String(e.id||``).trim();if(!t)return;e._detail_loading=!0;let n=e;try{let e=await tL(`/admin/audit/detail?log_id=`+encodeURIComponent(t),{label:`audit detail`});if(e.stale||!e.ok)return;n=this.mergeLiveAuditEntry(e.data,`audit.detail`)||n}catch(e){console.error(`Failed to fetch audit detail:`,e)}finally{this.clearAuditDetailLoading(n)}}};Object.assign(RQ.prototype,LQ());var zQ=new RQ,BQ=null;Fn(()=>{Nn(()=>{let e=G.refreshTick;if(BQ===null){BQ=e;return}e!==BQ&&(BQ=e,kr(()=>{zQ.stopLiveLogs(),zQ.startLiveLogs()}))})});function VQ(){return{total_requests:0,total_input_tokens:0,total_output_tokens:0,total_tokens:0,uncached_input_tokens:0,cached_input_tokens:0,cache_write_input_tokens:0,total_input_cost:null,total_output_cost:null,total_cost:null,rewrite_tokens_saved:0,rewrite_cost_saved:null}}function HQ(){return{entries:[],total:0,limit:50,offset:0}}function UQ(e,t){let n=[[`model`,e&&e.model],[`provider`,e&&e.provider],[`label`,e&&e.label],[`user_path`,e&&e.user_path]],r=``;for(let[e,i]of n)!i||e===t||(r+=`&`+e+`=`+encodeURIComponent(i));return r}function WQ({limit:e,offset:t,hideCached:n,search:r}){let i=`&limit=`+e+`&offset=`+t;return i+=`&cache_mode=`+(n?`uncached`:`all`),r&&(i+=`&search=`+encodeURIComponent(r)),i}function GQ(e,t){let n=new Set(e||[]);return t&&n.add(t),[...n].sort()}function KQ(e,t){let n=Number(t&&t.total_requests||0)-Number(e&&e.total_requests||0);return Number.isFinite(n)&&n>0?n:0}function qQ(e,t,n){let r=n?e:t,i=Number(r&&r.total_requests||0);return Number.isFinite(i)?i:0}function JQ(e,t,n){let r=KQ(e,t);return r<=0?``:n?BL(r)+` cached requests hidden`:BL(Number(e&&e.total_requests||0))+` to providers + `+BL(r)+` from cache`}function YQ(e){let t=e||{};return t.total_input_cost===null||t.total_input_cost===void 0?``:VL(t.total_input_cost)+` input + `+VL(t.total_output_cost)+` output`}function XQ(e){let t=Number(e&&e.rewrite_tokens_saved||0);return Number.isFinite(t)&&t>0?t:0}function ZQ(e){return XQ(e)>0}function QQ(e){let t=e||{};return t.rewrite_cost_saved===void 0?null:t.rewrite_cost_saved}function $Q(e,t){return t===`costs`?VL(QQ(e)):WL(XQ(e))}function e$(e,t){let n=t===`costs`,r=n?Number(QQ(e)):XQ(e);if(!Number.isFinite(r)||r<=0)return null;let i=e&&(n?e.total_cost:e.total_tokens);if(i==null)return null;let a=Number(i);return!Number.isFinite(a)||a<0?null:r/(a+r)*100}function t$(e,t){let n=e$(e,t);return n===null?``:(n<.1?`<0.1`:n.toFixed(1))+`% less`}function n$(e,t){let n=XQ(e);if(n<=0)return``;let r=[BL(n)+` prompt tokens removed by request rewriters before reaching providers`],i=QQ(e);i!=null&&r.push(VL(i)+` saved at the requests' input pricing`);let a=t$(e,t);return a&&r.push(a+` than the same traffic without rewriting (`+(t===`costs`?`cost`:`tokens`)+`)`),r.join(` +`))}catch{return}this.applyLiveLogEvent(r)},applyLiveLogEvent(e){if(!e||typeof e!=`object`)return;let t=Number(e.seq||0);Number.isFinite(t)&&t>this.liveLogsLastSeq&&(this.liveLogsLastSeq=t);let n=String(e.type||``).trim();if(n!==`heartbeat`){if(n===`reset`){this.reloadLiveLogSources();return}if(n===`audit.removed`){this.removeLiveAuditEntry(e.data);return}if(n.indexOf(`audit.`)===0){this.mergeLiveAuditEntry(e.data||{},n);return}n.indexOf(`usage.`)===0&&(this.mergeLiveUsageEntry(e.data||{},n),typeof this.noteLiveTokenUsage==`function`&&this.noteLiveTokenUsage(n))}},reloadLiveLogSources(){typeof this.fetchUsage==`function`&&this.fetchUsage(),this.page===`audit-logs`&&typeof this.fetchAuditLog==`function`&&this.fetchAuditLog(!0)},auditLiveInsertAllowed(){return this.auditLog&&this.auditLog.offset===0&&!this.auditSearch&&!this.auditMethod&&!this.auditStatusCode&&!this.auditStream&&!this.customStartDate&&!this.customEndDate},usageLiveInsertAllowed(){return this.usageLog&&this.usageLog.offset===0&&!this.usageLogSearch&&!this.usageFilterModel&&!this.usageFilterProvider&&!this.usageFilterLabel&&!this.usageFilterUserPath},mergeLiveAuditEntry(e,t){if(!e||typeof e!=`object`)return;let n=String(e.id||e.request_id||``).trim();if(!n)return;let r=this.auditLog&&Array.isArray(this.auditLog.entries)?this.auditLog.entries:[],i=r.findIndex(t=>String(t.id||``).trim()===n||e.request_id&&String(t.request_id||``).trim()===String(e.request_id).trim()),a=i>=0&&r[i]||{};if(t===`audit.detail`){let t={...e,_detail_loaded:!0,_response_partial:!1,bodies_omitted:!1};if(i>=0){let e=this.mergeLiveAuditPatch(a,t);return r.splice(i,1,e),this.auditLog.entries=[...r],this.notifyLiveConversation(e),e}return this.auditLiveInsertAllowed()?(this.auditLog.entries=[this.mergeLiveAuditUsagePatch(t),...r].slice(0,this.auditLog.limit||25),this.auditLog.total=Number(this.auditLog.total||0)+1,this.auditLog.entries[0]):void 0}let o=this.liveAuditStateAfter(a._live_state,t),s=this.liveAuditEventFlushed(a._live_state)||this.liveAuditEventFlushed(o),c={...e,_live:!0,_live_state:o,_audit_flushed:s};if(s?c._live_pending=!1:c._live_pending=!0,t===`audit.stream`?c._response_partial=!0:this.liveAuditStateSettled(t)&&(c._response_partial=!1),i>=0){let e=this.mergeLiveAuditPatch(a,c);return r.splice(i,1,e),this.auditLog.entries=[...r],this.fetchExpandedAuditDetailIfReady(e),this.notifyLiveConversation(e),e}if(!this.auditLiveInsertAllowed())return;this.auditLog.entries=[this.mergeLiveAuditUsagePatch(c),...r].slice(0,this.auditLog.limit||25),this.auditLog.total=Number(this.auditLog.total||0)+1;let l=this.auditLog.entries[0];return this.fetchExpandedAuditDetailIfReady(l),this.notifyLiveConversation(l),l},mergeLiveAuditPatch(e,t){let n={...e,...t};return t.data===void 0&&e.data!==void 0?n.data=e.data:e.data&&t.data&&typeof e.data==`object`&&typeof t.data==`object`&&!Array.isArray(e.data)&&!Array.isArray(t.data)&&(n.data={...e.data,...t.data}),this.mergeLiveAuditUsagePatch(n)},mergeLiveAuditUsagePatch(e){let t=this.liveUsageEntryForAudit(e);if(!t)return e;let n=this.auditEntryWithLiveUsage(e,t);return this.removeSkippedLiveUsage(t),n},liveUsageEntryForAudit(e){let t=String(e&&e.request_id||``).trim();return t&&((this.usageLog&&Array.isArray(this.usageLog.entries)?this.usageLog.entries:[]).find(e=>String(e&&e.request_id||``).trim()===t)||this.skippedLiveUsageByRequestId&&this.skippedLiveUsageByRequestId[t])||null},notifyLiveConversation(e){e&&typeof this.refreshLiveConversation==`function`&&this.refreshLiveConversation(e)},fetchExpandedAuditDetailIfReady(e){!e||!this.isAuditEntryExpanded||!this.isAuditEntryExpanded(e)||String(e._live_state||``).trim()!==`audit.flushed`&&!e._audit_flushed||typeof this.fetchAuditEntryDetail==`function`&&this.fetchAuditEntryDetail(e)},liveAuditStateRank(e){switch(String(e||``).trim()){case`audit.started`:return 10;case`audit.updated`:case`audit.stream`:return 20;case`audit.completed`:return 30;case`audit.failed`:case`audit.flushed`:case`audit.detail`:return 40;default:return 0}},liveAuditStateAfter(e,t){let n=String(e||``).trim(),r=String(t||``).trim();return this.liveAuditStateRank(n)>this.liveAuditStateRank(r)?n:r},liveAuditStateSettled(e){return this.liveAuditStateRank(e)>=this.liveAuditStateRank(`audit.completed`)},liveAuditEventFlushed(e){let t=String(e||``).trim();return t===`audit.failed`||t===`audit.flushed`||t===`audit.detail`},removeLiveAuditEntry(e){if(!e||!this.auditLog||!Array.isArray(this.auditLog.entries))return;let t=String(e.id||``).trim(),n=String(e.request_id||``).trim();if(!t&&!n)return;let r=this.auditLog.entries.filter(e=>!(t&&String(e.id||``).trim()===t||n&&String(e.request_id||``).trim()===n)),i=this.auditLog.entries.length-r.length;i>0&&(this.auditLog.entries=r,this.auditLog.total=Math.max(0,Number(this.auditLog.total||0)-i))},mergeLiveUsageEntry(e,t){if(!e||typeof e!=`object`)return;e={...e,_live_state:t||e._live_state||`usage.completed`};let n=String(e.id||``).trim();if(!n)return;let r=this.usageLog&&Array.isArray(this.usageLog.entries)?this.usageLog.entries:[],i=r.findIndex(e=>String(e.id||``).trim()===n);if(i>=0){let t=r[i]||{},n=this.mergeLiveUsagePatch(t,e);if(this.applyLiveUsageToAudit(n),this.liveUsageShouldSkip(n)){r.splice(i,1),this.usageLog.entries=[...r],this.usageLog.total=Math.max(0,Number(this.usageLog.total||0)-1),this.storeSkippedLiveUsage(n);return}r.splice(i,1,n),this.usageLog.entries=[...r],this.removeSkippedLiveUsage(n);return}let a=this.mergeLiveUsagePatch(this.liveUsageSeedForEntry(e),e);if(this.applyLiveUsageToAudit(a),this.liveUsageShouldSkip(a)){this.storeSkippedLiveUsage(a);return}this.removeSkippedLiveUsage(a),this.usageLog.entries=[a,...r].slice(0,this.usageLog.limit||50),this.usageLog.total=Number(this.usageLog.total||0)+1},mergeLiveUsagePatch(e,t){e=e&&typeof e==`object`?e:{};let n=this.liveUsageStateAfter(e._live_state,t&&t._live_state),r=this.liveUsageEventFlushed(e)||this.liveUsageEventFlushed({...t,_live_state:n});return{...e,...t,_live:!0,_live_state:n||`usage.completed`,_live_pending:!r,_usage_flushed:r}},liveUsageShouldSkip(e){return!!(this.usageLogHideCached&&this.liveUsageEntryCached(e))||!this.usageLiveInsertAllowed()},liveUsageSeedForEntry(e){return this.skippedLiveUsageForEntry(e)||this.auditLiveUsageForEntry(e)},skippedLiveUsageForEntry(e){let t=String(e&&e.request_id||``).trim();return t&&this.skippedLiveUsageByRequestId?this.skippedLiveUsageByRequestId[t]:null},auditLiveUsageForEntry(e){let t=String(e&&e.request_id||``).trim();if(!t||!this.auditLog||!Array.isArray(this.auditLog.entries))return null;let n=this.auditLog.entries.find(e=>String(e&&e.request_id||``).trim()===t),r=n&&n.usage&&typeof n.usage==`object`&&!Array.isArray(n.usage)?n.usage:null;return r?{id:e&&e.id,request_id:t,entries:r.entries,input_tokens:r.input_tokens,uncached_input_tokens:r.uncached_input_tokens,cached_input_tokens:r.cached_input_tokens,cache_write_input_tokens:r.cache_write_input_tokens,output_tokens:r.output_tokens,total_tokens:r.total_tokens,cached_input_ratio:r.cached_input_ratio,estimated_cached_characters:r.estimated_cached_characters,_live_state:n._usage_live_state,_live_pending:n._usage_live_pending,_usage_flushed:n._usage_flushed}:null},storeSkippedLiveUsage(e){let t=String(e&&e.request_id||``).trim();t&&((!this.skippedLiveUsageByRequestId||typeof this.skippedLiveUsageByRequestId!=`object`||Array.isArray(this.skippedLiveUsageByRequestId))&&(this.skippedLiveUsageByRequestId={}),this.skippedLiveUsageByRequestId[t]=e)},removeSkippedLiveUsage(e){let t=String(e&&e.request_id||``).trim();t&&this.skippedLiveUsageByRequestId&&delete this.skippedLiveUsageByRequestId[t]},liveUsageEntryCached(e){let t=String(e&&e.cache_type||``).trim().toLowerCase();return t===`exact`||t===`semantic`||!!(e&&e.cache_hit)},liveUsageEventFlushed(e){let t=String(e&&e._live_state||``).trim();return!!(e&&e._usage_flushed)||t===`usage.failed`||t===`usage.flushed`},liveUsageStateRank(e){switch(String(e||``).trim()){case`usage.completed`:return 10;case`usage.failed`:case`usage.flushed`:return 20;default:return 0}},liveUsageStateAfter(e,t){let n=String(e||``).trim(),r=String(t||``).trim();return this.liveUsageStateRank(n)>this.liveUsageStateRank(r)?n:r},applyLiveUsageToAudit(e){let t=String(e&&e.request_id||``).trim();if(!t||!this.auditLog||!Array.isArray(this.auditLog.entries))return;let n=this.auditLog.entries.findIndex(e=>String(e.request_id||``).trim()===t);if(n<0)return;let r=this.auditLog.entries[n];this.auditLog.entries.splice(n,1,this.auditEntryWithLiveUsage(r,e)),this.auditLog.entries=[...this.auditLog.entries]},auditEntryWithLiveUsage(e,t){let n=this.liveUsageStateAfter(e._usage_live_state,t._live_state||`usage.completed`),r=this.liveUsageEventFlushed({_live_state:n,_usage_flushed:e._usage_flushed||t._usage_flushed});return{...e,usage:this.liveUsageSummary(t,e.usage),_usage_live_state:n||`usage.completed`,_usage_live_pending:!r,_usage_flushed:r}},liveUsageSummary(e,t){let n=t&&typeof t==`object`&&!Array.isArray(t)?t:{},r=this.liveNumber(e.input_tokens,this.liveNumber(n.input_tokens,0)),i=this.liveNumber(e.output_tokens,this.liveNumber(n.output_tokens,0)),a=this.liveNumber(e.uncached_input_tokens,this.liveNumber(n.uncached_input_tokens,0)),o=this.liveNumber(e.cached_input_tokens,this.liveNumber(n.cached_input_tokens,0)),s=this.liveNumber(e.cache_write_input_tokens,this.liveNumber(n.cache_write_input_tokens,0));r>0&&a+o+s===0&&(a=r);let c=a+o+s||r,l=c+i||this.liveNumber(e.total_tokens,this.liveNumber(n.total_tokens,0)),u=this.liveNumber(e.cached_input_ratio,this.liveNumber(n.cached_input_ratio,c>0?o/c:0));return{entries:Math.max(1,this.liveNumber(e.entries,this.liveNumber(n.entries,1))),input_tokens:c,uncached_input_tokens:a,cached_input_tokens:o,cache_write_input_tokens:s,output_tokens:i,total_tokens:l,cached_input_ratio:u,estimated_cached_characters:this.liveNumber(e.estimated_cached_characters,this.liveNumber(n.estimated_cached_characters,o*4))}},liveNumber(e,t){let n=Number(e);return Number.isFinite(n)?n:t},auditEntryShouldFetchDetail(e){return!e||e._detail_loading||e._detail_loaded||this.auditEntryLiveDetailPending(e)?!1:this.auditEntryNeedsPersistedLiveDetail(e)||e.bodies_omitted?!0:!this.auditEntryHasDetailData(e)},auditEntryLiveDetailPending(e){if(!e||!e._live)return!1;let t=String(e._live_state||``).trim();return t===`audit.failed`||!e._audit_flushed&&t!==`audit.flushed`&&t!==`audit.detail`},auditEntryNeedsPersistedLiveDetail(e){return!!(e&&e._live&&!e._detail_loaded)},auditEntryHasDetailData(e){let t=e&&e.data;return!t||typeof t!=`object`?!1:t.request_headers!==void 0||t.response_headers!==void 0||t.request_body!==void 0||t.response_body!==void 0||t.request_body_too_big_to_handle!==void 0||t.response_body_too_big_to_handle!==void 0||t.user_agent!==void 0||t.api_key_hash!==void 0||t.temperature!==void 0||t.max_tokens!==void 0||t.error_message!==void 0||t.error_code!==void 0},clearAuditDetailLoading(e){if(!e)return;let t=String(e.id||``).trim(),n=String(e.request_id||``).trim(),r=this.auditLog&&Array.isArray(this.auditLog.entries)?this.auditLog.entries:[],i=r.find(e=>t&&String(e.id||``).trim()===t?!0:!!(n&&String(e.request_id||``).trim()===n)),a=i||e;a._detail_loading=!1,i&&(this.auditLog.entries=[...r])}}}var RQ=class{#e=A(fn({entries:[],total:0,limit:25,offset:0}));get auditLog(){return F(this.#e)}set auditLog(e){j(this.#e,e,!0)}#t=A(fn({entries:[],total:0,limit:50,offset:0}));get usageLog(){return F(this.#t)}set usageLog(e){j(this.#t,e,!0)}#n=A(``);get auditSearch(){return F(this.#n)}set auditSearch(e){j(this.#n,e,!0)}#r=A(``);get auditMethod(){return F(this.#r)}set auditMethod(e){j(this.#r,e,!0)}#i=A(``);get auditStatusCode(){return F(this.#i)}set auditStatusCode(e){j(this.#i,e,!0)}#a=A(``);get auditStream(){return F(this.#a)}set auditStream(e){j(this.#a,e,!0)}#o=A(``);get usageLogSearch(){return F(this.#o)}set usageLogSearch(e){j(this.#o,e,!0)}#s=A(``);get usageFilterModel(){return F(this.#s)}set usageFilterModel(e){j(this.#s,e,!0)}#c=A(``);get usageFilterProvider(){return F(this.#c)}set usageFilterProvider(e){j(this.#c,e,!0)}#l=A(``);get usageFilterLabel(){return F(this.#l)}set usageFilterLabel(e){j(this.#l,e,!0)}#u=A(``);get usageFilterUserPath(){return F(this.#u)}set usageFilterUserPath(e){j(this.#u,e,!0)}#d=A(!1);get usageLogHideCached(){return F(this.#d)}set usageLogHideCached(e){j(this.#d,e,!0)}liveLogsLastSeq=0;liveLogsReconnectAttempts=0;liveLogsReconnectTimer=null;liveLogsController=null;skippedLiveUsageByRequestId=null;fetchUsage=null;fetchAuditLog=null;isAuditEntryExpanded=null;refreshLiveConversation=null;noteLiveTokenUsage=null;get page(){return LI.page}get customStartDate(){return tR.customStartDate}get customEndDate(){return tR.customEndDate}liveLogsEnabled(){return aL.liveLogsVisible()}async startLiveLogs(){typeof fetch!=`function`||typeof ReadableStream>`u`||(await aL.ensureLoaded(),this.liveLogsEnabled()&&(this.stopLiveLogs(),this.liveLogsController=typeof AbortController==`function`?new AbortController:null,this.readLiveLogsStream(this.liveLogsController)))}stopLiveLogs(){this.liveLogsReconnectTimer&&=(clearTimeout(this.liveLogsReconnectTimer),null),this.liveLogsController&&typeof this.liveLogsController.abort==`function`&&this.liveLogsController.abort(),this.liveLogsController=null}ensureLiveLogs(){this.liveLogsController||this.liveLogsReconnectTimer||this.startLiveLogs()}async readLiveLogsStream(e){let t={};e&&(t.signal=e.signal);let n=IQ(this.liveLogsLastSeq),r=G.generation;try{let e=await $I(n,t);if(e.status===401){if(G.handleUnauthorized(r),r{this.liveLogsReconnectTimer=null,this.startLiveLogs()},t)}async fetchAuditEntryDetail(e){if(!this.auditEntryShouldFetchDetail(e))return;let t=String(e.id||``).trim();if(!t)return;e._detail_loading=!0;let n=e;try{let e=await tL(`/admin/audit/detail?log_id=`+encodeURIComponent(t),{label:`audit detail`});if(e.stale||!e.ok)return;n=this.mergeLiveAuditEntry(e.data,`audit.detail`)||n}catch(e){console.error(`Failed to fetch audit detail:`,e)}finally{this.clearAuditDetailLoading(n)}}};Object.assign(RQ.prototype,LQ());var zQ=new RQ,BQ=null;Fn(()=>{Nn(()=>{let e=G.refreshTick;if(BQ===null){BQ=e;return}e!==BQ&&(BQ=e,kr(()=>{zQ.stopLiveLogs(),zQ.startLiveLogs()}))})});function VQ(){return{total_requests:0,total_input_tokens:0,total_output_tokens:0,total_tokens:0,uncached_input_tokens:0,cached_input_tokens:0,cache_write_input_tokens:0,total_input_cost:null,total_output_cost:null,total_cost:null,rewrite_tokens_saved:0,rewrite_cost_saved:null}}function HQ(){return{entries:[],total:0,limit:50,offset:0}}function UQ(e,t){let n=[[`model`,e&&e.model],[`provider`,e&&e.provider],[`label`,e&&e.label],[`user_path`,e&&e.user_path]],r=``;for(let[e,i]of n)!i||e===t||(r+=`&`+e+`=`+encodeURIComponent(i));return r}function WQ({limit:e,offset:t,hideCached:n,search:r}){let i=`&limit=`+e+`&offset=`+t;return i+=`&cache_mode=`+(n?`uncached`:`all`),r&&(i+=`&search=`+encodeURIComponent(r)),i}function GQ(e,t){let n=new Set(e||[]);return t&&n.add(t),[...n].sort()}function KQ(e,t){let n=Number(t&&t.total_requests||0)-Number(e&&e.total_requests||0);return Number.isFinite(n)&&n>0?n:0}function qQ(e,t,n){let r=n?e:t,i=Number(r&&r.total_requests||0);return Number.isFinite(i)?i:0}function JQ(e,t,n){let r=KQ(e,t);return r<=0?``:n?BL(r)+` cached requests hidden`:BL(Number(e&&e.total_requests||0))+` to providers + `+BL(r)+` from cache`}function YQ(e){let t=e||{};return t.total_input_cost===null||t.total_input_cost===void 0?``:VL(t.total_input_cost)+` input + `+VL(t.total_output_cost)+` output`}function XQ(e){let t=Number(e&&e.rewrite_tokens_saved||0);return Number.isFinite(t)&&t>0?t:0}function ZQ(e){return XQ(e)>0}function QQ(e){let t=e||{};return t.rewrite_cost_saved===void 0?null:t.rewrite_cost_saved}function $Q(e,t){return t===`costs`?VL(QQ(e)):WL(XQ(e))}function e$(e,t){let n=t===`costs`,r=n?Number(QQ(e)):XQ(e);if(!Number.isFinite(r)||r<=0)return null;let i=e&&(n?e.total_cost:e.total_tokens);if(i==null)return null;let a=Number(i);return!Number.isFinite(a)||a<0?null:r/(a+r)*100}function t$(e,t){let n=e$(e,t);return n===null?``:(n<.1?`<0.1`:n.toFixed(1))+`% less`}function n$(e,t){let n=XQ(e);if(n<=0)return``;let r=[BL(n)+` prompt tokens removed by request rewriters before reaching providers`],i=QQ(e);i!=null&&r.push(VL(i)+` saved at the requests' input pricing`);let a=t$(e,t);return a&&r.push(a+` than the same traffic without rewriting (`+(t===`costs`?`cost`:`tokens`)+`)`),r.join(` `)}function r$(e){return String(e&&e.cost_source||``).trim()}function i$(e){let t=r$(e);return t===`openrouter_credits`||t===`xai_cost_in_usd_ticks`}function a$(e){switch(r$(e)){case`openrouter_credits`:return`Costs from OpenRouter USD-based credits.`;case`xai_cost_in_usd_ticks`:return`Costs from xAI usage.cost_in_usd_ticks.`;default:return``}}function o$(e){return String(e&&e.cache_type||``).trim().toLowerCase()}function s$(e){let t=o$(e);return t===`exact`||t===`semantic`}function c$(e){let t=o$(e);return t===`exact`?`Exact`:t===`semantic`?`Semantic`:`-`}function l$(e,t){let n=t?String(t):``;return s$(e)?n?`Saved by cache — not charged `+n:`Saved by cache — not charged`:n}function u$(e){let t=Number(e&&e.cached_input_ratio);return!Number.isFinite(t)||t<=0?0:Math.min(1,t)}function d$(e){return Number(e&&e.cached_input_tokens||0)>0}function f$(e){return d$(e)?(u$(e)*100).toFixed(1)+`%`:``}function p$(e){if(!d$(e))return``;let t=Number(e.cached_input_tokens||0),n=Number(e.uncached_input_tokens||0),r=Number(e.cache_write_input_tokens||0),i=t+n+r,a=[BL(t)+` cached / `+BL(i)+` input tokens`];return r>0&&a.push(BL(r)+` cache write`),a.join(` `)}function m$(e){let t=[];if(a$(e)&&(t.push(a$(e)),t.push(``)),t.push(`Input: `+VL(e.input_cost)),t.push(`Output: `+VL(e.output_cost)),e.raw_data){t.push(``);for(let[n,r]of Object.entries(e.raw_data)){let e=n.replace(/_/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),i=r&&typeof r==`object`?JSON.stringify(r):BL(r);t.push(e+`: `+i)}}return t.join(` @@ -37,7 +37,7 @@ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],en without a database migration.`),O6=L(`
No pricing fields set.
`),k6=L(`
`),A6=L(``),j6=L(``),M6=L(``);function N6(e,t){D(t,!0);let n=u3;pL(e,{get open(){return n.modelPricingOverrideFormOpen},onclose:()=>n.closeModelPricingOverrideForm(),children:(e,t)=>{var r=M6(),i=M(r),a=M(i),o=M(a),s=N(M(o),2),c=M(s,!0);E(s),E(o),dL(N(o,2),{label:`Close model pricing editor`,onclick:()=>n.closeModelPricingOverrideForm()}),E(a);var l=N(a,2),u=M(l),d=N(M(u),2);Qi(d),E(u);var f=N(u,2),p=e=>{var t=T6(),r=N(M(t),2);V(r,21,()=>n.modelPricingOverrideFormScopeOptions,e=>e.value,(e,t)=>{var n=w6(),r=M(n,!0);E(n);var i={};P(()=>{z(r,F(t).label),i!==(i=F(t).value)&&(n.value=(n.__value=F(t).value)??``)}),R(e,n)}),E(r),E(t),I(`change`,r,()=>n.setModelPricingOverrideScope(n.modelPricingOverrideFormScope)),Vi(r,()=>n.modelPricingOverrideFormScope,e=>n.modelPricingOverrideFormScope=e),R(e,t)};B(f,e=>{n.modelPricingOverrideFormScopeOptions.length>1&&e(p)}),E(l);var m=N(l,4);V(m,21,()=>n.modelPricingOverrideRows,e=>e.id,(e,t,r)=>{var i=E6(),a=M(i),o=M(a),s=N(o,2);V(s,21,()=>n.availablePricingFieldOptions(F(t)),e=>e.value,(e,t)=>{var n=w6(),r=M(n,!0);E(n);var i={};P(()=>{z(r,F(t).group+` - `+F(t).label),i!==(i=F(t).value)&&(n.value=(n.__value=F(t).value)??``)}),R(e,n)}),E(s),E(a);var c=N(a,2),l=M(c),u=N(l,2);Qi(u),E(c);var d=N(c,2);{let e=k(()=>`Remove `+n.pricingFieldLabel(F(t).field));S1(d,{get label(){return F(e)},class:`table-action-btn-danger table-icon-btn pricing-override-remove-row`,onclick:()=>n.removeModelPricingOverrideRow(F(t)),children:(e,t)=>{W(e,{name:`x`,class:`table-icon-svg`})},$$slots:{default:!0}})}E(i),P(()=>{U(o,`for`,`pricing-type-`+F(t).id),U(s,`id`,`pricing-type-`+F(t).id),U(l,`for`,`pricing-value-`+F(t).id),U(u,`id`,`pricing-value-`+F(t).id)}),Vi(s,()=>F(t).field,e=>F(t).field=e),sa(u,()=>F(t).value,e=>F(t).value=e),R(e,i)}),E(m);var h=N(m,2),g=M(h);W(M(g),{name:`plus`,class:`form-action-icon`}),We(2),E(g),E(h);var _=N(h,2),v=e=>{R(e,D6())};B(_,e=>{n.modelPricingOverrideFormPreservedTiers.length>0&&e(v)});var y=N(_,2),b=N(M(y),2),x=e=>{R(e,O6())},S=k(()=>n.modelPricingEffectivePreviewRows().length===0);B(b,e=>{F(S)&&e(x)}),V(N(b,2),17,()=>n.modelPricingEffectivePreviewRows(),e=>e.field,(e,t)=>{var n=k6(),r=M(n),i=M(r,!0);E(r);var a=N(r,2),o=M(a,!0);E(a);var s=N(a,2),c=M(s,!0);E(s),E(n),P(e=>{z(i,F(t).label),z(o,e),z(c,F(t).source)},[()=>F(t).value===null||F(t).value===void 0?`-`:UL(Number(F(t).value))]),R(e,n)}),E(y);var C=N(y,2),w=e=>{var t=A6(),r=M(t,!0);E(t),P(()=>z(r,n.modelPricingOverrideError)),R(e,t)};B(C,e=>{n.modelPricingOverrideError&&e(w)});var T=N(C,2),ee=M(T),te=N(ee,2),ne=e=>{var t=j6();P(()=>t.disabled=n.modelPricingOverrideSubmitting),I(`click`,t,()=>n.deleteModelPricingOverride()),R(e,t)};B(te,e=>{n.modelPricingOverrideFormHasExistingOverride&&e(ne)});var re=N(te,2),ie=M(re);W(ie,{name:`save`,class:`form-action-icon`});var ae=N(ie,2),oe=M(ae,!0);E(ae),E(re),E(T),E(i),E(r),P(()=>{z(c,n.modelPricingOverrideFormDisplayName||n.modelPricingOverrideForm.selector||`Pricing`),re.disabled=n.modelPricingOverrideSubmitting,z(oe,n.modelPricingOverrideSubmitting?`Saving...`:`Save Pricing`)}),Hr(`submit`,i,e=>{e.preventDefault(),n.submitModelPricingOverrideForm()}),sa(d,()=>n.modelPricingOverrideForm.selector,e=>n.modelPricingOverrideForm.selector=e),I(`click`,g,()=>n.addModelPricingOverrideRow()),I(`click`,ee,()=>n.closeModelPricingOverrideForm()),R(e,r)},$$slots:{default:!0}}),O()}Ur([`change`,`click`]);var P6=L(`

This failover mapping is defined in configuration and is read-only here.

`),F6=L(``),I6=L(`
`),L6=L(``),R6=L(``),z6=L(``),B6=L(``);function V6(e,t){D(t,!0),pL(e,{get open(){return X.failoverFormOpen},variant:`editor`,onclose:()=>X.closeFailoverForm(),children:(e,t)=>{var n=B6(),r=M(n),i=M(r),a=M(i),o=N(M(a),2),s=M(o,!0);E(o),E(a),dL(N(a,2),{label:`Close failover editor`,onclick:()=>X.closeFailoverForm()}),E(i);var c=N(i,2),l=e=>{R(e,P6())};B(c,e=>{X.failoverFormManaged&&e(l)});var u=N(c,2);V(u,21,()=>IL.models,oi,(e,t)=>{var n=F6(),r=M(n,!0);E(n);var i={};P((e,t)=>{z(r,e),i!==(i=t)&&(n.value=(n.__value=t)??``)},[()=>K2(F(t)),()=>K2(F(t))]),R(e,n)}),E(u);var d=N(u,2),f=N(M(d),2),p=M(f),m=M(p);Qi(m);var h=N(m,2),g=e=>{S1(e,{label:`Remove fallback model`,class:`table-action-btn-danger table-icon-btn vm-target-remove`,onclick:()=>X.removePrimaryFailoverTarget(),get disabled(){return X.failoverFormManaged},children:(e,t)=>{W(e,{name:`trash-2`,class:`table-icon-svg`})},$$slots:{default:!0}})};B(h,e=>{X.failoverForm.target_model&&e(g)}),E(p),V(N(p,2),17,()=>X.failoverForm.targets,oi,(e,t,n)=>{var r=I6(),i=M(r);Qi(i),S1(N(i,2),{label:`Remove fallback model`,class:`table-action-btn-danger table-icon-btn vm-target-remove`,onclick:()=>X.removeFailoverTarget(n),get disabled(){return X.failoverFormManaged},children:(e,t)=>{W(e,{name:`trash-2`,class:`table-icon-svg`})},$$slots:{default:!0}}),E(r),P(()=>i.disabled=X.failoverFormManaged),sa(i,()=>F(t).model,e=>F(t).model=e),R(e,r)}),E(f);var _=N(f,2),v=M(_);W(M(v),{name:`plus`,class:`form-action-icon`}),We(2),E(v);var y=N(v,2),b=M(y);W(b,{name:`wand-sparkles`,class:`form-action-icon`});var x=N(b,2),S=M(x,!0);E(x),E(y),E(_),E(d);var C=N(d,2),w=M(C),T=M(w);let ee;var te=N(M(T),2),ne=M(te,!0);E(te),E(T),E(w),E(C);var re=N(C,2),ie=e=>{var t=L6(),n=M(t,!0);E(t),P(()=>z(n,X.failoverError)),R(e,t)};B(re,e=>{X.failoverError&&e(ie)});var ae=N(re,2),oe=M(ae),se=N(oe,2),ce=e=>{var t=R6();P(()=>t.disabled=X.failoverSaving||X.failoverGenerating),I(`click`,t,()=>X.deleteFailoverRule()),R(e,t)};B(se,e=>{X.failoverFormMode===`edit`&&!X.failoverFormManaged&&e(ce)});var le=N(se,2),ue=e=>{var t=z6(),n=M(t);W(n,{name:`save`,class:`form-action-icon`});var r=N(n,2),i=M(r,!0);E(r),E(t),P(()=>{t.disabled=X.failoverSaving||X.failoverGenerating,z(i,X.failoverSaving?`Saving...`:`Save`)}),R(e,t)};B(le,e=>{X.failoverFormManaged||e(ue)}),E(ae),E(r),E(n),P(e=>{z(s,X.failoverForm.source||`Failover`),m.disabled=X.failoverFormManaged,v.disabled=X.failoverFormManaged||X.failoverGenerating||X.failoverSaving,y.disabled=e,z(S,X.failoverGenerating?`Generating...`:`Generate automatically`),ee=H(T,1,`alias-toggle`,null,ee,{enabled:X.failoverForm.enabled}),T.disabled=X.failoverFormManaged,U(T,`aria-label`,(X.failoverForm.enabled?`Disable`:`Enable`)+` failover mapping`),z(ne,X.failoverForm.enabled?`Enabled`:`Disabled`)},[()=>X.failoverFormManaged||X.failoverGenerating||X.failoverSaving||!X.failoverEnabled()]),Hr(`submit`,r,e=>{e.preventDefault(),X.submitFailoverForm()}),sa(m,()=>X.failoverForm.target_model,e=>X.failoverForm.target_model=e),I(`click`,v,()=>X.addFailoverTarget()),I(`click`,y,()=>X.generateFailoverForForm()),I(`click`,T,()=>{X.failoverFormManaged||(X.failoverForm.enabled=!X.failoverForm.enabled)}),I(`click`,oe,()=>X.closeFailoverForm()),R(e,n)},$$slots:{default:!0}}),O()}Ur([`click`]);var H6=L(` `),U6=L(`
`),W6=L(``),G6=L(`
`),K6=L(`

No failover suggestions were generated.

`),q6=L(`

No failover drafts match the filter.

`),J6=L(``),Y6=L(``);function X6(e,t){D(t,!0),pL(e,{get open(){return X.failoverDraftsOpen},variant:`editor`,onclose:()=>X.closeFailoverDraftsModal(),children:(e,t)=>{var n=Y6(),r=M(n),i=N(M(r),2),a=M(i),o=e=>{var t=H6(),n=M(t,!0);E(t),P(e=>z(n,e),[()=>X.failoverDraftCountLabel()]),R(e,t)};B(a,e=>{X.failoverGeneratedRules.length>0&&e(o)}),dL(N(a,2),{label:`Close failover drafts`,onclick:()=>X.closeFailoverDraftsModal(),get disabled(){return X.failoverDraftSaving}}),E(i),E(r);var s=N(r,2),c=e=>{b1(e,{label:`Generating failover drafts...`,class:`failover-drafts-loading`})};B(s,e=>{X.failoverGenerating&&e(c)});var l=N(s,2),u=e=>{var t=U6(),n=M(t);T$(n,{placeholder:`Filter failover drafts...`,label:`Filter failover drafts`,get value(){return X.failoverDraftFilter},set value(e){X.failoverDraftFilter=e}});var r=N(n,2),i=M(r);W(i,{name:`check`,class:`form-action-icon`});var a=N(i,2),o=M(a,!0);E(a),E(r),E(t),P(e=>{r.disabled=X.failoverDraftSaving,z(o,e)},[()=>X.allFailoverDraftsSelected()?`Deselect all`:`Select all`]),I(`click`,r,()=>X.toggleAllFailoverDrafts()),R(e,t)};B(l,e=>{!X.failoverGenerating&&X.failoverGeneratedRules.length>0&&e(u)});var d=N(l,2),f=e=>{var t=G6();V(t,21,()=>X.filteredFailoverDrafts(),e=>`failover-draft:`+X.failoverPrimaryModel(e),(e,t)=>{var n=W6(),r=M(n);Qi(r);var i=N(r,2),a=M(i),o=M(a,!0);E(a);var s=N(a,2),c=M(s,!0);E(s),E(i),E(n),P((e,t,n,i)=>{ea(r,e),r.disabled=X.failoverDraftSaving,U(r,`aria-label`,t),z(o,n),z(c,i)},[()=>X.failoverDraftSelected(F(t)),()=>`Select failover draft for `+X.failoverPrimaryModel(F(t)),()=>X.failoverPrimaryModel(F(t)),()=>X.failoverTargetLabel(F(t))]),I(`change`,r,e=>X.setFailoverDraftSelected(F(t),e.currentTarget.checked)),R(e,n)}),E(t),R(e,t)},p=k(()=>!X.failoverGenerating&&X.filteredFailoverDrafts().length>0);B(d,e=>{F(p)&&e(f)});var m=N(d,2),h=e=>{R(e,K6())};B(m,e=>{!X.failoverGenerating&&X.failoverGeneratedRules.length===0&&!X.failoverError&&e(h)});var g=N(m,2),_=e=>{R(e,q6())},v=k(()=>!X.failoverGenerating&&X.failoverGeneratedRules.length>0&&X.filteredFailoverDrafts().length===0);B(g,e=>{F(v)&&e(_)});var y=N(g,2),b=e=>{var t=J6(),n=M(t,!0);E(t),P(()=>z(n,X.failoverError)),R(e,t)};B(y,e=>{X.failoverError&&e(b)});var x=N(y,2),S=M(x),C=N(S,2),w=M(C);W(w,{name:`save`,class:`form-action-icon`});var T=N(w,2),ee=M(T,!0);E(T),E(C),E(x),E(n),P(e=>{S.disabled=X.failoverDraftSaving,C.disabled=e,z(ee,X.failoverDraftSaving?`Saving...`:`Save selected`)},[()=>X.failoverGenerating||X.failoverDraftSaving||X.selectedFailoverDraftCount()===0]),I(`click`,S,()=>X.closeFailoverDraftsModal()),I(`click`,C,()=>X.saveSelectedFailoverDrafts()),R(e,n)},$$slots:{default:!0}}),O()}Ur([`click`,`change`]);var Z6=L(`
Rate limit management is unavailable.
`),Q6=L(` Add`,1),$6=L(`

`),e8=L(`

No rules.

`),t8=L(` Edit`,1),n8=L(`
`),r8=L(`
`),i8=L(`

`),a8=L(``),o8=L(``);function s8(e,t){D(t,!0);function n(){G.dialogOpen||Y.closeRateLimitInspector()}pL(e,{get open(){return Y.rateLimitInspectorOpen},variant:`editor`,onclose:n,children:(e,t)=>{var n=o8(),r=M(n),i=M(r),a=N(M(i),2),o=M(a),s=M(o,!0);E(o),E(a),E(i),dL(N(i,2),{label:`Close rate limits inspector`,onclick:()=>Y.closeRateLimitInspector()}),E(r);var c=N(r,2),l=e=>{b1(e,{label:`Loading rate limits...`})},u=e=>{R(e,Z6())},d=e=>{var t=$r();V(Cn(t),17,()=>Y.rateLimitInspectorSections(),e=>e.key,(e,t)=>{var n=i8(),r=M(n),i=M(r),a=M(i,!0);E(i);var o=N(i,2);{let e=k(()=>`Add `+F(t).title.toLowerCase());S1(o,{get label(){return F(e)},class:`budget-action-btn`,onclick:()=>Y.openRateLimitFormFromInspector(F(t).scope,F(t).subject),children:(e,t)=>{var n=Q6();W(Cn(n),{name:`plus`,class:`table-icon-svg`}),We(2),R(e,n)},$$slots:{default:!0}})}E(r);var s=N(r,2),c=e=>{var n=$6(),r=M(n,!0);E(n),P(()=>z(r,F(t).hint)),R(e,n)};B(s,e=>{F(t).hint&&e(c)});var l=N(s,2),u=e=>{R(e,e8())},d=e=>{var n=r8();V(n,21,()=>F(t).items,e=>Y.rateLimitKey(e),(e,t)=>{var n=n8(),r=M(n),i=M(r),a=M(i),o=M(a,!0);E(a);var s=N(a,2),c=M(s),l=M(c);{let e=k(()=>Y.rateLimitIsConcurrent(F(t))?`activity`:`timer`);W(l,{get name(){return F(e)},class:`budget-period-icon`})}var u=N(l,2),d=M(u,!0);E(u),E(c),E(s);var f=N(s,2),p=M(f),m=M(p),h=M(m,!0);E(m);var g=N(m,2),_=M(g,!0);E(g),E(p);var v=N(p,2),y=M(v),b=e=>{S1(e,{label:`Edit rate limit`,class:`budget-action-btn`,onclick:()=>Y.openRateLimitFormFromInspector(null,null,F(t)),children:(e,t)=>{var n=t8();W(Cn(n),{name:`pencil`,class:`budget-action-icon`}),We(2),R(e,n)},$$slots:{default:!0}})},x=k(()=>!Y.rateLimitIsReadOnly(F(t)));B(y,e=>{F(x)&&e(b)}),E(v),E(f),E(i),E(r),E(n),P((e,t,r,i,a,s,c,l)=>{H(n,1,`budget-row ${e??``}`),Ri(n,t),U(n,`title`,r),z(o,i),z(d,a),z(h,s),U(g,`title`,c),z(_,l)},[()=>Y.rateLimitPressureClass(F(t)),()=>Y.rateLimitPressureStyle(F(t)),()=>Y.rateLimitPressurePercent(F(t))+`% of the most constrained cap used`,()=>Y.rateLimitSubject(F(t)),()=>Y.rateLimitPeriodLabel(F(t)),()=>Y.rateLimitInspectorSummary(F(t)),()=>Y.rateLimitIsReadOnly(F(t))?`Declared in configuration; read-only in the dashboard`:`Managed via dashboard or admin API`,()=>Y.rateLimitSourceLabel(F(t))]),R(e,n)}),E(n),R(e,n)};B(l,e=>{F(t).items.length===0?e(u):e(d,-1)}),E(n),P(()=>z(a,F(t).title)),R(e,n)}),R(e,t)};B(c,e=>{Y.rateLimitsLoading?e(l):Y.rateLimitsAvailable?e(d,-1):e(u,1)});var f=N(c,2),p=M(f),m=N(p,2),h=e=>{var t=a8();I(`click`,t,()=>{Y.closeRateLimitInspector(),LI.navigate(`rate-limits`)}),R(e,t)},g=k(()=>Y.rateLimitsEnabled());B(m,e=>{F(g)&&e(h)}),E(f),E(n),P(()=>z(s,Y.rateLimitInspector.title)),I(`click`,p,()=>Y.closeRateLimitInspector()),R(e,n)},$$slots:{default:!0}}),O()}Ur([`click`]);var c8=L(`
models
`),l8=L(`
Virtual models feature is unavailable.
`),u8=L(`
`),d8=L(``),f8=L(`
`),p8=L(``),m8=L(`
`),h8=L(`

No models registered.

`),g8=L(`

No models in this category.

`),_8=L(`

No models match your filter.

`),v8=L(`
`);function y8(e,t){D(t,!0),Nn(()=>{G.refreshTick,LI.page===`models`&&(U4.fetchVirtualModels(),u3.fetchModelPricingOverrides(),X.fetchFailoverRules(),Y.fetchRateLimitsPage())}),Nn(()=>{let e=U4.filteredDisplayModels.length;return kr(()=>U4.restartModelRendering(e)),()=>U4.stopModelRendering()});let n=k(()=>G.needsAuth);var r=v8(),i=M(r),a=N(M(i),2),o=e=>{var t=c8(),n=M(t),r=M(n,!0);E(n),We(),E(t),P(()=>z(r,IL.filter?U4.filteredDisplayModels.length+` / `+U4.displayModels.length:U4.displayModels.length)),R(e,t)};B(a,e=>{U4.displayModels.length>0&&e(o)}),E(i);var s=N(i,2);RL(s,{});var c=N(s,2),l=e=>{R(e,l8())};B(c,e=>{!U4.virtualModelsAvailable&&!F(n)&&e(l)});var u=N(c,2),d=e=>{var t=u8(),n=M(t,!0);E(t),P(()=>z(n,U4.aliasError)),R(e,t)};B(u,e=>{U4.aliasError&&!F(n)&&e(d)});var f=N(u,2),p=e=>{var t=u8(),n=M(t,!0);E(t),P(()=>z(n,u3.modelPricingOverrideError)),R(e,t)};B(f,e=>{u3.modelPricingOverrideError&&!F(n)&&!u3.modelPricingOverrideFormOpen&&e(p)});var m=N(f,2),h=e=>{var t=f8();V(t,21,()=>IL.categories,e=>e.category,(e,t)=>{var n=d8();let r;var i=M(n),a=M(i,!0);E(i);var o=N(i,2),s=M(o,!0);E(o),E(n),P(()=>{r=H(n,1,`category-tab svelte-scpjps`,null,r,{active:IL.activeCategory===F(t).category}),z(a,F(t).display_name),z(s,F(t).count)}),I(`click`,n,()=>IL.selectCategory(F(t).category)),R(e,n)}),E(t),R(e,t)};B(m,e=>{IL.categories.length>0&&e(h)});var g=N(m,2),_=e=>{var t=m8(),n=M(t);T$(M(n),{placeholder:`Filter by provider, provider/model, alias, or owner...`,label:`Filter models by provider, provider/model, alias, or owner`,get value(){return IL.filter},set value(e){IL.filter=e}}),E(n);var r=N(n,2),i=M(r),a=e=>{var t=p8();W(M(t),{name:`plus`,class:`alias-create-icon`}),We(2),E(t),I(`click`,t,()=>U4.openVirtualModelCreate()),R(e,t)};B(i,e=>{U4.virtualModelsAvailable&&e(a)}),E(r),E(t),R(e,t)};B(g,e=>{(U4.displayModels.length>0||IL.filter||U4.virtualModelsAvailable)&&e(_)});var v=N(g,2),y=e=>{{let t=k(()=>U4.modelLoadingText());b1(e,{get label(){return F(t)},class:`models-loading-state`})}},b=k(()=>U4.modelsBusy()&&!F(n));B(v,e=>{F(b)&&e(y)});var x=N(v,2);C6(x,{});var S=N(x,2);N6(S,{});var C=N(S,2),w=e=>{s6(e,{})};B(C,e=>{(U4.displayModels.length>0||IL.filter)&&e(w)});var T=N(C,2),ee=e=>{R(e,h8())};B(T,e=>{U4.displayModels.length===0&&!IL.loading&&!F(n)&&!IL.filter&&(IL.activeCategory===`all`||!IL.activeCategory)&&e(ee)});var te=N(T,2),ne=e=>{R(e,g8())};B(te,e=>{U4.displayModels.length===0&&!IL.loading&&!F(n)&&!IL.filter&&IL.activeCategory&&IL.activeCategory!==`all`&&e(ne)});var re=N(te,2),ie=e=>{R(e,_8())};B(re,e=>{U4.displayModels.length>0&&U4.filteredDisplayModels.length===0&&IL.filter&&e(ie)});var ae=N(re,2);s8(ae,{});var oe=N(ae,2);E2(oe,{});var se=N(oe,2);V6(se,{}),X6(N(se,2),{}),E(r),R(e,r),O()}Ur([`click`]);var b8=`draft-workflow-preview`;function x8(){return{scope_provider:``,scope_model:``,scope_user_path:``,name:``,description:``,features:{cache:!0,audit:!0,usage:!0,budget:!0,guardrails:!1,failover:!0},guardrails:[]}}function S8(){return{scope_provider:``,scope_model:``,scope_user_path:``}}function C8(e){return{ref:``,step:Number.isFinite(e)?e:10}}function w8(e){let t=e==null?``:String(e).trim();if(t===``)return NaN;let n=Number(t);return Number.isFinite(n)?n:NaN}function T8(e,t,n){if(!e||typeof e!=`object`||Array.isArray(e))return n;let r=t.charAt(0).toUpperCase()+t.slice(1);for(let n of[t,r])if(Object.prototype.hasOwnProperty.call(e,n)&&e[n]!==null&&e[n]!==void 0)return e[n];return n}function E8(e,t){return!e||typeof e!=`object`||Array.isArray(e)?!1:[t,t.charAt(0).toUpperCase()+t.slice(1)].some(t=>Object.prototype.hasOwnProperty.call(e,t)&&e[t]!==null&&e[t]!==void 0)}function D8(e){return{cache:!!T8(e,`cache`,!1),audit:!!T8(e,`audit`,!1),usage:!!T8(e,`usage`,!1),budget:T8(e,`budget`,!0)!==!1,guardrails:!!T8(e,`guardrails`,!1),failover:T8(e,`failover`,!0)!==!1}}function O8(e,t){let n=D8(e),r=t||{},i=n.usage&&!!r.usage;return{cache:n.cache&&!!r.cache,audit:n.audit&&!!r.audit,usage:i,budget:i&&n.budget&&!!r.budget,guardrails:n.guardrails&&!!r.guardrails,failover:n.failover&&!!r.failover}}function k8(e,t){let n=e&&e.workflow_payload&&e.workflow_payload.features?e.workflow_payload.features:e&&e.features?e.features:{};return{...O8((e&&e.effective_features&&typeof e.effective_features==`object`&&!Array.isArray(e.effective_features)?e.effective_features:null)||n,t),failover:D8(n).failover}}function A8(e,t){return k8(e,t).failover?`On`:`Off`}function j8(e){return(Array.isArray(e&&e.workflow_payload&&e.workflow_payload.guardrails)?e.workflow_payload.guardrails:Array.isArray(e&&e.guardrails)?e.guardrails:[]).map(e=>({ref:String(e&&e.ref||``).trim(),step:w8(e&&e.step)})).filter(e=>Number.isInteger(e.step)&&e.step>=0)}function M8(e,t){return k8(e,t).guardrails&&Array.isArray(e&&e.workflow_payload&&e.workflow_payload.guardrails)?e.workflow_payload.guardrails:[]}function N8(e){return String(e&&(e.scope_provider_name||e.scope_provider)||``).trim()}function P8(e){return String(e&&(e.provider_name||e.provider_type)||``).trim()}function F8(e,t){let n=new Set,r=String(t&&t.scope_provider||``).trim();return r&&n.add(r),(Array.isArray(e)?e:[]).forEach(e=>{let t=P8(e);t&&n.add(t)}),[...n].sort()}function I8(e,t,n){let r=String(t||``).trim(),i=new Set,a=String(n&&n.scope_provider||``).trim(),o=String(n&&n.scope_model||``).trim();return r&&r===a&&o&&i.add(o),(Array.isArray(e)?e:[]).forEach(e=>{if(r&&P8(e)!==r)return;let t=String(e&&e.model&&e.model.id||``).trim();t&&i.add(t)}),[...i].sort()}function L8(e){let t=String(e&&e.scope_type||``).trim();return t===`provider_model`?`Provider Name + Model`:t===`provider_model_path`?`Provider Name + Model + Path`:t===`provider_path`?`Provider Name + Path`:t===`path`?`Path`:t===`provider`?`Provider Name`:`Global`}function R8(e){return String(e&&e.scope_display||`global`).trim()||`global`}function z8(e){let t=String(e&&e.name||``).trim();if(t)return t;let n=R8(e);return n===`global`?`All models`:n}function B8(e){let t=String(e||``).trim();if(!t)return``;let n=(t.startsWith(`/`)?t:`/`+t).split(`/`);for(let e of n){let t=String(e||``).trim();if(t){if(t===`.`||t===`..`)return`User path cannot contain "." or ".." segments.`;if(t.includes(`:`))return`User path cannot contain ":" segments.`}}return``}function V8(e){if(B8(e))return``;let t=String(e||``).trim();if(!t)return``;let n=(t.startsWith(`/`)?t:`/`+t).split(`/`),r=[];for(let e of n){let t=String(e||``).trim();t&&r.push(t)}return r.length?`/`+r.join(`/`):`/`}function H8(e){let t=e||x8(),n=String(t.scope_provider||``).trim(),r=V8(t.scope_user_path);return{scope_provider:n,scope_model:n?String(t.scope_model||``).trim():``,scope_user_path:r}}function U8(e){let t=String(e&&e.scope_provider||``).trim(),n=t?String(e&&e.scope_model||``).trim():``,r=V8(e&&e.scope_user_path);return!t&&!r?`global`:!t&&r?`path`:!n&&!r?`provider`:!n&&r?`provider_path`:r?`provider_model_path`:`provider_model`}function W8(e){let t=String(e&&e.scope_provider||``).trim(),n=t?String(e&&e.scope_model||``).trim():``,r=V8(e&&e.scope_user_path),i=U8({scope_provider:t,scope_model:n,scope_user_path:r});return i===`global`?`global`:i===`path`?r:i===`provider`?t:i===`provider_path`?t+` @ `+r:i===`provider_model_path`?t+`/`+n+` @ `+r:t+`/`+n}function G8(e,t){let n=t||S8(),r=N8(e&&e.scope),i=r?String(e&&e.scope&&e.scope.scope_model||``).trim():``,a=V8(e&&e.scope&&e.scope.scope_user_path);return r===String(n.scope_provider||``).trim()&&i===String(n.scope_model||``).trim()&&a===V8(n.scope_user_path)}function K8(e,t,n){let r=H8(t);return!(r.scope_provider!==``||r.scope_model!==``||r.scope_user_path!==``)&&!n?null:(Array.isArray(e)?e:[]).find(e=>G8(e,r))||null}function q8(e){return String(e&&e.scope_type||``).trim()!==`global`}function J8(e){let t=String(e||``).trim();return t?t.length<=14?t:t.slice(0,12)+`…`:`—`}function Y8(e,t){let n=Array.isArray(e)?e:[];if(!t)return n;let r=String(t).toLowerCase();return n.filter(e=>[e.name,e.description,e.scope_display,e.scope_type,N8(e&&e.scope),e.scope&&e.scope.scope_model,e.scope&&e.scope.scope_user_path,e.workflow_hash,...Array.isArray(e.workflow_payload&&e.workflow_payload.guardrails)?e.workflow_payload.guardrails.map(e=>e.ref):[]].some(e=>String(e||``).toLowerCase().includes(r)))}function X8(e,t){let n=e||x8(),r=H8(n),i=D8(n.features||{}),a=O8(i,t);a.failover=i.failover;let o=!!a.guardrails,s=o?j8(n):[];return{id:b8,scope_type:U8(r),scope_display:W8(r),scope:{scope_provider_name:r.scope_provider,scope_model:r.scope_model,...r.scope_user_path?{scope_user_path:r.scope_user_path}:{}},name:String(n.name||``).trim(),description:String(n.description||``).trim(),workflow_payload:{schema_version:1,features:{cache:!!a.cache,audit:!!a.audit,usage:!!a.usage,budget:!!a.budget,guardrails:o,failover:!!a.failover},guardrails:s}}}function Z8({form:e,caps:t,workflows:n=[],formHydrated:r=!1,hydratedScope:i=null}){let a=e||x8(),o=String(a.scope_provider||``).trim(),s=o?String(a.scope_model||``).trim():``,c=V8(a.scope_user_path),l=D8(a.features||{}),u=O8(l,t),d=K8(n,a,r),f=d&&d.workflow_payload&&d.workflow_payload.features,p=E8(f,`failover`),m=p?T8(f,`failover`,!0)!==!1:null,h=i||S8(),g=String(h.scope_provider||``).trim()===o&&String(h.scope_model||``).trim()===s&&V8(h.scope_user_path)===V8(c),_=!!(t&&t.failover),v=_||!!r&&g&&Object.prototype.hasOwnProperty.call(l,`failover`)||!r&&!!d&&p,y=u.guardrails?(Array.isArray(a.guardrails)?a.guardrails:[]).map(e=>({ref:String(e&&e.ref||``).trim(),step:w8(e&&e.step)})):[],b={scope_provider_name:o,scope_model:s,...c?{scope_user_path:c}:{},name:String(a.name||``).trim(),description:String(a.description||``).trim(),workflow_payload:{schema_version:1,features:{cache:!!u.cache,audit:!!u.audit,usage:!!u.usage,budget:!!u.budget,guardrails:!!u.guardrails},guardrails:y}};return v&&(b.workflow_payload.features.failover=!_&&!r&&d&&p?m:!!l.failover),b}function Q8(e,{models:t=[],hydratedScope:n=null}={}){let r=n||S8(),i=String(r.scope_provider||``).trim(),a=String(r.scope_model||``).trim(),o=String(e&&(e.scope_provider_name||e.scope_provider)||``).trim(),s=String(e&&e.scope_model||``).trim();if(o&&!F8(t,r).includes(o)&&o!==i)return`Choose a registered provider name.`;if(s&&!o)return`Model selection requires a provider name.`;if(s){let e=I8(t,o,r),n=o===i&&s===a;if(!e.includes(s)&&!n)return`Choose a registered model for the selected provider name.`}let c=B8(e.scope_user_path);if(c)return c;let l=e.workflow_payload&&e.workflow_payload.features?e.workflow_payload.features:{},u=Array.isArray(e.workflow_payload&&e.workflow_payload.guardrails)?e.workflow_payload.guardrails:[];if(!l.guardrails)return``;let d=new Set;for(let e of u){if(!e.ref)return`Each guardrail step needs a guardrail ref.`;if(!Number.isInteger(e.step)||e.step<0)return`Each guardrail step must use a non-negative integer step number.`;if(d.has(e.ref))return`Each guardrail ref may appear only once in a workflow.`;d.add(e.ref)}return``}var $8=new class{#e=A(fn([]));get workflows(){return F(this.#e)}set workflows(e){j(this.#e,e,!0)}#t=A(!0);get available(){return F(this.#t)}set available(e){j(this.#t,e,!0)}#n=A(!1);get loading(){return F(this.#n)}set loading(e){j(this.#n,e,!0)}#r=A(``);get error(){return F(this.#r)}set error(e){j(this.#r,e,!0)}#i=A(``);get filter(){return F(this.#i)}set filter(e){j(this.#i,e,!0)}#a=A(!1);get formOpen(){return F(this.#a)}set formOpen(e){j(this.#a,e,!0)}#o=A(!1);get submitting(){return F(this.#o)}set submitting(e){j(this.#o,e,!0)}#s=A(``);get deactivatingID(){return F(this.#s)}set deactivatingID(e){j(this.#s,e,!0)}#c=A(``);get formError(){return F(this.#c)}set formError(e){j(this.#c,e,!0)}#l=A(!1);get formHydrated(){return F(this.#l)}set formHydrated(e){j(this.#l,e,!0)}#u=A(fn(S8()));get hydratedScope(){return F(this.#u)}set hydratedScope(e){j(this.#u,e,!0)}#d=A(fn([]));get guardrailRefs(){return F(this.#d)}set guardrailRefs(e){j(this.#d,e,!0)}#f=A(fn(x8()));get form(){return F(this.#f)}set form(e){j(this.#f,e,!0)}#p=null;failoverVisible(){return aL.booleanFlag(`FAILOVER_ENABLED`,!0)}featureCaps(){return{cache:aL.cacheVisible(),audit:aL.auditVisible(),usage:aL.usageVisible(),budget:aL.budgetsVisible(),guardrails:aL.guardrailsVisible(),failover:this.failoverVisible()}}get filteredWorkflows(){return Y8(this.workflows,this.filter)}providerOptions(){return F8(IL.models,this.hydratedScope)}modelOptions(e){return I8(IL.models,e,this.hydratedScope)}activeScopeMatch(){return K8(this.workflows,this.form,this.formHydrated)}submitMode(){return this.activeScopeMatch()?`save`:`create`}submitLabel(){return this.submitMode()===`save`?`Save`:`Create`}submittingLabel(){return this.submitMode()===`save`?`Saving...`:`Creating...`}preview(){return X8(this.form,this.featureCaps())}openCreate(e){if(this.formOpen=!0,this.submitting=!1,this.formError=``,!e){this.formHydrated=!1,this.hydratedScope=S8(),this.form=x8();return}this.formHydrated=!0,this.hydratedScope={scope_provider:N8(e.scope),scope_model:String(e.scope&&e.scope.scope_model||``).trim(),scope_user_path:String(e.scope&&e.scope.scope_user_path||``).trim()};let t=e.workflow_payload&&e.workflow_payload.features?D8(e.workflow_payload.features):k8(e,this.featureCaps()),n=Array.isArray(e.workflow_payload&&e.workflow_payload.guardrails)?e.workflow_payload.guardrails.map(e=>({ref:String(e&&e.ref||``).trim(),step:w8(e&&e.step)})).filter(e=>Number.isInteger(e.step)&&e.step>=0):j8(e);this.form={scope_provider:N8(e.scope),scope_model:String(e.scope&&e.scope.scope_model||``),scope_user_path:String(e.scope&&e.scope.scope_user_path||``),name:String(e.name||``),description:String(e.description||``),features:{cache:!!t.cache,audit:!!t.audit,usage:!!t.usage,budget:!!t.budget,guardrails:!!t.guardrails,failover:!!t.failover},guardrails:n.map(e=>({ref:String(e&&e.ref||``),step:Number.isFinite(e&&e.step)?e.step:10}))}}closeForm(){this.formOpen=!1,this.submitting=!1,this.formError=``,this.formHydrated=!1,this.hydratedScope=S8(),this.form=x8()}setProvider(e){if(this.form.scope_provider=String(e||``).trim(),!this.form.scope_provider){this.form.scope_model=``;return}this.modelOptions(this.form.scope_provider).includes(String(this.form.scope_model||``).trim())||(this.form.scope_model=``)}addGuardrailStep(){let e=(Array.isArray(this.form.guardrails)?this.form.guardrails:[]).reduce((e,t)=>{let n=Number(t&&t.step);return Number.isFinite(n)?Math.max(e,n):e},0)+10;this.form.guardrails.push(C8(e))}removeGuardrailStep(e){Array.isArray(this.form.guardrails)&&this.form.guardrails.splice(e,1)}buildRequest(){return Z8({form:this.form,caps:this.featureCaps(),workflows:this.workflows,formHydrated:this.formHydrated,hydratedScope:this.hydratedScope})}async fetchWorkflows(){this.#p&&this.#p.abort();let e=new AbortController;this.#p=e,this.loading=!0,this.error=``;let t=setTimeout(()=>e.abort(),1e4);try{let t=await tL(`/admin/workflows`,{label:`workflows`,signal:e.signal});if(t.stale)return;if(t.status===503){this.available=!1,this.workflows=[];return}if(this.available=!0,!t.ok){this.workflows=[];return}this.workflows=Array.isArray(t.data)?t.data:[]}catch(t){if(rL(t)&&this.#p!==e)return;console.error(`Failed to fetch workflows:`,t),this.workflows=[],this.error=rL(t)?`Loading workflows timed out.`:`Unable to load workflows.`}finally{clearTimeout(t),this.#p===e&&(this.#p=null,this.loading=!1)}}async fetchGuardrailRefs(){try{let e=await tL(`/admin/workflows/guardrails`,{label:`workflow guardrails`});if(e.stale)return;this.guardrailRefs=e.ok&&Array.isArray(e.data)?e.data:[]}catch(e){console.error(`Failed to fetch workflow guardrails:`,e),this.guardrailRefs=[]}}async fetchPage(){await Promise.all([aL.ensureLoaded(),this.fetchWorkflows(),this.fetchGuardrailRefs()])}async submitForm(){if(this.submitting)return;this.formError=``;let e=this.buildRequest(),t=Q8(e,{models:IL.models,hydratedScope:this.hydratedScope});if(t){this.formError=t;return}this.submitting=!0;try{let t=await nL(`/admin/workflows`,`POST`,e,{label:`create workflow`});if(t.stale||t.status===401)return;if(!t.ok){this.formError=ZI(t,`Unable to create workflow.`),console.error(`Failed to create workflow:`,t.status,this.formError);return}K.success(`Workflow created and activated.`),this.closeForm(),this.fetchPage()}catch(e){console.error(`Failed to create workflow:`,e),this.formError=`Unable to create workflow.`}finally{this.submitting=!1}}async deactivate(e){let t=String(e&&e.id||``).trim();if(!t||this.deactivatingID||!q8(e))return;let n=z8(e);if(confirm(`Deactivate workflow "`+n+`"? Requests will fall back to the next active workflow for this scope.`)){this.deactivatingID=t;try{let e=await nL(`/admin/workflows/`+encodeURIComponent(t)+`/deactivate`,`POST`,void 0,{label:`deactivate workflow`});if(e.stale||e.status===401)return;if(!e.ok){let t=ZI(e,`Unable to deactivate workflow.`);console.error(`Failed to deactivate workflow:`,e.status,t),K.error(t);return}K.success(`Workflow deactivated.`),this.fetchPage()}catch(e){console.error(`Failed to deactivate workflow:`,e),K.error(`Unable to deactivate workflow.`)}finally{this.deactivatingID=``}}}};function e5(e){let t=String(e??``),n=typeof navigator<`u`?navigator.clipboard:null;if(n&&typeof n.writeText==`function`)return n.writeText(t);let r=typeof document<`u`?document:null;if(!r||!r.body||typeof r.execCommand!=`function`)return Promise.reject(Error(`Clipboard API unavailable`));let i=r.createElement(`textarea`);i.value=t,i.setAttribute(`readonly`,``),i.style.position=`fixed`,i.style.top=`0`,i.style.left=`0`,i.style.opacity=`0`;try{if(r.body.appendChild(i),i.focus(),i.select(),i.setSelectionRange(0,i.value.length),!r.execCommand(`copy`))throw Error(`execCommand copy returned false`)}finally{i.parentNode&&i.parentNode.removeChild(i)}return Promise.resolve()}function t5({resetDelayMs:e=2e3,logPrefix:t}={}){let n=fn({copied:!1,error:!1}),r=null;function i(){r!==null&&clearTimeout(r),r=null}function a(){i(),r=setTimeout(()=>{n.copied=!1,n.error=!1,r=null},e)}return{get copied(){return n.copied},get error(){return n.error},reset(){i(),n.copied=!1,n.error=!1},async copy(e,r){if(!(e==null||e===``)){i(),n.copied=!1,n.error=!1;try{await e5(typeof r==`function`?r(e):String(e)),n.copied=!0,n.error=!1}catch(e){console.error(t||`Failed to copy text:`,e),n.copied=!1,n.error=!0}a()}}}}var n5=L(``);function r5(e,t){D(t,!0);let n=ha(t,`workflowID`,3,``),r=t5({logPrefix:`Failed to copy workflow ID:`});Nn(()=>{n(),r.reset()});let i=k(()=>r.error?`Unable to copy workflow ID`:r.copied?`Workflow ID copied`:`Copy workflow ID`),a=k(()=>n()?F(i)+` `+n():F(i));async function o(e){e.preventDefault(),n()&&await r.copy(n())}var s=n5();let c;var l=N(M(s),4),u=M(l,!0);E(l);var d=N(l,2);W(M(d),{name:`copy`}),E(d),E(s),P(()=>{c=H(s,1,`workflow-pipeline-meta mono svelte-1viff7o`,null,c,{"workflow-pipeline-meta-copied":r.copied,"workflow-pipeline-meta-error":r.error}),U(s,`title`,F(i)),U(s,`aria-label`,F(a)),z(u,n())}),I(`click`,s,o),R(e,s),O()}Ur([`click`]);var i5=(e,t)=>{let n=()=>(t?.()).icon,r=()=>(t?.()).label,i=kt(()=>_((t?.()).variant,`workflow-node-feature`)),a=()=>(t?.()).state,o=()=>(t?.()).sub,s=()=>(t?.()).badge;var c=c5(),l=M(c),u=e=>{var t=a5();let r;W(M(t),{get name(){return n()}}),E(t),P(()=>r=H(t,1,`workflow-node-icon svelte-nbptrg`,null,r,{"workflow-node-icon-endpoint":F(i)===`workflow-node-endpoint`})),R(e,t)};B(l,e=>{n()&&e(u)});var d=N(l,2),f=M(d,!0);E(d);var p=N(d,2),m=e=>{var t=o5(),n=M(t,!0);E(t),P(()=>z(n,s())),R(e,t)};B(p,e=>{s()&&e(m)});var h=N(p,2),g=e=>{var t=s5(),n=M(t,!0);E(t),P(()=>z(n,o())),R(e,t)};B(h,e=>{o()&&e(g)}),E(c),P(()=>{H(c,1,`workflow-node ${F(i)??``} ${(a()||``)??``}`,`svelte-nbptrg`),z(f,r())}),R(e,c)},a5=L(`
`),o5=L(` `),s5=L(` `),c5=L(`
`),l5=L(`
`,1),u5=L(`
`,1),d5=L(`
`),f5=L(`
Async
`),p5=L(`
`);function m5(e,t){D(t,!0);let n=ha(t,`chart`,19,()=>({}));var r=p5();let i;var a=M(r),o=e=>{r5(e,{get workflowID(){return n().workflowID}})};B(a,e=>{n().workflowID&&e(o)});var s=N(a,2),c=M(s);i5(c,()=>({icon:`user`,label:`Client`,variant:`workflow-node-endpoint`}));var l=N(c,4);i5(l,()=>({icon:`database`,label:`Auth`,state:n().authNodeClass,sub:n().authNodeSublabel}));var u=N(l,2),d=e=>{var t=l5(),r=Cn(t);i5(N(r,2),()=>({icon:`database`,label:`Cache`,state:n().cacheNodeClass,badge:n().cacheStatusLabel})),P(()=>H(r,1,`workflow-conn ${(n().cacheConnClass||``)??``}`,`svelte-nbptrg`)),R(e,t)};B(u,e=>{n().showCache&&e(d)});var f=N(u,2),p=e=>{var t=u5();i5(N(Cn(t),2),()=>({icon:`wallet`,label:`Budget`,state:n().budgetNodeClass,badge:n().budgetStatusLabel})),R(e,t)};B(f,e=>{n().showBudget&&e(p)});var m=N(f,2),h=e=>{var t=u5();i5(N(Cn(t),2),()=>({icon:`shield`,label:`Guardrails`,sub:n().guardrailLabel})),R(e,t)};B(m,e=>{n().showGuardrails&&e(h)});var g=N(m,2),_=N(g,2);i5(_,()=>({label:n().aiLabel,variant:`workflow-node-ai`,state:n().aiNodeClass,sub:n().aiSublabel}));var v=N(_,2),y=e=>{var t=l5(),r=Cn(t);i5(N(r,2),()=>({icon:`maximize-2`,label:`Failover`,state:n().failoverNodeClass,badge:n().failoverStatusLabel,sub:n().failoverTargetLabel})),P(()=>H(r,1,`workflow-conn ${(n().failoverConnClass||``)??``}`,`svelte-nbptrg`)),R(e,t)};B(v,e=>{n().showFailover&&e(y)});var b=N(v,2);i5(N(b,2),()=>({icon:`circle-check-big`,label:`Response`,variant:`workflow-node-endpoint`,state:n().responseNodeClass,sub:n().responseNodeSublabel})),E(s);var x=N(s,2),S=e=>{var t=f5(),r=M(t),i=M(r),a=e=>{i5(e,()=>({icon:`chart-column-increasing`,label:`Usage`,variant:`workflow-node-feature workflow-node-async`,state:n().usageNodeClass}))};B(i,e=>{n().showUsage&&e(a)});var o=N(i,2),s=e=>{R(e,d5())};B(o,e=>{n().showUsage&&n().showAudit&&e(s)});var c=N(o,2),l=e=>{i5(e,()=>({icon:`file-text`,label:`Audit Log`,variant:`workflow-node-feature workflow-node-async`,state:n().auditNodeClass}))};B(c,e=>{n().showAudit&&e(l)}),E(r),We(4),E(t),R(e,t)};B(x,e=>{n().showAsync&&e(S)}),E(r),P(()=>{i=H(r,1,`workflow-pipeline svelte-nbptrg`,null,i,{"workflow-pipeline-has-meta":n().workflowID}),H(g,1,`workflow-conn ${(n().aiConnClass||``)??``}`,`svelte-nbptrg`),H(b,1,`workflow-conn ${(n().responseConnClass||``)??``}`,`svelte-nbptrg`)}),R(e,r),O()}function h5(e){let t=j8(e).length;return t===0?``:t===1?`1 step`:t+` steps`}function g5(e,t){return t&&t.provider?t.provider:N8(e&&e.scope)||`AI`}function _5(e,t){return t&&t.model?t.model:e&&e.scope&&e.scope.scope_model||null}function v5(e,t){let n=String(e&&e.id||``).trim();if(n&&n!==`draft-workflow-preview`)return n;let r=String(t&&t.workflow_version_id||``).trim();return r&&r!==`draft-workflow-preview`?r:null}function y5(e){let t=e&&e.data&&e.data.workflow_features;return!t||typeof t!=`object`||Array.isArray(t)?null:D8(t)}function b5(e){let t=e&&e.data&&e.data.failover;if(!t||typeof t!=`object`||Array.isArray(t))return null;let n=String(t.target_model||t.targetModel||``).trim()||null;return n?{targetModel:n}:null}function x5(e,t=0){if(t>4||e==null)return``;if(typeof e==`string`){let n=e.trim();if(!n||n[0]!==`{`&&n[0]!==`[`)return``;try{return x5(JSON.parse(n),t+1)}catch{return``}}if(Array.isArray(e)){for(let n of e){let e=x5(n,t+1);if(e)return e}return``}return typeof e==`object`?String(e.code||``).trim()||(e.error===void 0?``:x5(e.error,t+1)):``}function S5(e){let t=e&&e.data&&typeof e.data==`object`&&!Array.isArray(e.data)?e.data:{};return String(t.error_code||t.errorCode||``).trim()||x5(t.response_body)}function C5(e){let t=String(e||``).trim();if(!t)return null;let n=t.indexOf(`/`);return n<=0||n>=t.length-1?null:{provider:t.slice(0,n),model:t.slice(n+1)}}function w5(e,t){let n=String(e&&(e.requested_model||e.model)||``).trim(),r=b5(e);if(!(r&&r.targetModel))return{provider:String(e&&e.provider||``).trim()||null,model:n||null};let i=C5(n);if(i)return i;let a=N8(t&&t.scope),o=a?String(t&&t.scope&&t.scope.scope_model||``).trim():``;return a||o?{provider:a||null,model:o||n||null}:{provider:null,model:n||null}}function T5(e,t){if(!e)return null;let n=(()=>{let t=String(e.cache_type||``).trim().toLowerCase();return t===`exact`||t===`semantic`?t:null})(),r=(()=>{if(e.status_code===void 0||e.status_code===null)return null;let t=String(e.status_code).trim();if(!t)return null;let n=Number(t);return Number.isFinite(n)?n:null})(),i=n?!0:e.cache_hit!==void 0&&e.cache_hit!==null&&!!e.cache_hit,a=b5(e),o=w5(e,t),s=Number.isFinite(r)&&r>=200&&r<300,c=String(e.error_type||``).trim().toLowerCase()===`authentication_error`,l=String(e.auth_method||``).trim().toLowerCase()||null,u=S5(e).toLowerCase()===`budget_exceeded`;return{cacheHit:i,cacheType:n||null,failoverTarget:a&&a.targetModel?a.targetModel:null,provider:o.provider,model:o.model,statusCode:r,responseSuccess:s,aiSuccess:s&&!i,authError:c,authMethod:l,budgetExceeded:u}}function E5(e){return!!(e&&e.cacheHit)}function D5(e){return!!(e&&e.failoverTarget)}function O5(e){return!!(e&&e.budgetExceeded)}function k5(e,t){return t?`workflow-node-current`:e&&e.cacheHit?`workflow-node-success`:``}function A5(e){return e&&e.cacheHit?`workflow-conn-hit`:``}function j5(e){return!e||!e.cacheHit?null:e.cacheType===`semantic`?`Hit (Semantic)`:`Hit (Exact)`}function M5(e,t,n,r){return e?O5(t)?`workflow-node-error`:r?`workflow-node-current`:n?`workflow-node-success`:``:``}function N5(e){return O5(e)?`Exceeded`:null}function P5(e){return e&&e.cacheHit?`workflow-node-skipped`:e&&e.failoverTarget?`workflow-node-success`:``}function F5(e){return e&&e.cacheHit?`workflow-conn-dim`:e&&e.failoverTarget?`workflow-conn-hit`:``}function I5(e){return e&&e.failoverTarget?`Redirected`:null}function L5(e){return e&&e.failoverTarget?e.failoverTarget:null}function R5(e){return e&&e.cacheHit?`workflow-conn-dim`:``}function z5(e,t){return e?e.cacheHit?`workflow-node-skipped`:t?`workflow-node-current`:e.aiSuccess?`workflow-node-success`:``:``}function B5(e,t){if(!e)return``;let n=e.statusCode;return!Number.isFinite(n)&&t?`workflow-node-current`:Number.isFinite(n)?n>=500?`workflow-node-error`:n>=400?`workflow-node-warning`:n>=300?`workflow-node-neutral`:n>=200?`workflow-node-success`:``:``}function V5(e){return!e||!Number.isFinite(e.statusCode)?null:String(e.statusCode)}function H5(e,t){return e?e.authError?`workflow-node-error`:t?`workflow-node-current`:e.authMethod===`api_key`||e.authMethod===`master_key`?`workflow-node-success`:``:``}function U5(e){return!e||!e.authMethod?null:e.authMethod}function W5(e,t,n){return e?n?`workflow-node-current`:t?`workflow-node-success`:``:``}function G5(e,t){if(!e||!e._live)return!!t;let n=String(e._live_state||``).trim();return!!e._audit_flushed||n===`audit.flushed`||n===`audit.detail`}function K5(e,t){if(!e)return!!t;let n=e.usage||{},r=Number(n.entries||0)>0;if(!e._live)return r;let i=String(e._usage_live_state||``).trim();return e._usage_flushed||i===`usage.flushed`?!0:!e._usage_live_pending&&r&&!e._live_pending}function q5(e){return!!(e&&e._live&&e._usage_live_pending&&!e._usage_flushed)}function J5(e,t){return!e||!e._live||G5(e,!1)?!1:String(e._live_state||``).trim()===`audit.completed`||!!(t&&Number.isFinite(t.statusCode))}function Y5(e,t,n){return!e||!e._live?``:q5(e)?`usage`:J5(e,t)?`audit`:G5(e,!1)&&!e._live_pending?``:t&&t.cacheHit?`cache`:t&&(t.provider||t.model)?`ai`:n&&n.budget&&(e.workflow_version_id||e.requested_model)?`budget`:t&&t.authMethod?``:`auth`}function X5(e,t,n,r){let i=n||{},a=i.features&&typeof i.features==`object`&&!Array.isArray(i.features)?D8(i.features):k8(e,r),o=!!i.forceAudit,s=!!i.highlightAsyncPresent,c=!!a.budget||O5(t),l=!!a.guardrails,u=!!a.usage,d=o||!!a.audit,f=!!i.forceAsync||!!(u||d),p=!!a.failover||D5(t),m=v5(e,i.entry),h=Y5(i.entry,t,a),g=q5(i.entry),_=J5(i.entry,t),v=G5(i.entry,s),y=K5(i.entry,s);return{showBudget:c,budgetNodeClass:M5(c,t,s,h===`budget`),budgetStatusLabel:N5(t),showGuardrails:l,guardrailLabel:l?h5(e):``,showCache:!!i.forceCache||!!a.cache||E5(t),cacheNodeClass:k5(t,h===`cache`),cacheConnClass:A5(t),cacheStatusLabel:j5(t),showFailover:p,failoverNodeClass:p?P5(t):``,failoverConnClass:p?F5(t):``,failoverStatusLabel:p?I5(t):null,failoverTargetLabel:p?L5(t):null,aiLabel:g5(e,t),aiSublabel:_5(e,t),aiConnClass:R5(t),aiNodeClass:z5(t,h===`ai`),responseConnClass:R5(t),responseNodeClass:B5(t,h===`response`),responseNodeSublabel:V5(t),authNodeClass:H5(t,h===`auth`),authNodeSublabel:U5(t),usageNodeClass:W5(u,y,g),auditNodeClass:W5(d,v,_),showAsync:f,showUsage:u,showAudit:d,workflowID:m}}function Z5(e,t){return X5(e,null,{forceCache:!1},t)}function Q5(e,t,n){return X5(t,T5(e,t),{entry:e,features:y5(e)||(t?k8(t,n):{cache:!1,audit:!1,usage:!1,budget:!1,guardrails:!1,failover:!1}),forceAudit:!0,forceAsync:!0,highlightAsyncPresent:!0},n)}var $5=L(`

`),e7=L(`

`),t7=L(`
`),n7=L(`
`),r7=L(`

No guardrails configured for this workflow.

`),i7=L(`

Guardrails

`),a7=L(``),o7=L(`

`);function s7(e,t){D(t,!0);let n=ha(t,`preview`,3,!1),r=k(()=>$8.featureCaps()),i=k(()=>z8(t.workflow)),a=k(()=>M8(t.workflow,F(r))),o=k(()=>Z5(t.workflow,F(r))),s=k(()=>n()?`draft-workflow-preview-guardrail-`:t.workflow.id+`-guardrail-`);var c=o7();let l;var u=M(c),d=M(u),f=M(d),p=M(f,!0);E(f);var m=N(f,2),h=M(m,!0);E(m),E(d);var g=N(d,2),_=M(g),v=M(_,!0);E(_),E(g),E(u);var y=N(u,2),b=e=>{var n=$5(),r=M(n,!0);E(n),P(()=>z(r,t.workflow.description)),R(e,n)};B(y,e=>{t.workflow.description&&e(b)});var x=N(y,2),S=e=>{var n=e7(),i=M(n);E(n),P(e=>z(i,`Failover: ${e??``}`),[()=>A8(t.workflow,F(r))]),R(e,n)},C=k(()=>$8.failoverVisible());B(x,e=>{F(C)&&e(S)});var w=N(x,2);m5(w,{get chart(){return F(o)}});var T=N(w,2),ee=e=>{var t=i7(),n=M(t),r=N(M(n),2),i=M(r,!0);E(r),E(n);var o=N(n,2),c=e=>{var t=n7();V(t,23,()=>F(a),(e,t)=>F(s)+t,(e,t)=>{var n=t7(),r=M(n),i=M(r,!0);E(r);var a=N(r,2),o=M(a);E(a),E(n),P(()=>{z(i,F(t).ref),z(o,`step ${F(t).step??``}`)}),R(e,n)}),E(t),R(e,t)},l=e=>{R(e,r7())};B(o,e=>{F(a).length>0?e(c):e(l,-1)}),E(t),P(()=>z(i,F(a).length?F(a).length+` steps`:`None`)),R(e,t)},te=k(()=>aL.guardrailsVisible());B(T,e=>{F(te)&&e(ee)});var ne=N(T,2),re=e=>{var n=a7(),r=M(n),a=M(r),o=M(a,!0);E(a);var s=N(a,2);{let e=k(()=>`Edit workflow `+F(i));S1(s,{get label(){return F(e)},class:`table-icon-btn`,onclick:()=>$8.openCreate(t.workflow),children:(e,t)=>{W(e,{name:`pencil`,class:`table-icon-svg`})},$$slots:{default:!0}})}E(r);var c=N(r,2),l=M(c),u=M(l);E(l);var d=N(l,2),f=M(d);E(d);var p=N(d,2),m=M(p);E(p),E(c),E(n),P((e,n,r,s)=>{a.disabled=e,U(a,`aria-label`,`Deactivate workflow `+F(i)),U(a,`title`,n),z(o,$8.deactivatingID===t.workflow.id?`Deactivating...`:`Deactivate`),z(u,`version: v${t.workflow.version??``}`),z(f,`created: ${r??``}`),z(m,`hash: ${s??``}`)},[()=>$8.deactivatingID===t.workflow.id||!q8(t.workflow),()=>q8(t.workflow)?`Deactivate active workflow`:`The global workflow cannot be deactivated.`,()=>YI.formatTimestamp(t.workflow.created_at),()=>J8(t.workflow.workflow_hash)]),I(`click`,a,()=>$8.deactivate(t.workflow)),R(e,n)};B(ne,e=>{n()||e(re)}),E(c),P((e,t)=>{l=H(c,1,`workflow-card svelte-1fo9fvq`,null,l,{"workflow-preview-card":n()}),z(p,e),z(h,F(i)),z(v,t)},[()=>L8(t.workflow),()=>R8(t.workflow)]),R(e,c),O()}Ur([`click`]);var c7=L(`

`),l7=L(``),u7=L(``),d7=L(`
`),f7=L(``),p7=L(``),m7=L(``),h7=L(``),g7=L(``),_7=L(``),v7=L(`
No named guardrails are currently registered on this deployment. You can still draft a workflow, but guardrail-backed creation may be rejected.
`),y7=L(`
`),b7=L(`
`),x7=L(`

No guardrail steps configured yet.

`),S7=L(`

Guardrail Steps

Guardrails in the same numeric step run together. Later steps wait for earlier ones to finish.

`),C7=L(``);function w7(e,t){D(t,!0);function n(){G.dialogOpen||$8.closeForm()}function r(e){e.preventDefault(),$8.submitForm()}pL(e,{get open(){return $8.formOpen},variant:`editor`,onclose:n,children:(e,t)=>{var i=C7(),a=M(i),o=M(a),s=M(o);fQ(M(s),{copyId:`workflow-help-copy`,label:`workflow help`,text:`Create immutable version. Submitting activates it for the selected scope.`,title:e=>{var t=c7(),n=M(t,!0);E(t),P(e=>z(n,e),[()=>$8.submitMode()===`save`?`Edit Workflow`:`Create Workflow`]),R(e,t)},$$slots:{title:!0}}),E(s),dL(N(s,2),{label:`Close workflow editor`,onclick:n}),E(o);var c=N(o,2),l=e=>{var t=l7(),n=M(t,!0);E(t),P(()=>z(n,$8.formError)),R(e,t)};B(c,e=>{$8.formError&&e(l)});var u=N(c,2),d=M(u),f=N(M(d),2),p=M(f);p.value=p.__value=``,V(N(p),16,()=>$8.providerOptions(),e=>e,(e,t)=>{var n=u7(),r=M(n,!0);E(n);var i={};P(()=>{z(r,t),i!==(i=t)&&(n.value=(n.__value=t)??``)}),R(e,n)}),E(f),E(d);var m=N(d,2),h=e=>{var t=d7(),n=N(M(t),2),r=M(n);r.value=r.__value=``,V(N(r),17,()=>$8.modelOptions($8.form.scope_provider),e=>$8.form.scope_provider+`-`+e,(e,t)=>{var n=u7(),r=M(n,!0);E(n);var i={};P(()=>{z(r,F(t)),i!==(i=F(t))&&(n.value=(n.__value=F(t))??``)}),R(e,n)}),E(n),E(t),Vi(n,()=>$8.form.scope_model,e=>$8.form.scope_model=e),R(e,t)};B(m,e=>{$8.form.scope_provider&&e(h)});var g=N(m,2),_=N(M(g),2);Qi(_),E(g);var v=N(g,2),y=N(M(v),2);Qi(y),E(v),E(u);var b=N(u,8),x=N(M(b),2);pt(x),E(b);var S=N(b,2),C=M(S),w=e=>{var t=f7(),n=M(t);Qi(n),We(2),E(t),ca(n,()=>$8.form.features.cache,e=>$8.form.features.cache=e),R(e,t)},T=k(()=>aL.cacheVisible());B(C,e=>{F(T)&&e(w)});var ee=N(C,2),te=e=>{var t=p7(),n=M(t);Qi(n),We(2),E(t),ca(n,()=>$8.form.features.audit,e=>$8.form.features.audit=e),R(e,t)},ne=k(()=>aL.auditVisible());B(ee,e=>{F(ne)&&e(te)});var re=N(ee,2),ie=e=>{var t=m7(),n=M(t);Qi(n),We(2),E(t),ca(n,()=>$8.form.features.usage,e=>$8.form.features.usage=e),R(e,t)},ae=k(()=>aL.usageVisible());B(re,e=>{F(ae)&&e(ie)});var oe=N(re,2),se=e=>{var t=h7(),n=M(t);Qi(n),We(2),E(t),ca(n,()=>$8.form.features.budget,e=>$8.form.features.budget=e),R(e,t)},ce=k(()=>aL.budgetsVisible());B(oe,e=>{F(ce)&&e(se)});var le=N(oe,2),ue=e=>{var t=g7(),n=M(t);Qi(n),We(2),E(t),ca(n,()=>$8.form.features.guardrails,e=>$8.form.features.guardrails=e),R(e,t)},de=k(()=>aL.guardrailsVisible());B(le,e=>{F(de)&&e(ue)});var fe=N(le,2),pe=e=>{var t=_7(),n=M(t);Qi(n),We(2),E(t),ca(n,()=>$8.form.features.failover,e=>$8.form.features.failover=e),R(e,t)},me=k(()=>$8.failoverVisible());B(fe,e=>{F(me)&&e(pe)}),E(S);var he=N(S,2),ge=N(M(he),2);{let e=k(()=>$8.preview());s7(ge,{get workflow(){return F(e)},preview:!0})}E(he);var _e=N(he,2),ve=e=>{var t=S7(),n=M(t),r=N(M(n),2);E(n);var i=N(n,2),a=e=>{var t=v7(),n=N(M(t),2);E(t),I(`click`,n,()=>LI.navigate(`guardrails`)),R(e,t)};B(i,e=>{$8.guardrailRefs.length===0&&e(a)});var o=N(i,2),s=e=>{var t=b7();V(t,21,()=>$8.form.guardrails,oi,(e,t,n)=>{var r=y7(),i=M(r),a=M(i);U(a,`for`,`workflow-guardrail-ref-`+n);var o=N(a,2);Qi(o),U(o,`id`,`workflow-guardrail-ref-`+n),U(o,`aria-label`,`Guardrail reference `+(n+1)),E(i);var s=N(i,2),c=M(s);U(c,`for`,`workflow-guardrail-step-`+n);var l=N(c,2);Qi(l),U(l,`id`,`workflow-guardrail-step-`+n),U(l,`aria-label`,`Guardrail step `+(n+1)),E(s);var u=N(s,2);E(r),sa(o,()=>F(t).ref,e=>F(t).ref=e),sa(l,()=>F(t).step,e=>F(t).step=e),I(`click`,u,()=>$8.removeGuardrailStep(n)),R(e,r)}),E(t),R(e,t)},c=e=>{R(e,x7())};B(o,e=>{$8.form.guardrails.length>0?e(s):e(c,-1)}),E(t),I(`click`,r,()=>$8.addGuardrailStep()),R(e,t)},ye=k(()=>$8.form.features.guardrails&&aL.guardrailsVisible());B(_e,e=>{F(ye)&&e(ve)});var be=N(_e,2),xe=M(be),Se=N(xe,2),Ce=M(Se),we=e=>{W(e,{name:`plus`,class:`form-action-icon`,"aria-hidden":`true`})},Te=k(()=>$8.submitMode()===`create`),Ee=e=>{W(e,{name:`save`,class:`form-action-icon`,"aria-hidden":`true`})};B(Ce,e=>{F(Te)?e(we):e(Ee,-1)});var De=N(Ce,2),Oe=M(De,!0);E(De),E(Se),E(be),E(a),E(i),P(e=>{Se.disabled=$8.submitting,z(Oe,e)},[()=>$8.submitting?$8.submittingLabel():$8.submitLabel()]),Hr(`submit`,a,r),I(`change`,f,e=>$8.setProvider(e.currentTarget.value)),Vi(f,()=>$8.form.scope_provider,e=>$8.form.scope_provider=e),sa(_,()=>$8.form.name,e=>$8.form.name=e),sa(y,()=>$8.form.scope_user_path,e=>$8.form.scope_user_path=e),sa(x,()=>$8.form.description,e=>$8.form.description=e),I(`click`,xe,n),R(e,i)},$$slots:{default:!0}}),O()}Ur([`change`,`click`]);var T7=L(`

Loading workflows...

`),E7=L(`
`),D7=L(`

No active workflows found.

`),O7=L(`

No workflows match your filter.

`),k7=L(`
`);function A7(e,t){D(t,!0);var n=k7(),r=M(n),i=e=>{var t=T7();LZ(M(t),{size:16,label:`Loading workflows`}),We(),E(t),R(e,t)};B(r,e=>{$8.loading&&!G.authError&&e(i)});var a=N(r,2),o=e=>{var t=E7();V(t,21,()=>$8.filteredWorkflows,e=>e.id,(e,t)=>{s7(e,{get workflow(){return F(t)}})}),E(t),R(e,t)};B(a,e=>{$8.filteredWorkflows.length>0&&e(o)});var s=N(a,2),c=e=>{R(e,D7())};B(s,e=>{$8.workflows.length===0&&!$8.loading&&!G.authError&&$8.available&&e(c)});var l=N(s,2),u=e=>{R(e,O7())};B(l,e=>{$8.workflows.length>0&&$8.filteredWorkflows.length===0&&!$8.loading&&e(u)}),E(n),R(e,n),O()}var j7=L(``),M7=L(`
Workflows feature is unavailable.
`),N7=L(`
`),P7=L(`
`),Cte=L(``),wte=L(`
`);function Tte(e,t){D(t,!0),Nn(()=>{G.refreshTick,$8.fetchPage()});var n=wte(),r=M(n),i=N(M(r),2),a=M(i),o=e=>{var t=j7();W(M(t),{name:`plus`,class:`form-action-icon`,"aria-hidden":`true`}),We(2),E(t),I(`click`,t,()=>$8.openCreate()),R(e,t)};B(a,e=>{$8.available&&e(o)}),E(i),E(r);var s=N(r,2),c=e=>{R(e,M7())};B(s,e=>{!$8.available&&!G.authError&&e(c)});var l=N(s,2),u=e=>{var t=N7(),n=M(t,!0);E(t),P(()=>z(n,$8.error)),R(e,t)};B(l,e=>{$8.error&&!G.authError&&e(u)});var d=N(l,2),f=e=>{var t=P7(),n=M(t);T$(M(n),{placeholder:`Filter by scope, name, hash, or guardrail...`,label:`Filter workflows by scope, name, hash, or guardrail`,get value(){return $8.filter},set value(e){$8.filter=e}}),E(n);var r=N(n,2),i=M(r),a=M(i,!0);E(i),E(r),E(t),P(()=>z(a,$8.filteredWorkflows.length+` active scopes`)),R(e,t)};B(d,e=>{$8.available&&e(f)});var p=N(d,2);w7(p,{});var m=N(p,2);A7(m,{});var h=N(m,2);V(h,20,()=>$8.guardrailRefs,e=>e,(e,t)=>{var n=Cte(),r={};P(()=>{r!==(r=t)&&(n.value=(n.__value=t)??``)}),R(e,n)}),E(h),E(n),R(e,n),O()}Ur([`click`]);var F7=new class{#e=A(fn({}));get workflowVersionsByID(){return F(this.#e)}set workflowVersionsByID(e){j(this.#e,e,!0)}workflowVersionRequests={};workflowFeatureCaps(){return{cache:aL.cacheVisible(),audit:aL.auditVisible(),usage:aL.usageVisible(),budget:aL.budgetsVisible(),guardrails:aL.guardrailsVisible(),failover:aL.booleanFlag(`FAILOVER_ENABLED`,!0)}}cacheWorkflowVersion(e){let t=String(e&&e.id||``).trim();return t?(this.workflowVersionsByID={...this.workflowVersionsByID||{},[t]:e},e):null}cacheMissingWorkflowVersion(e){let t=String(e||``).trim();t&&(this.workflowVersionsByID={...this.workflowVersionsByID||{},[t]:null})}workflowVersionCacheHas(e){return Object.prototype.hasOwnProperty.call(this.workflowVersionsByID||{},String(e||``).trim())}workflowVersionByID(e){let t=String(e||``).trim();return t&&this.workflowVersionCacheHas(t)?this.workflowVersionsByID[t]:null}async fetchWorkflowVersion(e){let t=String(e||``).trim();if(!t)return null;if(this.workflowVersionCacheHas(t))return this.workflowVersionsByID[t];if(this.workflowVersionRequests[t])return this.workflowVersionRequests[t];let n=(async()=>{let e=typeof AbortController==`function`?new AbortController:null,n=e?setTimeout(()=>e.abort(),1e4):null;try{let n=await tL(`/admin/workflows/`+encodeURIComponent(t),{label:`workflow`,signal:e?e.signal:void 0});if(n.stale)return null;if(n.status===404)return this.cacheMissingWorkflowVersion(t),null;if(!n.ok)return null;let r=n.data;return!r||typeof r!=`object`||Array.isArray(r)?(this.cacheMissingWorkflowVersion(t),null):this.cacheWorkflowVersion(r)}catch(e){return e&&e.name===`AbortError`||console.error(`Failed to fetch workflow version:`,e),null}finally{n!==null&&clearTimeout(n),delete this.workflowVersionRequests[t]}})();return this.workflowVersionRequests[t]=n,n}async prefetchAuditWorkflows(e){let t=[...new Set((Array.isArray(e)?e:[]).map(e=>String(e&&e.workflow_version_id||``).trim()).filter(Boolean))];t.length!==0&&await Promise.all(t.map(e=>this.fetchWorkflowVersion(e)))}auditEntryWorkflow(e){let t=String(e&&e.workflow_version_id||``).trim();return t?this.workflowVersionByID(t):null}};function I7(e){try{return JSON.parse(e)}catch{return null}}function L7(e,t){let n=String(e||``).trim();if(!n)return``;if(t>6)return n;let r=I7(n);return r==null?n:R7(r,t+1)||n}function Ete(e){return e==null?``:typeof e==`string`?L7(e,0):R7(e,0)}function R7(e,t){if(e==null||t>6)return``;if(typeof e==`string`){let n=I7(e.trim());return n==null?``:R7(n,t+1)}if(Array.isArray(e)){for(let n=0;n=400||Ote(t&&t.response_body)}function Ate(e){let t=e&&e.data?e.data:null;return t?Ete(t.error_message)||(kte(e,t)?R7(t.response_body,0):``):``}function z7(e){if(e==null||String(e).trim()===``)return null;let t=Number(e);return!Number.isInteger(t)||t<0?null:t}function jte(e){let t=z7(e);return t===null?``:t===0?`Audit logs are retained indefinitely.`:t===1?`Audit logs are retained for 1 day.`:`Audit logs are retained for `+t+` days.`}function Mte(e){let t=z7(e);return t===null?``:t===0?`Audit logs are retained `:`Audit logs are retained for `}function Nte(e){let t=z7(e);return t===null?``:t===0?`indefinitely`:t===1?`1 day`:t+` days`}function Pte({dateQuery:e,limit:t,offset:n,search:r,method:i,statusCode:a,stream:o}){let s=e;return s+=`&limit=`+t+`&offset=`+n,r&&(s+=`&search=`+encodeURIComponent(r)),i&&(s+=`&method=`+encodeURIComponent(i)),a&&(s+=`&status_code=`+encodeURIComponent(a)),o&&(s+=`&stream=`+encodeURIComponent(o)),s}function B7(e){return String(e&&e.id||``).trim()}function V7(e){if(!e)return[];let t=[],n=String(e.id||``).trim(),r=String(e.request_id||``).trim();return n&&t.push(`id:`+n),r&&t.push(`request:`+r),t}function Fte(e){return!!(e&&e._live&&e._live_pending&&!e._audit_flushed)}function Ite(e){let t=e&&e.customStartDate,n=e&&e.customEndDate;if(!t&&!n)return!0;let r=new Date;if(t){let e=new Date(t);if(e.setHours(0,0,0,0),Number.isFinite(e.getTime())&&re)return!1}return!0}function Lte(e,t){return e&&Number(e.offset||0)===0&&!(t&&t.search)&&!(t&&t.method)&&!(t&&t.statusCode)&&!(t&&t.stream)&&Ite(t)}function Rte(e,t,n){let r=e&&typeof e==`object`?{...e}:{entries:[],total:0,limit:25,offset:0},i=Array.isArray(r.entries)?r.entries:[];if(r.entries=i,!Lte(r,n))return r;let a=(Array.isArray(t)?t:[]).filter(e=>Fte(e));if(a.length===0)return r;let o=new Set(i.flatMap(e=>V7(e))),s=[];return a.forEach(e=>{let t=V7(e);t.length!==0&&(t.some(e=>o.has(e))||(t.forEach(e=>o.add(e)),s.push(e)))}),s.length===0?r:(r.entries=[...s,...i].slice(0,r.limit||25),r.total=Number(r.total||0)+s.length,r)}function zte(e,t){let n=B7(t),r=e||{};return!n||r[n]?r:{...r,[n]:!0}}function Bte(e,t){let n=e||{},r=new Set((Array.isArray(t)?t:[]).map(e=>B7(e)).filter(Boolean)),i={},a=!1;return Object.keys(n).forEach(e=>{if(r.has(e)){i[e]=!0;return}a=!0}),a?i:n}function Vte(e){if(e==null)return`-`;let t=Number(e);return Number.isFinite(t)?t<=0?`pending`:t<1e6?Math.round(t/1e3)+` µs`:t<1e9?(t/1e6).toFixed(2)+` ms`:(t/1e9).toFixed(2)+` s`:`-`}function H7(e){if(e==null||e===``)return`status-unknown`;let t=Number(e);return Number.isFinite(t)?t>=500?`status-error`:t>=400?`status-warning`:t>=300?`status-neutral`:`status-success`:`status-unknown`}function U7(e){if(!e||!e._live||!e._live_pending)return!1;let t=String(e._live_state||``).trim();if(t===`audit.completed`||t===`audit.flushed`||t===`audit.detail`)return!1;if(e._response_partial)return!0;if(e.status_code!==null&&e.status_code!==void 0&&e.status_code!==``||Number(e.duration_ns||0)>0||e.error_type||e.error_message)return!1;let n=e.data||{};return!(n.response_headers||n.response_body||n.error_message)}function W7(e){let t=e&&e.data&&e.data.failover;return!t||typeof t!=`object`||Array.isArray(t)?null:String(t.target_model||t.targetModel||``).trim()||null}function G7(e){return(e&&e.data&&Array.isArray(e.data.attempts)?e.data.attempts:[]).map((e,t)=>({...e,seq:Number(e&&e.seq||t+1)})).sort((e,t)=>e.seq-t.seq)}function K7(e){let t=G7(e);return t.length>1||t.some(e=>!(e&&e.success))}function Hte(e){if(!e)return`-`;let t=e.status_code||e.status;return t?String(t):e.success?`ok`:`error`}function q7(e){return String(e&&e.kind||``).trim()||`attempt`}function Ute(e){if(!e)return`-`;let t=String(e.provider_name||``).trim(),n=String(e.provider_type||e.provider||``).trim();return t&&n&&t!==n?t+` (`+n+`)`:t||n||`-`}function Wte(e){return String(e&&e.model||``).trim()||`-`}function J7(e){let t=G7(e);return t.length>1||t.some(e=>!(e&&e.success))?t:[]}function Gte(e){return J7(e).length>0}function Kte(e){return G7(e).length+`×`}function Y7(e){let t=G7(e),n=t.filter(e=>!(e&&e.success)).length,r=t.length===1?`attempt`:`attempts`,i=t.length+` provider `+r;return n>0?i+` · `+n+` failed`:i}function qte(e){if(!e)return``;let t=[`#`+Number(e.seq||0)],n=q7(e);n&&n!==`attempt`&&t.push(n),t.push(Hte(e));let r=Ute(e);r&&r!==`-`&&t.push(r);let i=Wte(e);return i&&i!==`-`&&t.push(i),t.push(e.success?`succeeded`:`failed`),t.join(` · `)}function Jte(e,t){if(!t)return null;if(t.success){let t=e&&e.data?e.data:null;return t&&t.response_body!=null?t.response_body:null}return t.response_body!=null&&t.response_body!==``?t.response_body:null}function Yte(e){if(!e||e.success)return``;let t=String(e.error_message||``).trim(),n=String(e.error_code||``).trim(),r=String(e.error_type||``).trim();return t&&n?n+`: `+t:t||n||r||`Provider attempt failed`}function Xte(e,t){if(!t)return null;if(t.success){let t=e&&e.data?e.data:null;return t?t.response_headers:null}return t.response_headers||null}function Zte(e){let t=Number(e&&e.status_code);return Number.isFinite(t)&&t>0?t:null}function Qte(e,t){let n=!!(t&&t.success),r=e&&e.data?e.data:null,i=Jte(e,t),a=Xte(e,t),o=Yte(t),s=i!=null&&i!==``,c=q7(t),l=G7(e).length<=1;return{title:`Response`,direction:`response`,seq:l?0:Number(t&&t.seq||0),kind:l||c===`attempt`?``:c,statusCode:l?null:Zte(t),layout:`split`,entry:e,copyHeaders:a,copyBody:i,showErrorMessage:!!o,errorMessage:o,showHeaders:!!a,headers:a,showBody:s,body:i,showEmpty:!o&&!s&&!a,emptyMessage:`No response was captured for this attempt.`,showTooLarge:!!(n&&r&&r.response_body_too_big_to_handle),tooLargeMessage:`Response body was too large to capture.`}}function X7(e){return e&&e.data&&Array.isArray(e.data.request_revisions)?e.data.request_revisions:[]}function Z7(e){return X7(e).filter(e=>!(e&&e.no_change))}function $te(e){return X7(e).filter(e=>e&&e.no_change).map(e=>{let t=String(e.rewriter||`rewriter`);return{id:`step-`+Number(e.seq||0),rewriter:t,label:t+`: no change`,title:t+` ran and forwarded the request unchanged`}})}function ene(e){let t=Number(e&&e.bytes_before),n=Number(e&&e.bytes_after);if(!Number.isFinite(t)||!Number.isFinite(n)||t<=0||n>=t)return``;let r=(1-n/t)*100;return`-`+(r>=10?String(Math.round(r)):r.toFixed(1))+`%`}function tne(e,t){let n=t&&t.body,r=n!=null&&n!==``,i=Z7(e).length<=1,a={rewriter:t&&t.rewriter||``,bytes:Number(t&&t.bytes_before||0)+` → `+Number(t&&t.bytes_after||0)};return t&&t.detail!=null&&(a.detail=t.detail),{title:`Rewritten`,direction:`request`,seq:i?0:Number(t&&t.seq||0),kind:t&&t.rewriter?String(t.rewriter):``,savingsLabel:ene(t),layout:`split`,entry:e,copyHeaders:a,copyBody:n,showErrorMessage:!1,errorMessage:null,showHeaders:!0,headers:a,headersTitle:`What changed`,showBody:r,body:n,showEmpty:!1,emptyMessage:``,showTooLarge:!r,tooLargeMessage:`Rewritten body not captured (body logging disabled or body too large).`}}function Q7(e){let t=e&&e.usage;return!t||typeof t!=`object`?null:t}function nne(e){let t=Q7(e);return Number(t&&t.cached_input_tokens||0)>0}function rne(e){let t=Q7(e),n=Number(t&&t.input_tokens||0),r=Number(t&&t.cached_input_tokens||0);return!Number.isFinite(n)||n<=0||!Number.isFinite(r)||r<=0?0:Math.max(0,Math.min(100,r/n*100))}function ine(e){let t=Q7(e);if(!t)return``;let n=Number(t.input_tokens||0),r=Number(t.cached_input_tokens||0);return n<=0?BL(r)+` cached`:rne(e).toFixed(1)+`% cached`}function ane(e){return nne(e)?ine(e):``}function one(e,t){let n=Q7(e);if(!n||!e||!e.data||!e.data.request_body)return null;let r=Number(n.estimated_cached_characters||0);if(!Number.isFinite(r)||r<=0||typeof t!=`function`)return null;let i=t(e.data.request_body);return!Array.isArray(i)||i.length===0?null:{characters:r,segments:i}}function $7(e){if(e==null||e===void 0||e===``)return`Not captured`;if(typeof e==`string`){let t=e.trim();if(t.startsWith(`{`)&&t.endsWith(`}`)||t.startsWith(`[`)&&t.endsWith(`]`))try{return JSON.stringify(JSON.parse(t),null,2)}catch{return e}return e}try{return JSON.stringify(e,null,2)}catch{return String(e)}}function sne(e,t){let n=e&&e.data?e.data:null,r=!n||!n.request_headers&&!n.request_body,i=r&&U7(e);return{title:`Request`,direction:`request`,layout:`split`,entry:e,copyHeaders:n&&n.request_headers,copyBody:n&&n.request_body,showErrorMessage:!1,errorMessage:null,showHeaders:!!(n&&n.request_headers),headers:n&&n.request_headers,showBody:!!(n&&n.request_body),body:n&&n.request_body,bodyCacheRatioLabel:ane(e),promptCacheHighlight:one(e,t),noChangeSteps:$te(e),showEmpty:r&&!i,emptyMessage:`Request details were not captured.`,showPending:i,pendingMessage:`Waiting for request data…`,showTooLarge:!!(n&&n.request_body_too_big_to_handle),tooLargeMessage:`Request body was too large to capture.`}}function cne(e){let t=e&&e.data?e.data:null,n=Ate(e),r=!t||!n&&!t.response_headers&&!t.response_body,i=r&&U7(e);return{title:`Response`,direction:`response`,layout:`split`,entry:e,copyHeaders:t&&t.response_headers,copyBody:t&&t.response_body,showErrorMessage:!!n,errorMessage:n,showHeaders:!!(t&&t.response_headers),headers:t&&t.response_headers,showBody:!!(t&&t.response_body),body:t&&t.response_body,streaming:!!(e&&e._response_partial&&t&&t.response_body)&&U7(e),showEmpty:r&&!i,emptyMessage:`Response details were not captured.`,showPending:i,pendingMessage:`Response in progress…`,showTooLarge:!!(t&&t.response_body_too_big_to_handle),tooLargeMessage:`Response body was too large to capture.`}}function e9(e,t){let n=[{id:`request`,pane:sne(e,t)}];return Z7(e).forEach(t=>{n.push({id:`revision-`+Number(t&&t.seq||0),pane:tne(e,t)})}),K7(e)?G7(e).forEach(t=>{n.push({id:`response-`+Number(t&&t.seq||0),pane:Qte(e,t)})}):n.push({id:`response`,pane:cne(e)}),n}function lne(e){if(!K7(e))return`response`;let t=G7(e),n=null;return t.forEach(e=>{e&&e.success&&(n=e)}),n||=t[t.length-1],n?`response-`+Number(n.seq||0):`request`}function une(e,t){return e&&e9(t).some(t=>t.id===e)?e:lne(t)}function dne(e,t,n){if(!t||!t.length)return null;let r=t.indexOf(n);r<0&&(r=0);let i;switch(e){case`ArrowRight`:case`ArrowDown`:i=(r+1)%t.length;break;case`ArrowLeft`:case`ArrowUp`:i=(r-1+t.length)%t.length;break;case`Home`:i=0;break;case`End`:i=t.length-1;break;default:return null}return t[i]}function t9(){return{entries:[],total:0,limit:25,offset:0}}var n9=new class{#e=A(fn({}));get auditExpandedEntries(){return F(this.#e)}set auditExpandedEntries(e){j(this.#e,e,!0)}#t=A(!1);get loading(){return F(this.#t)}set loading(e){j(this.#t,e,!0)}auditFetchToken=0;get auditLog(){return zQ.auditLog}set auditLog(e){zQ.auditLog=e}get auditSearch(){return zQ.auditSearch}set auditSearch(e){zQ.auditSearch=e}get auditMethod(){return zQ.auditMethod}set auditMethod(e){zQ.auditMethod=e}get auditStatusCode(){return zQ.auditStatusCode}set auditStatusCode(e){zQ.auditStatusCode=e}get auditStream(){return zQ.auditStream}set auditStream(e){zQ.auditStream=e}liveFilters(){return{search:this.auditSearch,method:this.auditMethod,statusCode:this.auditStatusCode,stream:this.auditStream,customStartDate:tR.customStartDate,customEndDate:tR.customEndDate}}async fetchAuditLog(e){let t=++this.auditFetchToken;this.loading=!0;try{e&&(this.auditLog.offset=0);let n=await tL(`/admin/audit/log?`+Pte({dateQuery:tR.queryStr(),limit:this.auditLog.limit,offset:this.auditLog.offset,search:this.auditSearch,method:this.auditMethod,statusCode:this.auditStatusCode,stream:this.auditStream}),{label:`audit log`});if(n.stale||t!==this.auditFetchToken)return;if(!n.ok){this.auditLog=t9();return}let r=Rte(n.data,this.auditLog&&this.auditLog.entries,this.liveFilters());Array.isArray(r.entries)||(r.entries=[]),this.auditLog=r,this.auditExpandedEntries=Bte(this.auditExpandedEntries,r.entries);try{await F7.prefetchAuditWorkflows(this.auditLog.entries)}catch(e){console.error(`Failed to prefetch audit workflows:`,e)}}catch(e){if(console.error(`Failed to fetch audit log:`,e),t!==this.auditFetchToken)return;this.auditLog=t9()}finally{t===this.auditFetchToken&&(this.loading=!1)}}clearAuditFilters(){this.auditSearch=``,this.auditMethod=``,this.auditStatusCode=``,this.auditStream=``,this.fetchAuditLog(!0)}auditLogNextPage(){this.auditLog.offset+this.auditLog.limit0&&(this.auditLog.offset=Math.max(0,this.auditLog.offset-this.auditLog.limit),this.fetchAuditLog(!1))}isAuditEntryExpanded(e){let t=B7(e);return t?!!(this.auditExpandedEntries&&this.auditExpandedEntries[t]):!1}markAuditEntryExpanded(e){this.auditExpandedEntries=zte(this.auditExpandedEntries,e)}};zQ.fetchAuditLog=e=>n9.fetchAuditLog(e),zQ.isAuditEntryExpanded=e=>n9.isAuditEntryExpanded(e);var fne=L(`
`);function pne(e,t){D(t,!0);let n=E$(()=>n9.fetchAuditLog(!0));Nn(()=>n.cancel);var r=fne(),i=M(r);T$(M(i),{id:`audit-filter-search`,placeholder:`Search by request ID, model, provider, path, user path, or error...`,label:`Search by request ID, model, provider, path, user path, or error`,get oninput(){return n},get value(){return n9.auditSearch},set value(e){n9.auditSearch=e}}),E(i);var a=N(i,2),o=M(a),s=M(o);s.value=s.__value=``;var c=N(s);c.value=c.__value=`GET`;var l=N(c);l.value=l.__value=`POST`;var u=N(l);u.value=u.__value=`PUT`;var d=N(u);d.value=d.__value=`PATCH`;var f=N(d);f.value=f.__value=`DELETE`,E(o);var p=N(o,2),m=M(p);m.value=m.__value=``;var h=N(m);h.value=h.__value=`200`;var g=N(h);g.value=g.__value=`201`;var _=N(g);_.value=_.__value=`400`;var v=N(_);v.value=v.__value=`401`;var y=N(v);y.value=y.__value=`403`;var b=N(y);b.value=b.__value=`404`;var x=N(b);x.value=x.__value=`429`;var S=N(x);S.value=S.__value=`500`;var C=N(S);C.value=C.__value=`502`;var w=N(C);w.value=w.__value=`503`;var T=N(w);T.value=T.__value=`504`,E(p);var ee=N(p,2),te=M(ee);te.value=te.__value=``;var ne=N(te);ne.value=ne.__value=`true`;var re=N(ne);re.value=re.__value=`false`,E(ee);var ie=N(ee,2);W(M(ie),{name:`x`,class:`table-icon-svg`}),We(2),E(ie),E(a),E(r),I(`change`,o,()=>n9.fetchAuditLog(!0)),Vi(o,()=>n9.auditMethod,e=>n9.auditMethod=e),I(`change`,p,()=>n9.fetchAuditLog(!0)),Vi(p,()=>n9.auditStatusCode,e=>n9.auditStatusCode=e),I(`change`,ee,()=>n9.fetchAuditLog(!0)),Vi(ee,()=>n9.auditStream,e=>n9.auditStream=e),I(`click`,ie,()=>n9.clearAuditFilters()),R(e,r),O()}Ur([`change`,`click`]);var mne=L(` `),hne=L(``);function gne(e,t){D(t,!0);let n=k(()=>[{key:`provider`,text:XL(t.entry)||`-`},{key:`model`,text:t.entry.requested_model||t.entry.model||`-`,mono:!0},{key:`user_path`,text:t.entry.user_path,mono:!0},{key:`request_id`,text:`request_id: `+(t.entry.request_id||`-`),mono:!0},{key:`ip`,text:t.entry.client_ip&&`ip: `+t.entry.client_ip,mono:!0},{key:`auth_key_id`,text:t.entry.auth_key_id&&`auth_key_id: `+t.entry.auth_key_id,mono:!0},{key:`alias`,text:t.entry.alias_used&&`alias`,class:`audit-alias-badge`},{key:`resolved`,text:t.entry.alias_used&&t.entry.resolved_model&&`resolved: `+$L(t.entry),mono:!0},{key:`failover`,text:W7(t.entry)&&`failover: `+W7(t.entry),mono:!0},{key:`stream`,text:t.entry.stream&&`stream`},{key:`error_type`,text:t.entry.error_type}].filter(e=>!!e.text));var r=hne(),i=N(M(r),2);V(i,21,()=>F(n),e=>e.key,(e,t)=>{var n=mne();let r;var i=M(n,!0);E(n),P(()=>{r=H(n,1,`provider-badge ${(F(t).class||``)??``}`,`svelte-hyopt0`,r,{mono:F(t).mono}),z(i,F(t).text)}),R(e,n)}),E(i),E(r),R(e,r),O()}var _ne=new Set([`instructions`,`messages`,`input`,`previous_response_id`,`choices`,`output`]);function r9(e){if(e==null)return``;if(typeof e==`string`)return e.trim();if(Array.isArray(e))return e.map(e=>typeof e==`string`?e:!e||typeof e!=`object`?``:typeof e.text==`string`?e.text:typeof e.output_text==`string`?e.output_text:``).filter(Boolean).join(` `).trim();if(typeof e==`object`){if(typeof e.text==`string`)return e.text.trim();try{return JSON.stringify(e,null,2)}catch{return``}}return String(e).trim()}function i9(e){if(e==null)return[];if(typeof e==`string`)return e?[e]:[];if(Array.isArray(e))return e.flatMap(e=>typeof e==`string`?e?[e]:[]:!e||typeof e!=`object`?[]:typeof e.text==`string`?e.text?[e.text]:[]:typeof e.output_text==`string`&&e.output_text?[e.output_text]:[]);if(typeof e==`object`)return typeof e.text==`string`&&e.text?[e.text]:[];let t=String(e);return t?[t]:[]}function vne(e){if(e==null)return[];if(typeof e==`string`){let t=e.trim();return t?[{role:`user`,text:t}]:[]}if(!Array.isArray(e)){let t=r9(e);return t?[{role:`user`,text:t}]:[]}return e.map(e=>{if(!e||typeof e!=`object`)return null;let t=String(e.role||`user`).toLowerCase(),n=r9(e.content);return n?{role:t,text:n}:null}).filter(Boolean)}function yne(e){return!e||typeof e!=`object`?``:Array.isArray(e.content)?e.content.map(e=>e&&typeof e.text==`string`?e.text:``).filter(Boolean).join(` -`).trim():r9(e.content)}function bne(e){if(!e||typeof e!=`object`)return[];let t=[];return t.push(...i9(e.instructions)),Array.isArray(e.messages)&&e.messages.forEach(e=>{!e||typeof e!=`object`||t.push(...i9(e.content))}),typeof e.input==`string`?t.push(e.input):Array.isArray(e.input)?e.input.forEach(e=>{!e||typeof e!=`object`||(t.push(...i9(e.content)),typeof e.text==`string`&&t.push(e.text))}):e.input&&typeof e.input==`object`&&(t.push(...i9(e.input.content)),typeof e.input.text==`string`&&t.push(e.input.text)),t.map(e=>String(e||``)).filter(e=>e.length>0)}function a9(e){if(typeof e!=`string`)return null;try{return JSON.parse(e)}catch{return null}}function o9(e,t){let n=String(e||``).trim();if(!n)return``;if(t>=4)return n;let r=a9(n);return!r||typeof r!=`object`?n:s9(r,t+1)||r9(r)||n}function s9(e,t=0){let n=new Set,r=[e];for(;r.length>0;){let e=r.shift();if(!e||typeof e!=`object`||n.has(e))continue;if(n.add(e),Array.isArray(e)){for(let t=0;t!e||typeof e!=`object`?!1:e.type===`message`||e.role===`assistant`||e.role===`user`||e.role===`system`?!0:Array.isArray(e.content)?e.content.some(e=>!e||typeof e!=`object`?!1:typeof e.text==`string`||e.type===`output_text`||e.type===`input_text`):!1):!1}function Cne(e){if(!e)return!1;let t=String(e).toLowerCase();return t===`/v1/embeddings`||t===`/v1/embeddings/`||t.startsWith(`/v1/embeddings?`)||t.startsWith(`/v1/embeddings/`)}function wne(e){if(!e)return!1;let t=String(e).toLowerCase();return t===`/v1/chat/completions`||t===`/v1/chat/completions/`||t.startsWith(`/v1/chat/completions?`)||t.startsWith(`/v1/chat/completions/`)||t===`/v1/responses`||t===`/v1/responses/`||t.startsWith(`/v1/responses?`)||t.startsWith(`/v1/responses/`)}function Tne(e){let t=e&&e.data?e.data.request_body:null,n=e&&e.data?e.data.response_body:null,r=t&&(Array.isArray(t.messages)||t.input!==void 0||typeof t.instructions==`string`||typeof t.previous_response_id==`string`),i=n&&(Array.isArray(n.choices)||Sne(n.output));return!!(r||i)}function Ene(e){return!e||Cne(e.path)?!1:wne(e.path)||Tne(e)}function c9(e){let t=0,n=!1,r=!1,i=String(e||``);for(let e=0;e0&&a+1`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}function kne(e){return!!(e&&typeof e==`object`&&e.__audio__===!0)}function Ane(e){let t=Number(e||0);if(!Number.isFinite(t)||t<=0)return`0 B`;let n=[`B`,`KB`,`MB`,`GB`],r=0,i=t;for(;i>=1024&&r`
`+l9(t)+``+l9(Mne(e[t]))+`
`);return t.length?``:``}function Pne(e){let t=jne(e.content_type),n=l9(t+` · `+Ane(e.bytes)),r=Nne(e.meta);if(e.stored&&e.encoding===`base64`&&e.data){let i=String(e.data).replace(/[^A-Za-z0-9+/=]/g,``);return`
`+n+`
`+r+`
`}let i=e.too_large?`Audio too large to store.`:`Audio not logged. Set LOGGING_LOG_AUDIO_BODIES=true to capture playable audio.`;return`
`+n+`
`+l9(i)+`
`+r+`
`}function u9(e){try{return JSON.stringify(String(e)).slice(1,-1)}catch{return``}}function Fne(e){if(!e||typeof e!=`object`)return null;let t=Number(e.characters||0);if(!Number.isFinite(t)||t<=0)return null;let n=Array.isArray(e.segments)?e.segments.map(e=>String(e||``)).filter(Boolean):[];return n.length===0?null:{remaining:Math.floor(t),segments:n,segmentIndex:0}}function d9(e,t){if(!t||t.remaining<=0||t.segmentIndex>=t.segments.length)return l9(e);let n=``,r=0,i=0;for(;t.remaining>0&&t.segmentIndex`+l9(l)+``,r=s+l.length,i=s+o.length,t.remaining-=c,c>=a.length){t.segmentIndex++;continue}break}return n?n+l9(e.slice(r)):l9(e)}function Ine(e,t,n){let r=n&&typeof n.formatJSON==`function`?n.formatJSON:e=>String(e),i=n&&typeof n.canShowConversation==`function`?n.canShowConversation:()=>!1,a=Fne(n&&n.promptCacheHighlight),o=r(t);if(!o||o===`Not captured`)return l9(o);if(!i(e))return o.split(` +`).trim():r9(e.content)}function bne(e){if(!e||typeof e!=`object`)return[];let t=[];return t.push(...i9(e.instructions)),Array.isArray(e.messages)&&e.messages.forEach(e=>{!e||typeof e!=`object`||t.push(...i9(e.content))}),typeof e.input==`string`?t.push(e.input):Array.isArray(e.input)?e.input.forEach(e=>{!e||typeof e!=`object`||(t.push(...i9(e.content)),typeof e.text==`string`&&t.push(e.text))}):e.input&&typeof e.input==`object`&&(t.push(...i9(e.input.content)),typeof e.input.text==`string`&&t.push(e.input.text)),t.map(e=>String(e||``)).filter(e=>e.length>0)}function a9(e){if(typeof e!=`string`)return null;try{return JSON.parse(e)}catch{return null}}function o9(e,t){let n=String(e||``).trim();if(!n)return``;if(t>=4)return n;let r=a9(n);return!r||typeof r!=`object`?n:s9(r,t+1)||r9(r)||n}function s9(e,t=0){let n=new Set,r=[e];for(;r.length>0;){let e=r.shift();if(!e||typeof e!=`object`||n.has(e))continue;if(n.add(e),Array.isArray(e)){for(let t=0;t!e||typeof e!=`object`?!1:e.type===`message`||e.role===`assistant`||e.role===`user`||e.role===`system`?!0:Array.isArray(e.content)?e.content.some(e=>!e||typeof e!=`object`?!1:typeof e.text==`string`||e.type===`output_text`||e.type===`input_text`):!1):!1}function Cne(e){if(!e)return!1;let t=String(e).toLowerCase();return t===`/v1/embeddings`||t===`/v1/embeddings/`||t.startsWith(`/v1/embeddings?`)||t.startsWith(`/v1/embeddings/`)}function wne(e){if(!e)return!1;let t=String(e).toLowerCase();return t===`/v1/chat/completions`||t===`/v1/chat/completions/`||t.startsWith(`/v1/chat/completions?`)||t.startsWith(`/v1/chat/completions/`)||t===`/v1/responses`||t===`/v1/responses/`||t.startsWith(`/v1/responses?`)||t.startsWith(`/v1/responses/`)}function Tne(e){if(e&&e.conversation_payload)return!0;let t=e&&e.data?e.data.request_body:null,n=e&&e.data?e.data.response_body:null,r=t&&(Array.isArray(t.messages)||t.input!==void 0||typeof t.instructions==`string`||typeof t.previous_response_id==`string`),i=n&&(Array.isArray(n.choices)||Sne(n.output));return!!(r||i)}function Ene(e){return!e||Cne(e.path)?!1:wne(e.path)||Tne(e)}function c9(e){let t=0,n=!1,r=!1,i=String(e||``);for(let e=0;e0&&a+1`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}function kne(e){return!!(e&&typeof e==`object`&&e.__audio__===!0)}function Ane(e){let t=Number(e||0);if(!Number.isFinite(t)||t<=0)return`0 B`;let n=[`B`,`KB`,`MB`,`GB`],r=0,i=t;for(;i>=1024&&r`
`+l9(t)+``+l9(Mne(e[t]))+`
`);return t.length?``:``}function Pne(e){let t=jne(e.content_type),n=l9(t+` · `+Ane(e.bytes)),r=Nne(e.meta);if(e.stored&&e.encoding===`base64`&&e.data){let i=String(e.data).replace(/[^A-Za-z0-9+/=]/g,``);return`
`+n+`
`+r+`
`}let i=e.too_large?`Audio too large to store.`:`Audio not logged. Set LOGGING_LOG_AUDIO_BODIES=true to capture playable audio.`;return`
`+n+`
`+l9(i)+`
`+r+`
`}function u9(e){try{return JSON.stringify(String(e)).slice(1,-1)}catch{return``}}function Fne(e){if(!e||typeof e!=`object`)return null;let t=Number(e.characters||0);if(!Number.isFinite(t)||t<=0)return null;let n=Array.isArray(e.segments)?e.segments.map(e=>String(e||``)).filter(Boolean):[];return n.length===0?null:{remaining:Math.floor(t),segments:n,segmentIndex:0}}function d9(e,t){if(!t||t.remaining<=0||t.segmentIndex>=t.segments.length)return l9(e);let n=``,r=0,i=0;for(;t.remaining>0&&t.segmentIndex`+l9(l)+``,r=s+l.length,i=s+o.length,t.remaining-=c,c>=a.length){t.segmentIndex++;continue}break}return n?n+l9(e.slice(r)):l9(e)}function Ine(e,t,n){let r=n&&typeof n.formatJSON==`function`?n.formatJSON:e=>String(e),i=n&&typeof n.canShowConversation==`function`?n.canShowConversation:()=>!1,a=Fne(n&&n.promptCacheHighlight),o=r(t);if(!o||o===`Not captured`)return l9(o);if(!i(e))return o.split(` `).map(e=>d9(e,a)).join(` `);let s=o.split(` `),c=[],l=0;for(;ld9(e,a)).join(` diff --git a/internal/admin/dashboard/static/dist/index.html b/internal/admin/dashboard/static/dist/index.html index 91c31fb4c..b50ea995b 100644 --- a/internal/admin/dashboard/static/dist/index.html +++ b/internal/admin/dashboard/static/dist/index.html @@ -7,7 +7,7 @@ GoModel Dashboard - + diff --git a/internal/admin/handler.go b/internal/admin/handler.go index 204284d9b..e176f3b75 100644 --- a/internal/admin/handler.go +++ b/internal/admin/handler.go @@ -131,6 +131,17 @@ type providerStatusResponse struct { type auditLogEntryResponse struct { auditlog.LogEntry Usage *usage.RequestUsageSummary `json:"usage,omitempty"` + + // BodiesOmitted marks a list entry whose request/response bodies (and + // other heavy payloads) were stripped server-side; the full entry is + // available from GET /admin/audit/detail. It also tells the dashboard + // the entry is persisted — a slim entry is never an in-flight request. + BodiesOmitted bool `json:"bodies_omitted,omitempty"` + + // ConversationPayload reports that the entry's (stripped) bodies were + // shaped like a conversation, preserving the Interactions-drawer + // eligibility signal the dashboard otherwise sniffs from the bodies. + ConversationPayload bool `json:"conversation_payload,omitempty"` } type auditLogListResponse struct { diff --git a/internal/admin/handler_audit.go b/internal/admin/handler_audit.go index 700e76b4a..1ee14d520 100644 --- a/internal/admin/handler_audit.go +++ b/internal/admin/handler_audit.go @@ -27,6 +27,10 @@ const defaultAuditLogLimit = 25 // AuditLog handles GET /admin/audit/log // // @Summary Get paginated audit log entries +// @Description Entries are slimmed for the wire: request/response bodies, +// @Description per-attempt error bodies, and rewritten revision bodies are +// @Description omitted (bodies_omitted=true). Fetch /admin/audit/detail for +// @Description the full payload of one entry. // @Tags admin // @Produce json // @Security BearerAuth @@ -144,6 +148,11 @@ func (h *Handler) AuditLog(c *echo.Context) error { if err != nil { return handleError(c, err) } + // The list ships scalar metadata only; full payloads stay behind + // /admin/audit/detail (see slimAuditListEntry). + for i := range response.Entries { + slimAuditListEntry(&response.Entries[i]) + } return c.JSON(http.StatusOK, response) } @@ -291,6 +300,9 @@ func (h *Handler) AuditLogDetail(c *echo.Context) error { // AuditConversation handles GET /admin/audit/conversation // // @Summary Get conversation thread around an audit log entry +// @Description Thread entries carry the request/response bodies the +// @Description transcript is built from; attempts, request revisions, and +// @Description header maps are omitted. // @Tags admin // @Produce json // @Security BearerAuth @@ -341,6 +353,9 @@ func (h *Handler) AuditConversation(c *echo.Context) error { if result.Entries == nil { result.Entries = []auditlog.LogEntry{} } + for i := range result.Entries { + slimConversationEntry(&result.Entries[i]) + } return c.JSON(http.StatusOK, result) } diff --git a/internal/admin/handler_test.go b/internal/admin/handler_test.go index 0ddcb34f9..b7928290b 100644 --- a/internal/admin/handler_test.go +++ b/internal/admin/handler_test.go @@ -1008,8 +1008,12 @@ func TestAuditLog_Success(t *testing.T) { if result.Entries[0].ID != "log-1" { t.Errorf("expected entry id log-1, got %s", result.Entries[0].ID) } - if result.Entries[0].Data == nil || result.Entries[0].Data.RequestBody == nil { - t.Errorf("expected request body data to be present") + // List entries are slim: bodies live behind /admin/audit/detail. + if result.Entries[0].Data == nil { + t.Fatal("expected entry data to be present") + } + if result.Entries[0].Data.RequestBody != nil || result.Entries[0].Data.ResponseBody != nil { + t.Errorf("expected list entry bodies to be stripped, got %+v", result.Entries[0].Data) } } diff --git a/internal/auditlog/auditlog.go b/internal/auditlog/auditlog.go index db053ba80..5d9398380 100644 --- a/internal/auditlog/auditlog.go +++ b/internal/auditlog/auditlog.go @@ -379,6 +379,11 @@ type Config struct { // this is off, audio responses are recorded as a lightweight placeholder. LogAudioBodies bool + // LogRevisionBodies refines LogBodies for the request-revision chain: + // rewriters that changed the body store the full rewritten copy only when + // both are enabled. Revision metadata is always kept. + LogRevisionBodies bool + // LogHeaders enables logging of request/response headers LogHeaders bool diff --git a/internal/auditlog/factory.go b/internal/auditlog/factory.go index 759cb698a..0af3ab529 100644 --- a/internal/auditlog/factory.go +++ b/internal/auditlog/factory.go @@ -73,6 +73,7 @@ func buildLoggerConfig(logCfg config.LogConfig) Config { Enabled: logCfg.Enabled, LogBodies: logCfg.LogBodies, LogAudioBodies: logCfg.LogAudioBodies, + LogRevisionBodies: logCfg.LogRevisionBodies, LogHeaders: logCfg.LogHeaders, BufferSize: logCfg.BufferSize, FlushInterval: time.Duration(logCfg.FlushInterval) * time.Second, diff --git a/internal/server/http.go b/internal/server/http.go index fde8bc464..a77fa7109 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -449,12 +449,22 @@ func New(provider core.RoutableProvider, cfg *Config) *Server { // need dashboard access to pass the gate; the master key always does. if cfg != nil && cfg.AdminEndpointsEnabled && cfg.AdminHandler != nil { adminGate := AdminAccessMiddleware() - cfg.AdminHandler.RegisterRoutes(e.Group("/admin", adminGate)) + // Admin responses are large, highly compressible JSON (audit entries + // carrying request/response bodies, usage aggregates), so gzip cuts + // the wire size roughly an order of magnitude. The live-log SSE + // stream is exempt: each event must flush to the client immediately, + // and buffering it behind a compressor delays delivery. + adminGzip := middleware.GzipWithConfig(middleware.GzipConfig{ + Skipper: func(c *echo.Context) bool { + return strings.HasSuffix(c.Request().URL.Path, "/live/logs") + }, + }) + cfg.AdminHandler.RegisterRoutes(e.Group("/admin", adminGate, adminGzip)) // Legacy alias under /admin/api/v1/* — accepted until adminLegacySunset // to give operators a window to migrate. Responses carry Deprecation, // Sunset, and Link headers per RFC 8594 / draft-ietf-httpapi-deprecation-header. - legacy := e.Group("/admin/api/v1", adminLegacyDeprecationMiddleware, adminGate) + legacy := e.Group("/admin/api/v1", adminLegacyDeprecationMiddleware, adminGate, adminGzip) cfg.AdminHandler.RegisterRoutes(legacy) // DashboardConfig moved within /admin from /dashboard/config to // /runtime/config; preserve the historical legacy path explicitly. diff --git a/internal/server/request_rewrite.go b/internal/server/request_rewrite.go index 5e06f7d82..8ba39d304 100644 --- a/internal/server/request_rewrite.go +++ b/internal/server/request_rewrite.go @@ -141,7 +141,7 @@ func recordRequestRevision(c *echo.Context, auditLogger auditlog.LoggerInterface TokensSaved: res.TokensSaved, Detail: res.Detail, } - if cfg.LogBodies && int64(len(res.Body)) <= auditlog.MaxBodyCapture { + if cfg.LogBodies && cfg.LogRevisionBodies && int64(len(res.Body)) <= auditlog.MaxBodyCapture { revision.Body = auditlog.CaptureLoggedBody(res.Body) } auditlog.EnrichEntryWithRequestRevision(c, revision) diff --git a/internal/server/request_rewrite_test.go b/internal/server/request_rewrite_test.go index 534ba6a44..8a8dc7694 100644 --- a/internal/server/request_rewrite_test.go +++ b/internal/server/request_rewrite_test.go @@ -373,9 +373,9 @@ func TestRequestRewriteMiddlewareRecordsRevisions(t *testing.T) { }, } - run := func(t *testing.T, logBodies bool) *auditlog.LogEntry { + run := func(t *testing.T, logBodies, logRevisionBodies bool) *auditlog.LogEntry { t.Helper() - auditLogger := &capturingAuditLogger{config: auditlog.Config{Enabled: true, LogBodies: logBodies}} + auditLogger := &capturingAuditLogger{config: auditlog.Config{Enabled: true, LogBodies: logBodies, LogRevisionBodies: logRevisionBodies}} srv := New(newRewriteTestProvider(), &Config{ AuditLogger: auditLogger, RequestRewriters: []ext.RequestRewriter{ @@ -395,7 +395,7 @@ func TestRequestRewriteMiddlewareRecordsRevisions(t *testing.T) { } t.Run("with body logging", func(t *testing.T) { - entry := run(t, true) + entry := run(t, true, true) revisions := entry.Data.RequestRevisions if len(revisions) != 2 { t.Fatalf("expected 2 revisions, got %d", len(revisions)) @@ -421,7 +421,7 @@ func TestRequestRewriteMiddlewareRecordsRevisions(t *testing.T) { }) t.Run("without body logging", func(t *testing.T) { - entry := run(t, false) + entry := run(t, false, true) revisions := entry.Data.RequestRevisions if len(revisions) != 2 { t.Fatalf("expected 2 revisions, got %d", len(revisions)) @@ -435,6 +435,27 @@ func TestRequestRewriteMiddlewareRecordsRevisions(t *testing.T) { } } }) + + // LOGGING_LOG_REVISION_BODIES=false keeps the revision metadata but + // drops the rewritten body copy even though body logging is on. + t.Run("without revision body logging", func(t *testing.T) { + entry := run(t, true, false) + revisions := entry.Data.RequestRevisions + if len(revisions) != 2 { + t.Fatalf("expected 2 revisions, got %d", len(revisions)) + } + for _, revision := range revisions { + if revision.Body != nil { + t.Errorf("revision %d must not capture the body when revision body logging is off", revision.Seq) + } + if revision.BytesBefore == 0 || revision.BytesAfter == 0 { + t.Errorf("revision %d sizes missing", revision.Seq) + } + if revision.Detail == nil && revision.Rewriter == "swap" { + t.Error("rewriter detail must survive without the body") + } + } + }) } func TestRequestRewriteMiddlewareRecordsNoChangeRevisions(t *testing.T) { diff --git a/web/dashboard/src/pages/audit-logs/conversation-helpers.js b/web/dashboard/src/pages/audit-logs/conversation-helpers.js index 0bd0272b1..b0ba6d407 100644 --- a/web/dashboard/src/pages/audit-logs/conversation-helpers.js +++ b/web/dashboard/src/pages/audit-logs/conversation-helpers.js @@ -257,6 +257,10 @@ function isConversationalPath(path) { } function hasConversationPayload(entry) { + // Slim list entries carry the server-computed signal instead of the + // bodies it was derived from. + if (entry && entry.conversation_payload) return true; + const requestBody = entry && entry.data ? entry.data.request_body : null; const responseBody = entry && entry.data ? entry.data.response_body : null; diff --git a/web/dashboard/src/pages/audit-logs/live-logs-logic.js b/web/dashboard/src/pages/audit-logs/live-logs-logic.js index f8ce28f27..d414b7a9d 100644 --- a/web/dashboard/src/pages/audit-logs/live-logs-logic.js +++ b/web/dashboard/src/pages/audit-logs/live-logs-logic.js @@ -129,7 +129,9 @@ export function liveLogsMethods() { }); const previous = index >= 0 ? currentEntries[index] || {} : {}; if (eventType === 'audit.detail') { - const patch = { ...incoming, _detail_loaded: true, _response_partial: false }; + // The detail entry carries the full payload, so the slim-list + // marker must not survive the merge from the previous row. + const patch = { ...incoming, _detail_loaded: true, _response_partial: false, bodies_omitted: false }; if (index >= 0) { const merged = this.mergeLiveAuditPatch(previous, patch); currentEntries.splice(index, 1, merged); @@ -480,6 +482,10 @@ export function liveLogsMethods() { if (!entry || entry._detail_loading || entry._detail_loaded) return false; if (this.auditEntryLiveDetailPending(entry)) return false; if (this.auditEntryNeedsPersistedLiveDetail(entry)) return true; + // The list endpoint ships slim entries (bodies stripped + // server-side); the flag is the explicit signal that the full + // payload lives behind the detail endpoint. + if (entry.bodies_omitted) return true; return !this.auditEntryHasDetailData(entry); }, From d982710c641b1a0a0b39c4a03a2084faded15365 Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Tue, 28 Jul 2026 00:31:37 +0200 Subject: [PATCH 2/2] fix(audit): bound the conversation chain walk and fix its Postgres plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /admin/audit/conversation chain lookups run `WHERE = ? ORDER BY timestamp ASC LIMIT 1`. With the bare expression indexes, PostgreSQL's planner routinely prefers walking the timestamp index and filtering — detoasting every row's JSON blob and scanning the whole table when the value is rare. On a user deployment one request hung until the fronting proxy returned 502 Bad Gateway. Replace both expression indexes with composite (expression, timestamp) indexes so the filter and the order are satisfied by one index and the timestamp-scan plan is never attractive; the old indexes are dropped at store init. As a safety net for degraded stores, the handler now bounds the whole walk with a 10s deadline. A deadline expiring mid-walk returns the partial thread collected so far (ConversationResult.Truncated) instead of failing; only a timeout before the anchor loads surfaces as a 504. Co-Authored-By: Claude Fable 5 --- cmd/gomodel/docs/docs.go | 4 + docs/openapi.json | 4 + internal/admin/handler_audit.go | 20 +++- internal/auditlog/conversation_helpers.go | 38 +++++- .../auditlog/conversation_helpers_test.go | 109 ++++++++++++++++++ internal/auditlog/reader.go | 5 + internal/auditlog/store_sql.go | 22 +++- 7 files changed, 194 insertions(+), 8 deletions(-) diff --git a/cmd/gomodel/docs/docs.go b/cmd/gomodel/docs/docs.go index ac4d461f0..8551b2129 100644 --- a/cmd/gomodel/docs/docs.go +++ b/cmd/gomodel/docs/docs.go @@ -8007,6 +8007,10 @@ const docTemplate = `{ "items": { "$ref": "#/definitions/auditlog.LogEntry" } + }, + "truncated": { + "description": "Truncated reports that the thread walk stopped early because the\ncaller's deadline expired; the entries collected up to that point are\nreturned rather than failing the whole request.", + "type": "boolean" } } }, diff --git a/docs/openapi.json b/docs/openapi.json index 1c89b337b..d7fc19eac 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -11129,6 +11129,10 @@ "items": { "$ref": "#/components/schemas/auditlog.LogEntry" } + }, + "truncated": { + "description": "Truncated reports that the thread walk stopped early because the\ncaller's deadline expired; the entries collected up to that point are\nreturned rather than failing the whole request.", + "type": "boolean" } } }, diff --git a/internal/admin/handler_audit.go b/internal/admin/handler_audit.go index 1ee14d520..158394aef 100644 --- a/internal/admin/handler_audit.go +++ b/internal/admin/handler_audit.go @@ -2,6 +2,7 @@ package admin import ( "context" + "errors" "log/slog" "net/http" "strconv" @@ -24,6 +25,12 @@ const maxAuditLogLimit = 100 // reports the same limit an enabled reader would. const defaultAuditLogLimit = 25 +// conversationBuildTimeout bounds the response-chain walk behind +// /admin/audit/conversation. Indexed lookups finish in milliseconds; the +// deadline exists so a degraded store (mis-planned query, lock contention, +// pool starvation) yields a partial thread instead of an endless request. +const conversationBuildTimeout = 10 * time.Second + // AuditLog handles GET /admin/audit/log // // @Summary Get paginated audit log entries @@ -340,8 +347,19 @@ func (h *Handler) AuditConversation(c *echo.Context) error { }) } - result, err := h.auditReader.GetConversation(c.Request().Context(), logID, limit) + // The chain walk runs up to ~2×limit sequential store lookups; without a + // deadline one slow lookup holds the request open until a fronting proxy + // kills it. Under the deadline the builder returns the partial thread it + // collected (Truncated=true); only a timeout before the anchor loads + // surfaces as an error. + ctx, cancel := context.WithTimeout(c.Request().Context(), conversationBuildTimeout) + defer cancel() + result, err := h.auditReader.GetConversation(ctx, logID, limit) if err != nil { + if errors.Is(err, context.DeadlineExceeded) || ctx.Err() != nil { + return handleError(c, core.NewProviderError("", http.StatusGatewayTimeout, + "audit conversation lookup timed out", err)) + } return handleError(c, err) } if result == nil { diff --git a/internal/auditlog/conversation_helpers.go b/internal/auditlog/conversation_helpers.go index f558c732b..d6567321b 100644 --- a/internal/auditlog/conversation_helpers.go +++ b/internal/auditlog/conversation_helpers.go @@ -2,6 +2,7 @@ package auditlog import ( "context" + "errors" "sort" "strings" @@ -10,6 +11,16 @@ import ( type entryLookup func(ctx context.Context, id string) (*LogEntry, error) +// buildConversationThread walks the response-ID chain outward from the anchor +// entry: backward via previous_response_id, then forward via the entries that +// chain onto the anchor's response ID. +// +// The anchor lookup is load-bearing — without it there is no thread, so its +// error (including a deadline) fails the build. Every later hop is an +// extension: when the caller's deadline expires mid-walk, the thread built so +// far is returned with Truncated set instead of an error. The drawer then +// renders the turns nearest the anchor rather than nothing — before this, +// one slow hop could hold the request open until a fronting proxy killed it. func buildConversationThread(ctx context.Context, logID string, limit int, getByID func(ctx context.Context, id string) (*LogEntry, error), findByResponseID, findByPreviousResponseID entryLookup) (*ConversationResult, error) { limit = clampConversationLimit(limit) @@ -26,6 +37,7 @@ func buildConversationThread(ctx context.Context, logID string, limit int, getBy thread := []*LogEntry{anchor} seen := map[string]struct{}{anchor.ID: {}} + truncated := false current := anchor for len(thread) < limit { @@ -35,6 +47,10 @@ func buildConversationThread(ctx context.Context, logID string, limit int, getBy } parent, err := findByResponseID(ctx, prevID) if err != nil { + if deadlineExpired(ctx, err) { + truncated = true + break + } return nil, err } if parent == nil { @@ -49,13 +65,17 @@ func buildConversationThread(ctx context.Context, logID string, limit int, getBy } current = anchor - for len(thread) < limit { + for !truncated && len(thread) < limit { respID := extractResponseID(current) if respID == "" { break } child, err := findByPreviousResponseID(ctx, respID) if err != nil { + if deadlineExpired(ctx, err) { + truncated = true + break + } return nil, err } if child == nil { @@ -81,11 +101,23 @@ func buildConversationThread(ctx context.Context, logID string, limit int, getBy } return &ConversationResult{ - AnchorID: anchor.ID, - Entries: entries, + AnchorID: anchor.ID, + Entries: entries, + Truncated: truncated, }, nil } +// deadlineExpired reports whether a lookup failure is attributable to the +// caller's context rather than the store: either the error itself is a +// context error, or the context expired while the query ran (drivers commonly +// wrap or replace the cancellation cause, so the context is checked too). +func deadlineExpired(ctx context.Context, err error) bool { + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { + return true + } + return ctx.Err() != nil +} + func extractResponseID(entry *LogEntry) string { if entry == nil || entry.Data == nil { return "" diff --git a/internal/auditlog/conversation_helpers_test.go b/internal/auditlog/conversation_helpers_test.go index a5552e913..a79f29698 100644 --- a/internal/auditlog/conversation_helpers_test.go +++ b/internal/auditlog/conversation_helpers_test.go @@ -1,7 +1,9 @@ package auditlog import ( + "context" "testing" + "time" "go.mongodb.org/mongo-driver/v2/bson" ) @@ -88,3 +90,110 @@ func TestExtractConversationIDsFromBSONBodies(t *testing.T) { t.Fatalf("extractResponseID() = %q, want %q", got, "resp_cur") } } + +// chainEntry builds a log entry linked into a response chain: it replays +// prevRespID as request_body.previous_response_id and answers with +// response_body.id = respID. +func chainEntry(id, prevRespID, respID string, at time.Time) *LogEntry { + data := &LogData{} + if prevRespID != "" { + data.RequestBody = map[string]any{"previous_response_id": prevRespID} + } + if respID != "" { + data.ResponseBody = map[string]any{"id": respID} + } + return &LogEntry{ID: id, Timestamp: at, Data: data} +} + +func TestBuildConversationThreadWalksBothDirections(t *testing.T) { + t.Parallel() + base := time.Date(2026, 7, 28, 10, 0, 0, 0, time.UTC) + // log-1 → log-2 → log-3, anchored in the middle. + byID := map[string]*LogEntry{ + "log-2": chainEntry("log-2", "resp-1", "resp-2", base.Add(time.Minute)), + } + byRespID := map[string]*LogEntry{ + "resp-1": chainEntry("log-1", "", "resp-1", base), + } + byPrevRespID := map[string]*LogEntry{ + "resp-2": chainEntry("log-3", "resp-2", "resp-3", base.Add(2*time.Minute)), + } + + result, err := buildConversationThread(context.Background(), "log-2", 40, + func(_ context.Context, id string) (*LogEntry, error) { return byID[id], nil }, + func(_ context.Context, id string) (*LogEntry, error) { return byRespID[id], nil }, + func(_ context.Context, id string) (*LogEntry, error) { return byPrevRespID[id], nil }, + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result.Truncated { + t.Error("complete walk must not be marked truncated") + } + if len(result.Entries) != 3 { + t.Fatalf("expected 3 entries, got %d", len(result.Entries)) + } + for i, want := range []string{"log-1", "log-2", "log-3"} { + if result.Entries[i].ID != want { + t.Errorf("entry %d = %s, want %s", i, result.Entries[i].ID, want) + } + } +} + +// A deadline expiring mid-walk must yield the partial thread collected so +// far, marked Truncated — not an error. Before this behavior one slow chain +// hop held /admin/audit/conversation open until a fronting proxy killed it. +func TestBuildConversationThreadReturnsPartialOnDeadline(t *testing.T) { + t.Parallel() + base := time.Date(2026, 7, 28, 10, 0, 0, 0, time.UTC) + anchor := chainEntry("log-2", "resp-1", "resp-2", base) + + t.Run("backward hop times out", func(t *testing.T) { + result, err := buildConversationThread(context.Background(), "log-2", 40, + func(_ context.Context, _ string) (*LogEntry, error) { return anchor, nil }, + func(_ context.Context, _ string) (*LogEntry, error) { return nil, context.DeadlineExceeded }, + func(_ context.Context, _ string) (*LogEntry, error) { + t.Fatal("forward walk must not run once the deadline expired") + return nil, nil + }, + ) + if err != nil { + t.Fatalf("deadline mid-walk must not fail the build: %v", err) + } + if !result.Truncated { + t.Error("partial thread must be marked truncated") + } + if len(result.Entries) != 1 || result.Entries[0].ID != "log-2" { + t.Errorf("expected the anchor alone, got %+v", result.Entries) + } + }) + + t.Run("forward hop times out", func(t *testing.T) { + parent := chainEntry("log-1", "", "resp-1", base.Add(-time.Minute)) + result, err := buildConversationThread(context.Background(), "log-2", 40, + func(_ context.Context, _ string) (*LogEntry, error) { return anchor, nil }, + func(_ context.Context, _ string) (*LogEntry, error) { return parent, nil }, + func(ctx context.Context, _ string) (*LogEntry, error) { return nil, context.Canceled }, + ) + if err != nil { + t.Fatalf("deadline mid-walk must not fail the build: %v", err) + } + if !result.Truncated { + t.Error("partial thread must be marked truncated") + } + if len(result.Entries) != 2 { + t.Errorf("expected anchor plus backward entry, got %+v", result.Entries) + } + }) + + t.Run("anchor lookup failure still errors", func(t *testing.T) { + _, err := buildConversationThread(context.Background(), "log-2", 40, + func(_ context.Context, _ string) (*LogEntry, error) { return nil, context.DeadlineExceeded }, + func(_ context.Context, _ string) (*LogEntry, error) { return nil, nil }, + func(_ context.Context, _ string) (*LogEntry, error) { return nil, nil }, + ) + if err == nil { + t.Fatal("a thread without its anchor is not partial, it is missing — must error") + } + }) +} diff --git a/internal/auditlog/reader.go b/internal/auditlog/reader.go index 753ff4aad..f1acbfd23 100644 --- a/internal/auditlog/reader.go +++ b/internal/auditlog/reader.go @@ -39,6 +39,11 @@ type LogListResult struct { type ConversationResult struct { AnchorID string `json:"anchor_id"` Entries []LogEntry `json:"entries"` + + // Truncated reports that the thread walk stopped early because the + // caller's deadline expired; the entries collected up to that point are + // returned rather than failing the whole request. + Truncated bool `json:"truncated,omitempty"` } // Reader provides read access to audit log data for the admin API. diff --git a/internal/auditlog/store_sql.go b/internal/auditlog/store_sql.go index 66e03a88c..6b1c4e128 100644 --- a/internal/auditlog/store_sql.go +++ b/internal/auditlog/store_sql.go @@ -164,17 +164,31 @@ func NewSQLStore(ctx context.Context, db sqlx.DB, retentionDays int) (*SQLStore, // jsonPathIndexes index the two JSON fields the Responses lookups filter on. // The expression is engine-specific, so it is built rather than tokenised. +// +// The indexes are composite on (expression, timestamp) because the lookups +// run `WHERE = ? ORDER BY timestamp ASC LIMIT 1`: with a bare +// expression index, PostgreSQL's planner routinely prefers walking the +// timestamp index and filtering — which detoasts the JSON blob of every row +// it passes and can scan the whole table when the value is rare. Measured on +// a user deployment, one such lookup held /admin/audit/conversation open +// until the fronting proxy gave up. The composite index satisfies both the +// filter and the order, so that plan is never attractive. The old +// single-expression indexes are dropped on startup. func jsonPathIndexes(dialect sqlx.Dialect) []string { switch dialect { case sqlx.SQLite: return []string{ - `CREATE INDEX IF NOT EXISTS idx_audit_response_id ON audit_logs(json_extract(data, '$.response_body.id'))`, - `CREATE INDEX IF NOT EXISTS idx_audit_previous_response_id ON audit_logs(json_extract(data, '$.request_body.previous_response_id'))`, + `DROP INDEX IF EXISTS idx_audit_response_id`, + `DROP INDEX IF EXISTS idx_audit_previous_response_id`, + `CREATE INDEX IF NOT EXISTS idx_audit_response_id_ts ON audit_logs(json_extract(data, '$.response_body.id'), timestamp)`, + `CREATE INDEX IF NOT EXISTS idx_audit_previous_response_id_ts ON audit_logs(json_extract(data, '$.request_body.previous_response_id'), timestamp)`, } case sqlx.PostgreSQL: return []string{ - `CREATE INDEX IF NOT EXISTS idx_audit_response_id ON audit_logs((data #>> '{response_body,id}'))`, - `CREATE INDEX IF NOT EXISTS idx_audit_previous_response_id ON audit_logs((data #>> '{request_body,previous_response_id}'))`, + `DROP INDEX IF EXISTS idx_audit_response_id`, + `DROP INDEX IF EXISTS idx_audit_previous_response_id`, + `CREATE INDEX IF NOT EXISTS idx_audit_response_id_ts ON audit_logs((data #>> '{response_body,id}'), timestamp)`, + `CREATE INDEX IF NOT EXISTS idx_audit_previous_response_id_ts ON audit_logs((data #>> '{request_body,previous_response_id}'), timestamp)`, } default: return nil