diff --git a/cmd/gomodel/docs/docs.go b/cmd/gomodel/docs/docs.go index f01cc2a8..8551b212 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": [ @@ -7997,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/config/config.example.yaml b/config/config.example.yaml index 400f1e58..405c2aa0 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 e12421f8..6ef79c64 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 f9d24382..b062d55d 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 c5435032..2a7043a8 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 466d8e0f..95518e16 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 ae21f834..d7fc19ea 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": [ @@ -11119,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/audit_projection.go b/internal/admin/audit_projection.go new file mode 100644 index 00000000..5e055828 --- /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 00000000..e76fdc51 --- /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 df2a45cc..97e4f0a9 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 91c31fb4..b50ea995 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 204284d9..e176f3b7 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 700e76b4..158394ae 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,9 +25,19 @@ 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 +// @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 +155,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 +307,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 @@ -328,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 { @@ -341,6 +371,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 0ddcb34f..b7928290 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 db053ba8..5d939838 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/conversation_helpers.go b/internal/auditlog/conversation_helpers.go index f558c732..d6567321 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 a5552e91..a79f2969 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/factory.go b/internal/auditlog/factory.go index 759cb698..0af3ab52 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/auditlog/reader.go b/internal/auditlog/reader.go index 753ff4aa..f1acbfd2 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 66e03a88..6b1c4e12 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 diff --git a/internal/server/http.go b/internal/server/http.go index fde8bc46..a77fa710 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 5e06f7d8..8ba39d30 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 534ba6a4..8a8dc769 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 0bd0272b..b0ba6d40 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 f8ce28f2..d414b7a9 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); },