@marimo-team/islands 0.14.18-dev37 → 0.14.18-dev39
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/dist/{ConnectedDataExplorerComponent-Agqj2ooM.js → ConnectedDataExplorerComponent-3RtvsK5H.js} +2 -2
- package/dist/{ImageComparisonComponent-DPGANUHl.js → ImageComparisonComponent-BNAZDH44.js} +1 -1
- package/dist/{_baseUniq-CpMSR4IQ.js → _baseUniq-BVfJUeQb.js} +1 -1
- package/dist/{any-language-editor-CFdNKVB5.js → any-language-editor-Ks7PGJCn.js} +1 -1
- package/dist/{architectureDiagram-SUXI7LT5-DtrTZbYk.js → architectureDiagram-SUXI7LT5-BolOAqtB.js} +5 -5
- package/dist/assets/{worker-IDMFdmDA.js → worker-COGufAQn.js} +70 -70
- package/dist/{blockDiagram-6J76NXCF-Cp-0RluB.js → blockDiagram-6J76NXCF-CWGz15YB.js} +5 -5
- package/dist/{c4Diagram-6F6E4RAY-DQ2kylH0.js → c4Diagram-6F6E4RAY-DiNVqHzw.js} +2 -2
- package/dist/{channel-BS-z-DLr.js → channel-94lD77dB.js} +1 -1
- package/dist/{chunk-353BL4L5-D0W0s9ka.js → chunk-353BL4L5-CETvj-tT.js} +1 -1
- package/dist/{chunk-67H74DCK-CA-VzWuj.js → chunk-67H74DCK-D5k3YQ75.js} +1 -1
- package/dist/{chunk-AACKK3MU-BHjbkUMB.js → chunk-AACKK3MU-CLcCi13Y.js} +1 -1
- package/dist/{chunk-BFAMUDN2-Dfk1hsa2.js → chunk-BFAMUDN2-DZ_4EPIC.js} +1 -1
- package/dist/{chunk-E2GYISFI-ATcFQCw7.js → chunk-E2GYISFI-D7WEFN0I.js} +1 -1
- package/dist/{chunk-OW32GOEJ-D5ZiaOi7.js → chunk-OW32GOEJ-xW1cu7Ta.js} +3 -3
- package/dist/{chunk-SKB7J2MH-C06x1AM-.js → chunk-SKB7J2MH-BCCadknO.js} +1 -1
- package/dist/{chunk-SZ463SBG-COrdHOzF.js → chunk-SZ463SBG-Da0noPdf.js} +4 -4
- package/dist/{classDiagram-v2-YAWTLIQI-ClXfLaA6.js → classDiagram-M3E45YP4-BgjOa5oq.js} +2 -2
- package/dist/{classDiagram-M3E45YP4-ClXfLaA6.js → classDiagram-v2-YAWTLIQI-BgjOa5oq.js} +2 -2
- package/dist/{clone-DVZ4x5LN.js → clone-Bu53USnE.js} +1 -1
- package/dist/{dagre-JOIXM2OF-CIM0a0_j.js → dagre-JOIXM2OF-lXTzFbYK.js} +6 -6
- package/dist/{data-grid-overlay-editor-B_f-XmH8.js → data-grid-overlay-editor-nbxFCqIc.js} +2 -2
- package/dist/{diagram-5UYTHUR4-CJ5-7BUI.js → diagram-5UYTHUR4-DpsLtTC4.js} +3 -3
- package/dist/{diagram-VMROVX33-xCZwSfL2.js → diagram-VMROVX33-Dl99FSjs.js} +5 -5
- package/dist/{diagram-ZTM2IBQH-BwMLyLo9.js → diagram-ZTM2IBQH-C6IZmxtP.js} +3 -3
- package/dist/{erDiagram-3M52JZNH-DChgswtA.js → erDiagram-3M52JZNH-BJAfHqCe.js} +4 -4
- package/dist/{flowDiagram-KYDEHFYC-D041bGnc.js → flowDiagram-KYDEHFYC-DQ4ZD8eZ.js} +5 -5
- package/dist/{ganttDiagram-EK5VF46D-B7M48jZ_.js → ganttDiagram-EK5VF46D-BiUE232l.js} +4 -4
- package/dist/{gitGraphDiagram-GW3U2K7C-QA3k2S0X.js → gitGraphDiagram-GW3U2K7C-DcXDrK5d.js} +4 -4
- package/dist/{glide-data-editor-uxbE445v.js → glide-data-editor-BCANW8UP.js} +3 -3
- package/dist/{graph-CU2kk2IH.js → graph-31Sn9Cfm.js} +3 -3
- package/dist/{index-RWaQzJSd.js → index-65RV0mXZ.js} +1 -1
- package/dist/{index-_qCoCBPD.js → index-BqBV4ZfA.js} +1 -1
- package/dist/{index-ByAs2ixu.js → index-Cfkm9mAj.js} +1 -1
- package/dist/{index-CtEeLUrb.js → index-pQ3WHloE.js} +1 -1
- package/dist/{index-B-Ueew3R.js → index-vELYSc2I.js} +3 -3
- package/dist/{infoDiagram-LHK5PUON-BMLaF5rR.js → infoDiagram-LHK5PUON-BmBFxuqN.js} +2 -2
- package/dist/{journeyDiagram-EWQZEKCU-DdcK-ELK.js → journeyDiagram-EWQZEKCU-yNJKWCpQ.js} +3 -3
- package/dist/{kanban-definition-ZSS6B67P-DlAqmjND.js → kanban-definition-ZSS6B67P-7VUAd5gR.js} +2 -2
- package/dist/{layout-DyfYCx-G.js → layout-DANNdOvY.js} +4 -4
- package/dist/{linear-60P382Ey.js → linear-C63wbs-u.js} +1 -1
- package/dist/{main-YndPucih.js → main-kUMPG4DJ.js} +12584 -12584
- package/dist/main.js +1 -1
- package/dist/{mermaid-bHfZQVOX.js → mermaid-CN_OTOT4.js} +29 -29
- package/dist/{min-dlJMdVHY.js → min-CQkFw4Vk.js} +2 -2
- package/dist/{mindmap-definition-6CBA2TL7-Dr0M7ZVJ.js → mindmap-definition-6CBA2TL7-B0Vipg3p.js} +2 -2
- package/dist/{number-overlay-editor-OG-7Y1Dx.js → number-overlay-editor-0wXc3j78.js} +2 -2
- package/dist/{pieDiagram-NIOCPIFQ-BmGwhj4Q.js → pieDiagram-NIOCPIFQ-DVmPkj3p.js} +3 -3
- package/dist/{quadrantDiagram-2OG54O6I-XUAIoa5o.js → quadrantDiagram-2OG54O6I-OyhnTUp0.js} +2 -2
- package/dist/{react-plotly-DDGiywdq.js → react-plotly-TMdrxs3a.js} +1 -1
- package/dist/{requirementDiagram-QOLK2EJ7-0ca9BSWg.js → requirementDiagram-QOLK2EJ7-CY3Oayeg.js} +3 -3
- package/dist/{sankeyDiagram-4UZDY2LN-FJagmBot.js → sankeyDiagram-4UZDY2LN-DZHba4BC.js} +1 -1
- package/dist/{sequenceDiagram-SKLFT4DO-CRzbF56l.js → sequenceDiagram-SKLFT4DO-nMdmIMLD.js} +3 -3
- package/dist/{slides-component-DVst__vX.js → slides-component-BABoru31.js} +1 -1
- package/dist/{stateDiagram-MI5ZYTHO-DIe7eGfc.js → stateDiagram-MI5ZYTHO-C4-gl_HF.js} +4 -4
- package/dist/{stateDiagram-v2-5AN5P6BG-C_re8HoC.js → stateDiagram-v2-5AN5P6BG-RVtW0Jhm.js} +2 -2
- package/dist/{time-AgleFMg4.js → time-Bc9yXFIT.js} +2 -2
- package/dist/{timeline-definition-MYPXXCX6-DTQKnm70.js → timeline-definition-MYPXXCX6-CXtqBPWZ.js} +1 -1
- package/dist/{treemap-75Q7IDZK-DsSdguo2.js → treemap-75Q7IDZK-dEViVYg5.js} +5 -5
- package/dist/{vega-component-DnXRdUc_.js → vega-component-Dx_TVBpx.js} +2 -2
- package/dist/{xychartDiagram-H2YORKM3-MLsV7Keh.js → xychartDiagram-H2YORKM3-D7xC_ETv.js} +2 -2
- package/package.json +1 -1
- package/src/components/ai/ai-model-dropdown.tsx +1 -1
- package/src/components/chat/chat-panel.tsx +8 -51
- package/src/components/editor/ai/add-cell-with-ai.tsx +114 -60
- package/src/components/editor/ai/ai-completion-editor.tsx +27 -15
- package/src/core/ai/config.ts +76 -0
- package/src/core/config/config-schema.ts +18 -12
- package/src/core/static/__tests__/files.test.ts +311 -0
- package/src/core/static/files.ts +35 -33
- package/src/plugins/impl/chat/chat-ui.tsx +0 -3
|
@@ -16,10 +16,11 @@ import ReactCodeMirror, {
|
|
|
16
16
|
type ReactCodeMirrorRef,
|
|
17
17
|
} from "@uiw/react-codemirror";
|
|
18
18
|
import { useCompletion } from "ai/react";
|
|
19
|
-
import { useAtom, useStore } from "jotai";
|
|
19
|
+
import { useAtom, useAtomValue, useStore } from "jotai";
|
|
20
20
|
import { atomWithStorage } from "jotai/utils";
|
|
21
21
|
import {
|
|
22
22
|
ChevronsUpDown,
|
|
23
|
+
DatabaseIcon,
|
|
23
24
|
Loader2Icon,
|
|
24
25
|
SendHorizontal,
|
|
25
26
|
SparklesIcon,
|
|
@@ -27,22 +28,28 @@ import {
|
|
|
27
28
|
} from "lucide-react";
|
|
28
29
|
import { useMemo, useState } from "react";
|
|
29
30
|
import useEvent from "react-use-event-hook";
|
|
31
|
+
import { AIModelDropdown } from "@/components/ai/ai-model-dropdown";
|
|
30
32
|
import { Button } from "@/components/ui/button";
|
|
31
33
|
import {
|
|
32
34
|
DropdownMenu,
|
|
33
35
|
DropdownMenuContent,
|
|
34
36
|
DropdownMenuItem,
|
|
37
|
+
DropdownMenuSeparator,
|
|
35
38
|
DropdownMenuTrigger,
|
|
36
39
|
} from "@/components/ui/dropdown-menu";
|
|
37
40
|
import { toast } from "@/components/ui/use-toast";
|
|
41
|
+
import { useModelChange } from "@/core/ai/config";
|
|
38
42
|
import { resourceExtension } from "@/core/codemirror/ai/resources";
|
|
39
43
|
import { customPythonLanguageSupport } from "@/core/codemirror/language/languages/python";
|
|
40
44
|
import { SQLLanguageAdapter } from "@/core/codemirror/language/languages/sql/sql";
|
|
45
|
+
import { aiAtom } from "@/core/config/config";
|
|
46
|
+
import { DEFAULT_AI_MODEL } from "@/core/config/config-schema";
|
|
41
47
|
import { useRuntimeManager } from "@/core/runtime/config";
|
|
42
|
-
import {
|
|
48
|
+
import { useTheme } from "@/theme/useTheme";
|
|
43
49
|
import { cn } from "@/utils/cn";
|
|
44
50
|
import { prettyError } from "@/utils/errors";
|
|
45
51
|
import { useCellActions } from "../../../core/cells/cells";
|
|
52
|
+
import { PythonIcon } from "../cell/code/icons";
|
|
46
53
|
import {
|
|
47
54
|
getAICompletionBody,
|
|
48
55
|
mentionsCompletionSource,
|
|
@@ -72,6 +79,10 @@ export const AddCellWithAI: React.FC<{
|
|
|
72
79
|
const { theme } = useTheme();
|
|
73
80
|
const runtimeManager = useRuntimeManager();
|
|
74
81
|
|
|
82
|
+
const ai = useAtomValue(aiAtom);
|
|
83
|
+
const editModel = ai?.models?.edit_model || DEFAULT_AI_MODEL;
|
|
84
|
+
const { saveModelChange } = useModelChange();
|
|
85
|
+
|
|
75
86
|
const {
|
|
76
87
|
completion,
|
|
77
88
|
input,
|
|
@@ -109,32 +120,50 @@ export const AddCellWithAI: React.FC<{
|
|
|
109
120
|
}
|
|
110
121
|
};
|
|
111
122
|
|
|
123
|
+
const pythonIcon = (
|
|
124
|
+
<>
|
|
125
|
+
<PythonIcon className="size-4 mr-2" />
|
|
126
|
+
Python
|
|
127
|
+
</>
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const sqlIcon = (
|
|
131
|
+
<>
|
|
132
|
+
<DatabaseIcon className="size-4 mr-2" />
|
|
133
|
+
SQL
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const languageDropdown = (
|
|
138
|
+
<DropdownMenu modal={false}>
|
|
139
|
+
<DropdownMenuTrigger asChild={true}>
|
|
140
|
+
<Button
|
|
141
|
+
variant="text"
|
|
142
|
+
className="ml-2"
|
|
143
|
+
size="xs"
|
|
144
|
+
data-testid="language-button"
|
|
145
|
+
>
|
|
146
|
+
{language === "python" ? pythonIcon : sqlIcon}
|
|
147
|
+
<ChevronsUpDown className="ml-1 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
148
|
+
</Button>
|
|
149
|
+
</DropdownMenuTrigger>
|
|
150
|
+
<DropdownMenuContent align="center">
|
|
151
|
+
<div className="px-2 py-1 font-semibold">Select language</div>
|
|
152
|
+
<DropdownMenuSeparator />
|
|
153
|
+
<DropdownMenuItem onClick={() => setLanguage("python")}>
|
|
154
|
+
{pythonIcon}
|
|
155
|
+
</DropdownMenuItem>
|
|
156
|
+
<DropdownMenuItem onClick={() => setLanguage("sql")}>
|
|
157
|
+
{sqlIcon}
|
|
158
|
+
</DropdownMenuItem>
|
|
159
|
+
</DropdownMenuContent>
|
|
160
|
+
</DropdownMenu>
|
|
161
|
+
);
|
|
162
|
+
|
|
112
163
|
const inputComponent = (
|
|
113
164
|
<div className="flex items-center px-3">
|
|
114
|
-
<SparklesIcon className="size-4 text-(--blue-11)" />
|
|
115
|
-
<DropdownMenu modal={false}>
|
|
116
|
-
<DropdownMenuTrigger asChild={true}>
|
|
117
|
-
<Button
|
|
118
|
-
variant="text"
|
|
119
|
-
className="ml-2"
|
|
120
|
-
size="xs"
|
|
121
|
-
data-testid="language-button"
|
|
122
|
-
>
|
|
123
|
-
{language === "python" ? "Python" : "SQL"}
|
|
124
|
-
<ChevronsUpDown className="ml-1 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
125
|
-
</Button>
|
|
126
|
-
</DropdownMenuTrigger>
|
|
127
|
-
<DropdownMenuContent align="center">
|
|
128
|
-
<DropdownMenuItem onClick={() => setLanguage("python")}>
|
|
129
|
-
Python
|
|
130
|
-
</DropdownMenuItem>
|
|
131
|
-
<DropdownMenuItem onClick={() => setLanguage("sql")}>
|
|
132
|
-
SQL
|
|
133
|
-
</DropdownMenuItem>
|
|
134
|
-
</DropdownMenuContent>
|
|
135
|
-
</DropdownMenu>
|
|
165
|
+
<SparklesIcon className="size-4 text-(--blue-11) mr-2" />
|
|
136
166
|
<PromptInput
|
|
137
|
-
theme={theme}
|
|
138
167
|
onClose={() => {
|
|
139
168
|
setCompletion("");
|
|
140
169
|
onClose();
|
|
@@ -158,33 +187,10 @@ export const AddCellWithAI: React.FC<{
|
|
|
158
187
|
Stop
|
|
159
188
|
</Button>
|
|
160
189
|
)}
|
|
161
|
-
{!isLoading && completion && (
|
|
162
|
-
<Button
|
|
163
|
-
data-testid="accept-completion-button"
|
|
164
|
-
variant="text"
|
|
165
|
-
size="sm"
|
|
166
|
-
className="mb-0"
|
|
167
|
-
disabled={isLoading}
|
|
168
|
-
onClick={() => {
|
|
169
|
-
createNewCell({
|
|
170
|
-
cellId: "__end__",
|
|
171
|
-
before: false,
|
|
172
|
-
code:
|
|
173
|
-
language === "python"
|
|
174
|
-
? completion
|
|
175
|
-
: SQLLanguageAdapter.fromQuery(completion),
|
|
176
|
-
});
|
|
177
|
-
setCompletion("");
|
|
178
|
-
onClose();
|
|
179
|
-
}}
|
|
180
|
-
>
|
|
181
|
-
<span className="text-(--grass-11) opacity-100">Accept</span>
|
|
182
|
-
</Button>
|
|
183
|
-
)}
|
|
184
190
|
<Button variant="text" size="sm" onClick={submit} title="Submit">
|
|
185
191
|
<SendHorizontal className="size-4" />
|
|
186
192
|
</Button>
|
|
187
|
-
<Button variant="text" size="sm" className="mb-0" onClick={onClose}>
|
|
193
|
+
<Button variant="text" size="sm" className="mb-0 px-1" onClick={onClose}>
|
|
188
194
|
<XIcon className="size-4" />
|
|
189
195
|
</Button>
|
|
190
196
|
</div>
|
|
@@ -193,16 +199,65 @@ export const AddCellWithAI: React.FC<{
|
|
|
193
199
|
return (
|
|
194
200
|
<div className={cn("flex flex-col w-full gap-2 py-2")}>
|
|
195
201
|
{inputComponent}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
<span>
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
<div className="flex flex-row justify-between -mt-1 ml-1 mr-3">
|
|
203
|
+
{!completion && (
|
|
204
|
+
<span className="text-xs text-muted-foreground px-3 flex flex-col gap-1">
|
|
205
|
+
<span>
|
|
206
|
+
You can mention{" "}
|
|
207
|
+
<span className="text-(--cyan-11)">@dataframe</span> or{" "}
|
|
208
|
+
<span className="text-(--cyan-11)">@sql_table</span> to pull
|
|
209
|
+
additional context such as column names.
|
|
210
|
+
</span>
|
|
211
|
+
<span>Code from other cells is automatically included.</span>
|
|
202
212
|
</span>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
)}
|
|
214
|
+
{completion && (
|
|
215
|
+
<>
|
|
216
|
+
<Button
|
|
217
|
+
data-testid="accept-completion-button"
|
|
218
|
+
variant="text"
|
|
219
|
+
size="sm"
|
|
220
|
+
className="mb-0"
|
|
221
|
+
onClick={() => {
|
|
222
|
+
createNewCell({
|
|
223
|
+
cellId: "__end__",
|
|
224
|
+
before: false,
|
|
225
|
+
code:
|
|
226
|
+
language === "python"
|
|
227
|
+
? completion
|
|
228
|
+
: SQLLanguageAdapter.fromQuery(completion),
|
|
229
|
+
});
|
|
230
|
+
setCompletion("");
|
|
231
|
+
onClose();
|
|
232
|
+
}}
|
|
233
|
+
>
|
|
234
|
+
<span className="text-(--grass-11)">Accept</span>
|
|
235
|
+
</Button>
|
|
236
|
+
<Button
|
|
237
|
+
data-testid="decline-completion-button"
|
|
238
|
+
variant="text"
|
|
239
|
+
size="sm"
|
|
240
|
+
className="mb-0 pl-1"
|
|
241
|
+
onClick={() => setCompletion("")}
|
|
242
|
+
>
|
|
243
|
+
<span className="text-(--red-10)">Reject</span>
|
|
244
|
+
</Button>
|
|
245
|
+
</>
|
|
246
|
+
)}
|
|
247
|
+
<div className="ml-auto flex items-center gap-1">
|
|
248
|
+
{languageDropdown}
|
|
249
|
+
<AIModelDropdown
|
|
250
|
+
value={editModel}
|
|
251
|
+
onSelect={(model) => {
|
|
252
|
+
saveModelChange(model, "edit");
|
|
253
|
+
}}
|
|
254
|
+
triggerClassName="h-7 text-xs max-w-64"
|
|
255
|
+
iconSize="small"
|
|
256
|
+
forRole="edit"
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
206
261
|
{completion && (
|
|
207
262
|
<ReactCodeMirror
|
|
208
263
|
value={completion}
|
|
@@ -230,7 +285,6 @@ interface PromptInputProps {
|
|
|
230
285
|
onChange: (value: string) => void;
|
|
231
286
|
onSubmit: (e: KeyboardEvent | undefined, value: string) => void;
|
|
232
287
|
additionalCompletions?: AdditionalCompletions;
|
|
233
|
-
theme: ResolvedTheme;
|
|
234
288
|
maxHeight?: string;
|
|
235
289
|
}
|
|
236
290
|
|
|
@@ -249,12 +303,12 @@ export const PromptInput = ({
|
|
|
249
303
|
onSubmit,
|
|
250
304
|
onClose,
|
|
251
305
|
additionalCompletions,
|
|
252
|
-
theme,
|
|
253
306
|
maxHeight,
|
|
254
307
|
}: PromptInputProps) => {
|
|
255
308
|
const handleSubmit = onSubmit;
|
|
256
309
|
const handleEscape = onClose;
|
|
257
310
|
const store = useStore();
|
|
311
|
+
const { theme } = useTheme();
|
|
258
312
|
|
|
259
313
|
const additionalCompletionsSource: CompletionSource = useEvent(
|
|
260
314
|
(context: CompletionContext) => {
|
|
@@ -150,7 +150,6 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
150
150
|
<SparklesIcon className="text-(--blue-10) shrink-0" size={16} />
|
|
151
151
|
<PromptInput
|
|
152
152
|
inputRef={inputRef}
|
|
153
|
-
theme={theme}
|
|
154
153
|
onClose={() => {
|
|
155
154
|
declineChange();
|
|
156
155
|
setCompletion("");
|
|
@@ -178,20 +177,33 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
178
177
|
Stop
|
|
179
178
|
</Button>
|
|
180
179
|
)}
|
|
181
|
-
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
180
|
+
{completion && (
|
|
181
|
+
<>
|
|
182
|
+
<Button
|
|
183
|
+
data-testid="accept-completion-button"
|
|
184
|
+
variant="text"
|
|
185
|
+
size="xs"
|
|
186
|
+
className="mb-0"
|
|
187
|
+
disabled={isLoading}
|
|
188
|
+
onClick={() => {
|
|
189
|
+
acceptChange(completion);
|
|
190
|
+
setCompletion("");
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
<span className="text-(--grass-11) opacity-100">Accept</span>
|
|
194
|
+
</Button>
|
|
195
|
+
<Button
|
|
196
|
+
data-testid="decline-completion-button"
|
|
197
|
+
variant="text"
|
|
198
|
+
size="xs"
|
|
199
|
+
className="mb-0 pl-1"
|
|
200
|
+
onClick={() => {
|
|
201
|
+
setCompletion("");
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
<span className="text-(--red-10)">Reject</span>
|
|
205
|
+
</Button>
|
|
206
|
+
</>
|
|
195
207
|
)}
|
|
196
208
|
<div className="h-full w-px bg-border mx-2" />
|
|
197
209
|
<Tooltip content="Include code from other cells">
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { Role } from "@marimo-team/llm-info";
|
|
4
|
+
import { useAtom } from "jotai";
|
|
5
|
+
import type { QualifiedModelId } from "@/core/ai/ids/ids";
|
|
6
|
+
import { userConfigAtom } from "@/core/config/config";
|
|
7
|
+
import type { AIModelKey, UserConfig } from "@/core/config/config-schema";
|
|
8
|
+
import { useRequestClient } from "@/core/network/requests";
|
|
9
|
+
|
|
10
|
+
// Extract only the supported roles from the Role type
|
|
11
|
+
type SupportedRole = Extract<Role, "chat" | "autocomplete" | "edit">;
|
|
12
|
+
|
|
13
|
+
const getModelKeyForRole = (forRole: SupportedRole): AIModelKey | null => {
|
|
14
|
+
switch (forRole) {
|
|
15
|
+
case "chat":
|
|
16
|
+
return "chat_model";
|
|
17
|
+
case "autocomplete":
|
|
18
|
+
return "autocomplete_model";
|
|
19
|
+
case "edit":
|
|
20
|
+
return "edit_model";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Hook for saving model and mode changes.
|
|
26
|
+
*/
|
|
27
|
+
export const useModelChange = () => {
|
|
28
|
+
const [userConfig, setUserConfig] = useAtom(userConfigAtom);
|
|
29
|
+
const { saveUserConfig } = useRequestClient();
|
|
30
|
+
|
|
31
|
+
const saveConfig = async (newConfig: UserConfig) => {
|
|
32
|
+
await saveUserConfig({ config: newConfig }).then(() => {
|
|
33
|
+
setUserConfig(newConfig);
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const saveModelChange = async (
|
|
38
|
+
model: QualifiedModelId,
|
|
39
|
+
forRole: SupportedRole,
|
|
40
|
+
) => {
|
|
41
|
+
const modelKey = getModelKeyForRole(forRole);
|
|
42
|
+
|
|
43
|
+
if (!modelKey) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const newConfig: UserConfig = {
|
|
48
|
+
...userConfig,
|
|
49
|
+
ai: {
|
|
50
|
+
...userConfig.ai,
|
|
51
|
+
models: {
|
|
52
|
+
custom_models: userConfig.ai?.models?.custom_models ?? [],
|
|
53
|
+
displayed_models: userConfig.ai?.models?.displayed_models ?? [],
|
|
54
|
+
...userConfig.ai?.models,
|
|
55
|
+
[modelKey]: model,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
saveConfig(newConfig);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const saveModeChange = async (newMode: "ask" | "manual") => {
|
|
64
|
+
const newConfig: UserConfig = {
|
|
65
|
+
...userConfig,
|
|
66
|
+
ai: {
|
|
67
|
+
...userConfig.ai,
|
|
68
|
+
mode: newMode,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
saveConfig(newConfig);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return { saveModelChange, saveModeChange };
|
|
76
|
+
};
|
|
@@ -51,6 +51,20 @@ const AiConfigSchema = z
|
|
|
51
51
|
})
|
|
52
52
|
.passthrough();
|
|
53
53
|
|
|
54
|
+
const AiModelsSchema = z.object({
|
|
55
|
+
chat_model: z.string().nullish(),
|
|
56
|
+
edit_model: z.string().nullish(),
|
|
57
|
+
autocomplete_model: z.string().nullish(),
|
|
58
|
+
displayed_models: z.array(z.string()).default([]),
|
|
59
|
+
custom_models: z.array(z.string()).default([]),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Extract the model key type from the schema
|
|
63
|
+
export type AIModelKey = keyof Pick<
|
|
64
|
+
z.infer<typeof AiModelsSchema>,
|
|
65
|
+
"chat_model" | "edit_model" | "autocomplete_model"
|
|
66
|
+
>;
|
|
67
|
+
|
|
54
68
|
export const UserConfigSchema = z
|
|
55
69
|
.object({
|
|
56
70
|
completion: z
|
|
@@ -158,18 +172,10 @@ export const UserConfigSchema = z
|
|
|
158
172
|
aws_secret_access_key: z.string().optional(),
|
|
159
173
|
})
|
|
160
174
|
.optional(),
|
|
161
|
-
models:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
autocomplete_model: z.string().nullish(),
|
|
166
|
-
displayed_models: z.array(z.string()).default([]),
|
|
167
|
-
custom_models: z.array(z.string()).default([]),
|
|
168
|
-
})
|
|
169
|
-
.default({
|
|
170
|
-
displayed_models: [],
|
|
171
|
-
custom_models: [],
|
|
172
|
-
}),
|
|
175
|
+
models: AiModelsSchema.default({
|
|
176
|
+
displayed_models: [],
|
|
177
|
+
custom_models: [],
|
|
178
|
+
}),
|
|
173
179
|
})
|
|
174
180
|
.passthrough()
|
|
175
181
|
.default({}),
|