@recombine-ai/bosun 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bosun.js +7 -0
- package/build/client/_app/immutable/assets/0.b0jhAoSC.css +1 -0
- package/build/client/_app/immutable/assets/0.b0jhAoSC.css.br +0 -0
- package/build/client/_app/immutable/assets/0.b0jhAoSC.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BTX1tKMW.js +1 -0
- package/build/client/_app/immutable/chunks/BTX1tKMW.js.br +0 -0
- package/build/client/_app/immutable/chunks/BTX1tKMW.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BVxLHCs8.js +1 -0
- package/build/client/_app/immutable/chunks/BVxLHCs8.js.br +0 -0
- package/build/client/_app/immutable/chunks/BVxLHCs8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{De8YgKS3.js → ChVBNPUb.js} +1 -1
- package/build/client/_app/immutable/chunks/ChVBNPUb.js.br +0 -0
- package/build/client/_app/immutable/chunks/ChVBNPUb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/E9rdsnHb.js +1 -0
- package/build/client/_app/immutable/chunks/E9rdsnHb.js.br +0 -0
- package/build/client/_app/immutable/chunks/E9rdsnHb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Z1_c4us_.js +3 -0
- package/build/client/_app/immutable/chunks/Z1_c4us_.js.br +0 -0
- package/build/client/_app/immutable/chunks/Z1_c4us_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Bj8jfBTi.js → lmnC3bvv.js} +1 -1
- package/build/client/_app/immutable/chunks/lmnC3bvv.js.br +0 -0
- package/build/client/_app/immutable/chunks/lmnC3bvv.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.DK_wVDeR.js → app.BY5DP2K_.js} +2 -2
- package/build/client/_app/immutable/entry/app.BY5DP2K_.js.br +0 -0
- package/build/client/_app/immutable/entry/app.BY5DP2K_.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BeIIwvvV.js +1 -0
- package/build/client/_app/immutable/entry/start.BeIIwvvV.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BeIIwvvV.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.5fEbn14Q.js → 0.Cao0LJwW.js} +1 -1
- package/build/client/_app/immutable/nodes/0.Cao0LJwW.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Cao0LJwW.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.CJrSN8zL.js → 1.Kjgu6yxL.js} +1 -1
- package/build/client/_app/immutable/nodes/1.Kjgu6yxL.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Kjgu6yxL.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{10.SZwTlOEA.js → 10.yW2mHq85.js} +1 -1
- package/build/client/_app/immutable/nodes/10.yW2mHq85.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.yW2mHq85.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{17.D-KFl7Vk.js → 17.COHkD98p.js} +3 -3
- package/build/client/_app/immutable/nodes/17.COHkD98p.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.COHkD98p.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.DQVit7Md.js +11 -0
- package/build/client/_app/immutable/nodes/18.DQVit7Md.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.DQVit7Md.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DVwYttKv.js +35 -0
- package/build/client/_app/immutable/nodes/2.DVwYttKv.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DVwYttKv.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.B1RGTOJ8.js → 3.BhmyTb3R.js} +1 -1
- package/build/client/_app/immutable/nodes/3.BhmyTb3R.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.BhmyTb3R.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.D2jcIUpD.js → 4.CxATX0ci.js} +1 -1
- package/build/client/_app/immutable/nodes/4.CxATX0ci.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CxATX0ci.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.CUyCIyRd.js → 5.Dal1qxB1.js} +1 -1
- package/build/client/_app/immutable/nodes/5.Dal1qxB1.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.Dal1qxB1.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.B8HsGA2V.js → 6.iAOxBhJw.js} +1 -1
- package/build/client/_app/immutable/nodes/6.iAOxBhJw.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.iAOxBhJw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.jY56-_OU.js → 8.DFhKu3dp.js} +1 -1
- package/build/client/_app/immutable/nodes/8.DFhKu3dp.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.DFhKu3dp.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.DRIEv5m9.js +2 -0
- package/build/client/_app/immutable/nodes/9.DRIEv5m9.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.DRIEv5m9.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/{0-4QCErf9j.js → 0-CIzAMtA3.js} +3 -3
- package/build/server/chunks/{0-4QCErf9j.js.map → 0-CIzAMtA3.js.map} +1 -1
- package/build/server/chunks/{1-Dsjy8Hzc.js → 1-D1heuNbf.js} +2 -2
- package/build/server/chunks/{1-Dsjy8Hzc.js.map → 1-D1heuNbf.js.map} +1 -1
- package/build/server/chunks/{10-8061c8ui.js → 10-CjLtjXyZ.js} +2 -2
- package/build/server/chunks/{10-8061c8ui.js.map → 10-CjLtjXyZ.js.map} +1 -1
- package/build/server/chunks/{17-3_5wsIEX.js → 17-DklEmmOx.js} +4 -4
- package/build/server/chunks/{17-3_5wsIEX.js.map → 17-DklEmmOx.js.map} +1 -1
- package/build/server/chunks/{18-D73Mw140.js → 18-BJS-eRcz.js} +4 -4
- package/build/server/chunks/18-BJS-eRcz.js.map +1 -0
- package/build/server/chunks/{2-DM0VjzFi.js → 2-DCrAVYXs.js} +4 -4
- package/build/server/chunks/2-DCrAVYXs.js.map +1 -0
- package/build/server/chunks/{3-B3zlFGkL.js → 3-BpTqa4DU.js} +2 -2
- package/build/server/chunks/{3-B3zlFGkL.js.map → 3-BpTqa4DU.js.map} +1 -1
- package/build/server/chunks/{4-tESdMZpI.js → 4-B7GjwQzI.js} +2 -2
- package/build/server/chunks/{4-tESdMZpI.js.map → 4-B7GjwQzI.js.map} +1 -1
- package/build/server/chunks/{5-BJMTlq4U.js → 5-XjS__bcp.js} +2 -2
- package/build/server/chunks/{5-BJMTlq4U.js.map → 5-XjS__bcp.js.map} +1 -1
- package/build/server/chunks/{6-CDK2Aia6.js → 6-DWkWptmB.js} +2 -2
- package/build/server/chunks/{6-CDK2Aia6.js.map → 6-DWkWptmB.js.map} +1 -1
- package/build/server/chunks/{8-BJbDytoU.js → 8-_swAx6gL.js} +2 -2
- package/build/server/chunks/{8-BJbDytoU.js.map → 8-_swAx6gL.js.map} +1 -1
- package/build/server/chunks/{9-BNfIeSqk.js → 9-NEe24O4u.js} +4 -4
- package/build/server/chunks/9-NEe24O4u.js.map +1 -0
- package/build/server/chunks/{ChatStepTrace-DQL3qlna.js → ChatStepTrace-CX2Zolmp.js} +21 -8
- package/build/server/chunks/ChatStepTrace-CX2Zolmp.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-DK7yl8vI.js → _layout.svelte-PtdpfgwJ.js} +66 -4
- package/build/server/chunks/_layout.svelte-PtdpfgwJ.js.map +1 -0
- package/build/server/chunks/{_page.svelte-ZWNBigwS.js → _page.svelte-C3gizoSD.js} +7 -4
- package/build/server/chunks/_page.svelte-C3gizoSD.js.map +1 -0
- package/build/server/chunks/{_page.svelte-D6bSXGHX.js → _page.svelte-CoCBnE2l.js} +8 -5
- package/build/server/chunks/_page.svelte-CoCBnE2l.js.map +1 -0
- package/build/server/chunks/{_page.svelte-ej3vdYeF.js → _page.svelte-DZvcjON6.js} +3 -2
- package/build/server/chunks/_page.svelte-DZvcjON6.js.map +1 -0
- package/build/server/chunks/steps-DzZCg39h.js +8 -0
- package/build/server/chunks/steps-DzZCg39h.js.map +1 -0
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +13 -13
- package/build/server/manifest.js.map +1 -1
- package/package.json +4 -4
- package/src/backend/lib/backend-restart.ts +15 -0
- package/src/backend/roleplay/router.ts +42 -5
- package/src/backend/roleplay/schema-file.spec.ts +38 -0
- package/src/backend/roleplay/schema-file.ts +33 -0
- package/src/backend/tests/academy.ts +4 -2
- package/src/backend/tests/analyze.ts +2 -1
- package/src/backend/tests/emulate-agents.ts +13 -21
- package/src/backend/tests/pii-sanitized-test-drafts.spec.ts +162 -0
- package/src/backend/tests/pii-sanitized-test-drafts.ts +106 -0
- package/src/backend/tests/pii-sanitizer.spec.ts +145 -0
- package/src/backend/tests/pii-sanitizer.ts +171 -0
- package/src/backend/tests/router.ts +37 -0
- package/src/lib/agent-catalog.ts +4 -0
- package/src/lib/agents-context.ts +8 -0
- package/src/lib/api-telescope/client.ts +174 -0
- package/src/lib/api-telescope/index.ts +4 -0
- package/src/lib/api-telescope/latency-timeline-view-state.spec.ts +25 -0
- package/src/lib/api-telescope/latency-timeline-view-state.ts +86 -0
- package/src/lib/api-telescope/latency.ts +252 -0
- package/src/lib/api-telescope/source-selection.ts +20 -0
- package/src/lib/api-telescope/sources.server.ts +94 -0
- package/src/lib/api-telescope/types.ts +143 -0
- package/src/lib/components/Button.svelte +62 -0
- package/src/lib/components/CollapseExpand.svelte +425 -0
- package/src/lib/components/CopyTracesDropdown.svelte +118 -0
- package/src/lib/components/Dialog.svelte +69 -0
- package/src/lib/components/Dropdown.svelte +111 -0
- package/src/lib/components/History.svelte +174 -0
- package/src/lib/components/Icon.svelte +94 -0
- package/src/lib/components/Input.svelte +43 -0
- package/src/lib/components/Link.svelte +45 -0
- package/src/lib/components/Modal.svelte +45 -0
- package/src/lib/components/Panel.svelte +12 -0
- package/src/lib/components/PopoverModal.svelte +34 -0
- package/src/lib/components/Popper.svelte +109 -0
- package/src/lib/components/Tab.svelte +91 -0
- package/src/lib/components/Tabs.svelte +140 -0
- package/src/lib/components/agent-colors.ts +70 -0
- package/src/lib/dom/text-selection.ts +97 -0
- package/src/lib/edit/edit-vm.spec.ts +115 -0
- package/src/lib/edit/edit-vm.ts +169 -0
- package/src/lib/edit/edit-workspace-context.ts +56 -0
- package/src/lib/edit/highlight-styles.spec.ts +14 -0
- package/src/lib/edit/prompt-highlighter.spec.ts +80 -0
- package/src/lib/edit/prompt-highlighter.ts +155 -0
- package/src/lib/edit/quote-matching.ts +97 -0
- package/src/lib/monaco/bosun-theme.ts +125 -0
- package/src/lib/monaco/font.ts +2 -0
- package/src/lib/monaco-workers.ts +28 -0
- package/src/lib/steps.ts +19 -0
- package/src/lib/tests/pii-sanitized-test-draft.ts +50 -0
- package/src/lib/trpc-client.ts +16 -0
- package/src/lib/ws-client.ts +144 -0
- package/src/lib/ws-message.ts +150 -0
- package/build/client/_app/immutable/assets/0.B-Br6U4N.css +0 -1
- package/build/client/_app/immutable/assets/0.B-Br6U4N.css.br +0 -0
- package/build/client/_app/immutable/assets/0.B-Br6U4N.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BOuo-4gP.js +0 -1
- package/build/client/_app/immutable/chunks/BOuo-4gP.js.br +0 -0
- package/build/client/_app/immutable/chunks/BOuo-4gP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bj8jfBTi.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bj8jfBTi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CX0Ekq8t.js +0 -1
- package/build/client/_app/immutable/chunks/CX0Ekq8t.js.br +0 -0
- package/build/client/_app/immutable/chunks/CX0Ekq8t.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cyj62X2W.js +0 -2
- package/build/client/_app/immutable/chunks/Cyj62X2W.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cyj62X2W.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DSDSeO7L.js +0 -1
- package/build/client/_app/immutable/chunks/DSDSeO7L.js.br +0 -0
- package/build/client/_app/immutable/chunks/DSDSeO7L.js.gz +0 -0
- package/build/client/_app/immutable/chunks/De8YgKS3.js.br +0 -1
- package/build/client/_app/immutable/chunks/De8YgKS3.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DK_wVDeR.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DK_wVDeR.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.xQy-2aGo.js +0 -1
- package/build/client/_app/immutable/entry/start.xQy-2aGo.js.br +0 -2
- package/build/client/_app/immutable/entry/start.xQy-2aGo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.5fEbn14Q.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.5fEbn14Q.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.CJrSN8zL.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.CJrSN8zL.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.SZwTlOEA.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.SZwTlOEA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/17.D-KFl7Vk.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.D-KFl7Vk.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.DqRdJY9A.js +0 -11
- package/build/client/_app/immutable/nodes/18.DqRdJY9A.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.DqRdJY9A.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.B3Uuk25v.js +0 -35
- package/build/client/_app/immutable/nodes/2.B3Uuk25v.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.B3Uuk25v.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.B1RGTOJ8.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.B1RGTOJ8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.D2jcIUpD.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.D2jcIUpD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.CUyCIyRd.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.CUyCIyRd.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.B8HsGA2V.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.B8HsGA2V.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.jY56-_OU.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.jY56-_OU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.v5dX6L9n.js +0 -2
- package/build/client/_app/immutable/nodes/9.v5dX6L9n.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.v5dX6L9n.js.gz +0 -0
- package/build/server/chunks/18-D73Mw140.js.map +0 -1
- package/build/server/chunks/2-DM0VjzFi.js.map +0 -1
- package/build/server/chunks/9-BNfIeSqk.js.map +0 -1
- package/build/server/chunks/ChatStepTrace-DQL3qlna.js.map +0 -1
- package/build/server/chunks/_layout.svelte-DK7yl8vI.js.map +0 -1
- package/build/server/chunks/_page.svelte-D6bSXGHX.js.map +0 -1
- package/build/server/chunks/_page.svelte-ZWNBigwS.js.map +0 -1
- package/build/server/chunks/_page.svelte-ej3vdYeF.js.map +0 -1
- /package/build/client/_app/immutable/assets/{Popper.CLTyHc88.css → steps.CLTyHc88.css} +0 -0
- /package/build/client/_app/immutable/assets/{Popper.CLTyHc88.css.br → steps.CLTyHc88.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{Popper.CLTyHc88.css.gz → steps.CLTyHc88.css.gz} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type LatencyTimelineViewport = {
|
|
2
|
+
startMs: number
|
|
3
|
+
endMs: number
|
|
4
|
+
originMs: number
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const STORAGE_KEY = 'bosun.traces.latencyTimelineViewports.v2'
|
|
8
|
+
|
|
9
|
+
const viewports = new Map<string, LatencyTimelineViewport>()
|
|
10
|
+
let loadedFromSession = false
|
|
11
|
+
|
|
12
|
+
function isValidViewport(value: unknown): value is LatencyTimelineViewport {
|
|
13
|
+
if (!value || typeof value !== 'object') return false
|
|
14
|
+
const next = value as Record<string, unknown>
|
|
15
|
+
return (
|
|
16
|
+
typeof next.startMs === 'number' &&
|
|
17
|
+
Number.isFinite(next.startMs) &&
|
|
18
|
+
typeof next.endMs === 'number' &&
|
|
19
|
+
Number.isFinite(next.endMs) &&
|
|
20
|
+
next.endMs > next.startMs &&
|
|
21
|
+
typeof next.originMs === 'number' &&
|
|
22
|
+
Number.isFinite(next.originMs)
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function rebaseLatencyTimelineViewport(
|
|
27
|
+
viewport: LatencyTimelineViewport,
|
|
28
|
+
nextOriginMs: number
|
|
29
|
+
): LatencyTimelineViewport {
|
|
30
|
+
if (!Number.isFinite(nextOriginMs)) return { ...viewport }
|
|
31
|
+
|
|
32
|
+
const originDeltaMs = nextOriginMs - viewport.originMs
|
|
33
|
+
return {
|
|
34
|
+
startMs: viewport.startMs + originDeltaMs,
|
|
35
|
+
endMs: viewport.endMs + originDeltaMs,
|
|
36
|
+
originMs: nextOriginMs
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ensureLoadedFromSession() {
|
|
41
|
+
if (loadedFromSession) return
|
|
42
|
+
loadedFromSession = true
|
|
43
|
+
if (typeof window === 'undefined') return
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const raw = window.sessionStorage.getItem(STORAGE_KEY)
|
|
47
|
+
if (!raw) return
|
|
48
|
+
|
|
49
|
+
const parsed = JSON.parse(raw) as unknown
|
|
50
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return
|
|
51
|
+
|
|
52
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
53
|
+
if (!key || !isValidViewport(value)) continue
|
|
54
|
+
viewports.set(key, value)
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// Ignore stale or unavailable session state.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function persistToSession() {
|
|
62
|
+
if (typeof window === 'undefined') return
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
window.sessionStorage.setItem(
|
|
66
|
+
STORAGE_KEY,
|
|
67
|
+
JSON.stringify(Object.fromEntries(viewports.entries()))
|
|
68
|
+
)
|
|
69
|
+
} catch {
|
|
70
|
+
// Ignore storage failures; the in-memory map still works for this app session.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getLatencyTimelineViewport(key: string): LatencyTimelineViewport | null {
|
|
75
|
+
if (!key) return null
|
|
76
|
+
ensureLoadedFromSession()
|
|
77
|
+
const viewport = viewports.get(key)
|
|
78
|
+
return viewport ? { ...viewport } : null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function persistLatencyTimelineViewport(key: string, viewport: LatencyTimelineViewport) {
|
|
82
|
+
if (!key || !isValidViewport(viewport)) return
|
|
83
|
+
ensureLoadedFromSession()
|
|
84
|
+
viewports.set(key, { ...viewport })
|
|
85
|
+
persistToSession()
|
|
86
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { LatencyTrace, Trace } from './types'
|
|
2
|
+
|
|
3
|
+
export const RESPONSE_LATENCY_THRESHOLD_MS = 800
|
|
4
|
+
const RESPONSE_LATENCY_TRACE_MATCH_TOLERANCE_MS = 250
|
|
5
|
+
|
|
6
|
+
export type ResponseLatencySpan = {
|
|
7
|
+
id: string
|
|
8
|
+
start: number
|
|
9
|
+
end: number
|
|
10
|
+
durationMs: number
|
|
11
|
+
isHigh: boolean
|
|
12
|
+
startTrace: LatencyTrace
|
|
13
|
+
speechCreatedTrace: LatencyTrace | null
|
|
14
|
+
endTrace: LatencyTrace
|
|
15
|
+
text: string | null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeNonEmptyString(value: unknown): string | null {
|
|
19
|
+
if (typeof value !== 'string') return null
|
|
20
|
+
const trimmed = value.trim()
|
|
21
|
+
return trimmed ? trimmed : null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function finiteNumber(value: unknown): number | null {
|
|
25
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function latencyPayload(trace: LatencyTrace): Record<string, unknown> {
|
|
29
|
+
const payload = trace.payload
|
|
30
|
+
return payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
31
|
+
? (payload as Record<string, unknown>)
|
|
32
|
+
: {}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function latencyText(trace: LatencyTrace): string | null {
|
|
36
|
+
return normalizeNonEmptyString(latencyPayload(trace).text)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isCancellationValue(value: unknown): boolean {
|
|
40
|
+
if (value === true) return true
|
|
41
|
+
if (typeof value === 'number') return value === 1
|
|
42
|
+
if (typeof value !== 'string') return false
|
|
43
|
+
|
|
44
|
+
const normalized = value.trim().toLowerCase()
|
|
45
|
+
return (
|
|
46
|
+
normalized === 'true' ||
|
|
47
|
+
normalized === '1' ||
|
|
48
|
+
normalized === 'cancelled' ||
|
|
49
|
+
normalized === 'canceled' ||
|
|
50
|
+
normalized === 'dropped' ||
|
|
51
|
+
normalized === 'discarded' ||
|
|
52
|
+
normalized === 'aborted' ||
|
|
53
|
+
normalized === 'superseded'
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function hasCancellationMarker(payload: Record<string, unknown>): boolean {
|
|
58
|
+
const flagKeys = [
|
|
59
|
+
'cancelled',
|
|
60
|
+
'canceled',
|
|
61
|
+
'isCancelled',
|
|
62
|
+
'isCanceled',
|
|
63
|
+
'wasCancelled',
|
|
64
|
+
'wasCanceled',
|
|
65
|
+
'dropped',
|
|
66
|
+
'discarded',
|
|
67
|
+
'aborted',
|
|
68
|
+
'superseded'
|
|
69
|
+
]
|
|
70
|
+
if (flagKeys.some((key) => isCancellationValue(payload[key]))) return true
|
|
71
|
+
|
|
72
|
+
const timestampKeys = [
|
|
73
|
+
'cancelledAt',
|
|
74
|
+
'canceledAt',
|
|
75
|
+
'droppedAt',
|
|
76
|
+
'discardedAt',
|
|
77
|
+
'abortedAt',
|
|
78
|
+
'supersededAt'
|
|
79
|
+
]
|
|
80
|
+
if (
|
|
81
|
+
timestampKeys.some((key) => {
|
|
82
|
+
const value = payload[key]
|
|
83
|
+
if (value == null) return false
|
|
84
|
+
if (typeof value === 'string') return value.trim() !== ''
|
|
85
|
+
return true
|
|
86
|
+
})
|
|
87
|
+
) {
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const status = normalizeNonEmptyString(payload.status)
|
|
92
|
+
const state = normalizeNonEmptyString(payload.state)
|
|
93
|
+
const lifecycle = normalizeNonEmptyString(payload.lifecycle)
|
|
94
|
+
return [status, state, lifecycle].some((value) => value != null && isCancellationValue(value))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isFinalSttTranscript(trace: LatencyTrace): boolean {
|
|
98
|
+
return trace.eventType === 'stt.final' || trace.eventType === 'stt.final_transcript'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isUncancelledSpeechCreated(trace: LatencyTrace): boolean {
|
|
102
|
+
if (trace.eventType !== 'speech.created') return false
|
|
103
|
+
const payload = latencyPayload(trace)
|
|
104
|
+
if (hasCancellationMarker(payload)) return false
|
|
105
|
+
|
|
106
|
+
const speech = payload.speech
|
|
107
|
+
if (speech && typeof speech === 'object' && !Array.isArray(speech)) {
|
|
108
|
+
return !hasCancellationMarker(speech as Record<string, unknown>)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isAudioSpeechStart(trace: LatencyTrace): boolean {
|
|
115
|
+
return (
|
|
116
|
+
trace.eventType === 'audio.output.wait.completed' ||
|
|
117
|
+
trace.eventType === 'audio.playback.started'
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function compareLatencyTraces(a: LatencyTrace, b: LatencyTrace): number {
|
|
122
|
+
return (
|
|
123
|
+
a.seq - b.seq ||
|
|
124
|
+
a.atMs - b.atMs ||
|
|
125
|
+
(a.createdAt ?? 0) - (b.createdAt ?? 0) ||
|
|
126
|
+
String(a.traceId ?? '').localeCompare(String(b.traceId ?? ''))
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function findNextLatencyEvent(
|
|
131
|
+
events: LatencyTrace[],
|
|
132
|
+
startIndex: number,
|
|
133
|
+
match: (trace: LatencyTrace) => boolean,
|
|
134
|
+
stop: (trace: LatencyTrace) => boolean
|
|
135
|
+
): LatencyTrace | null {
|
|
136
|
+
for (let index = startIndex + 1; index < events.length; index += 1) {
|
|
137
|
+
const next = events[index]
|
|
138
|
+
if (stop(next)) return null
|
|
139
|
+
if (match(next)) return next
|
|
140
|
+
}
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function isUserConversationalTrace(trace: Trace): boolean {
|
|
145
|
+
return (
|
|
146
|
+
trace.traceType === 'CONVERSATIONAL' &&
|
|
147
|
+
trace.role.toLowerCase() === 'user' &&
|
|
148
|
+
(trace.eventName === 'received-message' || trace.eventName === 'llm-handler-stream-requested')
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function isAgentConversationalTrace(trace: Trace): boolean {
|
|
153
|
+
return (
|
|
154
|
+
trace.traceType === 'CONVERSATIONAL' &&
|
|
155
|
+
(trace.eventName === 'finished-main-streaming-step' || trace.eventName === 'sent-message')
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function findPreviousConversationalTrace(items: Trace[], beforeIndex: number): Trace | null {
|
|
160
|
+
for (let index = beforeIndex - 1; index >= 0; index -= 1) {
|
|
161
|
+
const candidate = items[index]
|
|
162
|
+
if (candidate.traceType === 'CONVERSATIONAL') return candidate
|
|
163
|
+
}
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function findResponseLatencyUserTurn(
|
|
168
|
+
items: Trace[],
|
|
169
|
+
span: ResponseLatencySpan
|
|
170
|
+
): Trace | null {
|
|
171
|
+
const startCreatedAt = finiteNumber(span.startTrace.createdAt)
|
|
172
|
+
const endCreatedAt = finiteNumber(span.endTrace.createdAt)
|
|
173
|
+
if (startCreatedAt == null) return null
|
|
174
|
+
|
|
175
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
176
|
+
const candidate = items[index]
|
|
177
|
+
if (!isAgentConversationalTrace(candidate)) continue
|
|
178
|
+
if (candidate.createdAt < startCreatedAt - RESPONSE_LATENCY_TRACE_MATCH_TOLERANCE_MS) {
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
if (
|
|
182
|
+
endCreatedAt != null &&
|
|
183
|
+
candidate.createdAt < endCreatedAt - RESPONSE_LATENCY_TRACE_MATCH_TOLERANCE_MS
|
|
184
|
+
) {
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const previous = findPreviousConversationalTrace(items, index)
|
|
189
|
+
if (!previous || !isUserConversationalTrace(previous)) continue
|
|
190
|
+
return previous
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function buildResponseLatencySpans(input: LatencyTrace[]): ResponseLatencySpan[] {
|
|
197
|
+
const events = input
|
|
198
|
+
.slice()
|
|
199
|
+
.filter(
|
|
200
|
+
(trace) =>
|
|
201
|
+
typeof trace.atMs === 'number' &&
|
|
202
|
+
Number.isFinite(trace.atMs) &&
|
|
203
|
+
typeof trace.seq === 'number' &&
|
|
204
|
+
Number.isFinite(trace.seq)
|
|
205
|
+
)
|
|
206
|
+
.sort(compareLatencyTraces)
|
|
207
|
+
|
|
208
|
+
const built: ResponseLatencySpan[] = []
|
|
209
|
+
|
|
210
|
+
events.forEach((trace, index) => {
|
|
211
|
+
if (!isFinalSttTranscript(trace)) return
|
|
212
|
+
|
|
213
|
+
const speechCreated = findNextLatencyEvent(
|
|
214
|
+
events,
|
|
215
|
+
index,
|
|
216
|
+
isUncancelledSpeechCreated,
|
|
217
|
+
(candidate) =>
|
|
218
|
+
candidate.eventType === 'call.ended' ||
|
|
219
|
+
candidate.eventType === 'vad.activated' ||
|
|
220
|
+
candidate.eventType === 'stt.started' ||
|
|
221
|
+
isFinalSttTranscript(candidate)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
const speechCreatedIndex = speechCreated ? events.indexOf(speechCreated) : index
|
|
225
|
+
const next = findNextLatencyEvent(
|
|
226
|
+
events,
|
|
227
|
+
speechCreatedIndex,
|
|
228
|
+
isAudioSpeechStart,
|
|
229
|
+
(candidate) =>
|
|
230
|
+
candidate.eventType === 'call.ended' ||
|
|
231
|
+
candidate.eventType === 'vad.activated' ||
|
|
232
|
+
candidate.eventType === 'stt.started' ||
|
|
233
|
+
isFinalSttTranscript(candidate)
|
|
234
|
+
)
|
|
235
|
+
if (!next || next.atMs < trace.atMs) return
|
|
236
|
+
|
|
237
|
+
const durationMs = next.atMs - trace.atMs
|
|
238
|
+
built.push({
|
|
239
|
+
id: `response-latency:${trace.seq}:${next.seq}`,
|
|
240
|
+
start: trace.atMs,
|
|
241
|
+
end: next.atMs,
|
|
242
|
+
durationMs,
|
|
243
|
+
isHigh: durationMs > RESPONSE_LATENCY_THRESHOLD_MS,
|
|
244
|
+
startTrace: trace,
|
|
245
|
+
speechCreatedTrace: speechCreated,
|
|
246
|
+
endTrace: next,
|
|
247
|
+
text: latencyText(trace)
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
return built
|
|
252
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const TELESCOPE_SOURCE_STORAGE_KEY = 'bosun.traces.telescopeSourceId'
|
|
2
|
+
|
|
3
|
+
export function getPersistedTelescopeSourceId(): string | null {
|
|
4
|
+
if (typeof window === 'undefined') return null
|
|
5
|
+
try {
|
|
6
|
+
return window.localStorage.getItem(TELESCOPE_SOURCE_STORAGE_KEY)
|
|
7
|
+
} catch {
|
|
8
|
+
return null
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function persistTelescopeSourceId(sourceId: string) {
|
|
13
|
+
if (typeof window === 'undefined') return
|
|
14
|
+
if (!sourceId || !sourceId.trim()) return
|
|
15
|
+
try {
|
|
16
|
+
window.localStorage.setItem(TELESCOPE_SOURCE_STORAGE_KEY, sourceId)
|
|
17
|
+
} catch {
|
|
18
|
+
// ignore
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { env } from '$env/dynamic/private'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
const TelescopeSourceInputSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
id: z.string().optional(),
|
|
7
|
+
label: z.string().min(1),
|
|
8
|
+
// Accept either camelCase or the user-friendly names ("url"/"secret")
|
|
9
|
+
baseUrl: z.string().optional(),
|
|
10
|
+
url: z.string().optional(),
|
|
11
|
+
apiKey: z.string().optional(),
|
|
12
|
+
secret: z.string().optional()
|
|
13
|
+
})
|
|
14
|
+
.superRefine((value, ctx) => {
|
|
15
|
+
const baseUrl = (value.baseUrl ?? value.url ?? '').trim()
|
|
16
|
+
const apiKey = (value.apiKey ?? value.secret ?? '').trim()
|
|
17
|
+
|
|
18
|
+
if (!baseUrl) {
|
|
19
|
+
ctx.addIssue({
|
|
20
|
+
code: z.ZodIssueCode.custom,
|
|
21
|
+
path: ['baseUrl'],
|
|
22
|
+
message: 'baseUrl/url is required'
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
if (!apiKey) {
|
|
26
|
+
ctx.addIssue({
|
|
27
|
+
code: z.ZodIssueCode.custom,
|
|
28
|
+
path: ['apiKey'],
|
|
29
|
+
message: 'apiKey/secret is required'
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const TelescopeSourcesJsonSchema = z.array(TelescopeSourceInputSchema)
|
|
35
|
+
|
|
36
|
+
export interface TelescopeSourceConfig {
|
|
37
|
+
id: string
|
|
38
|
+
label: string
|
|
39
|
+
baseUrl: string
|
|
40
|
+
apiKey: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function slugify(value: string): string {
|
|
44
|
+
return value
|
|
45
|
+
.trim()
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
48
|
+
.replace(/^-+|-+$/g, '')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cleanBaseUrl(value: string): string {
|
|
52
|
+
return value.trim().replace(/\/+$/, '')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeSources(
|
|
56
|
+
inputs: z.infer<typeof TelescopeSourcesJsonSchema>
|
|
57
|
+
): TelescopeSourceConfig[] {
|
|
58
|
+
const usedIds = new Set<string>()
|
|
59
|
+
|
|
60
|
+
return inputs.map((input, idx) => {
|
|
61
|
+
const label = input.label.trim()
|
|
62
|
+
const baseUrl = cleanBaseUrl(input.baseUrl ?? input.url ?? '')
|
|
63
|
+
const apiKey = (input.apiKey ?? input.secret ?? '').trim()
|
|
64
|
+
|
|
65
|
+
const preferred = typeof input.id === 'string' && input.id.trim() ? input.id.trim() : label
|
|
66
|
+
let id = slugify(preferred) || `source-${idx + 1}`
|
|
67
|
+
let deduped = id
|
|
68
|
+
let i = 2
|
|
69
|
+
while (usedIds.has(deduped)) {
|
|
70
|
+
deduped = `${id}-${i++}`
|
|
71
|
+
}
|
|
72
|
+
id = deduped
|
|
73
|
+
usedIds.add(id)
|
|
74
|
+
|
|
75
|
+
return { id, label, baseUrl, apiKey }
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getTelescopeSources(): TelescopeSourceConfig[] {
|
|
80
|
+
const rawJson = env.TELESCOPE_SOURCES_JSON
|
|
81
|
+
|
|
82
|
+
if (typeof rawJson === 'string' && rawJson.trim()) {
|
|
83
|
+
let parsed: unknown
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(rawJson)
|
|
86
|
+
} catch {
|
|
87
|
+
throw new Error('TELESCOPE_SOURCES_JSON is not valid JSON')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const inputs = TelescopeSourcesJsonSchema.parse(parsed)
|
|
91
|
+
return normalizeSources(inputs)
|
|
92
|
+
}
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const TranscriptSegmentSchema = z.object({
|
|
4
|
+
text: z.string(),
|
|
5
|
+
role: z.string(), // 'Agent' or 'User'
|
|
6
|
+
time: z.string(), // ISO timestamp
|
|
7
|
+
duration: z.number()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export const ConversationSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
conversationId: z.string(),
|
|
13
|
+
medium: z.string(), // 'phone' | 'whatsapp' | ...
|
|
14
|
+
agentId: z.string().optional(),
|
|
15
|
+
phoneNumber: z.string().optional(),
|
|
16
|
+
duration: z.number().optional(),
|
|
17
|
+
audioUrl: z.string().url().optional(),
|
|
18
|
+
transcript: z.union([z.string(), z.array(TranscriptSegmentSchema)]).optional(),
|
|
19
|
+
createdAt: z.number()
|
|
20
|
+
})
|
|
21
|
+
.passthrough()
|
|
22
|
+
|
|
23
|
+
export type Conversation = z.infer<typeof ConversationSchema>
|
|
24
|
+
|
|
25
|
+
export const PaginatedConversationsResponseSchema = z.object({
|
|
26
|
+
limit: z.number(),
|
|
27
|
+
items: z.array(ConversationSchema),
|
|
28
|
+
lastConversationId: z.string().optional()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export type PaginatedConversationsResponse = z.infer<typeof PaginatedConversationsResponseSchema>
|
|
32
|
+
|
|
33
|
+
export type GetConversationsRequest = {
|
|
34
|
+
phoneNumber?: string
|
|
35
|
+
agentId?: string
|
|
36
|
+
limit?: number
|
|
37
|
+
lastConversationId?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const LlmUsageSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
promptTokens: z.number().optional(),
|
|
43
|
+
completionTokens: z.number().optional(),
|
|
44
|
+
totalTokens: z.number().optional(),
|
|
45
|
+
cachedPromptTokens: z.number().optional()
|
|
46
|
+
})
|
|
47
|
+
.passthrough()
|
|
48
|
+
|
|
49
|
+
export const StepTraceSchema = z
|
|
50
|
+
.object({
|
|
51
|
+
traceType: z.literal('STEP'),
|
|
52
|
+
traceId: z.string().optional(),
|
|
53
|
+
conversationId: z.string().optional(),
|
|
54
|
+
scopeId: z.string().optional(),
|
|
55
|
+
workflowId: z.string().optional(),
|
|
56
|
+
workflowRunId: z.string().optional(),
|
|
57
|
+
name: z.string().optional(),
|
|
58
|
+
renderedPrompt: z.string().optional(),
|
|
59
|
+
receivedContext: z.any().optional(),
|
|
60
|
+
receivedPrompt: z.any().optional(),
|
|
61
|
+
stringifiedConversation: z.string().optional(),
|
|
62
|
+
response: z.string().optional(),
|
|
63
|
+
model: z.string().optional(),
|
|
64
|
+
llmUsage: LlmUsageSchema.optional(),
|
|
65
|
+
createdAt: z.number(),
|
|
66
|
+
error: z.any().optional()
|
|
67
|
+
})
|
|
68
|
+
.passthrough()
|
|
69
|
+
|
|
70
|
+
export const ApiCallTraceSchema = z
|
|
71
|
+
.object({
|
|
72
|
+
traceType: z.literal('API_CALL'),
|
|
73
|
+
traceId: z.string().optional(),
|
|
74
|
+
conversationId: z.string().optional(),
|
|
75
|
+
scopeId: z.string().optional(),
|
|
76
|
+
operationName: z.string(),
|
|
77
|
+
method: z.string(),
|
|
78
|
+
path: z.string(),
|
|
79
|
+
baseUrl: z.string().optional(),
|
|
80
|
+
request: z.any().optional(),
|
|
81
|
+
response: z.any().optional(),
|
|
82
|
+
status: z.number().optional(),
|
|
83
|
+
ok: z.boolean().optional(),
|
|
84
|
+
durationMs: z.number().optional(),
|
|
85
|
+
createdAt: z.number(),
|
|
86
|
+
error: z.any().optional()
|
|
87
|
+
})
|
|
88
|
+
.passthrough()
|
|
89
|
+
|
|
90
|
+
export const ConversationalTraceSchema = z
|
|
91
|
+
.object({
|
|
92
|
+
traceType: z.literal('CONVERSATIONAL'),
|
|
93
|
+
traceId: z.string().optional(),
|
|
94
|
+
conversationId: z.string().optional(),
|
|
95
|
+
scopeId: z.string().optional(),
|
|
96
|
+
eventName: z.string(),
|
|
97
|
+
role: z.string(),
|
|
98
|
+
medium: z.string(),
|
|
99
|
+
content: z.string(),
|
|
100
|
+
createdAt: z.number(),
|
|
101
|
+
error: z.any().optional()
|
|
102
|
+
})
|
|
103
|
+
.passthrough()
|
|
104
|
+
|
|
105
|
+
export const LatencyTraceSchema = z
|
|
106
|
+
.object({
|
|
107
|
+
traceType: z.literal('LATENCY'),
|
|
108
|
+
traceId: z.string().optional(),
|
|
109
|
+
conversationId: z.string().optional(),
|
|
110
|
+
seq: z.number(),
|
|
111
|
+
atMs: z.number(),
|
|
112
|
+
eventType: z.string(),
|
|
113
|
+
payload: z.any().optional(),
|
|
114
|
+
createdAt: z.number()
|
|
115
|
+
})
|
|
116
|
+
.passthrough()
|
|
117
|
+
|
|
118
|
+
export const TraceSchema = z.discriminatedUnion('traceType', [
|
|
119
|
+
StepTraceSchema,
|
|
120
|
+
ApiCallTraceSchema,
|
|
121
|
+
ConversationalTraceSchema
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
export type StepTrace = z.infer<typeof StepTraceSchema>
|
|
125
|
+
export type ApiCallTrace = z.infer<typeof ApiCallTraceSchema>
|
|
126
|
+
export type ConversationalTrace = z.infer<typeof ConversationalTraceSchema>
|
|
127
|
+
export type LatencyTrace = z.infer<typeof LatencyTraceSchema>
|
|
128
|
+
export type Trace = z.infer<typeof TraceSchema>
|
|
129
|
+
|
|
130
|
+
export const PaginatedTracesResponseSchema = z.object({
|
|
131
|
+
items: z.array(TraceSchema),
|
|
132
|
+
limit: z.number(),
|
|
133
|
+
lastTraceId: z.string().optional()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
export const PaginatedLatencyTracesResponseSchema = z.object({
|
|
137
|
+
items: z.array(LatencyTraceSchema),
|
|
138
|
+
limit: z.number(),
|
|
139
|
+
lastTraceId: z.string().optional()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
export type PaginatedTracesResponse = z.infer<typeof PaginatedTracesResponseSchema>
|
|
143
|
+
export type PaginatedLatencyTracesResponse = z.infer<typeof PaginatedLatencyTracesResponseSchema>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements'
|
|
4
|
+
|
|
5
|
+
type Variant = 'primary' | 'secondary' | 'outlined' | 'ghost'
|
|
6
|
+
type Size = 'big' | 'medium' | 'small'
|
|
7
|
+
|
|
8
|
+
interface Props extends HTMLButtonAttributes {
|
|
9
|
+
variant?: Variant
|
|
10
|
+
size?: Size
|
|
11
|
+
type?: 'button' | 'submit' | 'reset'
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
class?: string
|
|
14
|
+
children: Snippet
|
|
15
|
+
el?: HTMLButtonElement | null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
variant = 'primary',
|
|
20
|
+
size = 'medium',
|
|
21
|
+
type = 'button',
|
|
22
|
+
disabled = false,
|
|
23
|
+
class: className = '',
|
|
24
|
+
children,
|
|
25
|
+
el = $bindable(),
|
|
26
|
+
...rest
|
|
27
|
+
}: Props = $props()
|
|
28
|
+
|
|
29
|
+
const variantClasses = {
|
|
30
|
+
primary:
|
|
31
|
+
'border border-thin border-rc-green-450 backdrop-blur-xl rounded-lg text-rc-gray-100 font-bold bg-rc-green-350 hover:bg-rc-green-250 focus:bg-rc-green-200 focus:outline-none active:bg-rc-green-250',
|
|
32
|
+
secondary:
|
|
33
|
+
'border border-thin border-rc-gray-600 backdrop-blur-2xl rounded-lg bg-rc-gray-800 text-rc-gray-100 font-bold hover:bg-rc-gray-700 focus:bg-rc-gray-700 focus:outline-none active:bg-rc-gray-650',
|
|
34
|
+
outlined:
|
|
35
|
+
'border border-rc-gray-500 bg-rc-gray-900 font-semibold hover:bg-rc-gray-700 hover:border-rc-gray-400 focus:outline-none focus:ring-0 focus:border-rc-gray-400 active:border-rc-gray-400',
|
|
36
|
+
ghost: 'bg-transparent rounded-lg hover:text-gray-100 focus:text-gray-100 active:text-gray-100'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sizeClasses = {
|
|
40
|
+
big: 'px-4 py-2 text-base',
|
|
41
|
+
medium: 'px-4 py-2 text-base',
|
|
42
|
+
small: 'px-4 py-2 text-base'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let buttonClasses = $derived(
|
|
46
|
+
`text-gray-200
|
|
47
|
+
rounded
|
|
48
|
+
transition-colors
|
|
49
|
+
disabled:opacity-50
|
|
50
|
+
disabled:cursor-not-allowed
|
|
51
|
+
disabled:pointer-events-none
|
|
52
|
+
${variantClasses[variant]}
|
|
53
|
+
${sizeClasses[size]}
|
|
54
|
+
cursor-pointer
|
|
55
|
+
${className}
|
|
56
|
+
`.trim()
|
|
57
|
+
)
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<button bind:this={el} {type} class={buttonClasses} {disabled} {...rest}>
|
|
61
|
+
{@render children()}
|
|
62
|
+
</button>
|