Skip to content

Commit 5e7bc89

Browse files
authored
Merge pull request #30 from beekeeper-studio/upgrade-ai-sdk-v5
upgrade ai sdk to v5
2 parents 4444ff0 + 05a6e68 commit 5e7bc89

File tree

19 files changed

+814
-424
lines changed

19 files changed

+814
-424
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v22.21.1

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,29 @@
1515
"test:run": "vitest run"
1616
},
1717
"dependencies": {
18-
"@ai-sdk/anthropic": "^1.2.12",
19-
"@ai-sdk/google": "^1.2.22",
20-
"@ai-sdk/openai": "^1.3.22",
21-
"@ai-sdk/vue": "^1.2.12",
18+
"@ai-sdk/amazon-bedrock": "^3.0.33",
19+
"@ai-sdk/anthropic": "^2.0.25",
20+
"@ai-sdk/google": "^2.0.18",
21+
"@ai-sdk/openai": "^2.0.46",
22+
"@ai-sdk/openai-compatible": "^1.0.20",
23+
"@ai-sdk/vue": "^2.0.62",
2224
"@beekeeperstudio/plugin": "^1.4.0",
2325
"@langchain/core": "^0.3.61",
2426
"@material-symbols/font-400": "^0.31.2",
27+
"ai": "^5.0.101",
2528
"@pdanpdan/vue-keyboard-trap": "^1.2.0",
26-
"ai": "^4.3.16",
2729
"highlight.js": "^11.11.1",
2830
"langchain": "^0.3.19",
2931
"lodash": "^4.17.21",
3032
"marked": "^15.0.12",
3133
"marked-highlight": "^2.2.1",
32-
"ollama-ai-provider": "^1.2.0",
34+
"ollama-ai-provider-v2": "^1.3.1",
3335
"pinia": "^3.0.2",
3436
"pluralize": "^8.0.0",
3537
"sql-query-identifier": "^2.8.0",
3638
"typeface-roboto": "^0.0.75",
3739
"vue": "^3.4.21",
38-
"zod": "^3.25.32"
40+
"zod": "^4.1.8"
3941
},
4042
"devDependencies": {
4143
"@beekeeperstudio/vite-plugin": "^1.0.3",

src/assets/styles/components/_message.scss

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,33 @@
200200
font-style: italic;
201201
--theme-text-message-system: var(--text-muted);
202202
}
203+
204+
[data-tool-state="input-streaming"] {
205+
&>.markdown code .hljs-comment::after {
206+
content: "";
207+
animation: dots 2.5s steps(4, end) infinite;
208+
}
209+
210+
@keyframes dots {
211+
0% {
212+
content: "";
213+
}
214+
215+
25% {
216+
content: ".";
217+
}
218+
219+
50% {
220+
content: "..";
221+
}
222+
223+
75% {
224+
content: "...";
225+
}
226+
227+
100% {
228+
content: "";
229+
}
230+
}
231+
}
203232
}

src/components/ChatInterface.vue

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ import { useChatStore, Model } from "@/stores/chat";
8181
import Dropdown from "./common/Dropdown.vue";
8282
import DropdownOption from "./common/DropdownOption.vue";
8383
import _ from "lodash";
84-
import ToolMessage from "@/components/messages/ToolMessage.vue";
8584
import Markdown from "@/components/messages/Markdown.vue";
8685
import Message from "@/components/messages/Message.vue";
8786
import { Message as MessageType } from "ai";
@@ -99,7 +98,6 @@ export default {
9998
Dropdown,
10099
DropdownOption,
101100
Message,
102-
ToolMessage,
103101
Markdown,
104102
BaseInput,
105103
PromptInput,
@@ -118,12 +116,7 @@ export default {
118116
},
119117
120118
setup(props) {
121-
const ai = useAI({
122-
initialMessages: props.initialMessages,
123-
anthropicApiKey: props.anthropicApiKey,
124-
openaiApiKey: props.openaiApiKey,
125-
googleApiKey: props.googleApiKey,
126-
});
119+
const ai = useAI({ initialMessages: props.initialMessages });
127120
128121
return {
129122
send: ai.send,
@@ -254,10 +247,10 @@ export default {
254247
this.noModelError = false;
255248
256249
if (this.askingPermission) {
257-
this.rejectPermission(input);
258-
} else {
259-
this.send(input, this.getSendOptions());
250+
this.rejectPermission();
260251
}
252+
253+
this.send(input, this.getSendOptions());
261254
},
262255
263256
async reload() {

src/components/messages/Message.vue

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
<template v-if="message.role === 'user'">{{ part.text }}</template>
88
<markdown v-else :content="part.text" />
99
</template>
10-
<tool-message v-else-if="part.type === 'tool-invocation'" :toolCall="part.toolInvocation" :askingPermission="pendingToolCallIds.includes(part.toolInvocation.toolCallId)
11-
" @accept="$emit('accept-permission', part.toolInvocation.toolCallId)"
12-
@reject="$emit('reject-permission', part.toolInvocation.toolCallId)" />
10+
<tool-message
11+
v-else-if="isToolUIPart(part)"
12+
:toolCall="part"
13+
:askingPermission="pendingToolCallIds.includes(part.toolCallId)"
14+
@accept="$emit('accept-permission', part.toolCallId)"
15+
@reject="$emit('reject-permission', part.toolCallId)"
16+
/>
1317
</template>
1418
<span v-if="isEmpty">
1519
Empty response
@@ -30,7 +34,7 @@
3034

3135
<script lang="ts">
3236
import { PropType } from "vue";
33-
import { UIMessage } from "ai";
37+
import { isToolUIPart, UIMessage } from "ai";
3438
import Markdown from "@/components/messages/Markdown.vue";
3539
import ToolMessage from "@/components/messages/ToolMessage.vue";
3640
import { clipboard } from "@beekeeperstudio/plugin";
@@ -72,12 +76,11 @@ export default {
7276
for (const part of parts) {
7377
if (part.type === "text") {
7478
text += `${part.text}\n\n`;
75-
} else if (
76-
part.type === "tool-invocation" &&
77-
part.toolInvocation.toolName === "run_query" &&
78-
part.toolInvocation.args?.query
79-
) {
80-
text += "```sql\n" + part.toolInvocation.args.query + "\n```\n\n";
79+
} else if (part.type === "tool-run_query") {
80+
const query: string | undefined = part.input?.query;
81+
if (query) {
82+
text += "```sql\n" + query + "\n```\n\n";
83+
}
8184
}
8285
}
8386
return text.trim();
@@ -88,6 +91,7 @@ export default {
8891
},
8992
9093
methods: {
94+
isToolUIPart,
9195
async handleCopyClick() {
9296
await clipboard.writeText(this.text);
9397
this.copied = true;

src/components/messages/ToolMessage.vue

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
<template>
2-
<div class="tool" :data-tool-name="toolCall.toolName" :data-tool-state="toolCall.state"
2+
<div class="tool" :data-tool-name="name" :data-tool-state="toolCall.state"
33
:data-tool-empty-result="isEmptyResult" :data-tool-error="!!error">
44
<div class="tool-name">{{ displayName }}</div>
5-
<markdown v-if="toolCall.toolName === 'run_query'" :content="'```sql\n' + toolCall.args.query + '\n```'" />
5+
<markdown v-if="name === 'run_query'" :content="'```sql\n' +
6+
(toolCall.input?.query ||
7+
(toolCall.state === 'output-available' ? '(empty)' : '-- Generating')) +
8+
'\n```'
9+
" />
610
<div v-if="askingPermission">
711
{{
8-
toolCall.toolName === "run_query"
12+
name === "run_query"
913
? "Do you want to run this query?"
1014
: "Do you want to proceed?"
1115
}}
@@ -22,25 +26,25 @@
2226
</div>
2327
<div class="tool-error error" v-if="error" v-text="error" />
2428
<div class="tool-result" v-else-if="data">
25-
<template v-if="toolCall.toolName === 'get_connection_info'">
29+
<template v-if="name === 'get_connection_info'">
2630
{{ data.connectionType }}
2731
</template>
28-
<template v-if="toolCall.toolName === 'get_tables'">
32+
<template v-if="name === 'get_tables'">
2933
{{ data.length }}
3034
{{ $pluralize("table", data.length) }}
3135
</template>
32-
<template v-if="toolCall.toolName === 'get_columns'">
36+
<template v-if="name === 'get_columns'">
3337
{{ data.length }}
3438
{{ $pluralize("column", data.length) }}
3539
</template>
36-
<run-query-result v-else-if="toolCall.toolName === 'run_query' && data" :data="data" />
40+
<run-query-result v-else-if="name === 'run_query' && data" :data="data" />
3741
</div>
3842
</div>
3943
</template>
4044

4145
<script lang="ts">
4246
import Markdown from "@/components/messages/Markdown.vue";
43-
import { ToolInvocation } from "ai";
47+
import { type ToolUIPart } from "ai";
4448
import { PropType } from "vue";
4549
import { safeJSONStringify } from "@/utils";
4650
import RunQueryResult from "@/components/messages/tool/RunQueryResult.vue";
@@ -52,16 +56,20 @@ export default {
5256
props: {
5357
askingPermission: Boolean,
5458
toolCall: {
55-
type: Object as PropType<ToolInvocation>,
59+
type: Object as PropType<ToolUIPart>,
5660
required: true,
5761
},
62+
args: null,
5863
},
5964
emits: ["accept", "reject"],
6065
computed: {
66+
name() {
67+
return this.toolCall.type.replace("tool-", "");
68+
},
6169
isEmptyResult() {
62-
if (this.toolCall.state === "result") {
70+
if (this.toolCall.state === "output-available") {
6371
return _.isEmpty(
64-
this.toolCall.toolName === "run_query"
72+
this.name === "run_query"
6573
? this.data.results?.[0]?.rows
6674
: this.data,
6775
);
@@ -84,26 +92,35 @@ export default {
8492
return "";
8593
},
8694
data() {
95+
if (this.toolCall.state !== "output-available") {
96+
return null;
97+
}
98+
8799
try {
88-
return JSON.parse(this.toolCall.result);
100+
return JSON.parse(this.toolCall.output);
89101
} catch (e) {
90102
return null;
91103
}
92104
},
93105
error() {
94-
if (isErrorContent(this.toolCall.result)) {
95-
const err = parseErrorContent(this.toolCall.result);
106+
if (
107+
this.toolCall.state === "output-available" &&
108+
isErrorContent(this.toolCall.output)
109+
) {
110+
const err = parseErrorContent(this.toolCall.output);
96111
return err.message ?? err;
112+
} else if (this.toolCall.state === "output-error") {
113+
return this.toolCall.errorText;
97114
}
98115
},
99116
displayName() {
100-
if (this.toolCall.toolName === "get_columns") {
101-
if (this.toolCall.args.schema) {
102-
return `Get Columns (schema: ${this.toolCall.args.schema}, table: ${this.toolCall.args.table})`;
117+
if (this.name === "get_columns") {
118+
if (this.toolCall.input?.schema) {
119+
return `Get Columns (schema: ${this.toolCall.input?.schema}, table: ${this.toolCall.input?.table || "..."})`;
103120
}
104-
return `Get Columns (${this.toolCall.args.table})`;
121+
return `Get Columns (${this.toolCall.input?.table || "..."})`;
105122
}
106-
return this.toolCall.toolName.split("_").map(_.capitalize).join(" ");
123+
return this.name.split("_").map(_.capitalize).join(" ");
107124
},
108125
},
109126
methods: {

0 commit comments

Comments
 (0)