@marimo-team/frontend 0.15.0 → 0.15.1-dev12

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.
Files changed (108) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-Bl5fR_87.js → ConnectedDataExplorerComponent-BvA88co5.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-CRfr-CiC.js → ImageComparisonComponent-DMstqRhm.js} +1 -1
  3. package/dist/assets/{VegaLite-Bo_A9ZZm.js → VegaLite-Mesl0wXW.js} +1 -1
  4. package/dist/assets/{_baseEach-CIMlsWNn.js → _baseEach-4LREA2xJ.js} +1 -1
  5. package/dist/assets/_baseMap-C6LNFWWn.js +1 -0
  6. package/dist/assets/{_baseUniq-CGK6su7v.js → _baseUniq-C4wq7Gz_.js} +1 -1
  7. package/dist/assets/{_createAggregator--z161kAx.js → _createAggregator-Di1sij95.js} +1 -1
  8. package/dist/assets/{any-language-editor-hSTaStg9.js → any-language-editor-CNdroSlJ.js} +1 -1
  9. package/dist/assets/{architectureDiagram-KFL7JDKH-CqrWvX5_.js → architectureDiagram-KFL7JDKH-BxXZfRHj.js} +1 -1
  10. package/dist/assets/{blockDiagram-ZYB65J3Q-CRgdhCYU.js → blockDiagram-ZYB65J3Q-8mg_ND0j.js} +1 -1
  11. package/dist/assets/{c4Diagram-AAMF2YG6-DUHfB-bk.js → c4Diagram-AAMF2YG6-BH1zFYgt.js} +1 -1
  12. package/dist/assets/channel-B3CCoyYy.js +1 -0
  13. package/dist/assets/{chunk-ANTBXLJU-ZgvmG8Pb.js → chunk-ANTBXLJU-wUIE1C6t.js} +1 -1
  14. package/dist/assets/{chunk-FHKO5MBM-B-EzrMTM.js → chunk-FHKO5MBM-BnGig1B_.js} +1 -1
  15. package/dist/assets/{chunk-GLLZNHP4-DRBUPzV6.js → chunk-GLLZNHP4-BeuHRwGr.js} +1 -1
  16. package/dist/assets/{chunk-JBRWN2VN-vkbXs27X.js → chunk-JBRWN2VN-BEuqVuwn.js} +1 -1
  17. package/dist/assets/{chunk-LXBSTHXV-itspTgVu.js → chunk-LXBSTHXV-DIaccDrz.js} +1 -1
  18. package/dist/assets/{chunk-NRVI72HA-gO-7Uy2L.js → chunk-NRVI72HA-I0EqNhLo.js} +1 -1
  19. package/dist/assets/{chunk-OMD6QJNC-CwDxph2q.js → chunk-OMD6QJNC-DBDKfLns.js} +1 -1
  20. package/dist/assets/{chunk-WVR4S24B-DkjWv9uk.js → chunk-WVR4S24B-D9vksrD4.js} +1 -1
  21. package/dist/assets/{circle-play-BURxVoHD.js → circle-play-Xdbicaqg.js} +1 -1
  22. package/dist/assets/classDiagram-3BZAVTQC-CLXskjCP.js +1 -0
  23. package/dist/assets/classDiagram-v2-QTMF73CY-CLXskjCP.js +1 -0
  24. package/dist/assets/clone-BCc-guNO.js +1 -0
  25. package/dist/assets/{compile-CQlZ79Z5.js → compile-D-SKgFzl.js} +1 -1
  26. package/dist/assets/{dagre-2BBEFEWP-DEiqD84H.js → dagre-2BBEFEWP-Dj4VJDXi.js} +1 -1
  27. package/dist/assets/{data-grid-overlay-editor-CS6yXy6C.js → data-grid-overlay-editor-BHUr5Xhc.js} +1 -1
  28. package/dist/assets/{diagram-4IRLE6MV-ommhV7Ge.js → diagram-4IRLE6MV-9wayoyRN.js} +1 -1
  29. package/dist/assets/{diagram-GUPCWM2R-D09B2kyR.js → diagram-GUPCWM2R-5NhVlGLr.js} +1 -1
  30. package/dist/assets/{diagram-RP2FKANI-D1AZYHbD.js → diagram-RP2FKANI-CNBOj37W.js} +1 -1
  31. package/dist/assets/{edit-page-DhSV7e13.js → edit-page-D8Xj7wsn.js} +16 -16
  32. package/dist/assets/{erDiagram-HZWUO2LU-Civ5tj0J.js → erDiagram-HZWUO2LU-KEjz95KS.js} +1 -1
  33. package/dist/assets/{flowDiagram-THRYKUMA--HeKh8ql.js → flowDiagram-THRYKUMA-9PtbKpm0.js} +1 -1
  34. package/dist/assets/{ganttDiagram-WV7ZQ7D5-YEcfQSSf.js → ganttDiagram-WV7ZQ7D5-CFXRYmNt.js} +1 -1
  35. package/dist/assets/{gitGraphDiagram-OJR772UL-DhkURReZ.js → gitGraphDiagram-OJR772UL-hFrAcG26.js} +1 -1
  36. package/dist/assets/{glide-data-editor-CudlNmdp.js → glide-data-editor-HLSiEHg4.js} +4 -4
  37. package/dist/assets/{graph-CQvdQbxU.js → graph-iTnwmAye.js} +1 -1
  38. package/dist/assets/{home-page-_l6RVaRB.js → home-page-DzRg-dzP.js} +1 -1
  39. package/dist/assets/{index-CAtmZMbK.js → index-B3l0i9L2.js} +1 -1
  40. package/dist/assets/{index-DZJOZ8C0.js → index-BA1eBtgJ.js} +1 -1
  41. package/dist/assets/{index-B9dXZoY1.js → index-BMCpi_hV.js} +1 -1
  42. package/dist/assets/index-BRPMSgmu.css +1 -0
  43. package/dist/assets/{index-DbsrZX7Q.js → index-Bj_MIGqG.js} +1 -1
  44. package/dist/assets/{index-QoqjKBP_.js → index-BlHS2xQv.js} +1 -1
  45. package/dist/assets/{index-DvySElQ5.js → index-BzQQnlIk.js} +1 -1
  46. package/dist/assets/{index-CvLIL6JV.js → index-BzZRHBMs.js} +1 -1
  47. package/dist/assets/{index-DMtmziVF.js → index-CEoUSuyW.js} +1 -1
  48. package/dist/assets/{index-6WfEFtf2.js → index-CJRYE40I.js} +1 -1
  49. package/dist/assets/{index-nyTtvXGx.js → index-CQ7rf4K7.js} +1 -1
  50. package/dist/assets/{index-C6CvZyWd.js → index-Cl5g0z7j.js} +1 -1
  51. package/dist/assets/{index-CbNkOFUU.js → index-Cni7Zj6s.js} +1 -1
  52. package/dist/assets/{index-CqRTe3S2.js → index-CrltF-5r.js} +1 -1
  53. package/dist/assets/{index-BCdqiRjH.js → index-CswmrBaY.js} +1 -1
  54. package/dist/assets/{index-Do3KUHEe.js → index-D1rqBcHl.js} +1 -1
  55. package/dist/assets/{index-DhRZE5tA.js → index-DApZsb--.js} +1 -1
  56. package/dist/assets/{index-3z3sI3MY.js → index-DNVhZM1y.js} +1 -1
  57. package/dist/assets/{index-BvRQ66y_.js → index-DWJwqJmD.js} +1 -1
  58. package/dist/assets/{index-D-8oCPO1.js → index-DmunXcbP.js} +1 -1
  59. package/dist/assets/{index-D4bXoNM3.js → index-WZpn7zIz.js} +157 -157
  60. package/dist/assets/infoDiagram-6WOFNB3A-BvitCnlF.js +2 -0
  61. package/dist/assets/{journeyDiagram-FFXJYRFH-CC3RaMZl.js → journeyDiagram-FFXJYRFH-BEiF2rN9.js} +1 -1
  62. package/dist/assets/{kanban-definition-KOZQBZVT-1Z4QU83I.js → kanban-definition-KOZQBZVT-BZZVRqX0.js} +1 -1
  63. package/dist/assets/{layout-DuQ9W6pS.js → layout-2hUFrdoK.js} +1 -1
  64. package/dist/assets/{linear-BsI0Wmp7.js → linear-DM7ygpKu.js} +1 -1
  65. package/dist/assets/{links-D_cg4TpC.js → links-BJFGdYRd.js} +8 -8
  66. package/dist/assets/{mermaid-D59lkToe.js → mermaid-CxGPSzDz.js} +4 -4
  67. package/dist/assets/{min-DrLfF3uL.js → min-DS87IVOa.js} +1 -1
  68. package/dist/assets/{mindmap-definition-LNHGMQRG-ePnX66wd.js → mindmap-definition-LNHGMQRG-C7yfXmRD.js} +1 -1
  69. package/dist/assets/{number-overlay-editor-CPxl07sM.js → number-overlay-editor-qOf48nx-.js} +1 -1
  70. package/dist/assets/{pieDiagram-DBDJKBY4-BWYaH6Hi.js → pieDiagram-DBDJKBY4-CRluUHH0.js} +1 -1
  71. package/dist/assets/{quadrantDiagram-YPSRARAO-BwuEJiK-.js → quadrantDiagram-YPSRARAO-FZO2_Kyx.js} +1 -1
  72. package/dist/assets/{react-plotly-eq8YBLKm.js → react-plotly-BV6Vu1ZC.js} +1 -1
  73. package/dist/assets/{requirementDiagram-EGVEC5DT-B0dO70r7.js → requirementDiagram-EGVEC5DT-CFEV56bq.js} +1 -1
  74. package/dist/assets/{run-page-DP_FNoql.js → run-page-BiiAHZuC.js} +1 -1
  75. package/dist/assets/{sankeyDiagram-HRAUVNP4-DlbnyCg4.js → sankeyDiagram-HRAUVNP4-cf6xzlF-.js} +1 -1
  76. package/dist/assets/{sequenceDiagram-WFGC7UMF-BfhFFLfz.js → sequenceDiagram-WFGC7UMF-CEIC5fba.js} +1 -1
  77. package/dist/assets/{slides-component-Bso9rHdK.js → slides-component-BXl359nW.js} +1 -1
  78. package/dist/assets/{sortBy-utaS96Bi.js → sortBy-Dv7jjKnm.js} +1 -1
  79. package/dist/assets/{stateDiagram-UUKSUZ4H-ByVZAeGw.js → stateDiagram-UUKSUZ4H-DLvG9b91.js} +1 -1
  80. package/dist/assets/stateDiagram-v2-EYPG3UTE-Z_xESMay.js +1 -0
  81. package/dist/assets/{storage-DrvlAZZd.js → storage-B47VVzw1.js} +3 -3
  82. package/dist/assets/{terminal-CuPjZ1vE.js → terminal-2p_4lGQJ.js} +1 -1
  83. package/dist/assets/{time-1P0zCPa4.js → time-OI-T4MiC.js} +1 -1
  84. package/dist/assets/{timeline-definition-3HZDQTIS-BLUL1ZU4.js → timeline-definition-3HZDQTIS-Da1Ro9aI.js} +1 -1
  85. package/dist/assets/{tracing-BpM0fbsC.js → tracing-CRxB69vv.js} +2 -2
  86. package/dist/assets/{trash-D5wptdZI.js → trash-DM7Eop-E.js} +1 -1
  87. package/dist/assets/{treemap-75Q7IDZK-Bh5pVSmo.js → treemap-75Q7IDZK-DaZTYidc.js} +1 -1
  88. package/dist/assets/{vega-component-tH6bw1Lr.js → vega-component-D1rpMJYI.js} +1 -1
  89. package/dist/assets/{xychartDiagram-FDP5SA34-qY_Wo73t.js → xychartDiagram-FDP5SA34-COsO2s8u.js} +1 -1
  90. package/dist/index.html +2 -2
  91. package/package.json +1 -1
  92. package/src/components/ai/ai-model-dropdown.tsx +165 -41
  93. package/src/components/datasources/datasources.tsx +19 -2
  94. package/src/components/ui/dropdown-menu.tsx +11 -10
  95. package/src/core/ai/__tests__/model-registry.test.ts +12 -12
  96. package/src/core/ai/model-registry.ts +19 -25
  97. package/src/core/config/config.ts +4 -0
  98. package/src/core/config/feature-flag.tsx +1 -5
  99. package/src/core/datasets/engines.ts +1 -0
  100. package/src/css/katex.min.css +1 -0
  101. package/dist/assets/_baseMap-1GEe_WcR.js +0 -1
  102. package/dist/assets/channel-D31q3TW_.js +0 -1
  103. package/dist/assets/classDiagram-3BZAVTQC-0O2jYKfn.js +0 -1
  104. package/dist/assets/classDiagram-v2-QTMF73CY-0O2jYKfn.js +0 -1
  105. package/dist/assets/clone-DFaYgbfI.js +0 -1
  106. package/dist/assets/index-CrH6PPjG.css +0 -1
  107. package/dist/assets/infoDiagram-6WOFNB3A-B7BrcI_a.js +0 -2
  108. package/dist/assets/stateDiagram-v2-EYPG3UTE-C6PYJGwL.js +0 -1
@@ -1,8 +1,15 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
3
  import type { Role } from "@marimo-team/llm-info";
4
+ import { useAtomValue } from "jotai";
4
5
  import { capitalize } from "lodash-es";
5
- import { ChevronDownIcon, CircleHelpIcon } from "lucide-react";
6
+ import {
7
+ BotIcon,
8
+ BrainIcon,
9
+ ChevronDownIcon,
10
+ CircleHelpIcon,
11
+ } from "lucide-react";
12
+ import React from "react";
6
13
  import {
7
14
  AiModelId,
8
15
  isKnownAIProvider,
@@ -10,7 +17,7 @@ import {
10
17
  type QualifiedModelId,
11
18
  } from "@/core/ai/ids/ids";
12
19
  import { type AiModel, AiModelRegistry } from "@/core/ai/model-registry";
13
- import { useResolvedMarimoConfig } from "@/core/config/config";
20
+ import { aiAtom, completionAtom } from "@/core/config/config";
14
21
  import {
15
22
  DropdownMenu,
16
23
  DropdownMenuContent,
@@ -22,6 +29,7 @@ import {
22
29
  DropdownMenuSubTrigger,
23
30
  DropdownMenuTrigger,
24
31
  } from "../ui/dropdown-menu";
32
+ import { Tooltip } from "../ui/tooltip";
25
33
  import { AiProviderIcon } from "./ai-provider-icon";
26
34
 
27
35
  interface AIModelDropdownProps {
@@ -46,36 +54,37 @@ export const AIModelDropdown = ({
46
54
  forRole,
47
55
  }: AIModelDropdownProps) => {
48
56
  const currentValue = value ? AiModelId.parse(value) : undefined;
57
+ const [isOpen, setIsOpen] = React.useState(false);
49
58
 
50
- const [marimoConfig] = useResolvedMarimoConfig();
51
- const configModels = marimoConfig.ai?.models;
59
+ const ai = useAtomValue(aiAtom);
60
+ const completion = useAtomValue(completionAtom);
52
61
 
53
62
  // Only include autocompleteModel if copilot is set to "custom"
54
63
  const autocompleteModel =
55
- marimoConfig.completion.copilot === "custom"
56
- ? configModels?.autocomplete_model
64
+ completion.copilot === "custom"
65
+ ? ai?.models?.autocomplete_model
57
66
  : undefined;
58
67
 
59
68
  const aiModelRegistry = AiModelRegistry.create({
60
69
  // We add all the custom models and the models used in the editor.
61
70
  // If they among the known models, they won't overwrite them.
62
71
  customModels: [
63
- ...(configModels?.custom_models ?? []),
64
- configModels?.chat_model,
72
+ ...(ai?.models?.custom_models ?? []),
73
+ ai?.models?.chat_model,
65
74
  autocompleteModel,
66
- configModels?.edit_model,
75
+ ai?.models?.edit_model,
67
76
  ].filter(Boolean),
68
- displayedModels: configModels?.displayed_models,
77
+ displayedModels: ai?.models?.displayed_models,
69
78
  });
70
79
  const modelsByProvider = aiModelRegistry.getGroupedModelsByProvider();
71
80
 
72
81
  const activeModel =
73
82
  forRole === "autocomplete"
74
- ? configModels?.autocomplete_model
83
+ ? ai?.models?.autocomplete_model
75
84
  : forRole === "chat"
76
- ? configModels?.chat_model
85
+ ? ai?.models?.chat_model
77
86
  : forRole === "edit"
78
- ? configModels?.edit_model
87
+ ? ai?.models?.edit_model
79
88
  : undefined;
80
89
 
81
90
  const iconSizeClass = iconSize === "medium" ? "h-4 w-4" : "h-3 w-3";
@@ -95,19 +104,26 @@ export const AIModelDropdown = ({
95
104
  </div>
96
105
 
97
106
  <div className="ml-auto flex gap-1">
98
- <span
99
- key={role}
100
- className={`text-xs px-1.5 py-0.5 rounded font-medium ${getTagColour(role)}`}
101
- >
102
- {role}
103
- </span>
107
+ <Tooltip content={getTagTooltip(role)}>
108
+ <span
109
+ key={role}
110
+ className={`text-xs px-1.5 py-0.5 rounded font-medium ${getTagColour(role)}`}
111
+ >
112
+ {role}
113
+ </span>
114
+ </Tooltip>
104
115
  </div>
105
116
  </div>
106
117
  );
107
118
  };
108
119
 
120
+ const handleSelect = (modelId: QualifiedModelId) => {
121
+ onSelect(modelId);
122
+ setIsOpen(false);
123
+ };
124
+
109
125
  return (
110
- <DropdownMenu>
126
+ <DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
111
127
  <DropdownMenuTrigger
112
128
  className={`flex items-center justify-between px-2 py-0.5 border rounded-md
113
129
  hover:bg-accent hover:text-accent-foreground ${triggerClassName}`}
@@ -144,7 +160,7 @@ export const AIModelDropdown = ({
144
160
  <ProviderDropdownContent
145
161
  key={provider}
146
162
  provider={provider}
147
- onSelect={onSelect}
163
+ onSelect={handleSelect}
148
164
  models={models}
149
165
  iconSizeClass={iconSizeClass}
150
166
  />
@@ -177,13 +193,11 @@ const ProviderDropdownContent = ({
177
193
  provider,
178
194
  onSelect,
179
195
  models,
180
- customModelIcon,
181
196
  iconSizeClass,
182
197
  }: {
183
198
  provider: ProviderId;
184
199
  onSelect: (modelId: QualifiedModelId) => void;
185
200
  models: AiModel[];
186
- customModelIcon?: React.ReactNode;
187
201
  iconSizeClass: string;
188
202
  }) => {
189
203
  const iconProvider = isKnownAIProvider(provider)
@@ -207,7 +221,8 @@ const ProviderDropdownContent = ({
207
221
  <DropdownMenuPortal>
208
222
  <DropdownMenuSubContent
209
223
  className="max-h-[40vh] overflow-y-auto"
210
- alignOffset={-90}
224
+ alignOffset={maybeProviderInfo ? -90 : 0}
225
+ sideOffset={5}
211
226
  >
212
227
  {maybeProviderInfo && (
213
228
  <>
@@ -236,22 +251,21 @@ const ProviderDropdownContent = ({
236
251
  const qualifiedModelId =
237
252
  `${provider}/${model.model}` as QualifiedModelId;
238
253
  return (
239
- <DropdownMenuItem
240
- key={qualifiedModelId}
241
- className="flex items-center gap-2"
242
- onSelect={() => {
243
- onSelect(qualifiedModelId);
244
- }}
245
- >
246
- <AiProviderIcon provider={iconProvider} className="h-4 w-4" />
247
- <div className="pl-1 flex flex-col">
248
- <span>{model.name}</span>
249
- <span className="text-xs text-muted-foreground">
250
- {model.model}
251
- </span>
252
- </div>
253
- {model.custom && customModelIcon}
254
- </DropdownMenuItem>
254
+ <DropdownMenuSub key={qualifiedModelId}>
255
+ <DropdownMenuSubTrigger showChevron={false} className="py-2">
256
+ <div
257
+ className="flex items-center gap-2 w-full cursor-pointer"
258
+ onClick={() => {
259
+ onSelect(qualifiedModelId);
260
+ }}
261
+ >
262
+ <AiModelDropdownItem model={model} provider={provider} />
263
+ </div>
264
+ </DropdownMenuSubTrigger>
265
+ <DropdownMenuSubContent className="p-4 w-80">
266
+ <AiModelInfoDisplay model={model} provider={provider} />
267
+ </DropdownMenuSubContent>
268
+ </DropdownMenuSub>
255
269
  );
256
270
  })}
257
271
  </DropdownMenuSubContent>
@@ -260,6 +274,99 @@ const ProviderDropdownContent = ({
260
274
  );
261
275
  };
262
276
 
277
+ const AiModelDropdownItem = ({
278
+ model,
279
+ provider,
280
+ }: {
281
+ model: AiModel;
282
+ provider: ProviderId;
283
+ }) => {
284
+ const iconProvider = isKnownAIProvider(provider)
285
+ ? provider
286
+ : "openai-compatible";
287
+
288
+ return (
289
+ <>
290
+ <AiProviderIcon provider={iconProvider} className="h-4 w-4" />
291
+ <div className="flex flex-row w-full items-center">
292
+ <span>{model.name}</span>
293
+ <div className="ml-auto">
294
+ {model.thinking && (
295
+ <Tooltip content="Reasoning model">
296
+ <BrainIcon
297
+ className={`h-5 w-5 rounded-md p-1 ${getTagColour("thinking")}`}
298
+ />
299
+ </Tooltip>
300
+ )}
301
+ </div>
302
+ </div>
303
+ {model.custom && (
304
+ <Tooltip content="Custom model">
305
+ <BotIcon className="h-5 w-5" />
306
+ </Tooltip>
307
+ )}
308
+ </>
309
+ );
310
+ };
311
+
312
+ const AiModelInfoDisplay = ({
313
+ model,
314
+ provider,
315
+ }: {
316
+ model: AiModel;
317
+ provider: ProviderId;
318
+ }) => {
319
+ return (
320
+ <div className="space-y-3">
321
+ <div>
322
+ <h4 className="font-semibold text-base text-foreground">
323
+ {model.name}
324
+ </h4>
325
+ <p className="text-xs text-muted-foreground font-mono">{model.model}</p>
326
+ </div>
327
+
328
+ <p className="text-sm text-muted-foreground leading-relaxed">
329
+ {model.description}
330
+ </p>
331
+
332
+ {model.roles.length > 0 && (
333
+ <div>
334
+ <p className="text-xs font-medium text-muted-foreground mb-2">
335
+ Capabilities:
336
+ </p>
337
+ <div className="flex flex-wrap gap-1">
338
+ {model.roles.map((role) => (
339
+ <span
340
+ key={role}
341
+ className={`px-2 py-1 text-xs rounded-md font-medium ${getTagColour(role)}`}
342
+ title={getTagTooltip(role)}
343
+ >
344
+ {role}
345
+ </span>
346
+ ))}
347
+ </div>
348
+ </div>
349
+ )}
350
+
351
+ {model.thinking && (
352
+ <div className="flex items-center gap-2">
353
+ <div className="w-2 h-2 bg-purple-500 rounded-full animate-pulse" />
354
+ <span className="text-xs text-muted-foreground">
355
+ Supports thinking mode
356
+ </span>
357
+ </div>
358
+ )}
359
+
360
+ <div className="flex items-center gap-2 pt-2 border-t border-border">
361
+ <AiProviderIcon provider={provider} className="h-4 w-4" />
362
+ <span className="text-xs text-muted-foreground">
363
+ {getProviderLabel(provider)}
364
+ </span>
365
+ </div>
366
+ </div>
367
+ );
368
+ };
369
+
263
370
  function getProviderLabel(provider: ProviderId): string {
264
371
  const providerInfo = AiModelRegistry.getProviderInfo(provider);
265
372
  if (providerInfo) {
@@ -268,7 +375,7 @@ function getProviderLabel(provider: ProviderId): string {
268
375
  return capitalize(provider);
269
376
  }
270
377
 
271
- function getTagColour(role: Role): string {
378
+ function getTagColour(role: Role | "thinking"): string {
272
379
  switch (role) {
273
380
  case "chat":
274
381
  return "bg-[var(--purple-3)] text-[var(--purple-11)]";
@@ -276,6 +383,23 @@ function getTagColour(role: Role): string {
276
383
  return "bg-[var(--green-3)] text-[var(--green-11)]";
277
384
  case "edit":
278
385
  return "bg-[var(--blue-3)] text-[var(--blue-11)]";
386
+ case "thinking":
387
+ return "bg-[var(--purple-4)] text-[var(--purple-12)]";
279
388
  }
280
389
  return "bg-[var(--mauve-3)] text-[var(--mauve-11)]";
281
390
  }
391
+
392
+ function getTagTooltip(role: Role): string {
393
+ switch (role) {
394
+ case "chat":
395
+ return "Current model used for chat conversations";
396
+ case "autocomplete":
397
+ return "Current model used for autocomplete autocomplete";
398
+ case "edit":
399
+ return "Current model used for code edits";
400
+ case "rerank":
401
+ return "Current model used for reranking completions";
402
+ case "embed":
403
+ return "Current model used for embedding";
404
+ }
405
+ }
@@ -30,7 +30,11 @@ import {
30
30
  type SQLTableContext,
31
31
  useDataSourceActions,
32
32
  } from "@/core/datasets/data-source-connections";
33
- import { DUCKDB_ENGINE, INTERNAL_SQL_ENGINES } from "@/core/datasets/engines";
33
+ import {
34
+ DEFAULT_DUCKDB_DATABASE,
35
+ DUCKDB_ENGINE,
36
+ INTERNAL_SQL_ENGINES,
37
+ } from "@/core/datasets/engines";
34
38
  import {
35
39
  PreviewSQLTable,
36
40
  PreviewSQLTableList,
@@ -101,9 +105,22 @@ const connectionsAtom = atom((get) => {
101
105
  const dataConnections = new Map(get(dataConnectionsMapAtom));
102
106
 
103
107
  // Filter out the internal engines if it has no databases
108
+ // Or if it has only the in-memory database and no schemas
104
109
  for (const engine of INTERNAL_SQL_ENGINES) {
105
110
  const connection = dataConnections.get(engine);
106
- if (connection && connection.databases.length === 0) {
111
+ if (!connection) {
112
+ continue;
113
+ }
114
+
115
+ if (connection.databases.length === 0) {
116
+ dataConnections.delete(engine);
117
+ }
118
+
119
+ if (
120
+ connection.databases.length === 1 &&
121
+ connection.databases[0].name === DEFAULT_DUCKDB_DATABASE &&
122
+ connection.databases[0].schemas.length === 0
123
+ ) {
107
124
  dataConnections.delete(engine);
108
125
  }
109
126
  }
@@ -25,25 +25,26 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
25
25
  const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
26
26
 
27
27
  const DropdownMenuSubTrigger = React.forwardRef<
28
- React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
28
+ React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>,
29
29
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
30
30
  inset?: boolean;
31
+ showChevron?: boolean;
31
32
  }
32
- >(({ className, inset, children, ...props }, ref) => (
33
+ >(({ className, inset, showChevron = true, children, ...props }, ref) => (
33
34
  <DropdownMenuPrimitive.SubTrigger
34
35
  ref={ref}
35
36
  className={menuSubTriggerVariants({ className, inset })}
36
37
  {...props}
37
38
  >
38
39
  {children}
39
- <ChevronRight className="ml-auto h-4 w-4" />
40
+ {showChevron && <ChevronRight className="ml-auto h-4 w-4" />}
40
41
  </DropdownMenuPrimitive.SubTrigger>
41
42
  ));
42
43
  DropdownMenuSubTrigger.displayName =
43
44
  DropdownMenuPrimitive.SubTrigger.displayName;
44
45
 
45
46
  const DropdownMenuSubContent = React.forwardRef<
46
- React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
47
+ React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>,
47
48
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
48
49
  >(({ className, ...props }, ref) => (
49
50
  <DropdownMenuPrimitive.SubContent
@@ -60,7 +61,7 @@ DropdownMenuSubContent.displayName =
60
61
  DropdownMenuPrimitive.SubContent.displayName;
61
62
 
62
63
  const DropdownMenuContent = React.forwardRef<
63
- React.ElementRef<typeof DropdownMenuPrimitive.Content>,
64
+ React.ComponentRef<typeof DropdownMenuPrimitive.Content>,
64
65
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & {
65
66
  scrollable?: boolean;
66
67
  }
@@ -90,7 +91,7 @@ const DropdownMenuContent = React.forwardRef<
90
91
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
91
92
 
92
93
  const DropdownMenuItem = React.forwardRef<
93
- React.ElementRef<typeof DropdownMenuPrimitive.Item>,
94
+ React.ComponentRef<typeof DropdownMenuPrimitive.Item>,
94
95
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> &
95
96
  VariantProps<typeof menuItemVariants>
96
97
  >(({ className, inset, variant, ...props }, ref) => (
@@ -103,7 +104,7 @@ const DropdownMenuItem = React.forwardRef<
103
104
  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
104
105
 
105
106
  const DropdownMenuCheckboxItem = React.forwardRef<
106
- React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
107
+ React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem>,
107
108
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
108
109
  >(({ className, children, checked, ...props }, ref) => (
109
110
  <DropdownMenuPrimitive.CheckboxItem
@@ -124,7 +125,7 @@ DropdownMenuCheckboxItem.displayName =
124
125
  DropdownMenuPrimitive.CheckboxItem.displayName;
125
126
 
126
127
  const DropdownMenuRadioItem = React.forwardRef<
127
- React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
128
+ React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>,
128
129
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
129
130
  >(({ className, children, ...props }, ref) => (
130
131
  <DropdownMenuPrimitive.RadioItem
@@ -143,7 +144,7 @@ const DropdownMenuRadioItem = React.forwardRef<
143
144
  DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
144
145
 
145
146
  const DropdownMenuLabel = React.forwardRef<
146
- React.ElementRef<typeof DropdownMenuPrimitive.Label>,
147
+ React.ComponentRef<typeof DropdownMenuPrimitive.Label>,
147
148
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
148
149
  inset?: boolean;
149
150
  }
@@ -157,7 +158,7 @@ const DropdownMenuLabel = React.forwardRef<
157
158
  DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
158
159
 
159
160
  const DropdownMenuSeparator = React.forwardRef<
160
- React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
161
+ React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,
161
162
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
162
163
  >(({ className, ...props }, ref) => (
163
164
  <DropdownMenuPrimitive.Separator
@@ -66,6 +66,12 @@ describe("AiModelRegistry", () => {
66
66
 
67
67
  expect(registry.getCustomModels()).toEqual(new Set(customModels));
68
68
  expect(registry.getDisplayedModels()).toEqual(new Set());
69
+
70
+ // Expect custom models to appear first
71
+ expect(registry.getModelsByProvider("openai")[0].name).toBe("custom-gpt");
72
+ expect(registry.getModelsByProvider("anthropic")[0].name).toBe(
73
+ "custom-claude",
74
+ );
69
75
  });
70
76
 
71
77
  it("should create registry with displayed models", () => {
@@ -114,7 +120,7 @@ describe("AiModelRegistry", () => {
114
120
 
115
121
  const customModel = openaiModels.find((model) => model.custom);
116
122
  expect(customModel).toBeDefined();
117
- expect(customModel?.name).toBe("openai/custom-gpt");
123
+ expect(customModel?.name).toBe("custom-gpt");
118
124
  expect(customModel?.model).toBe("custom-gpt");
119
125
  expect(customModel?.description).toBe("Custom model");
120
126
  expect(customModel?.providers).toEqual(["openai"]);
@@ -142,16 +148,10 @@ describe("AiModelRegistry", () => {
142
148
  const openaiModels = registry.getModelsByProvider("openai");
143
149
  const anthropicModels = registry.getModelsByProvider("anthropic");
144
150
 
145
- expect(
146
- openaiModels.some(
147
- (model) => model.custom && model.model === "custom-gpt",
148
- ),
149
- ).toBe(true);
150
- expect(
151
- anthropicModels.some(
152
- (model) => model.custom && model.model === "custom-claude",
153
- ),
154
- ).toBe(false);
151
+ expect(openaiModels.length).toBe(1);
152
+ expect(openaiModels[0].name).toBe("custom-gpt");
153
+
154
+ expect(anthropicModels.length).toBe(0);
155
155
  });
156
156
  });
157
157
 
@@ -328,7 +328,7 @@ describe("AiModelRegistry", () => {
328
328
  "custom": true,
329
329
  "description": "Custom model",
330
330
  "model": "custom-gpt",
331
- "name": "openai/custom-gpt",
331
+ "name": "custom-gpt",
332
332
  "providers": [
333
333
  "openai",
334
334
  ],
@@ -82,43 +82,22 @@ export class AiModelRegistry {
82
82
 
83
83
  /**
84
84
  * Builds the maps of models by provider and custom models.
85
- * Custom models are added first as they are specified by the user, so we want to surface them first.
86
85
  */
87
86
  private buildMaps() {
88
87
  const displayedModels = this.displayedModels;
89
88
  const hasDisplayedModels = displayedModels.size > 0;
90
89
  const knownModelMap = getKnownModelMap();
91
- let modelsMap = new Map<QualifiedModelId, AiModel>();
90
+ const customModelsMap = new Map<QualifiedModelId, AiModel>();
92
91
 
93
- // Start with known models
94
- if (hasDisplayedModels) {
95
- for (const model of displayedModels) {
96
- if (knownModelMap.has(model)) {
97
- const knownModel = knownModelMap.get(model);
98
- if (knownModel) {
99
- modelsMap.set(model, knownModel);
100
- }
101
- }
102
- }
103
- } else {
104
- modelsMap = new Map(knownModelMap);
105
- }
92
+ let modelsMap = new Map<QualifiedModelId, AiModel>();
106
93
 
107
- // Add custom models
108
94
  for (const model of this.customModels) {
109
- // Skip custom models that are not displayed
110
95
  if (hasDisplayedModels && !displayedModels.has(model)) {
111
96
  continue;
112
97
  }
113
-
114
- // If custom model conflicts with a known model, skip it
115
- if (modelsMap.has(model)) {
116
- continue;
117
- }
118
-
119
98
  const modelId = AiModelId.parse(model);
120
99
  const modelInfo: AiModel = {
121
- name: modelId.id,
100
+ name: modelId.shortModelId,
122
101
  model: modelId.shortModelId,
123
102
  description: "Custom model",
124
103
  providers: [modelId.providerId],
@@ -126,9 +105,24 @@ export class AiModelRegistry {
126
105
  thinking: false,
127
106
  custom: true,
128
107
  };
129
- modelsMap.set(model, modelInfo);
108
+ customModelsMap.set(model, modelInfo);
130
109
  }
131
110
 
111
+ // Add known models
112
+ if (hasDisplayedModels) {
113
+ for (const model of displayedModels) {
114
+ const modelInfo = knownModelMap.get(model);
115
+ if (modelInfo) {
116
+ modelsMap.set(model, modelInfo);
117
+ }
118
+ }
119
+ } else {
120
+ modelsMap = new Map(knownModelMap);
121
+ }
122
+
123
+ // Set custom models first, then known models
124
+ modelsMap = new Map([...customModelsMap, ...modelsMap]);
125
+
132
126
  // Group by provider
133
127
  for (const [qualifiedModelId, model] of modelsMap.entries()) {
134
128
  const modelId = AiModelId.parse(qualifiedModelId);
@@ -48,6 +48,10 @@ export const aiAtom = atom((get) => {
48
48
  return get(resolvedMarimoConfigAtom).ai;
49
49
  });
50
50
 
51
+ export const completionAtom = atom((get) => {
52
+ return get(resolvedMarimoConfigAtom).completion;
53
+ });
54
+
51
55
  export const keymapPresetAtom = atom((get) => {
52
56
  return get(resolvedMarimoConfigAtom).keymap.preset;
53
57
  });
@@ -1,6 +1,5 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import type { UserConfig } from "vite";
4
3
  import { repl } from "@/utils/repl";
5
4
  import { getRequestClient } from "../network/requests";
6
5
  import { getResolvedMarimoConfig } from "./config";
@@ -37,10 +36,7 @@ export function getFeatureFlag<T extends keyof ExperimentalFeatures>(
37
36
  );
38
37
  }
39
38
 
40
- function setFeatureFlag(
41
- feature: keyof UserConfig["experimental"],
42
- value: boolean,
43
- ) {
39
+ function setFeatureFlag(feature: keyof ExperimentalFeatures, value: boolean) {
44
40
  const userConfig = getResolvedMarimoConfig();
45
41
  userConfig.experimental = userConfig.experimental ?? {};
46
42
  userConfig.experimental[feature] = value;
@@ -8,3 +8,4 @@ export type ConnectionName = TypedString<"ConnectionName">;
8
8
  // Keep this in sync with the backend name
9
9
  export const DUCKDB_ENGINE = "__marimo_duckdb" as ConnectionName;
10
10
  export const INTERNAL_SQL_ENGINES = new Set([DUCKDB_ENGINE]);
11
+ export const DEFAULT_DUCKDB_DATABASE = "memory";
@@ -4,6 +4,7 @@
4
4
  text-rendering: auto;
5
5
  font-family: "KaTeX_Main", "Times New Roman", serif;
6
6
  font-size: 110%;
7
+ font-style: normal;
7
8
  line-height: inherit;
8
9
  text-indent: 0;
9
10
  }
@@ -1 +0,0 @@
1
- import{b as m}from"./_baseEach-CIMlsWNn.js";import{x as s}from"./index-D4bXoNM3.js";function e(r,o){var a=-1,t=s(r)?Array(r.length):[];return m(r,function(n,f,i){t[++a]=o(n,f,i)}),t}export{e as b};
@@ -1 +0,0 @@
1
- import{U as s,C as o}from"./mermaid-D59lkToe.js";const n=(a,r)=>s.lang.round(o.parse(a)[r]);export{n as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as t,C as o}from"./chunk-JBRWN2VN-vkbXs27X.js";import{_ as e}from"./mermaid-D59lkToe.js";import"./transform-B8bpuzxV.js";import"./chunk-GLLZNHP4-DRBUPzV6.js";import"./chunk-WVR4S24B-DkjWv9uk.js";import"./chunk-NRVI72HA-gO-7Uy2L.js";import"./index-D4bXoNM3.js";import"./step-BwsUM5iJ.js";import"./timer-BwIYMJWC.js";var i={parser:t,get db(){return new o},renderer:s,styles:a,init:e(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{i as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as t,C as o}from"./chunk-JBRWN2VN-vkbXs27X.js";import{_ as e}from"./mermaid-D59lkToe.js";import"./transform-B8bpuzxV.js";import"./chunk-GLLZNHP4-DRBUPzV6.js";import"./chunk-WVR4S24B-DkjWv9uk.js";import"./chunk-NRVI72HA-gO-7Uy2L.js";import"./index-D4bXoNM3.js";import"./step-BwsUM5iJ.js";import"./timer-BwIYMJWC.js";var i={parser:t,get db(){return new o},renderer:s,styles:a,init:e(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{i as diagram};
@@ -1 +0,0 @@
1
- import{b as n}from"./_baseUniq-CGK6su7v.js";function o(r){return n(r,4)}export{o as c};