@budibase/frontend-core 3.23.38 → 3.23.48
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 +3 -3
- package/src/api/agents.ts +8 -115
- package/src/api/automations.ts +18 -3
- package/src/api/chatApps.ts +190 -0
- package/src/api/datasources.ts +18 -0
- package/src/api/index.ts +2 -0
- package/src/api/tables.ts +13 -0
- package/src/api/types.ts +2 -0
- package/src/components/Chatbox/index.svelte +102 -10
- package/src/components/grid/cells/HeaderCell.svelte +11 -2
- package/src/components/grid/cells/TextCell.svelte +14 -0
- package/src/components/grid/overlays/KeyboardManager.svelte +4 -0
- package/src/components/grid/stores/rows.ts +4 -0
- package/src/fetch/DataFetch.ts +1 -0
- package/src/utils/{table.js → table.ts} +10 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "3.23.
|
|
3
|
+
"version": "3.23.48",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"@budibase/bbui": "*",
|
|
13
13
|
"@budibase/shared-core": "*",
|
|
14
14
|
"@budibase/types": "*",
|
|
15
|
-
"ai": "^
|
|
15
|
+
"ai": "^6.0.3",
|
|
16
16
|
"dayjs": "^1.10.8",
|
|
17
17
|
"lodash": "4.17.21",
|
|
18
18
|
"shortid": "2.2.15",
|
|
19
19
|
"socket.io-client": "^4.7.5"
|
|
20
20
|
},
|
|
21
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "0a78faa2adbb8fb3b81de2b9b00352e60178f12f"
|
|
22
22
|
}
|
package/src/api/agents.ts
CHANGED
|
@@ -1,136 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AgentChat,
|
|
3
|
-
AgentToolSource,
|
|
4
|
-
AgentToolSourceWithTools,
|
|
5
|
-
ChatAgentRequest,
|
|
6
2
|
CreateAgentRequest,
|
|
7
3
|
CreateAgentResponse,
|
|
8
|
-
CreateToolSourceRequest,
|
|
9
|
-
FetchAgentHistoryResponse,
|
|
10
4
|
FetchAgentsResponse,
|
|
11
|
-
|
|
5
|
+
ToolMetadata,
|
|
12
6
|
UpdateAgentRequest,
|
|
13
7
|
UpdateAgentResponse,
|
|
14
8
|
} from "@budibase/types"
|
|
15
9
|
|
|
16
|
-
import { Header } from "@budibase/shared-core"
|
|
17
10
|
import { BaseAPIClient } from "./types"
|
|
18
|
-
import { readUIMessageStream, UIMessage, UIMessageChunk } from "ai"
|
|
19
|
-
import { createSseToJsonTransformStream } from "../utils/utils"
|
|
20
11
|
|
|
21
12
|
export interface AgentEndpoints {
|
|
22
|
-
|
|
23
|
-
chat: AgentChat,
|
|
24
|
-
workspaceId: string
|
|
25
|
-
) => Promise<AsyncIterable<UIMessage>>
|
|
26
|
-
|
|
27
|
-
removeChat: (chatId: string) => Promise<void>
|
|
28
|
-
fetchChats: (agentId: string) => Promise<FetchAgentHistoryResponse>
|
|
29
|
-
|
|
30
|
-
fetchToolSources: (agentId: string) => Promise<AgentToolSourceWithTools[]>
|
|
31
|
-
fetchAvailableTools: (toolSourceType: string) => Promise<Tool[]>
|
|
32
|
-
createToolSource: (
|
|
33
|
-
toolSource: CreateToolSourceRequest
|
|
34
|
-
) => Promise<{ created: true }>
|
|
35
|
-
updateToolSource: (toolSource: AgentToolSource) => Promise<AgentToolSource>
|
|
36
|
-
deleteToolSource: (toolSourceId: string) => Promise<{ deleted: true }>
|
|
13
|
+
fetchTools: () => Promise<ToolMetadata[]>
|
|
37
14
|
fetchAgents: () => Promise<FetchAgentsResponse>
|
|
38
15
|
createAgent: (agent: CreateAgentRequest) => Promise<CreateAgentResponse>
|
|
39
16
|
updateAgent: (agent: UpdateAgentRequest) => Promise<UpdateAgentResponse>
|
|
40
17
|
deleteAgent: (agentId: string) => Promise<{ deleted: true }>
|
|
41
18
|
}
|
|
42
19
|
|
|
43
|
-
const throwOnErrorChunk = () =>
|
|
44
|
-
new TransformStream<UIMessageChunk, UIMessageChunk>({
|
|
45
|
-
transform(chunk, controller) {
|
|
46
|
-
if (chunk.type === "error") {
|
|
47
|
-
throw new Error(chunk.errorText || "Agent action failed")
|
|
48
|
-
}
|
|
49
|
-
controller.enqueue(chunk)
|
|
50
|
-
},
|
|
51
|
-
})
|
|
52
|
-
|
|
53
20
|
export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
|
|
54
|
-
agentChatStream: async (chat, workspaceId) => {
|
|
55
|
-
const body: ChatAgentRequest = chat
|
|
56
|
-
|
|
57
|
-
const response = await fetch("/api/agent/chat/stream", {
|
|
58
|
-
method: "POST",
|
|
59
|
-
headers: {
|
|
60
|
-
"Content-Type": "application/json",
|
|
61
|
-
Accept: "application/json",
|
|
62
|
-
[Header.APP_ID]: workspaceId,
|
|
63
|
-
},
|
|
64
|
-
body: JSON.stringify(body),
|
|
65
|
-
credentials: "same-origin",
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
if (!response.ok) {
|
|
69
|
-
const errorBody = await response.json()
|
|
70
|
-
throw new Error(
|
|
71
|
-
errorBody.message || `HTTP error! status: ${response.status}`
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!response.body) {
|
|
76
|
-
throw new Error("Failed to get response body")
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const chunkStream = response.body
|
|
80
|
-
.pipeThrough(new TextDecoderStream())
|
|
81
|
-
.pipeThrough(createSseToJsonTransformStream<UIMessageChunk>())
|
|
82
|
-
.pipeThrough(throwOnErrorChunk())
|
|
83
|
-
|
|
84
|
-
return readUIMessageStream({
|
|
85
|
-
stream: chunkStream,
|
|
86
|
-
terminateOnError: true,
|
|
87
|
-
})
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
removeChat: async (chatId: string) => {
|
|
91
|
-
return await API.delete({
|
|
92
|
-
url: `/api/agent/chats/${chatId}`,
|
|
93
|
-
})
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
fetchChats: async (agentId: string) => {
|
|
97
|
-
return await API.get({
|
|
98
|
-
url: `/api/agent/${agentId}/chats`,
|
|
99
|
-
})
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
fetchToolSources: async (agentId: string) => {
|
|
103
|
-
return await API.get({
|
|
104
|
-
url: `/api/agent/${agentId}/toolsource`,
|
|
105
|
-
})
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
fetchAvailableTools: async (toolSourceType: string) => {
|
|
109
|
-
return await API.get({
|
|
110
|
-
url: `/api/agent/toolsource/${toolSourceType}/tools`,
|
|
111
|
-
})
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
createToolSource: async (toolSource: CreateToolSourceRequest) => {
|
|
115
|
-
return await API.post({
|
|
116
|
-
url: "/api/agent/toolsource",
|
|
117
|
-
body: toolSource as any,
|
|
118
|
-
})
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
updateToolSource: async (toolSource: AgentToolSource) => {
|
|
122
|
-
return await API.put({
|
|
123
|
-
url: "/api/agent/toolsource",
|
|
124
|
-
body: toolSource as any,
|
|
125
|
-
})
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
deleteToolSource: async (toolSourceId: string) => {
|
|
129
|
-
return await API.delete({
|
|
130
|
-
url: `/api/agent/toolsource/${toolSourceId}`,
|
|
131
|
-
})
|
|
132
|
-
},
|
|
133
|
-
|
|
134
21
|
fetchAgents: async () => {
|
|
135
22
|
return await API.get({
|
|
136
23
|
url: "/api/agent",
|
|
@@ -156,4 +43,10 @@ export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
|
|
|
156
43
|
url: `/api/agent/${agentId}`,
|
|
157
44
|
})
|
|
158
45
|
},
|
|
46
|
+
|
|
47
|
+
fetchTools: async () => {
|
|
48
|
+
return await API.get({
|
|
49
|
+
url: `/api/agent/tools`,
|
|
50
|
+
})
|
|
51
|
+
},
|
|
159
52
|
})
|
package/src/api/automations.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
SearchAutomationLogsResponse,
|
|
11
11
|
TestAutomationRequest,
|
|
12
12
|
TestAutomationResponse,
|
|
13
|
+
TestProgressState,
|
|
13
14
|
TriggerAutomationRequest,
|
|
14
15
|
TriggerAutomationResponse,
|
|
15
16
|
UpdateAutomationRequest,
|
|
@@ -40,8 +41,10 @@ export interface AutomationEndpoints {
|
|
|
40
41
|
) => Promise<TriggerAutomationResponse>
|
|
41
42
|
testAutomation: (
|
|
42
43
|
automationdId: string,
|
|
43
|
-
data: TestAutomationRequest
|
|
44
|
+
data: TestAutomationRequest,
|
|
45
|
+
options?: { async?: boolean }
|
|
44
46
|
) => Promise<TestAutomationResponse>
|
|
47
|
+
getAutomationTestStatus: (automationId: string) => Promise<TestProgressState>
|
|
45
48
|
getAutomationDefinitions: () => Promise<GetAutomationStepDefinitionsResponse>
|
|
46
49
|
getAutomationLogs: (
|
|
47
50
|
options: SearchAutomationLogsRequest
|
|
@@ -69,13 +72,25 @@ export const buildAutomationEndpoints = (
|
|
|
69
72
|
* @param automationId the ID of the automation to test
|
|
70
73
|
* @param data the test data to run against the automation
|
|
71
74
|
*/
|
|
72
|
-
testAutomation: async (automationId, data) => {
|
|
75
|
+
testAutomation: async (automationId, data, options = {}) => {
|
|
76
|
+
const params = new URLSearchParams()
|
|
77
|
+
if (options.async) {
|
|
78
|
+
params.set("async", "true")
|
|
79
|
+
}
|
|
80
|
+
const qs = params.toString()
|
|
81
|
+
const url = `/api/automations/${automationId}/test${qs ? `?${qs}` : ""}`
|
|
73
82
|
return await API.post({
|
|
74
|
-
url
|
|
83
|
+
url,
|
|
75
84
|
body: data,
|
|
76
85
|
})
|
|
77
86
|
},
|
|
78
87
|
|
|
88
|
+
getAutomationTestStatus: async automationId => {
|
|
89
|
+
return await API.get({
|
|
90
|
+
url: `/api/automations/${automationId}/test/status`,
|
|
91
|
+
})
|
|
92
|
+
},
|
|
93
|
+
|
|
79
94
|
/**
|
|
80
95
|
* Gets a list of all automations.
|
|
81
96
|
*/
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChatAgentRequest,
|
|
3
|
+
ChatConversation,
|
|
4
|
+
ChatConversationRequest,
|
|
5
|
+
CreateChatConversationRequest,
|
|
6
|
+
ChatApp,
|
|
7
|
+
FetchAgentHistoryResponse,
|
|
8
|
+
UpdateChatAppRequest,
|
|
9
|
+
} from "@budibase/types"
|
|
10
|
+
import { Header } from "@budibase/shared-core"
|
|
11
|
+
import { BaseAPIClient } from "./types"
|
|
12
|
+
import { readUIMessageStream, UIMessage, UIMessageChunk } from "ai"
|
|
13
|
+
import { createSseToJsonTransformStream } from "../utils/utils"
|
|
14
|
+
|
|
15
|
+
export interface ChatAppEndpoints {
|
|
16
|
+
streamChatConversation: (
|
|
17
|
+
chat: ChatConversationRequest,
|
|
18
|
+
workspaceId: string
|
|
19
|
+
) => Promise<AsyncIterable<UIMessage>>
|
|
20
|
+
deleteChatConversation: (
|
|
21
|
+
chatConversationId: string,
|
|
22
|
+
chatAppId: string
|
|
23
|
+
) => Promise<void>
|
|
24
|
+
fetchChatConversation: (
|
|
25
|
+
chatAppId: string,
|
|
26
|
+
chatConversationId: string
|
|
27
|
+
) => Promise<ChatConversation>
|
|
28
|
+
fetchChatHistory: (chatAppId: string) => Promise<FetchAgentHistoryResponse>
|
|
29
|
+
fetchChatApp: (workspaceId?: string) => Promise<ChatApp | null>
|
|
30
|
+
setChatAppAgent: (chatAppId: string, agentId: string) => Promise<ChatApp>
|
|
31
|
+
createChatConversation: (
|
|
32
|
+
chat: CreateChatConversationRequest,
|
|
33
|
+
workspaceId?: string
|
|
34
|
+
) => Promise<ChatConversation>
|
|
35
|
+
updateChatApp: (chatApp: UpdateChatAppRequest) => Promise<ChatApp>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const throwOnErrorChunk = () =>
|
|
39
|
+
new TransformStream<UIMessageChunk, UIMessageChunk>({
|
|
40
|
+
transform(chunk, controller) {
|
|
41
|
+
if (chunk.type === "error") {
|
|
42
|
+
throw new Error(chunk.errorText || "Agent action failed")
|
|
43
|
+
}
|
|
44
|
+
controller.enqueue(chunk)
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const buildChatAppEndpoints = (
|
|
49
|
+
API: BaseAPIClient
|
|
50
|
+
): ChatAppEndpoints => ({
|
|
51
|
+
streamChatConversation: async (chat, workspaceId) => {
|
|
52
|
+
if (!chat.chatAppId) {
|
|
53
|
+
throw new Error("chatAppId is required to stream a chat conversation")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const body: ChatAgentRequest = chat
|
|
57
|
+
const conversationId = chat._id || "new"
|
|
58
|
+
|
|
59
|
+
const response = await fetch(
|
|
60
|
+
`/api/chatapps/${chat.chatAppId}/conversations/${conversationId}/stream`,
|
|
61
|
+
{
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
Accept: "application/json",
|
|
66
|
+
[Header.APP_ID]: workspaceId,
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
credentials: "same-origin",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const errorBody = await response.json()
|
|
75
|
+
throw new Error(
|
|
76
|
+
errorBody.message || `HTTP error! status: ${response.status}`
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!response.body) {
|
|
81
|
+
throw new Error("Failed to get response body")
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const chunkStream = response.body
|
|
85
|
+
.pipeThrough(new TextDecoderStream())
|
|
86
|
+
.pipeThrough(createSseToJsonTransformStream<UIMessageChunk>())
|
|
87
|
+
.pipeThrough(throwOnErrorChunk())
|
|
88
|
+
|
|
89
|
+
return readUIMessageStream({
|
|
90
|
+
stream: chunkStream,
|
|
91
|
+
terminateOnError: true,
|
|
92
|
+
})
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
deleteChatConversation: async (
|
|
96
|
+
chatConversationId: string,
|
|
97
|
+
chatAppId: string
|
|
98
|
+
) => {
|
|
99
|
+
return await API.delete({
|
|
100
|
+
url: `/api/chatapps/${chatAppId}/conversations/${chatConversationId}`,
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
fetchChatConversation: async (
|
|
105
|
+
chatAppId: string,
|
|
106
|
+
chatConversationId: string
|
|
107
|
+
) => {
|
|
108
|
+
return await API.get({
|
|
109
|
+
url: `/api/chatapps/${chatAppId}/conversations/${chatConversationId}`,
|
|
110
|
+
})
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
fetchChatHistory: async (chatAppId: string) => {
|
|
114
|
+
return await API.get({
|
|
115
|
+
url: `/api/chatapps/${chatAppId}/conversations`,
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
fetchChatApp: async (workspaceId?: string) => {
|
|
120
|
+
const url = "/api/chatapps"
|
|
121
|
+
const headers = workspaceId
|
|
122
|
+
? {
|
|
123
|
+
[Header.APP_ID]: workspaceId,
|
|
124
|
+
}
|
|
125
|
+
: undefined
|
|
126
|
+
return await API.get({
|
|
127
|
+
url,
|
|
128
|
+
...(headers && { headers }),
|
|
129
|
+
})
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
setChatAppAgent: async (chatAppId: string, agentId: string) => {
|
|
133
|
+
if (!chatAppId) {
|
|
134
|
+
throw new Error("chatAppId is required to set chat app agent")
|
|
135
|
+
}
|
|
136
|
+
if (!agentId) {
|
|
137
|
+
throw new Error("agentId is required to set chat app agent")
|
|
138
|
+
}
|
|
139
|
+
return await API.post({
|
|
140
|
+
url: `/api/chatapps/${chatAppId}/agent`,
|
|
141
|
+
body: { agentId } as any,
|
|
142
|
+
})
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
createChatConversation: async (
|
|
146
|
+
chat: CreateChatConversationRequest,
|
|
147
|
+
workspaceId?: string
|
|
148
|
+
) => {
|
|
149
|
+
const resolvedWorkspaceId = workspaceId || API.getAppID()
|
|
150
|
+
const { chatAppId } = chat
|
|
151
|
+
if (!chatAppId) {
|
|
152
|
+
throw new Error("chatAppId is required to create a chat conversation")
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const headers: Record<string, string> = {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
Accept: "application/json",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (resolvedWorkspaceId) {
|
|
161
|
+
headers[Header.APP_ID] = resolvedWorkspaceId
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const response = await fetch(`/api/chatapps/${chatAppId}/conversations`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers,
|
|
167
|
+
credentials: "same-origin",
|
|
168
|
+
body: JSON.stringify(chat),
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
const errorBody = await response.json().catch(() => null)
|
|
173
|
+
throw new Error(
|
|
174
|
+
errorBody?.message || `HTTP error! status: ${response.status}`
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return (await response.json()) as ChatConversation
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
updateChatApp: async (chatApp: UpdateChatAppRequest) => {
|
|
182
|
+
if (!chatApp._id) {
|
|
183
|
+
throw new Error("chatAppId is required to update a chat app")
|
|
184
|
+
}
|
|
185
|
+
return await API.put({
|
|
186
|
+
url: `/api/chatapps/${chatApp._id}`,
|
|
187
|
+
body: chatApp as any,
|
|
188
|
+
})
|
|
189
|
+
},
|
|
190
|
+
})
|
package/src/api/datasources.ts
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
DeleteDatasourceResponse,
|
|
8
8
|
FetchDatasourceInfoRequest,
|
|
9
9
|
FetchDatasourceInfoResponse,
|
|
10
|
+
FetchDatasourceRelationshipInfoRequest,
|
|
11
|
+
FetchDatasourceRelationshipInfoResponse,
|
|
10
12
|
FetchDatasourceViewInfoRequest,
|
|
11
13
|
FetchDatasourceViewInfoResponse,
|
|
12
14
|
UpdateDatasourceRequest,
|
|
@@ -41,6 +43,9 @@ export interface DatasourceEndpoints {
|
|
|
41
43
|
fetchViewInfoForDatasource: (
|
|
42
44
|
datasource: Datasource
|
|
43
45
|
) => Promise<FetchDatasourceViewInfoResponse>
|
|
46
|
+
fetchRelationshipInfoForDatasource: (
|
|
47
|
+
datasource: Datasource
|
|
48
|
+
) => Promise<FetchDatasourceRelationshipInfoResponse>
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
export const buildDatasourceEndpoints = (
|
|
@@ -134,4 +139,17 @@ export const buildDatasourceEndpoints = (
|
|
|
134
139
|
body: { datasource },
|
|
135
140
|
})
|
|
136
141
|
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Fetch relationship names available within the datasource
|
|
145
|
+
*/
|
|
146
|
+
fetchRelationshipInfoForDatasource: async (datasource: Datasource) => {
|
|
147
|
+
return await API.post<
|
|
148
|
+
FetchDatasourceRelationshipInfoRequest,
|
|
149
|
+
FetchDatasourceRelationshipInfoResponse
|
|
150
|
+
>({
|
|
151
|
+
url: `/api/datasources/relationships`,
|
|
152
|
+
body: { datasource },
|
|
153
|
+
})
|
|
154
|
+
},
|
|
137
155
|
})
|
package/src/api/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { buildMigrationEndpoints } from "./migrations"
|
|
|
47
47
|
import { buildRowActionEndpoints } from "./rowActions"
|
|
48
48
|
import { buildOAuth2Endpoints } from "./oauth2"
|
|
49
49
|
import { buildAgentEndpoints } from "./agents"
|
|
50
|
+
import { buildChatAppEndpoints } from "./chatApps"
|
|
50
51
|
import { buildFeatureFlagEndpoints } from "./features"
|
|
51
52
|
import { buildNavigationEndpoints } from "./navigation"
|
|
52
53
|
import { buildWorkspaceAppEndpoints } from "./workspaceApps"
|
|
@@ -313,6 +314,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
|
|
|
313
314
|
...buildLogsEndpoints(API),
|
|
314
315
|
...buildMigrationEndpoints(API),
|
|
315
316
|
...buildAgentEndpoints(API),
|
|
317
|
+
...buildChatAppEndpoints(API),
|
|
316
318
|
...buildFeatureFlagEndpoints(API),
|
|
317
319
|
deployment: buildDeploymentEndpoints(API),
|
|
318
320
|
viewV2: buildViewV2Endpoints(API),
|
package/src/api/tables.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
MigrateTableResponse,
|
|
19
19
|
MigrateTableRequest,
|
|
20
20
|
DeleteTableResponse,
|
|
21
|
+
PublishTableRequest,
|
|
22
|
+
PublishTableResponse,
|
|
21
23
|
} from "@budibase/types"
|
|
22
24
|
import { BaseAPIClient } from "./types"
|
|
23
25
|
|
|
@@ -52,6 +54,10 @@ export interface TableEndpoints {
|
|
|
52
54
|
oldColumn: string,
|
|
53
55
|
newColumn: string
|
|
54
56
|
) => Promise<MigrateTableResponse>
|
|
57
|
+
publishTable: (
|
|
58
|
+
tableId: string,
|
|
59
|
+
opts?: PublishTableRequest
|
|
60
|
+
) => Promise<PublishTableResponse>
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export const buildTableEndpoints = (API: BaseAPIClient): TableEndpoints => ({
|
|
@@ -191,6 +197,13 @@ export const buildTableEndpoints = (API: BaseAPIClient): TableEndpoints => ({
|
|
|
191
197
|
})
|
|
192
198
|
},
|
|
193
199
|
|
|
200
|
+
publishTable: async (tableId, opts) => {
|
|
201
|
+
return await API.post<PublishTableRequest, PublishTableResponse>({
|
|
202
|
+
url: `/api/tables/${tableId}/publish`,
|
|
203
|
+
body: opts,
|
|
204
|
+
})
|
|
205
|
+
},
|
|
206
|
+
|
|
194
207
|
/**
|
|
195
208
|
* Duplicates a table without its data.
|
|
196
209
|
* @param tableId the ID of the table to duplicate
|
package/src/api/types.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { UserEndpoints } from "./user"
|
|
|
35
35
|
import { ViewEndpoints } from "./views"
|
|
36
36
|
import { ViewV2Endpoints } from "./viewsV2"
|
|
37
37
|
import { AgentEndpoints } from "./agents"
|
|
38
|
+
import { ChatAppEndpoints } from "./chatApps"
|
|
38
39
|
import { NavigationEndpoints } from "./navigation"
|
|
39
40
|
import { WorkspaceAppEndpoints } from "./workspaceApps"
|
|
40
41
|
import { ResourceEndpoints } from "./resource"
|
|
@@ -113,6 +114,7 @@ export type APIError = {
|
|
|
113
114
|
export type APIClient = BaseAPIClient &
|
|
114
115
|
AIEndpoints &
|
|
115
116
|
AgentEndpoints &
|
|
117
|
+
ChatAppEndpoints &
|
|
116
118
|
AnalyticsEndpoints &
|
|
117
119
|
AppEndpoints &
|
|
118
120
|
AttachmentEndpoints &
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { MarkdownViewer, notifications } from "@budibase/bbui"
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ChatConversation,
|
|
5
|
+
ChatConversationRequest,
|
|
6
|
+
} from "@budibase/types"
|
|
7
|
+
import { Header } from "@budibase/shared-core"
|
|
4
8
|
import BBAI from "../../icons/BBAI.svelte"
|
|
5
9
|
import { tick } from "svelte"
|
|
6
10
|
import { onDestroy } from "svelte"
|
|
@@ -10,14 +14,21 @@
|
|
|
10
14
|
import type { UIMessage } from "ai"
|
|
11
15
|
import { v4 as uuidv4 } from "uuid"
|
|
12
16
|
|
|
13
|
-
export let API = createAPIClient()
|
|
14
|
-
|
|
15
17
|
export let workspaceId: string
|
|
16
|
-
export let
|
|
18
|
+
export let API = createAPIClient({
|
|
19
|
+
attachHeaders: headers => {
|
|
20
|
+
if (workspaceId) {
|
|
21
|
+
headers[Header.APP_ID] = workspaceId
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
type ChatConversationLike = ChatConversation | ChatConversationRequest
|
|
26
|
+
|
|
27
|
+
export let chat: ChatConversationLike
|
|
17
28
|
export let loading: boolean = false
|
|
18
29
|
|
|
19
30
|
const dispatch = createEventDispatcher<{
|
|
20
|
-
chatSaved: { chatId?: string; chat:
|
|
31
|
+
chatSaved: { chatId?: string; chat: ChatConversationLike }
|
|
21
32
|
}>()
|
|
22
33
|
|
|
23
34
|
let inputValue = ""
|
|
@@ -25,12 +36,32 @@
|
|
|
25
36
|
let observer: MutationObserver
|
|
26
37
|
let textareaElement: HTMLTextAreaElement
|
|
27
38
|
let lastFocusedChatId: string | undefined
|
|
28
|
-
let lastFocusedNewChat:
|
|
39
|
+
let lastFocusedNewChat: ChatConversationLike | undefined
|
|
29
40
|
|
|
30
41
|
$: if (chat?.messages?.length) {
|
|
31
42
|
scrollToBottom()
|
|
32
43
|
}
|
|
33
44
|
|
|
45
|
+
const ensureChatApp = async (): Promise<string | undefined> => {
|
|
46
|
+
if (chat?.chatAppId) {
|
|
47
|
+
return chat.chatAppId
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const chatApp = await API.fetchChatApp(workspaceId)
|
|
51
|
+
if (chatApp?._id) {
|
|
52
|
+
const baseChat = chat || { title: "", messages: [], chatAppId: "" }
|
|
53
|
+
chat = {
|
|
54
|
+
...baseChat,
|
|
55
|
+
chatAppId: chatApp._id,
|
|
56
|
+
}
|
|
57
|
+
return chatApp._id
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(err)
|
|
61
|
+
}
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
|
|
34
65
|
async function scrollToBottom() {
|
|
35
66
|
await tick()
|
|
36
67
|
if (chatAreaElement) {
|
|
@@ -46,8 +77,41 @@
|
|
|
46
77
|
}
|
|
47
78
|
|
|
48
79
|
async function prompt() {
|
|
80
|
+
const resolvedChatAppId = await ensureChatApp()
|
|
81
|
+
|
|
49
82
|
if (!chat) {
|
|
50
|
-
chat = { title: "", messages: [] }
|
|
83
|
+
chat = { title: "", messages: [], chatAppId: "" }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const chatAppId = chat.chatAppId || resolvedChatAppId
|
|
87
|
+
|
|
88
|
+
if (!chatAppId) {
|
|
89
|
+
notifications.error("Chat app could not be created")
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!chat._id && (!chat.messages || chat.messages.length === 0)) {
|
|
94
|
+
try {
|
|
95
|
+
const newChat = await API.createChatConversation(
|
|
96
|
+
{
|
|
97
|
+
chatAppId,
|
|
98
|
+
title: chat.title,
|
|
99
|
+
},
|
|
100
|
+
workspaceId
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
chat = {
|
|
104
|
+
...chat,
|
|
105
|
+
...newChat,
|
|
106
|
+
chatAppId,
|
|
107
|
+
}
|
|
108
|
+
} catch (err: any) {
|
|
109
|
+
console.error(err)
|
|
110
|
+
notifications.error(
|
|
111
|
+
err?.message || "Could not start a new chat conversation"
|
|
112
|
+
)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
51
115
|
}
|
|
52
116
|
|
|
53
117
|
const userMessage: UIMessage = {
|
|
@@ -56,8 +120,9 @@
|
|
|
56
120
|
parts: [{ type: "text", text: inputValue }],
|
|
57
121
|
}
|
|
58
122
|
|
|
59
|
-
const updatedChat:
|
|
123
|
+
const updatedChat: ChatConversationLike = {
|
|
60
124
|
...chat,
|
|
125
|
+
chatAppId: chat.chatAppId,
|
|
61
126
|
messages: [...chat.messages, userMessage],
|
|
62
127
|
}
|
|
63
128
|
|
|
@@ -68,16 +133,41 @@
|
|
|
68
133
|
loading = true
|
|
69
134
|
|
|
70
135
|
try {
|
|
71
|
-
const messageStream = await API.
|
|
136
|
+
const messageStream = await API.streamChatConversation(
|
|
137
|
+
updatedChat,
|
|
138
|
+
workspaceId
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
let streamedMessages: UIMessage[] = [...updatedChat.messages]
|
|
72
142
|
|
|
73
143
|
for await (const message of messageStream) {
|
|
144
|
+
streamedMessages = [...streamedMessages, message]
|
|
74
145
|
chat = {
|
|
75
146
|
...updatedChat,
|
|
76
|
-
messages:
|
|
147
|
+
messages: streamedMessages,
|
|
77
148
|
}
|
|
78
149
|
scrollToBottom()
|
|
79
150
|
}
|
|
80
151
|
|
|
152
|
+
// When a chat is created for the first time the server generates the ID.
|
|
153
|
+
// If we don't have it locally yet, retrieve the saved conversation so
|
|
154
|
+
// subsequent prompts append to the same document instead of creating a new one.
|
|
155
|
+
if (!chat._id && chat.chatAppId) {
|
|
156
|
+
try {
|
|
157
|
+
const history = await API.fetchChatHistory(chat.chatAppId)
|
|
158
|
+
const lastMessageId = chat.messages[chat.messages.length - 1]?.id
|
|
159
|
+
const savedConversation =
|
|
160
|
+
history?.find(convo =>
|
|
161
|
+
convo.messages.some(message => message.id === lastMessageId)
|
|
162
|
+
) || history?.[0]
|
|
163
|
+
if (savedConversation) {
|
|
164
|
+
chat = savedConversation
|
|
165
|
+
}
|
|
166
|
+
} catch (historyError) {
|
|
167
|
+
console.error(historyError)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
81
171
|
loading = false
|
|
82
172
|
dispatch("chatSaved", { chatId: chat._id, chat })
|
|
83
173
|
} catch (err: any) {
|
|
@@ -93,6 +183,8 @@
|
|
|
93
183
|
}
|
|
94
184
|
|
|
95
185
|
onMount(async () => {
|
|
186
|
+
await ensureChatApp()
|
|
187
|
+
|
|
96
188
|
// Ensure we always autoscroll to reveal new messages
|
|
97
189
|
observer = new MutationObserver(async () => {
|
|
98
190
|
await tick()
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
import { getColumnIcon } from "../../../utils/schema"
|
|
7
7
|
import MigrationModal from "../controls/MigrationModal.svelte"
|
|
8
8
|
import { debounce } from "../../../utils/utils"
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
FieldType,
|
|
11
|
+
FormulaType,
|
|
12
|
+
SortOrder,
|
|
13
|
+
isStaticFormula,
|
|
14
|
+
} from "@budibase/types"
|
|
10
15
|
import { TableNames } from "../../../constants"
|
|
11
16
|
import GridPopover from "../overlays/GridPopover.svelte"
|
|
12
17
|
|
|
@@ -73,7 +78,11 @@
|
|
|
73
78
|
descending: "high-low",
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
|
-
|
|
81
|
+
let schemaType = column.schema?.type
|
|
82
|
+
if (isStaticFormula(column.schema)) {
|
|
83
|
+
schemaType = column.schema.responseType
|
|
84
|
+
}
|
|
85
|
+
switch (schemaType) {
|
|
77
86
|
case FieldType.NUMBER:
|
|
78
87
|
case FieldType.BIGINT:
|
|
79
88
|
return {
|
|
@@ -11,10 +11,19 @@
|
|
|
11
11
|
|
|
12
12
|
let input
|
|
13
13
|
let active = false
|
|
14
|
+
let isComposing = false
|
|
14
15
|
|
|
15
16
|
$: editable = focused && !readonly
|
|
16
17
|
$: displayValue = format?.(value) ?? value ?? ""
|
|
17
18
|
|
|
19
|
+
const handleCompositionStart = () => {
|
|
20
|
+
isComposing = true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handleCompositionEnd = () => {
|
|
24
|
+
isComposing = false
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
const handleChange = e => {
|
|
19
28
|
onChange(e.target.value)
|
|
20
29
|
}
|
|
@@ -23,6 +32,9 @@
|
|
|
23
32
|
if (!active) {
|
|
24
33
|
return false
|
|
25
34
|
}
|
|
35
|
+
if (e.key === "Enter" && (isComposing || e.isComposing)) {
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
26
38
|
if (e.key === "Enter") {
|
|
27
39
|
input?.blur()
|
|
28
40
|
const event = new KeyboardEvent("keydown", { key: "ArrowDown" })
|
|
@@ -46,6 +58,8 @@
|
|
|
46
58
|
bind:this={input}
|
|
47
59
|
on:focus={() => (active = true)}
|
|
48
60
|
on:blur={() => (active = false)}
|
|
61
|
+
on:compositionstart={handleCompositionStart}
|
|
62
|
+
on:compositionend={handleCompositionEnd}
|
|
49
63
|
{type}
|
|
50
64
|
value={value ?? ""}
|
|
51
65
|
on:change={handleChange}
|
|
@@ -35,6 +35,7 @@ interface RowStore {
|
|
|
35
35
|
interface RowDerivedStore {
|
|
36
36
|
rows: RowStore["rows"]
|
|
37
37
|
rowLookupMap: Readable<Record<string, IndexedUIRow>>
|
|
38
|
+
rowCount: Readable<number>
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
interface RowActionStore {
|
|
@@ -168,12 +169,15 @@ export const deriveStores = (context: StoreContext): RowDerivedStore => {
|
|
|
168
169
|
return map
|
|
169
170
|
})
|
|
170
171
|
|
|
172
|
+
const rowCount = derived(rows, $rows => $rows.length)
|
|
173
|
+
|
|
171
174
|
return {
|
|
172
175
|
rows: {
|
|
173
176
|
...rows,
|
|
174
177
|
subscribe: enrichedRows.subscribe,
|
|
175
178
|
},
|
|
176
179
|
rowLookupMap,
|
|
180
|
+
rowCount,
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
|
package/src/fetch/DataFetch.ts
CHANGED
|
@@ -241,6 +241,7 @@ export default abstract class BaseDataFetch<
|
|
|
241
241
|
if (
|
|
242
242
|
fieldSchema?.type === FieldType.NUMBER ||
|
|
243
243
|
fieldSchema?.type === FieldType.BIGINT ||
|
|
244
|
+
fieldSchema?.responseType === FieldType.NUMBER ||
|
|
244
245
|
("calculationType" in fieldSchema && fieldSchema?.calculationType)
|
|
245
246
|
) {
|
|
246
247
|
this.options.sortType = SortType.NUMBER
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as sharedCore from "@budibase/shared-core"
|
|
2
|
+
import { UIFieldSchema, isStaticFormula } from "@budibase/types"
|
|
2
3
|
|
|
3
|
-
export function canBeDisplayColumn(column) {
|
|
4
|
+
export function canBeDisplayColumn(column: UIFieldSchema) {
|
|
4
5
|
if (!sharedCore.canBeDisplayColumn(column.type)) {
|
|
5
6
|
return false
|
|
6
7
|
}
|
|
@@ -11,16 +12,20 @@ export function canBeDisplayColumn(column) {
|
|
|
11
12
|
return true
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export function canBeSortColumn(
|
|
15
|
+
export function canBeSortColumn(columnSchema: UIFieldSchema) {
|
|
15
16
|
// Allow sorting by calculation columns
|
|
16
|
-
if (
|
|
17
|
+
if (columnSchema.calculationType) {
|
|
17
18
|
return true
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
// Allow static-only formula columns to be sorted
|
|
21
|
+
if (isStaticFormula(columnSchema)) {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
if (!sharedCore.canBeSortColumn(columnSchema.type)) {
|
|
20
25
|
return false
|
|
21
26
|
}
|
|
22
27
|
// If it's a related column (only available in the frontend), don't allow using it as display column
|
|
23
|
-
if (
|
|
28
|
+
if (columnSchema.related) {
|
|
24
29
|
return false
|
|
25
30
|
}
|
|
26
31
|
return true
|