@swiss-ai-hub/web 0.290.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +479 -0
  3. package/app.config.ts +1 -0
  4. package/app.vue +52 -0
  5. package/assets/css/main.css +4 -0
  6. package/assets/images/logo.png +0 -0
  7. package/components/Agent/Avatar.vue +40 -0
  8. package/components/Agent/Card.vue +139 -0
  9. package/components/Agent/Configuration.vue +20 -0
  10. package/components/Agent/CreateModal.vue +287 -0
  11. package/components/Agent/EmptyCard.vue +35 -0
  12. package/components/Agent/List.vue +85 -0
  13. package/components/Agent/TemplateCard.vue +58 -0
  14. package/components/AppLoader.vue +55 -0
  15. package/components/Chat/Message.vue +90 -0
  16. package/components/Chat/SourceNodes.vue +120 -0
  17. package/components/Chat/Thread.vue +134 -0
  18. package/components/Costs/Table.vue +56 -0
  19. package/components/Dashboard/Component/BarChart.vue +46 -0
  20. package/components/Dashboard/Component/LineChart.vue +138 -0
  21. package/components/Dashboard/Component/Number.vue +31 -0
  22. package/components/Dashboard/Grid.vue +214 -0
  23. package/components/Dashboard/Item.vue +75 -0
  24. package/components/Dashboard/Trend.vue +34 -0
  25. package/components/Display/List/DisplayList.vue +93 -0
  26. package/components/Evaluation/Dataset/Card.vue +70 -0
  27. package/components/Evaluation/Dataset/Create.vue +81 -0
  28. package/components/Evaluation/Dataset/Edit.vue +132 -0
  29. package/components/Event/Display/AddMemoryToChatHistoryEvent.vue +60 -0
  30. package/components/Event/Display/AgentInTheLoopRequestEvent.vue +56 -0
  31. package/components/Event/Display/AgentInTheLoopResponseEvent.vue +17 -0
  32. package/components/Event/Display/Base.vue +125 -0
  33. package/components/Event/Display/BaseRetrieveMemoryEvent.vue +101 -0
  34. package/components/Event/Display/BaseStoreMemoryEvent.vue +182 -0
  35. package/components/Event/Display/ChunkEvent.vue +40 -0
  36. package/components/Event/Display/EmbeddingEvent.vue +25 -0
  37. package/components/Event/Display/ExceptionEvent.vue +21 -0
  38. package/components/Event/Display/GuardAcceptEvent.vue +25 -0
  39. package/components/Event/Display/GuardEvent.vue +17 -0
  40. package/components/Event/Display/GuardRejectionEvent.vue +25 -0
  41. package/components/Event/Display/HumanInTheLoopRequestEvent.vue +53 -0
  42. package/components/Event/Display/HumanInTheLoopResponseEvent.vue +40 -0
  43. package/components/Event/Display/LLMCostEvent.vue +25 -0
  44. package/components/Event/Display/LLMEvent.vue +92 -0
  45. package/components/Event/Display/LimitChatHistoryEvent.vue +60 -0
  46. package/components/Event/Display/RAGFailureStopEvent.vue +28 -0
  47. package/components/Event/Display/RAGStartEvent.vue +77 -0
  48. package/components/Event/Display/RAGSuccessStopEvent.vue +16 -0
  49. package/components/Event/Display/RawDataContent.vue +69 -0
  50. package/components/Event/Display/RerankerEvent.vue +39 -0
  51. package/components/Event/Display/RetrieverEvent.vue +22 -0
  52. package/components/Event/Display/RouterEvent.vue +54 -0
  53. package/components/Event/Display/StandaloneQuestionCondenserEvent.vue +38 -0
  54. package/components/Event/Display/StopEvent.vue +16 -0
  55. package/components/Event/Display/ThoughtEvent.vue +20 -0
  56. package/components/Event/Display/ToolEvent.vue +38 -0
  57. package/components/Event/Display/UnknownEvent.vue +17 -0
  58. package/components/Event/Display/UserMessageEvent.vue +35 -0
  59. package/components/Event/List/EventList.vue +249 -0
  60. package/components/Event/Statistics.vue +49 -0
  61. package/components/Event/Timeseries.vue +224 -0
  62. package/components/FormKit/AgentSelector.vue +307 -0
  63. package/components/FormKit/ChipsInput.vue +62 -0
  64. package/components/FormKit/DynamicConfiguration.vue +155 -0
  65. package/components/FormKit/IconSelector.vue +72 -0
  66. package/components/FormKit/KnowledgeDatabaseSelector.vue +92 -0
  67. package/components/FormKit/LocaleInput.vue +150 -0
  68. package/components/FormKit/ModelSelect.vue +110 -0
  69. package/components/FormKit/Repeater.vue +93 -0
  70. package/components/FormKit/VectorStoreInput.vue +247 -0
  71. package/components/Knowledge/Document/List.vue +140 -0
  72. package/components/Knowledge/Document/Overview.vue +28 -0
  73. package/components/Knowledge/Document/UploadModal.vue +298 -0
  74. package/components/Knowledge/Document/WithNodes.vue +105 -0
  75. package/components/Knowledge/Namespace/Card.vue +108 -0
  76. package/components/Knowledge/Namespace/CreateModal.vue +203 -0
  77. package/components/Knowledge/Namespace/EditModal.vue +134 -0
  78. package/components/Knowledge/Namespace/EmptyCard.vue +35 -0
  79. package/components/Knowledge/Node/Content.vue +71 -0
  80. package/components/Markdown/Renderer.vue +87 -0
  81. package/components/Memory/DetailPage.vue +48 -0
  82. package/components/Memory/Edit.vue +241 -0
  83. package/components/Memory/Graph.vue +318 -0
  84. package/components/Memory/List.vue +155 -0
  85. package/components/Memory/MemoryManagementPage.vue +178 -0
  86. package/components/Memory/OpenWebUIContent.vue +96 -0
  87. package/components/Memory/PageLayout.vue +72 -0
  88. package/components/Models/ModelDetailsPanel.vue +250 -0
  89. package/components/Models/NamespaceCard.vue +79 -0
  90. package/components/Navigation/Left.vue +85 -0
  91. package/components/Navigation/Top.vue +31 -0
  92. package/components/Notification/Item.vue +88 -0
  93. package/components/Notification/NotificationsOverlay.vue +164 -0
  94. package/components/Process/Card.vue +119 -0
  95. package/components/Process/Configuration.vue +20 -0
  96. package/components/Process/CreateModal.vue +276 -0
  97. package/components/Process/EmptyCard.vue +35 -0
  98. package/components/Process/Form.vue +153 -0
  99. package/components/Process/Starts.vue +44 -0
  100. package/components/Process/Walkthrough/List.vue +162 -0
  101. package/components/Role/AccessRulesEditor.vue +132 -0
  102. package/components/Role/Card.vue +68 -0
  103. package/components/Role/Create.vue +55 -0
  104. package/components/Role/Edit.vue +82 -0
  105. package/components/Role/UsageLimitsEditor.vue +225 -0
  106. package/components/Service/Selection.vue +148 -0
  107. package/components/Structural/Column.vue +74 -0
  108. package/components/Structural/Screen.vue +10 -0
  109. package/components/Structural/Substructure.vue +5 -0
  110. package/components/Tenant/Switcher.vue +102 -0
  111. package/components/Thread/Details.vue +135 -0
  112. package/components/Thread/Hierarchy.vue +136 -0
  113. package/components/Thread/Info.vue +41 -0
  114. package/components/Thread/List.vue +136 -0
  115. package/components/User/Bar.vue +74 -0
  116. package/components/User/List.vue +86 -0
  117. package/components/User/RoleChips.vue +83 -0
  118. package/components/User/Settings.vue +79 -0
  119. package/components/Workflow/Modal.vue +39 -0
  120. package/components/Workflow/NodeCard.vue +41 -0
  121. package/components/Workflow/StartNode.vue +24 -0
  122. package/components/Workflow/StepNode.vue +27 -0
  123. package/components/Workflow/StopNode.vue +24 -0
  124. package/components/Workflow/Visualization.vue +265 -0
  125. package/components/mdc/MarkdownFigure.vue +9 -0
  126. package/components/mdc/MarkdownTable.vue +9 -0
  127. package/components/mdc/ResolveImageComponent.vue +58 -0
  128. package/composables/agent/useAgentClass.ts +27 -0
  129. package/composables/agent/useAgentClassInstances.ts +27 -0
  130. package/composables/agent/useAgentClasses.ts +27 -0
  131. package/composables/agent/useAgentIconFromThread.ts +8 -0
  132. package/composables/agent/useAgentInstance.ts +28 -0
  133. package/composables/agent/useAgentInstanceThreads.ts +76 -0
  134. package/composables/agent/useAgentInstances.ts +25 -0
  135. package/composables/agent/useAgentNavigation.ts +35 -0
  136. package/composables/agent/useCreateAgentInstance.ts +33 -0
  137. package/composables/agent/useDeleteAgentInstance.ts +31 -0
  138. package/composables/agent/useUpdateAgentInstance.ts +40 -0
  139. package/composables/auth/useAuth.ts +54 -0
  140. package/composables/auth/useAuthProviders.ts +14 -0
  141. package/composables/chat/useChatCompletions.ts +30 -0
  142. package/composables/dashboard/useAgentNameFromDashboardWidget.ts +27 -0
  143. package/composables/dashboard/useDashboardComponent.ts +27 -0
  144. package/composables/dashboard/useSaveDashboard.ts +21 -0
  145. package/composables/document/useCreateNamespace.ts +26 -0
  146. package/composables/document/useDatabases.ts +23 -0
  147. package/composables/document/useDocument.ts +29 -0
  148. package/composables/document/useDocumentUrl.ts +20 -0
  149. package/composables/document/useDocuments.ts +107 -0
  150. package/composables/document/useNodes.ts +29 -0
  151. package/composables/document/useSummaryNodes.ts +32 -0
  152. package/composables/document/useUpdateNamespace.ts +22 -0
  153. package/composables/evaluation/useCreateDataset.ts +19 -0
  154. package/composables/evaluation/useDataset.ts +26 -0
  155. package/composables/evaluation/useDatasets.ts +25 -0
  156. package/composables/evaluation/useUpdateDataset.ts +23 -0
  157. package/composables/event/useBasicEventStatistics.ts +83 -0
  158. package/composables/event/useEventColor.ts +25 -0
  159. package/composables/event/useEventComponent.ts +87 -0
  160. package/composables/event/useEventTimeseries.ts +39 -0
  161. package/composables/event/useEventTimeseriesStats.ts +26 -0
  162. package/composables/file/useFileUpload.ts +91 -0
  163. package/composables/file/useSupportedFileTypes.ts +22 -0
  164. package/composables/form/useCreateInstanceForm.ts +251 -0
  165. package/composables/form/useFormKitTransform.ts +753 -0
  166. package/composables/memory/useMemoryCRUD.ts +88 -0
  167. package/composables/memory/useMemoryFactory.ts +319 -0
  168. package/composables/memory/useMemorySearchFilter.ts +74 -0
  169. package/composables/models/useModelsList.ts +24 -0
  170. package/composables/models/useSingleModel.ts +30 -0
  171. package/composables/notification/useNotificationPoller.ts +58 -0
  172. package/composables/notification/useNotifications.ts +57 -0
  173. package/composables/notification/useUpdateMultipleNotifications.ts +17 -0
  174. package/composables/notification/useUpdateNotification.ts +17 -0
  175. package/composables/process/useCreateProcessInstance.ts +32 -0
  176. package/composables/process/useDeleteProcessInstance.ts +31 -0
  177. package/composables/process/useProcessClasses.ts +27 -0
  178. package/composables/process/useProcessInstance.ts +28 -0
  179. package/composables/process/useProcessInstances.ts +27 -0
  180. package/composables/process/useProcessWalkthroughs.ts +73 -0
  181. package/composables/process/useSendProcessStartForm.ts +43 -0
  182. package/composables/process/useUpdateProcessInstance.ts +40 -0
  183. package/composables/role/useCreateRole.ts +19 -0
  184. package/composables/role/useDeleteRole.ts +21 -0
  185. package/composables/role/useRole.ts +30 -0
  186. package/composables/role/useRoles.ts +25 -0
  187. package/composables/role/useUpdateRole.ts +22 -0
  188. package/composables/suite/useApps.ts +31 -0
  189. package/composables/suite/useSuite.ts +26 -0
  190. package/composables/tenant/useActiveTenant.ts +27 -0
  191. package/composables/tenant/useSysadminNavigation.ts +19 -0
  192. package/composables/tenant/useTenant.ts +38 -0
  193. package/composables/tenant/useTenantMemberships.ts +15 -0
  194. package/composables/tenant/useTenantPath.ts +20 -0
  195. package/composables/tenant/useTenantPolling.ts +30 -0
  196. package/composables/tenant/useTenantReady.ts +12 -0
  197. package/composables/theme/useDarkMode.ts +5 -0
  198. package/composables/thread/useThread.ts +27 -0
  199. package/composables/thread/useThreadEvents.ts +91 -0
  200. package/composables/thread/useThreadUtils.ts +49 -0
  201. package/composables/thread/useThreads.ts +64 -0
  202. package/composables/thread/useThreadsInfinite.ts +56 -0
  203. package/composables/translation/useTranslate.ts +20 -0
  204. package/composables/useRouteReady.ts +21 -0
  205. package/composables/useTimeAgo.ts +40 -0
  206. package/composables/user/useAssignRoleToUser.ts +22 -0
  207. package/composables/user/useMyUser.ts +25 -0
  208. package/composables/user/useRevokeRoleFromUser.ts +21 -0
  209. package/composables/user/useUser.ts +30 -0
  210. package/composables/user/useUsers.ts +63 -0
  211. package/composables/utils/useJsonTree.ts +138 -0
  212. package/formkit.config.ts +44 -0
  213. package/i18n/locales/de.yaml +815 -0
  214. package/i18n/locales/en.yaml +804 -0
  215. package/i18n/locales/fr.yaml +812 -0
  216. package/i18n/locales/it.yaml +808 -0
  217. package/layouts/anonymous.vue +8 -0
  218. package/layouts/default.vue +116 -0
  219. package/middleware/auth.global.ts +62 -0
  220. package/nuxt.config.ts +145 -0
  221. package/package.json +114 -0
  222. package/pages/[tenant]/index.vue +31 -0
  223. package/pages/[tenant]/notifications/index.vue +235 -0
  224. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/chat.vue +67 -0
  225. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/configuration.vue +122 -0
  226. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/memories/[memory_id].vue +3 -0
  227. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/memories.vue +20 -0
  228. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/overview.vue +72 -0
  229. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/threads.vue +52 -0
  230. package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/workflow.vue +19 -0
  231. package/pages/[tenant]/service/agents/[agent_class]-[agent_id].vue +63 -0
  232. package/pages/[tenant]/service/agents/templates.vue +102 -0
  233. package/pages/[tenant]/service/agents.vue +185 -0
  234. package/pages/[tenant]/service/datasets/[dataset_id].vue +81 -0
  235. package/pages/[tenant]/service/datasets.vue +53 -0
  236. package/pages/[tenant]/service/health/index.vue +3 -0
  237. package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id]/nodes.vue +20 -0
  238. package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id]/overview.vue +40 -0
  239. package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id]/summary.vue +88 -0
  240. package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id].vue +48 -0
  241. package/pages/[tenant]/service/knowledge/[db]/[namespace].vue +144 -0
  242. package/pages/[tenant]/service/knowledge.vue +126 -0
  243. package/pages/[tenant]/service/models/[model_name].vue +84 -0
  244. package/pages/[tenant]/service/models.vue +61 -0
  245. package/pages/[tenant]/service/my-account.vue +117 -0
  246. package/pages/[tenant]/service/openai/[thread_id]/[display_id]/memories.vue +66 -0
  247. package/pages/[tenant]/service/openai/[thread_id]/[display_id]/sources.vue +100 -0
  248. package/pages/[tenant]/service/openai/[thread_id]/[display_id]/tracing.vue +49 -0
  249. package/pages/[tenant]/service/openai.vue +101 -0
  250. package/pages/[tenant]/service/organization-memories/graph.vue +97 -0
  251. package/pages/[tenant]/service/organization-memories/list/[memory_id].vue +3 -0
  252. package/pages/[tenant]/service/organization-memories/list.vue +150 -0
  253. package/pages/[tenant]/service/organization-memories.vue +3 -0
  254. package/pages/[tenant]/service/processes/[process_class]-[process_id]/[process_walkthrough_id].vue +7 -0
  255. package/pages/[tenant]/service/processes/[process_class]-[process_id]/configuration.vue +106 -0
  256. package/pages/[tenant]/service/processes/[process_class]-[process_id]/overview.vue +67 -0
  257. package/pages/[tenant]/service/processes/[process_class]-[process_id]/start.vue +26 -0
  258. package/pages/[tenant]/service/processes/[process_class]-[process_id]/walkthroughs/[process_walkthrough_id]/overview.vue +14 -0
  259. package/pages/[tenant]/service/processes/[process_class]-[process_id]/walkthroughs.vue +54 -0
  260. package/pages/[tenant]/service/processes/[process_class]-[process_id].vue +60 -0
  261. package/pages/[tenant]/service/processes.vue +129 -0
  262. package/pages/[tenant]/service/roles/[role_id].vue +54 -0
  263. package/pages/[tenant]/service/roles.vue +84 -0
  264. package/pages/[tenant]/service/threads/[thread_id]/chat.vue +51 -0
  265. package/pages/[tenant]/service/threads/[thread_id]/display/[display_id].vue +21 -0
  266. package/pages/[tenant]/service/threads/[thread_id]/display.vue +29 -0
  267. package/pages/[tenant]/service/threads/[thread_id]/hierarchy.vue +14 -0
  268. package/pages/[tenant]/service/threads/[thread_id]/memories/[memory_id].vue +3 -0
  269. package/pages/[tenant]/service/threads/[thread_id]/memories.vue +19 -0
  270. package/pages/[tenant]/service/threads/[thread_id]/overview.vue +100 -0
  271. package/pages/[tenant]/service/threads/[thread_id].vue +54 -0
  272. package/pages/[tenant]/service/threads.vue +52 -0
  273. package/pages/[tenant]/service/user-memories/graph.vue +97 -0
  274. package/pages/[tenant]/service/user-memories/list/[memory_id].vue +3 -0
  275. package/pages/[tenant]/service/user-memories/list.vue +150 -0
  276. package/pages/[tenant]/service/user-memories.vue +3 -0
  277. package/pages/[tenant]/service/users/[user_id].vue +117 -0
  278. package/pages/[tenant]/service/users.vue +88 -0
  279. package/pages/auth/callback.vue +52 -0
  280. package/pages/auth/login.vue +80 -0
  281. package/pages/auth/renew.vue +24 -0
  282. package/pages/index.vue +59 -0
  283. package/pages/select-tenant.vue +76 -0
  284. package/plugins/0.runtime-config.client.ts +55 -0
  285. package/plugins/apexcharts.client.ts +5 -0
  286. package/plugins/api-client.client.ts +38 -0
  287. package/plugins/dark-mode.client.ts +12 -0
  288. package/plugins/keycloak-client.ts +41 -0
  289. package/plugins/oidc-client.ts +78 -0
  290. package/sdk/client/client/client.gen.ts +237 -0
  291. package/sdk/client/client/index.ts +24 -0
  292. package/sdk/client/client/types.gen.ts +213 -0
  293. package/sdk/client/client/utils.gen.ts +407 -0
  294. package/sdk/client/client.gen.ts +25 -0
  295. package/sdk/client/core/auth.gen.ts +42 -0
  296. package/sdk/client/core/bodySerializer.gen.ts +96 -0
  297. package/sdk/client/core/params.gen.ts +181 -0
  298. package/sdk/client/core/pathSerializer.gen.ts +180 -0
  299. package/sdk/client/core/queryKeySerializer.gen.ts +136 -0
  300. package/sdk/client/core/serverSentEvents.gen.ts +265 -0
  301. package/sdk/client/core/types.gen.ts +118 -0
  302. package/sdk/client/core/utils.gen.ts +143 -0
  303. package/sdk/client/index.ts +1013 -0
  304. package/sdk/client/schemas.gen.ts +35395 -0
  305. package/sdk/client/sdk.gen.ts +3438 -0
  306. package/sdk/client/transformers.gen.ts +143 -0
  307. package/sdk/client/types.gen.ts +27567 -0
  308. package/tailwind.config.mjs +27 -0
  309. package/themes/aihub-theme.ts +125 -0
  310. package/types/DashboardWidget.ts +13 -0
  311. package/types/EventChartInput.ts +7 -0
  312. package/types/NavItem.ts +6 -0
  313. package/types/TimeseriesInput.ts +7 -0
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div class="hidden">
3
+ {{ t('auth.renew.processing') }}
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ const { $auth } = useNuxtApp()
9
+
10
+ definePageMeta({
11
+ layout: 'anonymous',
12
+ })
13
+
14
+ const { t } = useI18n()
15
+
16
+ await $auth.signinSilentCallback()
17
+ </script>
18
+
19
+ <style scoped>
20
+ /* Hide this page since it's loaded in an iframe */
21
+ .hidden {
22
+ display: none;
23
+ }
24
+ </style>
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <div class="flex h-screen items-center justify-center">
3
+ <ProgressSpinner v-if="tenantsAreLoading" />
4
+ <div
5
+ v-else-if="!tenants?.length"
6
+ class="text-center"
7
+ >
8
+ <h1 class="mb-4 text-2xl font-bold">
9
+ {{ t('tenant.no_tenant_title') }}
10
+ </h1>
11
+ <p class="text-muted-color">
12
+ {{ t('tenant.no_tenant_description') }}
13
+ </p>
14
+ </div>
15
+ </div>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { getMyTenants } from '@core/sdk/client'
20
+
21
+ const REDIRECT_KEY = 'aihub_redirect_after_login'
22
+
23
+ const { t } = useI18n()
24
+ const localePath = useLocalePath()
25
+
26
+ const tenantsAreLoading = ref(true)
27
+ const tenants = ref<{ id: string }[] | null>(null)
28
+
29
+ onMounted(async () => {
30
+ try {
31
+ const response = await getMyTenants({ composable: '$fetch' })
32
+ tenants.value = response.tenants
33
+
34
+ if (!response.tenants?.length) {
35
+ tenantsAreLoading.value = false
36
+ return
37
+ }
38
+
39
+ const storedRedirect = sessionStorage.getItem(REDIRECT_KEY)
40
+ sessionStorage.removeItem(REDIRECT_KEY)
41
+
42
+ if (storedRedirect && storedRedirect !== '/') {
43
+ await navigateTo(storedRedirect, { replace: true })
44
+ return
45
+ }
46
+
47
+ if (response.tenants.length === 1) {
48
+ const tenant = response.tenants[0]
49
+ await navigateTo(localePath(`/${tenant.id}/service/openai`), { replace: true })
50
+ return
51
+ }
52
+
53
+ await navigateTo(localePath('/select-tenant'), { replace: true })
54
+ }
55
+ catch {
56
+ tenantsAreLoading.value = false
57
+ }
58
+ })
59
+ </script>
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <div class="flex h-screen items-center justify-center">
3
+ <div class="w-full max-w-2xl px-4">
4
+ <h1 class="mb-2 text-center text-2xl font-bold">
5
+ {{ t('tenant.select_title') }}
6
+ </h1>
7
+ <p class="mb-8 text-center text-muted-color">
8
+ {{ t('tenant.select_description') }}
9
+ </p>
10
+
11
+ <div
12
+ v-if="tenantsAreLoading"
13
+ class="flex justify-center"
14
+ >
15
+ <AppLoader :size="48" />
16
+ </div>
17
+
18
+ <div
19
+ v-else
20
+ class="grid grid-cols-1 gap-4 md:grid-cols-2"
21
+ >
22
+ <Card
23
+ v-for="tenant in tenants"
24
+ :key="tenant.id"
25
+ class="cursor-pointer border border-transparent hover:border-primary-200 dark:hover:border-primary-700"
26
+ @click="selectTenant(tenant)"
27
+ >
28
+ <template #title>
29
+ {{ tenant.name }}
30
+ </template>
31
+ <template #content>
32
+ <p class="text-sm text-muted-color">
33
+ {{ tenant.description }}
34
+ </p>
35
+ </template>
36
+ </Card>
37
+
38
+ <Card
39
+ v-if="isSysAdmin"
40
+ class="cursor-pointer border border-dashed border-surface-300 hover:border-primary-200 dark:border-surface-600 dark:hover:border-primary-700"
41
+ @click="enterSysAdmin"
42
+ >
43
+ <template #title>
44
+ <div class="flex items-center gap-2">
45
+ <i class="pi pi-cog text-primary" />
46
+ {{ t('tenant.administration_title') }}
47
+ </div>
48
+ </template>
49
+ <template #content>
50
+ <p class="text-sm text-muted-color">
51
+ {{ t('tenant.administration_description') }}
52
+ </p>
53
+ </template>
54
+ </Card>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </template>
59
+
60
+ <script setup lang="ts">
61
+ import { setMyActiveTenant } from '@core/sdk/client'
62
+
63
+ import type { TenantMembershipDto } from '@core/sdk/client'
64
+
65
+ definePageMeta({ layout: 'anonymous' })
66
+
67
+ const { t } = useI18n()
68
+ const localePath = useLocalePath()
69
+ const { tenants, tenantsAreLoading, isSysAdmin } = useTenantMemberships()
70
+ const { enterSysadmin: enterSysAdmin } = useSysadminNavigation()
71
+
72
+ async function selectTenant(tenant: TenantMembershipDto) {
73
+ await setMyActiveTenant({ composable: '$fetch', body: { tenant_id: tenant.id } })
74
+ await navigateTo(localePath(`/${tenant.id}/service/openai`), { replace: true })
75
+ }
76
+ </script>
@@ -0,0 +1,55 @@
1
+ // The ONE place runtime configuration enters the app.
2
+ //
3
+ // In a production container, nginx envsubst's config.template.js into
4
+ // /config.js, which index.html loads as a classic <script> in <head> — so
5
+ // `window.__AIHUB_CONFIG__` is already populated, synchronously, before any
6
+ // Nuxt plugin runs. This plugin maps it into `runtimeConfig.public` so every
7
+ // existing downstream reader (oidc-client, api-client, composables) keeps
8
+ // working unchanged. No $fetch, no async, no plugin-ordering concerns — which
9
+ // is why it replaced three separate config.json-fetching plugins.
10
+ //
11
+ // The numeric `0.` filename prefix makes this the first plugin in the layer
12
+ // so api-client.client.ts (and any extender plugin) sees a fully-populated
13
+ // runtimeConfig.
14
+ //
15
+ // Ships in the @swiss-ai-hub/web layer, so it also runs in extenders
16
+ // (@swiss-ai-hub/sysadmin-web, customer UIs). It writes defensively: only into
17
+ // config groups the consuming app declared, and only from injected keys that
18
+ // are present, so an extender that uses a subset of the config is unaffected.
19
+ export default defineNuxtPlugin(() => {
20
+ const isConfigLoaded = useState('isConfigLoaded', () => false)
21
+ const publicConfig = useRuntimeConfig().public as Record<string, unknown>
22
+
23
+ // Dev: values are already injected from .env via .app/nuxt.config.ts and
24
+ // there is no nginx to produce /config.js — nothing to map.
25
+ if (publicConfig.env === 'dev') {
26
+ isConfigLoaded.value = true
27
+ return
28
+ }
29
+
30
+ const injected
31
+ = (globalThis as unknown as { __AIHUB_CONFIG__?: Record<string, string> }).__AIHUB_CONFIG__ ?? {}
32
+
33
+ const group = (name: string): Record<string, unknown> | undefined =>
34
+ publicConfig[name] as Record<string, unknown> | undefined
35
+
36
+ // Assigns only when the consuming app declared `target` and the injected
37
+ // value is a non-empty string (envsubst leaves unset vars as '').
38
+ const assign = (
39
+ target: Record<string, unknown> | undefined,
40
+ key: string,
41
+ value: string | undefined,
42
+ ): void => {
43
+ if (target && value) target[key] = value
44
+ }
45
+
46
+ assign(group('oidc'), 'clientId', injected.OAUTH_CLIENT_ID)
47
+ assign(group('oidc'), 'authorityUrl', injected.OAUTH_AUTHORITY_URL)
48
+ assign(group('webui'), 'url', injected.WEBUI_URL)
49
+ assign(group('ws'), 'endpoint', injected.WS_ENDPOINT)
50
+ assign(group('sysadmin'), 'url', injected.SYSADMIN_URL)
51
+ assign(group('mainApp'), 'url', injected.MAIN_APP_URL)
52
+ if (injected.API_BASE_URL) publicConfig.apiBaseUrl = injected.API_BASE_URL
53
+
54
+ isConfigLoaded.value = true
55
+ })
@@ -0,0 +1,5 @@
1
+ import VueApexCharts from 'vue3-apexcharts'
2
+
3
+ export default defineNuxtPlugin((nuxtApp) => {
4
+ nuxtApp.vueApp.use(VueApexCharts)
5
+ })
@@ -0,0 +1,38 @@
1
+ import { client } from '@core/sdk/client/client.gen'
2
+
3
+ // Configures the @swiss-ai-hub/web SDK client (`@core/sdk/client`) that every
4
+ // composable shipped in this layer uses.
5
+ //
6
+ // This MUST live in a plugin, not in app.vue: app.vue is app-singular, so any
7
+ // app that EXTENDS this layer (@swiss-ai-hub/sysadmin-web, or a customer UI)
8
+ // supplies its own app.vue and the layer's app.vue never runs — leaving the
9
+ // SDK client unconfigured and every inherited layer composable firing against
10
+ // an unconfigured client. Plugins, by contrast, run in every extender.
11
+ //
12
+ // Where the AI-Hub API lives is the layer's extension point. The base URL is
13
+ // resolved once by 0.runtime-config.client.ts (which runs first — numeric
14
+ // filename prefix) into `runtimeConfig.public.apiBaseUrl`:
15
+ // - dev: from .env via .app/nuxt.config.ts (defaults to same-origin /api/v1)
16
+ // - prod container: from API_BASE_URL in window.__AIHUB_CONFIG__ (/config.js)
17
+ // web leaves it at the same-origin default; sysadmin-web (and customers whose
18
+ // UI is on a different origin than the API) point it at the API instance.
19
+ export default defineNuxtPlugin((nuxtApp) => {
20
+ const publicConfig = useRuntimeConfig().public as Record<string, unknown>
21
+ const { getToken } = useAuth()
22
+
23
+ const baseURL = (publicConfig.apiBaseUrl as string) || '/api/v1'
24
+
25
+ client.setConfig({
26
+ baseURL,
27
+ // getToken() throws when the user is not logged in; auth-optional
28
+ // endpoints (e.g. auth providers on the login page) still resolve.
29
+ auth: async () => await getToken(),
30
+ onRequest: ({ options }) => {
31
+ const locale = (nuxtApp.$i18n as { locale?: { value?: string } } | undefined)?.locale?.value
32
+ if (locale) options.headers.set('lang', locale)
33
+ },
34
+ onResponseError: ({ response }) => {
35
+ console.error('AI-Hub API error', response.status, response._data?.detail)
36
+ },
37
+ })
38
+ })
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Eagerly initializes dark mode on app startup so the ``.dark`` class is on
3
+ * ``<html>`` before the first page renders. Without this, a reload of a page
4
+ * that doesn't itself call ``useDarkMode`` would render in light mode until
5
+ * some later component happens to touch the composable.
6
+ *
7
+ * Uses the shared composable (``useDarkMode``) rather than ``useDark``
8
+ * directly so the plugin and any component share the same ref instance.
9
+ */
10
+ export default defineNuxtPlugin(() => {
11
+ useDarkMode()
12
+ })
@@ -0,0 +1,41 @@
1
+ import { defineNuxtPlugin } from '#app'
2
+
3
+ class KeycloakClient {
4
+ private readonly logoutUrl: string
5
+ private readonly clientId: string
6
+
7
+ constructor(authorityUrl: string, clientId: string) {
8
+ this.logoutUrl = `${authorityUrl}/protocol/openid-connect/logout`
9
+ this.clientId = clientId
10
+ }
11
+
12
+ async logout(refreshToken: string): Promise<void> {
13
+ const response = await fetch(this.logoutUrl, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
16
+ body: new URLSearchParams({
17
+ client_id: this.clientId,
18
+ refresh_token: refreshToken,
19
+ }),
20
+ })
21
+
22
+ if (!response.ok) {
23
+ throw new Error(`Keycloak logout failed with status ${response.status}`)
24
+ }
25
+ }
26
+ }
27
+
28
+ export default defineNuxtPlugin(() => {
29
+ const config = useRuntimeConfig()
30
+
31
+ const keycloakClient = new KeycloakClient(
32
+ config.public.oidc.authorityUrl,
33
+ config.public.oidc.clientId,
34
+ )
35
+
36
+ return {
37
+ provide: {
38
+ keycloakClient,
39
+ },
40
+ }
41
+ })
@@ -0,0 +1,78 @@
1
+ import { UserManager, WebStorageStateStore } from 'oidc-client-ts'
2
+
3
+ import { defineNuxtPlugin } from '#app'
4
+
5
+ export default defineNuxtPlugin(async ({ $i18n, $router }) => {
6
+ const config = useRuntimeConfig()
7
+
8
+ // Keycloak-compatible OIDC configuration
9
+ // Authority URL is the Keycloak realm URL (e.g., http://localhost:8180/realms/aihub)
10
+ const auth = new UserManager({
11
+ authority: config.public.oidc.authorityUrl,
12
+ client_id: config.public.oidc.clientId,
13
+ redirect_uri: `${globalThis.location.origin}/${$i18n.locale.value}/auth/callback`,
14
+ silent_redirect_uri: `${globalThis.location.origin}/${$i18n.locale.value}/auth/renew`,
15
+ post_logout_redirect_uri: globalThis.location.origin,
16
+ response_type: 'code',
17
+ scope: 'openid profile email',
18
+ filterProtocolClaims: true,
19
+ automaticSilentRenew: true,
20
+ silentRequestTimeoutInSeconds: 30,
21
+ accessTokenExpiringNotificationTimeInSeconds: 120,
22
+ userStore: new WebStorageStateStore({ store: globalThis?.localStorage }),
23
+ // Keycloak supports PKCE
24
+ disablePKCE: false,
25
+ // Disabled: OpenWebUI logout destroys the Keycloak SSO session, but we
26
+ // intentionally keep the parent app session alive (local JWT stays valid).
27
+ // The iframe @load handler in openai.vue redirects to home instead.
28
+ monitorSession: false,
29
+ })
30
+
31
+ // Add event handlers for token lifecycle events
32
+ auth.events.addAccessTokenExpiring(() => {
33
+ console.log('Access token expiring, attempting silent renewal')
34
+ })
35
+
36
+ auth.events.addAccessTokenExpired(() => {
37
+ console.log('Access token expired')
38
+ // Redirect to login when token expires and cannot be renewed
39
+ const locale = $i18n.locale.value
40
+ $router.push(`/${locale}/auth/login`)
41
+ })
42
+
43
+ auth.events.addSilentRenewError((error) => {
44
+ console.error('Silent renew error:', error)
45
+ // The refresh token is rejected (typically: Keycloak invalidated it).
46
+ // Without a working renew path, the SDK starts sending unauthenticated
47
+ // requests on the next navigation — surfacing as 401s downstream. Send
48
+ // the user to the login page now so they re-auth in place.
49
+ const locale = $i18n.locale.value
50
+ $router.push(`/${locale}/auth/login`)
51
+ })
52
+
53
+ // Check for user session on startup
54
+ try {
55
+ const user = await auth.getUser()
56
+ if (user && !user.expired) {
57
+ console.log('User already logged in')
58
+ }
59
+ else if (user?.expired) {
60
+ console.log('User session expired, attempting renewal')
61
+ try {
62
+ await auth.signinSilent()
63
+ }
64
+ catch (e) {
65
+ console.error('Failed to renew session:', e)
66
+ }
67
+ }
68
+ }
69
+ catch (e) {
70
+ console.error('Error checking initial user state:', e)
71
+ }
72
+
73
+ return {
74
+ provide: {
75
+ auth,
76
+ },
77
+ }
78
+ })
@@ -0,0 +1,237 @@
1
+ // This file is auto-generated by @hey-api/openapi-ts
2
+
3
+ import {
4
+ useAsyncData,
5
+ useFetch,
6
+ useLazyAsyncData,
7
+ useLazyFetch,
8
+ } from "nuxt/app";
9
+ import { reactive, ref, toValue, watch } from "vue";
10
+
11
+ import { createSseClient } from "../core/serverSentEvents.gen";
12
+ import type { HttpMethod } from "../core/types.gen";
13
+ import { getValidRequestBody } from "../core/utils.gen";
14
+ import type { Client, Config, RequestOptions } from "./types.gen";
15
+ import {
16
+ buildUrl,
17
+ createConfig,
18
+ executeFetchFn,
19
+ mergeConfigs,
20
+ mergeHeaders,
21
+ mergeInterceptors,
22
+ serializeBody,
23
+ setAuthParams,
24
+ unwrapRefs,
25
+ } from "./utils.gen";
26
+
27
+ export const createClient = (config: Config = {}): Client => {
28
+ let _config = mergeConfigs(createConfig(), config);
29
+
30
+ const getConfig = (): Config => ({ ..._config });
31
+
32
+ const setConfig = (config: Config): Config => {
33
+ _config = mergeConfigs(_config, config);
34
+ return getConfig();
35
+ };
36
+
37
+ const beforeRequest = async (options: RequestOptions) => {
38
+ const opts = {
39
+ ..._config,
40
+ ...options,
41
+ $fetch: options.$fetch ?? _config.$fetch ?? $fetch,
42
+ headers: mergeHeaders(_config.headers, options.headers),
43
+ onRequest: mergeInterceptors(_config.onRequest, options.onRequest),
44
+ onResponse: mergeInterceptors(_config.onResponse, options.onResponse),
45
+ };
46
+
47
+ if (opts.security) {
48
+ await setAuthParams({
49
+ ...opts,
50
+ security: opts.security,
51
+ });
52
+ }
53
+
54
+ if (opts.requestValidator) {
55
+ await opts.requestValidator(opts);
56
+ }
57
+
58
+ const url = buildUrl(opts);
59
+
60
+ return { opts, url };
61
+ };
62
+
63
+ const request: Client["request"] = ({
64
+ asyncDataOptions,
65
+ composable = "$fetch",
66
+ ...options
67
+ }) => {
68
+ const key = options.key;
69
+ const opts = {
70
+ ..._config,
71
+ ...options,
72
+ $fetch: options.$fetch ?? _config.$fetch ?? $fetch,
73
+ headers: mergeHeaders(_config.headers, options.headers),
74
+ onRequest: mergeInterceptors(_config.onRequest, options.onRequest),
75
+ onResponse: mergeInterceptors(_config.onResponse, options.onResponse),
76
+ };
77
+
78
+ const {
79
+ requestValidator,
80
+ responseTransformer,
81
+ responseValidator,
82
+ security,
83
+ } = opts;
84
+ if (requestValidator || security) {
85
+ // auth must happen in interceptors otherwise we'd need to require
86
+ // asyncContext enabled
87
+ // https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext
88
+ opts.onRequest = [
89
+ async ({ options }) => {
90
+ if (security) {
91
+ await setAuthParams({
92
+ auth: opts.auth,
93
+ headers: options.headers,
94
+ query: options.query,
95
+ security,
96
+ });
97
+ }
98
+
99
+ if (requestValidator) {
100
+ await requestValidator({
101
+ ...options,
102
+ // @ts-expect-error
103
+ body: options.rawBody,
104
+ });
105
+ }
106
+ },
107
+ ...opts.onRequest,
108
+ ];
109
+ }
110
+
111
+ if (responseTransformer || responseValidator) {
112
+ opts.onResponse = [
113
+ ...opts.onResponse,
114
+ async ({ options, response }) => {
115
+ if (options.responseType && options.responseType !== "json") {
116
+ return;
117
+ }
118
+
119
+ if (!response.ok) {
120
+ return;
121
+ }
122
+
123
+ if (responseValidator) {
124
+ await responseValidator(response._data);
125
+ }
126
+
127
+ if (responseTransformer) {
128
+ response._data = await responseTransformer(response._data);
129
+ }
130
+ },
131
+ ];
132
+ }
133
+
134
+ // remove Content-Type header if body is empty to avoid sending invalid requests
135
+ if (opts.body === undefined || opts.body === "") {
136
+ opts.headers.delete("Content-Type");
137
+ }
138
+
139
+ const fetchFn = opts.$fetch;
140
+
141
+ if (composable === "$fetch") {
142
+ return executeFetchFn(
143
+ // @ts-expect-error
144
+ opts,
145
+ fetchFn,
146
+ );
147
+ }
148
+
149
+ if (composable === "useFetch" || composable === "useLazyFetch") {
150
+ opts.rawBody = opts.body;
151
+ const bodyParams = reactive({
152
+ body: opts.body,
153
+ bodySerializer: opts.bodySerializer,
154
+ });
155
+ const body = ref(serializeBody({ ...opts, body: toValue(opts.body) }));
156
+ opts.body = body;
157
+ watch(bodyParams, (changed) => {
158
+ body.value = serializeBody(changed);
159
+ });
160
+ return composable === "useLazyFetch"
161
+ ? useLazyFetch(() => buildUrl(opts), { ...opts, ...asyncDataOptions })
162
+ : useFetch(() => buildUrl(opts), { ...opts, ...asyncDataOptions });
163
+ }
164
+
165
+ const handler: any = () =>
166
+ executeFetchFn(
167
+ // @ts-expect-error
168
+ opts,
169
+ fetchFn,
170
+ );
171
+
172
+ if (composable === "useAsyncData") {
173
+ return key
174
+ ? useAsyncData(key, handler, asyncDataOptions)
175
+ : useAsyncData(handler, asyncDataOptions);
176
+ }
177
+
178
+ if (composable === "useLazyAsyncData") {
179
+ return key
180
+ ? useLazyAsyncData(key, handler, asyncDataOptions)
181
+ : useLazyAsyncData(handler, asyncDataOptions);
182
+ }
183
+
184
+ return undefined as any;
185
+ };
186
+
187
+ const makeMethodFn =
188
+ (method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
189
+ request({ ...options, method });
190
+
191
+ const makeSseFn =
192
+ (method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
193
+ const { opts, url } = await beforeRequest(options);
194
+ return createSseClient({
195
+ ...unwrapRefs(opts),
196
+ body: opts.body as BodyInit | null | undefined,
197
+ method,
198
+ onRequest: undefined,
199
+ serializedBody: getValidRequestBody(opts) as
200
+ | BodyInit
201
+ | null
202
+ | undefined,
203
+ signal: unwrapRefs(opts.signal) as AbortSignal,
204
+ url,
205
+ });
206
+ };
207
+
208
+ const _buildUrl: Client["buildUrl"] = (options) =>
209
+ buildUrl({ ..._config, ...options } as typeof options);
210
+
211
+ return {
212
+ buildUrl: _buildUrl,
213
+ connect: makeMethodFn("CONNECT"),
214
+ delete: makeMethodFn("DELETE"),
215
+ get: makeMethodFn("GET"),
216
+ getConfig,
217
+ head: makeMethodFn("HEAD"),
218
+ options: makeMethodFn("OPTIONS"),
219
+ patch: makeMethodFn("PATCH"),
220
+ post: makeMethodFn("POST"),
221
+ put: makeMethodFn("PUT"),
222
+ request,
223
+ setConfig,
224
+ sse: {
225
+ connect: makeSseFn("CONNECT"),
226
+ delete: makeSseFn("DELETE"),
227
+ get: makeSseFn("GET"),
228
+ head: makeSseFn("HEAD"),
229
+ options: makeSseFn("OPTIONS"),
230
+ patch: makeSseFn("PATCH"),
231
+ post: makeSseFn("POST"),
232
+ put: makeSseFn("PUT"),
233
+ trace: makeSseFn("TRACE"),
234
+ },
235
+ trace: makeMethodFn("TRACE"),
236
+ } as Client;
237
+ };
@@ -0,0 +1,24 @@
1
+ // This file is auto-generated by @hey-api/openapi-ts
2
+
3
+ export type { Auth } from "../core/auth.gen";
4
+ export type { QuerySerializerOptions } from "../core/bodySerializer.gen";
5
+ export {
6
+ formDataBodySerializer,
7
+ jsonBodySerializer,
8
+ urlSearchParamsBodySerializer,
9
+ } from "../core/bodySerializer.gen";
10
+ export { buildClientParams } from "../core/params.gen";
11
+ export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen";
12
+ export { createClient } from "./client.gen";
13
+ export type {
14
+ Client,
15
+ ClientOptions,
16
+ Composable,
17
+ Config,
18
+ CreateClientConfig,
19
+ Options,
20
+ RequestOptions,
21
+ RequestResult,
22
+ TDataShape,
23
+ } from "./types.gen";
24
+ export { createConfig } from "./utils.gen";