Skip to content

Commit 6693feb

Browse files
committed
Merge branch 'main' into feat/editable-query
2 parents fde3d59 + 9da1c92 commit 6693feb

File tree

6 files changed

+149
-54
lines changed

6 files changed

+149
-54
lines changed

src/App.vue

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,10 @@
77
<ChatInterface v-if="page === 'chat-interface'" :initialMessages="messages" @manage-models="handleManageModels"
88
@open-configuration="handleOpenConfiguration" />
99
<Configuration v-model:visible="showConfiguration" :reactivePage="configurationPage" @close="closeConfiguration" />
10-
<div class="onboarding-screen-popover-container" v-if="showOnboarding">
11-
<div class="onboarding-screen-popover" v-kbd-trap="true">
12-
<button class="btn close-btn" @click="closeOnboardingScreen">
13-
<span class="material-symbols-outlined">close</span>
14-
</button>
15-
<OnboardingScreen @submit="closeOnboardingScreen"
16-
@open-provider-config="closeOnboardingScreenAndOpenProviderConfig" />
17-
</div>
18-
</div>
10+
<Dialog :visible="showOnboarding" :closable="false" :draggable="false">
11+
<OnboardingScreen @submit="closeOnboardingScreen"
12+
@open-provider-config="closeOnboardingScreenAndOpenProviderConfig" />
13+
</Dialog>
1914
</div>
2015
</template>
2116

src/assets/styles/pages/_chat-interface.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@
7171

7272
.plugin-title {
7373
text-align: center;
74-
margin-bottom: 2rem;
74+
margin-bottom: 1rem;
75+
max-width: 32rem;
76+
77+
p {
78+
color: var(--text);
79+
}
7580
}
7681

7782
.chat-messages {

src/assets/styles/pages/_onboarding-screen.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454
max-width: 24rem;
5555
text-align: center;
5656

57+
h1 {
58+
margin-top: 0;
59+
}
60+
5761
.api-info {
5862
margin-top: 1.5rem;
5963
margin-bottom: 1rem;

src/components/ApiKeyForm.vue

Lines changed: 99 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,114 @@
11
<template>
2-
<div class="api-key-form">
3-
<BaseInput
4-
type="password"
5-
placeholder="sk-proj-..."
6-
v-model="openaiApiKey"
7-
>
2+
<div class="api-key-form" :class="{ 'dropdown-based': dropdownBased }">
3+
<div class="input-container" v-if="dropdownBased">
4+
<label for="select-provider">Provider</label>
5+
<Select for="select-provider" v-model="selectedProvider" :options="dropdownOptions" optionLabel="label"
6+
placeholder="Select a provider" />
7+
</div>
8+
<BaseInput v-if="!dropdownBased || selectedProvider.value === 'openai'" type="password" placeholder="sk-proj-..."
9+
v-model="openaiApiKey">
810
<template #label>
911
{{ providerConfigs["openai"].displayName }}
12+
{{ dropdownBased ? "API Key" : '' }}
1013
</template>
1114
<template #helper>
1215
Get your <ExternalLink href="https://platform.openai.com/account/api-keys">API Key here.</ExternalLink>
1316
</template>
1417
</BaseInput>
15-
<BaseInput
16-
type="password"
17-
placeholder="sk-ant-..."
18-
v-model="anthropicApiKey"
19-
>
18+
<BaseInput v-if="!dropdownBased || selectedProvider.value === 'anthropic'" type="password" placeholder="sk-ant-..."
19+
v-model="anthropicApiKey">
2020
<template #label>
2121
{{ providerConfigs["anthropic"].displayName }}
22+
{{ dropdownBased ? "API Key" : '' }}
2223
</template>
2324
<template #helper>
2425
Get your <ExternalLink href="https://console.anthropic.com/settings/keys">API Key here.</ExternalLink>
2526
</template>
2627
</BaseInput>
27-
<BaseInput
28-
type="password"
29-
placeholder="AIzaSy..."
30-
v-model="googleApiKey"
31-
>
28+
<BaseInput v-if="!dropdownBased || selectedProvider.value === 'google'" type="password" placeholder="AIzaSy..."
29+
v-model="googleApiKey">
3230
<template #label>
3331
{{ providerConfigs["google"].displayName }}
32+
{{ dropdownBased ? "API Key" : '' }}
3433
</template>
3534
<template #helper>
3635
Get your <ExternalLink href="https://aistudio.google.com/app/apikey">API Key here.</ExternalLink>
3736
</template>
3837
</BaseInput>
38+
<template v-if="selectedProvider.value === 'openaiCompat'">
39+
<BaseInput v-model="openaiCompatApiKey">
40+
<template #label>OpenAI Compatible API Key</template>
41+
</BaseInput>
42+
<BaseInput v-model="openaiCompatBaseUrl">
43+
<template #label>Base URL</template>
44+
</BaseInput>
45+
<BaseInput v-model="openaiCompatHeaders" type="textarea">
46+
<template #label>Custom Headers</template>
47+
</BaseInput>
48+
</template>
49+
<template v-if="selectedProvider.value === 'ollama'">
50+
<BaseInput v-model="ollamaBaseUrl">
51+
<template #label>Ollama Base URL</template>
52+
</BaseInput>
53+
<BaseInput v-model="ollamaHeaders" type="textarea">
54+
<template #label>Custom Headers</template>
55+
</BaseInput>
56+
</template>
3957
</div>
4058
</template>
4159

4260
<script lang="ts">
43-
import { mapState, mapActions } from "pinia";
61+
import { mapActions } from "pinia";
4462
import { useConfigurationStore } from "@/stores/configuration";
45-
import { providerConfigs } from "@/config";
63+
import { AvailableProviders, providerConfigs } from "@/config";
4664
import BaseInput from "./common/BaseInput.vue";
4765
import ExternalLink from "./common/ExternalLink.vue";
66+
import Select from 'primevue/select';
4867
4968
export default {
5069
name: "ApiKeyForm",
5170
5271
components: {
5372
BaseInput,
5473
ExternalLink,
74+
Select,
5575
},
5676
57-
emits: ["change"],
77+
emits: ["change", "changeProvider"],
78+
79+
props: {
80+
dropdownBased: Boolean,
81+
},
5882
5983
data() {
84+
const config = useConfigurationStore();
85+
6086
return {
61-
openaiApiKey: "",
62-
anthropicApiKey: "",
63-
googleApiKey: "",
87+
selectedProvider: {
88+
label: "" as typeof providerConfigs[AvailableProviders]['displayName'],
89+
value: "" as AvailableProviders,
90+
},
91+
openaiApiKey: config['providers.openai.apiKey'],
92+
anthropicApiKey: config['providers.anthropic.apiKey'],
93+
googleApiKey: config['providers.google.apiKey'],
94+
ollamaBaseUrl: config.providers_ollama_baseUrl,
95+
ollamaHeaders: config.providers_ollama_headers,
96+
openaiCompatBaseUrl: config.providers_openaiCompat_baseUrl,
97+
openaiCompatApiKey: config.providers_openaiCompat_apiKey,
98+
openaiCompatHeaders: config.providers_openaiCompat_headers,
6499
};
65100
},
66101
67102
computed: {
68-
...mapState(useConfigurationStore, [
69-
"providers.openai.apiKey",
70-
"providers.anthropic.apiKey",
71-
"providers.google.apiKey",
72-
]),
73103
providerConfigs() {
74104
return providerConfigs;
75105
},
106+
dropdownOptions() {
107+
return Object.keys(providerConfigs).map((provider) => ({
108+
label: this.providerConfigs[provider].displayName,
109+
value: provider,
110+
}));
111+
},
76112
},
77113
78114
watch: {
@@ -88,16 +124,46 @@ export default {
88124
this.configure("providers.google.apiKey", this.googleApiKey);
89125
this.$emit("change");
90126
},
127+
ollamaBaseUrl() {
128+
this.configure("providers_ollama_baseUrl", this.ollamaBaseUrl);
129+
this.$emit("change");
130+
},
131+
ollamaHeaders() {
132+
this.configure("providers_ollama_headers", this.ollamaHeaders);
133+
this.$emit("change");
134+
},
135+
openaiCompatBaseUrl() {
136+
this.configure("providers_openaiCompat_baseUrl", this.openaiCompatBaseUrl);
137+
this.$emit("change");
138+
},
139+
openaiCompatApiKey() {
140+
this.configure("providers_openaiCompat_apiKey", this.openaiCompatApiKey);
141+
this.$emit("change");
142+
},
143+
openaiCompatHeaders() {
144+
this.configure("providers_openaiCompat_headers", this.openaiCompatHeaders);
145+
this.$emit("change");
146+
},
147+
selectedProvider() {
148+
this.$emit("changeProvider", this.selectedProvider.value);
149+
},
91150
},
92151
93152
methods: {
94153
...mapActions(useConfigurationStore, ["configure"]),
95154
},
96-
97-
mounted() {
98-
this.openaiApiKey = this["providers.openai.apiKey"];
99-
this.anthropicApiKey = this["providers.anthropic.apiKey"];
100-
this.googleApiKey = this["providers.google.apiKey"];
101-
},
102155
};
103156
</script>
157+
158+
<style scoped>
159+
.input-container {
160+
display: flex;
161+
flex-direction: column;
162+
gap: 0.5rem;
163+
justify-content: flex-start;
164+
165+
label {
166+
align-self: flex-start;
167+
}
168+
}
169+
</style>

src/components/ChatInterface.vue

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77
<span class="title-popup">Settings</span>
88
</button>
99
</div>
10-
<h1 class="plugin-title">AI Shell</h1>
10+
<div class="plugin-title">
11+
<h1>AI Shell</h1>
12+
<p>
13+
The AI Shell can see your table schemas, and (with your permission)
14+
run {{ sqlOrCode }} to answer your questions.
15+
<ExternalLink href="https://docs.beekeeperstudio.io/user_guide/sql-ai-shell/">Learn more</ExternalLink>.
16+
</p>
17+
</div>
1118
<div class="chat-messages">
1219
<template v-for="(message, index) in messages" :key="message.id">
1320
<message
@@ -70,6 +77,8 @@ import { RootBinding } from "@/plugins/appEvent";
7077
import { useInternalDataStore } from "@/stores/internalData";
7178
import BaseInput from "@/components/common/BaseInput.vue";
7279
import PromptInput from "@/components/common/PromptInput.vue";
80+
import { getConnectionInfo } from "@beekeeperstudio/plugin";
81+
import ExternalLink from "@/components/common/ExternalLink.vue";
7382
7483
export default {
7584
name: "ChatInterface",
@@ -79,6 +88,7 @@ export default {
7988
Markdown,
8089
BaseInput,
8190
PromptInput,
91+
ExternalLink,
8292
},
8393
8494
emits: ["manage-models", "open-configuration"],
@@ -112,6 +122,7 @@ export default {
112122
isAtBottom: true,
113123
showFullError: false,
114124
noModelError: false,
125+
sqlOrCode: "SQL",
115126
};
116127
},
117128
@@ -193,6 +204,14 @@ export default {
193204
},
194205
195206
async mounted() {
207+
getConnectionInfo().then((connection) => {
208+
if (connection.databaseType === "mongodb"
209+
|| connection.connectionType === "surrealdb"
210+
|| connection.connectionType === "redis") {
211+
this.sqlOrCode = "Code";
212+
}
213+
});
214+
196215
const scrollContainer = this.$refs.scrollContainerRef as HTMLElement;
197216
scrollContainer.addEventListener("scroll", () => {
198217
// Calculate if we're near bottom (within 50px of bottom)

src/components/OnboardingScreen.vue

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
<template>
22
<div class="onboarding-screen">
3-
<h1>Welcome to AI Shell</h1>
4-
<p>Before we start, please enter your API keys below.</p>
5-
<ApiInfo />
3+
<h1>Welcome to the AI Shell</h1>
4+
<p>
5+
Your AI agent can explore your database and (with your permission) write code to
6+
answer your questions. Enter the API key below for your preffered agent to get started.
7+
</p>
8+
<p>
9+
<ExternalLink href="https://docs.beekeeperstudio.io/user_guide/sql-ai-shell/">Learn more</ExternalLink>
10+
-
11+
<ExternalLink href="https://www.youtube.com/watch?v=pAhQUFDeiwc">90s Walkthough Video</ExternalLink>
12+
</p>
613
<form @submit.prevent="$emit('submit')">
7-
<ApiKeyForm @change="changed = true" />
14+
<ApiKeyForm dropdown-based @change="changed = true" @change-provider="$event === 'ollama' && (changed = true)" />
815
<div class="actions">
9-
<button class="btn btn-flat" type="button" @click="$emit('open-provider-config')">
10-
<span class="material-symbols-outlined">settings</span>
11-
<span>View more providers</span>
12-
</button>
13-
<button class="btn btn-primary continue-btn" type="submit">
14-
{{ changed ? "Continue" : "Skip" }}
16+
<button class="btn btn-primary continue-btn" type="submit" :disabled="!changed">
17+
Get started
1518
</button>
1619
</div>
1720
</form>
@@ -21,12 +24,15 @@
2124
<script lang="ts">
2225
import ApiKeyForm from "@/components/ApiKeyForm.vue";
2326
import ApiInfo from "@/components/configuration/ApiInfo.vue";
27+
import ExternalLink from "@/components/common/ExternalLink.vue";
28+
2429
export default {
2530
emits: ["submit", "open-provider-config"],
2631
2732
components: {
2833
ApiKeyForm,
2934
ApiInfo,
35+
ExternalLink,
3036
},
3137
3238
data() {

0 commit comments

Comments
 (0)