@budibase/frontend-core 3.24.2 → 3.24.3

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "3.24.2",
3
+ "version": "3.24.3",
4
4
  "description": "Budibase frontend core libraries used in builder and client",
5
5
  "author": "Budibase",
6
6
  "license": "MPL-2.0",
@@ -18,5 +18,5 @@
18
18
  "shortid": "2.2.15",
19
19
  "socket.io-client": "^4.7.5"
20
20
  },
21
- "gitHead": "237b6a0e4013f770eb766e79596e6f52bfcb6ea7"
21
+ "gitHead": "a85e0ba10b7d19e204062fb63dc306953284e277"
22
22
  }
package/src/api/agents.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
+ AgentFileUploadResponse,
2
3
  CreateAgentRequest,
3
4
  CreateAgentResponse,
5
+ FetchAgentFilesResponse,
4
6
  FetchAgentsResponse,
5
7
  ToolMetadata,
6
8
  UpdateAgentRequest,
@@ -15,9 +17,26 @@ export interface AgentEndpoints {
15
17
  createAgent: (agent: CreateAgentRequest) => Promise<CreateAgentResponse>
16
18
  updateAgent: (agent: UpdateAgentRequest) => Promise<UpdateAgentResponse>
17
19
  deleteAgent: (agentId: string) => Promise<{ deleted: true }>
20
+ fetchAgentFiles: (agentId: string) => Promise<FetchAgentFilesResponse>
21
+ uploadAgentFile: (
22
+ agentId: string,
23
+ file: File
24
+ ) => Promise<AgentFileUploadResponse>
25
+ deleteAgentFile: (
26
+ agentId: string,
27
+ fileId: string
28
+ ) => Promise<{ deleted: true }>
18
29
  }
19
30
 
20
31
  export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
32
+ fetchTools: async (aiconfigId?: string) => {
33
+ const query = aiconfigId
34
+ ? `?aiconfigId=${encodeURIComponent(aiconfigId)}`
35
+ : ""
36
+ return await API.get({
37
+ url: `/api/agent/tools${query}`,
38
+ })
39
+ },
21
40
  fetchAgents: async () => {
22
41
  return await API.get({
23
42
  url: "/api/agent",
@@ -44,12 +63,25 @@ export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
44
63
  })
45
64
  },
46
65
 
47
- fetchTools: async (aiconfigId?: string) => {
48
- const query = aiconfigId
49
- ? `?aiconfigId=${encodeURIComponent(aiconfigId)}`
50
- : ""
66
+ fetchAgentFiles: async (agentId: string) => {
51
67
  return await API.get({
52
- url: `/api/agent/tools${query}`,
68
+ url: `/api/agent/${agentId}/files`,
69
+ })
70
+ },
71
+
72
+ uploadAgentFile: async (agentId: string, file: File) => {
73
+ const formData = new FormData()
74
+ formData.append("file", file)
75
+ return await API.post<FormData, AgentFileUploadResponse>({
76
+ url: `/api/agent/${agentId}/files`,
77
+ body: formData,
78
+ json: false,
79
+ })
80
+ },
81
+
82
+ deleteAgentFile: async (agentId: string, fileId: string) => {
83
+ return await API.delete({
84
+ url: `/api/agent/${agentId}/files/${fileId}`,
53
85
  })
54
86
  },
55
87
  })
@@ -6,6 +6,7 @@ import {
6
6
  ChatApp,
7
7
  FetchAgentHistoryResponse,
8
8
  UpdateChatAppRequest,
9
+ AgentMessageMetadata,
9
10
  } from "@budibase/types"
10
11
  import { Header } from "@budibase/shared-core"
11
12
  import { BaseAPIClient } from "./types"
@@ -16,7 +17,7 @@ export interface ChatAppEndpoints {
16
17
  streamChatConversation: (
17
18
  chat: ChatConversationRequest,
18
19
  workspaceId: string
19
- ) => Promise<AsyncIterable<UIMessage>>
20
+ ) => Promise<AsyncIterable<UIMessage<AgentMessageMetadata>>>
20
21
  deleteChatConversation: (
21
22
  chatConversationId: string,
22
23
  chatAppId: string
@@ -6,6 +6,7 @@ import {
6
6
  FindConfigResponse,
7
7
  GetPublicOIDCConfigResponse,
8
8
  GetPublicSettingsResponse,
9
+ GetPublicTranslationsResponse,
9
10
  OIDCLogosConfig,
10
11
  SaveConfigRequest,
11
12
  SaveConfigResponse,
@@ -17,6 +18,9 @@ export interface ConfigEndpoints {
17
18
  getConfig: (type: ConfigType) => Promise<FindConfigResponse>
18
19
  getTenantConfig: (tentantId: string) => Promise<GetPublicSettingsResponse>
19
20
  getOIDCConfigs: (tenantId: string) => Promise<GetPublicOIDCConfigResponse>
21
+ getPublicTranslations: (
22
+ tenantId?: string
23
+ ) => Promise<GetPublicTranslationsResponse>
20
24
  getOIDCLogos: () => Promise<Config<OIDCLogosConfig>>
21
25
  saveConfig: (config: SaveConfigRequest) => Promise<SaveConfigResponse>
22
26
  deleteConfig: (id: string, rev: string) => Promise<DeleteConfigResponse>
@@ -79,6 +83,13 @@ export const buildConfigEndpoints = (API: BaseAPIClient): ConfigEndpoints => ({
79
83
  })
80
84
  },
81
85
 
86
+ getPublicTranslations: async tenantId => {
87
+ const query = tenantId ? `?tenantId=${tenantId}` : ""
88
+ return await API.get({
89
+ url: `/api/global/configs/public/translations${query}`,
90
+ })
91
+ },
92
+
82
93
  /**
83
94
  * Gets the checklist for a specific tenant.
84
95
  * @param tenantId the tenant ID to get the checklist for
package/src/api/index.ts CHANGED
@@ -56,6 +56,8 @@ import { buildDeploymentEndpoints } from "./deploy"
56
56
  import { buildWorkspaceFavouriteEndpoints } from "./workspaceFavourites"
57
57
  import { buildRecaptchaEndpoints } from "./recaptcha"
58
58
  import { buildAIConfigEndpoints } from "./aiConfig"
59
+ import { buildRagConfigEndpoints } from "./ragConfigs"
60
+ import { buildVectorDbEndpoints } from "./vectorDbs"
59
61
 
60
62
  export type { APIClient } from "./types"
61
63
 
@@ -326,5 +328,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
326
328
  resource: buildResourceEndpoints(API),
327
329
  recaptcha: buildRecaptchaEndpoints(API),
328
330
  aiConfig: buildAIConfigEndpoints(API),
331
+ ragConfig: buildRagConfigEndpoints(API),
332
+ vectorDb: buildVectorDbEndpoints(API),
329
333
  }
330
334
  }
@@ -0,0 +1,44 @@
1
+ import {
2
+ CreateRagConfigRequest,
3
+ RagConfig,
4
+ RagConfigListResponse,
5
+ UpdateRagConfigRequest,
6
+ } from "@budibase/types"
7
+ import { BaseAPIClient } from "./types"
8
+
9
+ export interface RagConfigEndpoints {
10
+ fetch: () => Promise<RagConfigListResponse>
11
+ create: (config: CreateRagConfigRequest) => Promise<RagConfig>
12
+ update: (config: UpdateRagConfigRequest) => Promise<RagConfig>
13
+ delete: (id: string) => Promise<{ deleted: true }>
14
+ }
15
+
16
+ export const buildRagConfigEndpoints = (
17
+ API: BaseAPIClient
18
+ ): RagConfigEndpoints => ({
19
+ fetch: async () => {
20
+ return await API.get({
21
+ url: "/api/ragconfig",
22
+ })
23
+ },
24
+
25
+ create: async config => {
26
+ return await API.post({
27
+ url: "/api/ragconfig",
28
+ body: config,
29
+ })
30
+ },
31
+
32
+ update: async config => {
33
+ return await API.put({
34
+ url: "/api/ragconfig",
35
+ body: config,
36
+ })
37
+ },
38
+
39
+ delete: async id => {
40
+ return await API.delete({
41
+ url: `/api/ragconfig/${id}`,
42
+ })
43
+ },
44
+ })
package/src/api/types.ts CHANGED
@@ -43,6 +43,8 @@ import { DeploymentEndpoints } from "./deploy"
43
43
  import { WorkspaceFavouriteEndpoints } from "./workspaceFavourites"
44
44
  import { RecaptchaEndpoints } from "./recaptcha"
45
45
  import { AIConfigEndpoints } from "./aiConfig"
46
+ import { RagConfigEndpoints } from "./ragConfigs"
47
+ import { VectorDbEndpoints } from "./vectorDbs"
46
48
 
47
49
  export enum HTTPMethod {
48
50
  POST = "POST",
@@ -157,4 +159,6 @@ export type APIClient = BaseAPIClient &
157
159
  deployment: DeploymentEndpoints
158
160
  recaptcha: RecaptchaEndpoints
159
161
  aiConfig: AIConfigEndpoints
162
+ ragConfig: RagConfigEndpoints
163
+ vectorDb: VectorDbEndpoints
160
164
  }
@@ -0,0 +1,44 @@
1
+ import {
2
+ CreateVectorDbRequest,
3
+ UpdateVectorDbRequest,
4
+ VectorDb,
5
+ VectorDbListResponse,
6
+ } from "@budibase/types"
7
+ import { BaseAPIClient } from "./types"
8
+
9
+ export interface VectorDbEndpoints {
10
+ fetch: () => Promise<VectorDbListResponse>
11
+ create: (config: CreateVectorDbRequest) => Promise<VectorDb>
12
+ update: (config: UpdateVectorDbRequest) => Promise<VectorDb>
13
+ delete: (id: string) => Promise<{ deleted: true }>
14
+ }
15
+
16
+ export const buildVectorDbEndpoints = (
17
+ API: BaseAPIClient
18
+ ): VectorDbEndpoints => ({
19
+ fetch: async () => {
20
+ return await API.get({
21
+ url: "/api/vectordb",
22
+ })
23
+ },
24
+
25
+ create: async config => {
26
+ return await API.post({
27
+ url: "/api/vectordb",
28
+ body: config,
29
+ })
30
+ },
31
+
32
+ update: async config => {
33
+ return await API.put({
34
+ url: "/api/vectordb",
35
+ body: config,
36
+ })
37
+ },
38
+
39
+ delete: async id => {
40
+ return await API.delete({
41
+ url: `/api/vectordb/${id}`,
42
+ })
43
+ },
44
+ })
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { MarkdownViewer, notifications } from "@budibase/bbui"
3
3
  import type {
4
+ AgentMessageMetadata,
4
5
  ChatConversation,
5
6
  ChatConversationRequest,
6
7
  } from "@budibase/types"
@@ -11,8 +12,8 @@
11
12
  import { onMount } from "svelte"
12
13
  import { createEventDispatcher } from "svelte"
13
14
  import { createAPIClient } from "@budibase/frontend-core"
14
- import type { UIMessage } from "ai"
15
15
  import { v4 as uuidv4 } from "uuid"
16
+ import type { UIMessage } from "ai"
16
17
 
17
18
  export let workspaceId: string
18
19
  export let API = createAPIClient({
@@ -114,7 +115,7 @@
114
115
  }
115
116
  }
116
117
 
117
- const userMessage: UIMessage = {
118
+ const userMessage: UIMessage<AgentMessageMetadata> = {
118
119
  id: uuidv4(),
119
120
  role: "user",
120
121
  parts: [{ type: "text", text: inputValue }],
@@ -138,10 +139,35 @@
138
139
  workspaceId
139
140
  )
140
141
 
141
- let streamedMessages: UIMessage[] = [...updatedChat.messages]
142
+ let streamedMessages = [...updatedChat.messages]
142
143
 
143
144
  for await (const message of messageStream) {
144
- streamedMessages = [...streamedMessages, message]
145
+ if (message?.id) {
146
+ const existingIndex = streamedMessages.findIndex(
147
+ existing => existing.id === message.id
148
+ )
149
+ if (existingIndex !== -1) {
150
+ streamedMessages = streamedMessages.map((existing, index) =>
151
+ index === existingIndex ? message : existing
152
+ )
153
+ } else {
154
+ streamedMessages = [...streamedMessages, message]
155
+ }
156
+ } else if (message?.role === "assistant") {
157
+ const lastIndex = [...streamedMessages]
158
+ .reverse()
159
+ .findIndex(existing => existing.role === "assistant")
160
+ if (lastIndex !== -1) {
161
+ const targetIndex = streamedMessages.length - 1 - lastIndex
162
+ streamedMessages = streamedMessages.map((existing, index) =>
163
+ index === targetIndex ? message : existing
164
+ )
165
+ } else {
166
+ streamedMessages = [...streamedMessages, message]
167
+ }
168
+ } else {
169
+ streamedMessages = [...streamedMessages, message]
170
+ }
145
171
  chat = {
146
172
  ...updatedChat,
147
173
  messages: streamedMessages,
@@ -285,6 +311,27 @@
285
311
  </div>
286
312
  {/if}
287
313
  {/each}
314
+ {#if message.metadata?.ragSources?.length}
315
+ <div class="sources">
316
+ <div class="sources-title">Sources</div>
317
+ <ul>
318
+ {#each message.metadata.ragSources as source (source.sourceId)}
319
+ <li class="source-item">
320
+ <span class="source-name"
321
+ >{source.filename || source.sourceId}</span
322
+ >
323
+ {#if source.chunkCount > 0}
324
+ <span class="source-count"
325
+ >({source.chunkCount} chunk{source.chunkCount === 1
326
+ ? ""
327
+ : "s"})</span
328
+ >
329
+ {/if}
330
+ </li>
331
+ {/each}
332
+ </ul>
333
+ </div>
334
+ {/if}
288
335
  </div>
289
336
  {/if}
290
337
  {/each}
@@ -480,4 +527,42 @@
480
527
  color: var(--spectrum-global-color-gray-800);
481
528
  font-style: italic;
482
529
  }
530
+
531
+ .sources {
532
+ margin-top: var(--spacing-m);
533
+ padding-top: var(--spacing-s);
534
+ border-top: 1px solid var(--grey-3);
535
+ }
536
+
537
+ .sources-title {
538
+ font-size: 13px;
539
+ text-transform: uppercase;
540
+ letter-spacing: 0.08em;
541
+ color: var(--spectrum-global-color-gray-600);
542
+ margin-bottom: var(--spacing-xs);
543
+ }
544
+
545
+ .sources ul {
546
+ list-style: none;
547
+ padding: 0;
548
+ margin: 0;
549
+ display: flex;
550
+ flex-direction: column;
551
+ gap: 4px;
552
+ }
553
+
554
+ .source-item {
555
+ display: flex;
556
+ gap: 8px;
557
+ font-size: 14px;
558
+ color: var(--spectrum-global-color-gray-900);
559
+ }
560
+
561
+ .source-name {
562
+ font-weight: 500;
563
+ }
564
+
565
+ .source-count {
566
+ color: var(--spectrum-global-color-gray-600);
567
+ }
483
568
  </style>