@swiss-ai-hub/web 0.309.3 → 0.310.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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "bbv Software Services AG (https://www.bbv.ch)",
5
5
  "type": "module",
6
- "version": "0.309.3",
6
+ "version": "0.310.0",
7
7
  "description": "Swiss AI Hub - Admin & Management UI (Nuxt 3 layer)",
8
8
  "main": "./nuxt.config.ts",
9
9
  "repository": {
@@ -31,6 +31,7 @@
31
31
  </template>
32
32
 
33
33
  <script setup lang="ts">
34
+ import { resolveThreadForDisplay } from '@core/sdk/client'
34
35
  import { onBeforeUnmount, onMounted } from 'vue'
35
36
 
36
37
  const { t } = useI18n()
@@ -40,7 +41,7 @@ const router = useRouter()
40
41
  const tenantPath = useTenantPath()
41
42
  const localePath = useLocalePath()
42
43
  const { mismatchDetected, backendTenantId, backendTenantName } = useTenantPolling()
43
- const { setTenant } = useTenant()
44
+ const { setTenant, tenantId } = useTenant()
44
45
 
45
46
  async function onSwitchToBackendTenant() {
46
47
  if (!backendTenantId.value) return
@@ -64,27 +65,56 @@ const handleIframeLoad = () => {
64
65
  router.push(localePath('/'))
65
66
  }
66
67
 
67
- const handleMessage = (event: MessageEvent) => {
68
+ const VIEW_BY_ACTION: Record<string, string> = {
69
+ 'show-traces': 'tracing',
70
+ 'show-sources': 'sources',
71
+ 'show-memories': 'memories',
72
+ }
73
+
74
+ const openPanelView = (): string | null => {
75
+ if (route.path.endsWith('/tracing')) return 'tracing'
76
+ if (route.path.endsWith('/sources')) return 'sources'
77
+ if (route.path.endsWith('/memories')) return 'memories'
78
+ return null
79
+ }
80
+
81
+ const handleMessage = async (event: MessageEvent) => {
68
82
  console.log('received post event', event)
69
- if (event.origin === runtimeConfig.public.webui.url) {
70
- const data = event.data
83
+ if (event.origin !== runtimeConfig.public.webui.url) return
71
84
 
72
- if (!['show-traces', 'show-sources', 'show-memories', 'set-context'].includes(data.type)) {
73
- console.log('Unknown message type:', data.type)
74
- return
75
- }
85
+ const data = event.data
86
+ if (!['show-traces', 'show-sources', 'show-memories', 'set-context'].includes(data.type)) {
87
+ console.log('Unknown message type:', data.type)
88
+ return
89
+ }
76
90
 
77
- const thread_id = data.thread_id as string
78
- const display_id = data.display_id as string
91
+ const display_id = data.display_id as string
92
+ if (!display_id) return
79
93
 
80
- if (data.type === 'show-traces' || route.path.endsWith('/tracing')) {
81
- router.push(tenantPath(`/service/openai/${thread_id}/${display_id}/tracing`))
94
+ // Explicit action-button click: the action knows only the display, so resolve its owning thread from the backend
95
+ // (the correct per-agent, salted thread the pipe persisted events under).
96
+ const requestedView = VIEW_BY_ACTION[data.type]
97
+ if (requestedView) {
98
+ try {
99
+ const { thread_id } = await resolveThreadForDisplay({
100
+ composable: '$fetch',
101
+ path: { tenant_id: tenantId.value!, display_id },
102
+ })
103
+ router.push(tenantPath(`/service/openai/${thread_id}/${display_id}/${requestedView}`))
82
104
  }
83
- if (data.type === 'show-sources' || route.path.endsWith('/sources')) {
84
- router.push(tenantPath(`/service/openai/${thread_id}/${display_id}/sources`))
105
+ catch (error) {
106
+ // No AI-Hub thread owns this display (e.g. a plain-LLM message) — leave the panel as-is instead of erroring.
107
+ console.warn('No AI-Hub thread found for display', display_id, error)
85
108
  }
86
- if (data.type === 'show-memories' || route.path.endsWith('/memories')) {
87
- router.push(tenantPath(`/service/openai/${thread_id}/${display_id}/memories`))
109
+ return
110
+ }
111
+
112
+ // set-context: the pipe pushes the correct thread_id as each message streams; keep an already-open panel synced.
113
+ const view = openPanelView()
114
+ if (view) {
115
+ const thread_id = data.thread_id as string
116
+ if (thread_id) {
117
+ router.push(tenantPath(`/service/openai/${thread_id}/${display_id}/${view}`))
88
118
  }
89
119
  }
90
120
  }
@@ -88,6 +88,7 @@ export {
88
88
  receiveOpenwebuiWebhook,
89
89
  removeAgentFromThread,
90
90
  removeUserFromThread,
91
+ resolveThreadForDisplay,
91
92
  revokeRole,
92
93
  revokeTokenEndpoint,
93
94
  searchOrganizationMemories,
@@ -844,6 +845,11 @@ export {
844
845
  type RerankerEvent,
845
846
  type RerankerEventWritable,
846
847
  Resolution,
848
+ type ResolveThreadForDisplayData,
849
+ type ResolveThreadForDisplayError,
850
+ type ResolveThreadForDisplayErrors,
851
+ type ResolveThreadForDisplayResponse,
852
+ type ResolveThreadForDisplayResponses,
847
853
  type ResponseFormatJsonObject,
848
854
  type ResponseFormatJsonSchema,
849
855
  type ResponseFormatText,
@@ -939,6 +945,7 @@ export {
939
945
  type ThreadAgentDto,
940
946
  type ThreadDto,
941
947
  type ThreadDtoWritable,
948
+ type ThreadReference,
942
949
  TimeRange,
943
950
  type ToggleButton,
944
951
  type ToggleButtonWritable,
@@ -21445,6 +21445,21 @@ export const ThreadDTOSchema = {
21445
21445
  description: "Thread information and statistics for API response.",
21446
21446
  } as const;
21447
21447
 
21448
+ export const ThreadReferenceSchema = {
21449
+ properties: {
21450
+ thread_id: {
21451
+ type: "string",
21452
+ title: "Thread Id",
21453
+ description: "The thread ID that owns the requested display",
21454
+ },
21455
+ },
21456
+ type: "object",
21457
+ required: ["thread_id"],
21458
+ title: "ThreadReference",
21459
+ description:
21460
+ "The thread that owns a display, resolved so the chat-UI side panel can open the correct per-agent thread.",
21461
+ } as const;
21462
+
21448
21463
  export const TimeRangeSchema = {
21449
21464
  type: "string",
21450
21465
  enum: ["1h", "24h", "30d", "365d"],
@@ -256,6 +256,9 @@ import type {
256
256
  RemoveUserFromThreadData,
257
257
  RemoveUserFromThreadError,
258
258
  RemoveUserFromThreadResponse,
259
+ ResolveThreadForDisplayData,
260
+ ResolveThreadForDisplayError,
261
+ ResolveThreadForDisplayResponse,
259
262
  RevokeRoleData,
260
263
  RevokeRoleError,
261
264
  RevokeRoleResponse,
@@ -803,6 +806,38 @@ export const getAgentEventsInThread = <
803
806
  ...options,
804
807
  });
805
808
 
809
+ /**
810
+ * Resolve Thread For Display
811
+ *
812
+ * Resolves the thread that owns a display so the chat-UI side panel can open the correct per-agent thread
813
+ * without recomputing the salted thread_id.
814
+ */
815
+ export const resolveThreadForDisplay = <
816
+ TComposable extends Composable = "$fetch",
817
+ DefaultT extends ResolveThreadForDisplayResponse =
818
+ ResolveThreadForDisplayResponse,
819
+ >(
820
+ options: Options<
821
+ TComposable,
822
+ ResolveThreadForDisplayData,
823
+ ResolveThreadForDisplayResponse,
824
+ DefaultT
825
+ >,
826
+ ) =>
827
+ (options.client ?? client).get<
828
+ TComposable,
829
+ ResolveThreadForDisplayResponse | DefaultT,
830
+ ResolveThreadForDisplayError,
831
+ DefaultT
832
+ >({
833
+ security: [
834
+ { scheme: "bearer", type: "http" },
835
+ { scheme: "bearer", type: "http" },
836
+ ],
837
+ url: "/{tenant_id}/events/agents/displays/{display_id}/thread",
838
+ ...options,
839
+ });
840
+
806
841
  /**
807
842
  * Get Agent Event Timeseries
808
843
  *
@@ -14520,6 +14520,20 @@ export type ThreadDto = {
14520
14520
  llm_cost?: number;
14521
14521
  };
14522
14522
 
14523
+ /**
14524
+ * ThreadReference
14525
+ *
14526
+ * The thread that owns a display, resolved so the chat-UI side panel can open the correct per-agent thread.
14527
+ */
14528
+ export type ThreadReference = {
14529
+ /**
14530
+ * Thread Id
14531
+ *
14532
+ * The thread ID that owns the requested display
14533
+ */
14534
+ thread_id: string;
14535
+ };
14536
+
14523
14537
  /**
14524
14538
  * TimeRange
14525
14539
  */
@@ -25015,6 +25029,44 @@ export type GetAgentEventsInThreadResponses = {
25015
25029
  export type GetAgentEventsInThreadResponse =
25016
25030
  GetAgentEventsInThreadResponses[keyof GetAgentEventsInThreadResponses];
25017
25031
 
25032
+ export type ResolveThreadForDisplayData = {
25033
+ body?: never;
25034
+ path: {
25035
+ /**
25036
+ * Tenant Id
25037
+ *
25038
+ * Tenant identifier: a name, ObjectId, or 'active'
25039
+ */
25040
+ tenant_id: string;
25041
+ /**
25042
+ * Display ID
25043
+ */
25044
+ display_id: string;
25045
+ };
25046
+ query?: never;
25047
+ url: "/{tenant_id}/events/agents/displays/{display_id}/thread";
25048
+ };
25049
+
25050
+ export type ResolveThreadForDisplayErrors = {
25051
+ /**
25052
+ * Validation Error
25053
+ */
25054
+ 422: HttpValidationError;
25055
+ };
25056
+
25057
+ export type ResolveThreadForDisplayError =
25058
+ ResolveThreadForDisplayErrors[keyof ResolveThreadForDisplayErrors];
25059
+
25060
+ export type ResolveThreadForDisplayResponses = {
25061
+ /**
25062
+ * Successful Response
25063
+ */
25064
+ 200: ThreadReference;
25065
+ };
25066
+
25067
+ export type ResolveThreadForDisplayResponse =
25068
+ ResolveThreadForDisplayResponses[keyof ResolveThreadForDisplayResponses];
25069
+
25018
25070
  export type GetAgentEventTimeseriesData = {
25019
25071
  body?: never;
25020
25072
  path: {