@marimo-team/frontend 0.15.1-dev33 → 0.15.1-dev35

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 (113) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-B0S6KZz0.js → ConnectedDataExplorerComponent-BYxPTiA6.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-C-VZ4Tnc.js → ImageComparisonComponent-DBpvpxlw.js} +1 -1
  3. package/dist/assets/{VegaLite-BnVARnZA.js → VegaLite-DzkGo9LJ.js} +1 -1
  4. package/dist/assets/{_baseEach-CnkgAoU6.js → _baseEach-CnK_W3-W.js} +1 -1
  5. package/dist/assets/_baseMap-CIc0IDdl.js +1 -0
  6. package/dist/assets/{_baseUniq-CwJ2iszo.js → _baseUniq-DsEUejTO.js} +1 -1
  7. package/dist/assets/{_createAggregator-DzNuEiXE.js → _createAggregator-xSD3e6yX.js} +1 -1
  8. package/dist/assets/{any-language-editor-CkEw-cSX.js → any-language-editor-CSLfx3Fo.js} +1 -1
  9. package/dist/assets/{architectureDiagram-KFL7JDKH-D5bxGLSO.js → architectureDiagram-KFL7JDKH-DDJcEe4E.js} +1 -1
  10. package/dist/assets/{blockDiagram-ZYB65J3Q-lBSpJVzg.js → blockDiagram-ZYB65J3Q-DrajNxU6.js} +1 -1
  11. package/dist/assets/{c4Diagram-AAMF2YG6-Cp2ZcvGq.js → c4Diagram-AAMF2YG6-DkbBAlkB.js} +1 -1
  12. package/dist/assets/channel-D2aPrBH4.js +1 -0
  13. package/dist/assets/{chunk-ANTBXLJU-BI9zlp2d.js → chunk-ANTBXLJU-CyFpCIAD.js} +1 -1
  14. package/dist/assets/{chunk-FHKO5MBM-DmqxuwHW.js → chunk-FHKO5MBM-DaRG9EJ9.js} +1 -1
  15. package/dist/assets/{chunk-GLLZNHP4-DDRTaZzv.js → chunk-GLLZNHP4-C852Uilf.js} +1 -1
  16. package/dist/assets/{chunk-JBRWN2VN-CT2Hh2iP.js → chunk-JBRWN2VN-DF6c2w7p.js} +1 -1
  17. package/dist/assets/{chunk-LXBSTHXV-DAMSTx1Q.js → chunk-LXBSTHXV-DJc1jClD.js} +1 -1
  18. package/dist/assets/{chunk-NRVI72HA-BZuYvBej.js → chunk-NRVI72HA-BYzJjL14.js} +1 -1
  19. package/dist/assets/{chunk-OMD6QJNC-BU_2rBlE.js → chunk-OMD6QJNC-DpzW6M54.js} +1 -1
  20. package/dist/assets/{chunk-WVR4S24B-CovEV1lg.js → chunk-WVR4S24B-Ds_Fnulu.js} +1 -1
  21. package/dist/assets/{circle-play-D59wTwHu.js → circle-play-ILK7D0lo.js} +1 -1
  22. package/dist/assets/classDiagram-3BZAVTQC-DAUNRz1h.js +1 -0
  23. package/dist/assets/classDiagram-v2-QTMF73CY-DAUNRz1h.js +1 -0
  24. package/dist/assets/clone-CYXehyk_.js +1 -0
  25. package/dist/assets/{compile-BKBdwA1q.js → compile-Q6b1NW0Y.js} +1 -1
  26. package/dist/assets/{dagre-2BBEFEWP-BmXAp5fB.js → dagre-2BBEFEWP-DUplj_Iy.js} +1 -1
  27. package/dist/assets/{data-grid-overlay-editor-BycmLqkg.js → data-grid-overlay-editor-BHVNUwJL.js} +1 -1
  28. package/dist/assets/{diagram-4IRLE6MV-CI8uZuDu.js → diagram-4IRLE6MV-CqSPK4-w.js} +1 -1
  29. package/dist/assets/{diagram-GUPCWM2R-DM01dWOn.js → diagram-GUPCWM2R-Yz9pe62o.js} +1 -1
  30. package/dist/assets/{diagram-RP2FKANI-3A7xcD_S.js → diagram-RP2FKANI-DAvv4Av_.js} +1 -1
  31. package/dist/assets/{edit-page-r0MON4eX.js → edit-page-hlKtRc6p.js} +4 -4
  32. package/dist/assets/{erDiagram-HZWUO2LU-DVhn3kEV.js → erDiagram-HZWUO2LU-BIlDOHrw.js} +1 -1
  33. package/dist/assets/{flowDiagram-THRYKUMA-ByiKsMTR.js → flowDiagram-THRYKUMA-w6HYIjcR.js} +1 -1
  34. package/dist/assets/{ganttDiagram-WV7ZQ7D5-C3yWfK7N.js → ganttDiagram-WV7ZQ7D5-CS1t__7-.js} +1 -1
  35. package/dist/assets/{gitGraphDiagram-OJR772UL-DT2oVbFG.js → gitGraphDiagram-OJR772UL-DCcukZnI.js} +1 -1
  36. package/dist/assets/{glide-data-editor-B-hKUbt8.js → glide-data-editor-BoQ4MEIu.js} +4 -4
  37. package/dist/assets/{graph-DKF8gkY7.js → graph-DzywueYf.js} +1 -1
  38. package/dist/assets/{home-page-DG5IpRSH.js → home-page-CTLf7o0o.js} +1 -1
  39. package/dist/assets/{index-COS_Xbgw.js → index-B8egTd_Y.js} +1 -1
  40. package/dist/assets/{index-CxQa_cAw.js → index-BNpIaqHq.js} +1 -1
  41. package/dist/assets/{index-B4KTvqjC.js → index-BNuSEPHf.js} +1 -1
  42. package/dist/assets/{index-Cxy_1y9a.js → index-BOaSI40H.js} +1 -1
  43. package/dist/assets/{index-B_03AI_O.js → index-BVdia1-T.js} +1 -1
  44. package/dist/assets/{index-BDye4bMD.js → index-BbrvgVGd.js} +1 -1
  45. package/dist/assets/{index-kCp9Vp_G.js → index-BobSdMcV.js} +1 -1
  46. package/dist/assets/{index-BSq77Vg9.js → index-BsZupkBE.js} +1 -1
  47. package/dist/assets/{index-BciA4s2J.js → index-CTTXrsVd.js} +1 -1
  48. package/dist/assets/{index-CDzLk5Q3.js → index-C_ZOWmXX.js} +1 -1
  49. package/dist/assets/{index-tI3mZqES.js → index-CjvmeMRd.js} +1 -1
  50. package/dist/assets/{index-C89mOURj.js → index-DMgZFfTJ.js} +1 -1
  51. package/dist/assets/{index-DTE_Lol9.js → index-DMvScaq6.js} +133 -133
  52. package/dist/assets/{index-BJMIm3h5.js → index-DNjb9MyH.js} +1 -1
  53. package/dist/assets/{index-BQCqmfXS.js → index-DUumP-lt.js} +1 -1
  54. package/dist/assets/{index-RfoHYoLS.js → index-Dl6_NSX4.js} +1 -1
  55. package/dist/assets/{index-BKnuS3t4.js → index-DsMyERcC.js} +1 -1
  56. package/dist/assets/{index-BAre-3Jh.js → index-EaCZDGUn.js} +1 -1
  57. package/dist/assets/{index-CKZtF7JM.js → index-gZP0fYKG.js} +1 -1
  58. package/dist/assets/{index-CDxt4mvu.js → index-txir7kB2.js} +1 -1
  59. package/dist/assets/{infoDiagram-6WOFNB3A-CVxc3Oq6.js → infoDiagram-6WOFNB3A-B1zwcYam.js} +1 -1
  60. package/dist/assets/{journeyDiagram-FFXJYRFH-yx-qCooh.js → journeyDiagram-FFXJYRFH-IwybgEnB.js} +1 -1
  61. package/dist/assets/{kanban-definition-KOZQBZVT-DpJgEsNc.js → kanban-definition-KOZQBZVT-BnLxznwF.js} +1 -1
  62. package/dist/assets/{layout-HC2KG283.js → layout-lIKJEEeS.js} +1 -1
  63. package/dist/assets/{linear-DwZzRezN.js → linear-DzfVWjvh.js} +1 -1
  64. package/dist/assets/{links-_NwdJ11g.js → links-yIZ6advT.js} +1 -1
  65. package/dist/assets/{mermaid-BsNRTdZg.js → mermaid-CEpdDmik.js} +4 -4
  66. package/dist/assets/{min-DdOdESXu.js → min-CH9RTxJs.js} +1 -1
  67. package/dist/assets/{mindmap-definition-LNHGMQRG-Ds3h-hI5.js → mindmap-definition-LNHGMQRG-BGOBqdZQ.js} +1 -1
  68. package/dist/assets/{number-overlay-editor-CYrbVyKN.js → number-overlay-editor-Bp8EEFG4.js} +1 -1
  69. package/dist/assets/{pieDiagram-DBDJKBY4-Ce-oEPaV.js → pieDiagram-DBDJKBY4-BH18DMec.js} +1 -1
  70. package/dist/assets/{quadrantDiagram-YPSRARAO-Urm8U0LY.js → quadrantDiagram-YPSRARAO-DyEaA3Tj.js} +1 -1
  71. package/dist/assets/{react-plotly-DGqhBdP1.js → react-plotly-BWJyJ31U.js} +1 -1
  72. package/dist/assets/{requirementDiagram-EGVEC5DT-CCTq9hQZ.js → requirementDiagram-EGVEC5DT-BIDYwWds.js} +1 -1
  73. package/dist/assets/{run-page-CsCj50OD.js → run-page-CFGHZjjp.js} +1 -1
  74. package/dist/assets/{sankeyDiagram-HRAUVNP4-C2I9sfem.js → sankeyDiagram-HRAUVNP4-CI7WE82z.js} +1 -1
  75. package/dist/assets/{sequenceDiagram-WFGC7UMF-CLQmyPy2.js → sequenceDiagram-WFGC7UMF-CAt5_R3a.js} +1 -1
  76. package/dist/assets/{slides-component-DJCkXPus.js → slides-component-BE3m7wod.js} +1 -1
  77. package/dist/assets/{sortBy-Duwc1vyP.js → sortBy-D8wYLA9x.js} +1 -1
  78. package/dist/assets/{stateDiagram-UUKSUZ4H-VvU373AQ.js → stateDiagram-UUKSUZ4H-B9ghWYn7.js} +1 -1
  79. package/dist/assets/stateDiagram-v2-EYPG3UTE-DtL7XYtB.js +1 -0
  80. package/dist/assets/{storage-1hHZMyeU.js → storage-BseyfFaj.js} +3 -3
  81. package/dist/assets/{terminal-DV-tPxSZ.js → terminal-R2SnqHO_.js} +1 -1
  82. package/dist/assets/{time-4bh0fJAY.js → time-CftJc27E.js} +1 -1
  83. package/dist/assets/{timeline-definition-3HZDQTIS-D5z1lzVG.js → timeline-definition-3HZDQTIS-B8ej5Uev.js} +1 -1
  84. package/dist/assets/{tracing-C32etwYw.js → tracing-OU6KdAjg.js} +2 -2
  85. package/dist/assets/{trash-W9nmeKd9.js → trash-BX0buH-6.js} +1 -1
  86. package/dist/assets/{treemap-75Q7IDZK-CHza0lWj.js → treemap-75Q7IDZK-C6t8vi8s.js} +1 -1
  87. package/dist/assets/{vega-component-BDWLTrqk.js → vega-component-a-Wve7v1.js} +1 -1
  88. package/dist/assets/{xychartDiagram-FDP5SA34-DVdoRkWV.js → xychartDiagram-FDP5SA34-DmTMju1m.js} +1 -1
  89. package/dist/index.html +1 -1
  90. package/package.json +1 -1
  91. package/src/__mocks__/notebook.ts +108 -0
  92. package/src/components/editor/ai/__tests__/completion-utils.test.ts +119 -139
  93. package/src/components/editor/ai/completion-utils.ts +7 -33
  94. package/src/core/ai/context/__tests__/registry.test.ts +154 -0
  95. package/src/core/ai/context/__tests__/utils.test.ts +174 -0
  96. package/src/core/ai/context/context.ts +3 -1
  97. package/src/core/ai/context/providers/__tests__/__snapshots__/error.test.ts.snap +3 -0
  98. package/src/core/ai/context/providers/__tests__/__snapshots__/tables.test.ts.snap +8 -12
  99. package/src/core/ai/context/providers/__tests__/__snapshots__/variable.test.ts.snap +4 -20
  100. package/src/core/ai/context/providers/__tests__/error.test.ts +328 -0
  101. package/src/core/ai/context/providers/common.ts +1 -0
  102. package/src/core/ai/context/providers/error.ts +165 -0
  103. package/src/core/ai/context/providers/tables.ts +14 -6
  104. package/src/core/ai/context/providers/variable.ts +11 -2
  105. package/src/core/ai/context/utils.ts +49 -0
  106. package/src/core/cells/cells.ts +1 -1
  107. package/src/core/dom/outline.ts +16 -2
  108. package/dist/assets/_baseMap-BQjPNy9z.js +0 -1
  109. package/dist/assets/channel-8Z-5YHHd.js +0 -1
  110. package/dist/assets/classDiagram-3BZAVTQC-BOvgzS6u.js +0 -1
  111. package/dist/assets/classDiagram-v2-QTMF73CY-BOvgzS6u.js +0 -1
  112. package/dist/assets/clone-DVVP9Odz.js +0 -1
  113. package/dist/assets/stateDiagram-v2-EYPG3UTE-D41Oi10c.js +0 -1
@@ -52,41 +52,40 @@ describe("getAICompletionBody", () => {
52
52
  const input = "Use @data://dataset1 and @data://dataset2 for analysis";
53
53
  const result = getAICompletionBody({ input });
54
54
 
55
- expect(result).toEqual({
56
- includeOtherCode: "// Some other code",
57
- context: {
58
- schema: [
59
- {
60
- name: "dataset1",
61
- columns: [
62
- { name: "col1", type: "number" },
63
- { name: "col2", type: "string" },
64
- ],
65
- },
66
- {
67
- name: "dataset2",
68
- columns: [
69
- { name: "col3", type: "boolean" },
70
- { name: "col4", type: "date" },
71
- ],
72
- },
73
- ],
74
- variables: [],
75
- },
76
- });
55
+ expect(result).toMatchInlineSnapshot(`
56
+ {
57
+ "context": {
58
+ "plainText": "<data name="dataset1" source="unknown">
59
+ Columns:
60
+ - col1: number
61
+ - col2: string</data>
62
+
63
+ <data name="dataset2" source="unknown">
64
+ Columns:
65
+ - col3: boolean
66
+ - col4: date</data>",
67
+ "schema": [],
68
+ "variables": [],
69
+ },
70
+ "includeOtherCode": "// Some other code",
71
+ }
72
+ `);
77
73
  });
78
74
 
79
75
  it("should handle input with no mentioned datasets", () => {
80
76
  const input = "Perform some analysis without mentioning @data://datasets";
81
77
  const result = getAICompletionBody({ input });
82
78
 
83
- expect(result).toEqual({
84
- includeOtherCode: "// Some other code",
85
- context: {
86
- schema: [],
87
- variables: [],
88
- },
89
- });
79
+ expect(result).toMatchInlineSnapshot(`
80
+ {
81
+ "context": {
82
+ "plainText": "",
83
+ "schema": [],
84
+ "variables": [],
85
+ },
86
+ "includeOtherCode": "// Some other code",
87
+ }
88
+ `);
90
89
  });
91
90
 
92
91
  it("should handle input with non-existent datasets", () => {
@@ -106,21 +105,19 @@ describe("getAICompletionBody", () => {
106
105
  "Use @data://existingDataset and @data://nonExistentDataset for analysis";
107
106
  const result = getAICompletionBody({ input });
108
107
 
109
- expect(result).toEqual({
110
- includeOtherCode: "// Some other code",
111
- context: {
112
- schema: [
113
- {
114
- name: "existingDataset",
115
- columns: [
116
- { name: "col1", type: "number" },
117
- { name: "col2", type: "string" },
118
- ],
119
- },
120
- ],
121
- variables: [],
122
- },
123
- });
108
+ expect(result).toMatchInlineSnapshot(`
109
+ {
110
+ "context": {
111
+ "plainText": "<data name="existingDataset" source="unknown">
112
+ Columns:
113
+ - col1: number
114
+ - col2: string</data>",
115
+ "schema": [],
116
+ "variables": [],
117
+ },
118
+ "includeOtherCode": "// Some other code",
119
+ }
120
+ `);
124
121
  });
125
122
 
126
123
  it("should handle dataset names with dots", () => {
@@ -144,25 +141,23 @@ describe("getAICompletionBody", () => {
144
141
  "Use @data://dataset.with.dots and @data://regular_dataset for analysis";
145
142
  const result = getAICompletionBody({ input });
146
143
 
147
- expect(result).toEqual({
148
- includeOtherCode: "// Some other code",
149
- context: {
150
- schema: [
151
- {
152
- name: "dataset.with.dots",
153
- columns: [
154
- { name: "col1", type: "number" },
155
- { name: "col2", type: "string" },
156
- ],
157
- },
158
- {
159
- name: "regular_dataset",
160
- columns: [{ name: "col3", type: "boolean" }],
161
- },
162
- ],
163
- variables: [],
164
- },
165
- });
144
+ expect(result).toMatchInlineSnapshot(`
145
+ {
146
+ "context": {
147
+ "plainText": "<data name="dataset.with.dots" source="unknown">
148
+ Columns:
149
+ - col1: number
150
+ - col2: string</data>
151
+
152
+ <data name="regular_dataset" source="unknown">
153
+ Columns:
154
+ - col3: boolean</data>",
155
+ "schema": [],
156
+ "variables": [],
157
+ },
158
+ "includeOtherCode": "// Some other code",
159
+ }
160
+ `);
166
161
  });
167
162
 
168
163
  it("should handle connections", () => {
@@ -200,18 +195,18 @@ describe("getAICompletionBody", () => {
200
195
  const input = "Use @data://table1 for analysis";
201
196
  const result = getAICompletionBody({ input });
202
197
 
203
- expect(result).toEqual({
204
- includeOtherCode: "// Some other code",
205
- context: {
206
- schema: [
207
- {
208
- name: "table1",
209
- columns: [{ name: "col1", type: "number" }],
210
- },
211
- ],
212
- variables: [],
213
- },
214
- });
198
+ expect(result).toMatchInlineSnapshot(`
199
+ {
200
+ "context": {
201
+ "plainText": "<data name="table1" source="unknown">
202
+ Columns:
203
+ - col1: number</data>",
204
+ "schema": [],
205
+ "variables": [],
206
+ },
207
+ "includeOtherCode": "// Some other code",
208
+ }
209
+ `);
215
210
  });
216
211
 
217
212
  it("should return the correct completion body with mentioned variables", () => {
@@ -237,24 +232,18 @@ describe("getAICompletionBody", () => {
237
232
  const input = "Use @variable://var1 and @variable://var2 for analysis";
238
233
  const result = getAICompletionBody({ input });
239
234
 
240
- expect(result).toEqual({
241
- includeOtherCode: "// Some other code",
242
- context: {
243
- schema: [],
244
- variables: [
245
- {
246
- name: "var1",
247
- valueType: "string",
248
- previewValue: "string value",
249
- },
250
- {
251
- name: "var2",
252
- valueType: "number",
253
- previewValue: "42",
254
- },
255
- ],
256
- },
257
- });
235
+ expect(result).toMatchInlineSnapshot(`
236
+ {
237
+ "context": {
238
+ "plainText": "<variable name="var1" dataType="string">"string value"</variable>
239
+
240
+ <variable name="var2" dataType="number">"42"</variable>",
241
+ "schema": [],
242
+ "variables": [],
243
+ },
244
+ "includeOtherCode": "// Some other code",
245
+ }
246
+ `);
258
247
  });
259
248
 
260
249
  it("should handle input with both datasets and variables", () => {
@@ -284,27 +273,21 @@ describe("getAICompletionBody", () => {
284
273
  const input = "Use @data://dataset1 and @variable://var1 for analysis";
285
274
  const result = getAICompletionBody({ input });
286
275
 
287
- expect(result).toEqual({
288
- includeOtherCode: "// Some other code",
289
- context: {
290
- schema: [
291
- {
292
- name: "dataset1",
293
- columns: [
294
- { name: "col1", type: "number" },
295
- { name: "col2", type: "string" },
296
- ],
297
- },
298
- ],
299
- variables: [
300
- {
301
- name: "var1",
302
- valueType: "string",
303
- previewValue: "string value",
304
- },
305
- ],
306
- },
307
- });
276
+ expect(result).toMatchInlineSnapshot(`
277
+ {
278
+ "context": {
279
+ "plainText": "<data name="dataset1" source="unknown">
280
+ Columns:
281
+ - col1: number
282
+ - col2: string</data>
283
+
284
+ <variable name="var1" dataType="string">"string value"</variable>",
285
+ "schema": [],
286
+ "variables": [],
287
+ },
288
+ "includeOtherCode": "// Some other code",
289
+ }
290
+ `);
308
291
  });
309
292
 
310
293
  it("should handle non-existent variables", () => {
@@ -324,19 +307,16 @@ describe("getAICompletionBody", () => {
324
307
  "Use @variable://existingVar and @variable://nonExistentVar for analysis";
325
308
  const result = getAICompletionBody({ input });
326
309
 
327
- expect(result).toEqual({
328
- includeOtherCode: "// Some other code",
329
- context: {
330
- schema: [],
331
- variables: [
332
- {
333
- name: "existingVar",
334
- valueType: "string",
335
- previewValue: "string value",
336
- },
337
- ],
338
- },
339
- });
310
+ expect(result).toMatchInlineSnapshot(`
311
+ {
312
+ "context": {
313
+ "plainText": "<variable name="existingVar" dataType="string">"string value"</variable>",
314
+ "schema": [],
315
+ "variables": [],
316
+ },
317
+ "includeOtherCode": "// Some other code",
318
+ }
319
+ `);
340
320
  });
341
321
 
342
322
  it("should prioritize datasets over variables when there's a name conflict", () => {
@@ -363,17 +343,17 @@ describe("getAICompletionBody", () => {
363
343
  const input = "Use @data://conflict for analysis";
364
344
  const result = getAICompletionBody({ input });
365
345
 
366
- expect(result).toEqual({
367
- includeOtherCode: "// Some other code",
368
- context: {
369
- schema: [
370
- {
371
- name: "conflict",
372
- columns: [{ name: "col1", type: "number" }],
373
- },
374
- ],
375
- variables: [],
376
- },
377
- });
346
+ expect(result).toMatchInlineSnapshot(`
347
+ {
348
+ "context": {
349
+ "plainText": "<data name="conflict" source="unknown">
350
+ Columns:
351
+ - col1: number</data>",
352
+ "schema": [],
353
+ "variables": [],
354
+ },
355
+ "includeOtherCode": "// Some other code",
356
+ }
357
+ `);
378
358
  });
379
359
  });
@@ -6,13 +6,9 @@ import type {
6
6
  CompletionSource,
7
7
  } from "@codemirror/autocomplete";
8
8
  import { getAIContextRegistry } from "@/core/ai/context/context";
9
- import type { TableContextItem } from "@/core/ai/context/providers/tables";
10
- import type { VariableContextItem } from "@/core/ai/context/providers/variable";
11
9
  import { getCodes } from "@/core/codemirror/copilot/getCodes";
12
- import type { DataTable } from "@/core/kernel/messages";
13
10
  import type { AiCompletionRequest } from "@/core/network/types";
14
11
  import { store } from "@/core/state/jotai";
15
- import type { Variable } from "@/core/variables/types";
16
12
  import { Logger } from "@/utils/Logger";
17
13
 
18
14
  /**
@@ -23,26 +19,15 @@ export function getAICompletionBody({
23
19
  }: {
24
20
  input: string;
25
21
  }): Omit<AiCompletionRequest, "language" | "prompt" | "code"> {
26
- const { datasets, variables } = extractDatasetsAndVariables(input);
27
- Logger.debug("Included datasets", datasets);
28
- Logger.debug("Included variables", variables);
22
+ const contextString = extractDatasetsAndVariables(input);
23
+ Logger.debug("Included context", contextString);
29
24
 
30
25
  return {
31
26
  includeOtherCode: getCodes(""),
32
27
  context: {
33
- schema: datasets.map((dataset) => ({
34
- name: dataset.name,
35
- columns: dataset.columns.map((column) => ({
36
- name: column.name,
37
- type: column.type,
38
- sampleValues: column.sample_values,
39
- })),
40
- })),
41
- variables: variables.map((variable) => ({
42
- name: variable.name,
43
- valueType: variable.dataType ?? "",
44
- previewValue: variable.value,
45
- })),
28
+ plainText: contextString,
29
+ schema: [],
30
+ variables: [],
46
31
  },
47
32
  };
48
33
  }
@@ -52,21 +37,10 @@ export function getAICompletionBody({
52
37
  * References are with @<name> in the input.
53
38
  * Prioritizes datasets over variables if there's a name conflict.
54
39
  */
55
- function extractDatasetsAndVariables(input: string): {
56
- datasets: DataTable[];
57
- variables: Variable[];
58
- } {
40
+ function extractDatasetsAndVariables(input: string): string {
59
41
  const registry = getAIContextRegistry(store);
60
42
  const contextIds = registry.parseAllContextIds(input);
61
- const contextInfo = registry.getContextInfo(contextIds);
62
- const datasets: DataTable[] = contextInfo
63
- .filter((info): info is TableContextItem => info.type === "data")
64
- .map((info) => info.data);
65
- const variables: Variable[] = contextInfo
66
- .filter((info): info is VariableContextItem => info.type === "variable")
67
- .map((info) => info.data.variable);
68
-
69
- return { datasets, variables };
43
+ return registry.formatContextForAI(contextIds);
70
44
  }
71
45
 
72
46
  /**
@@ -1,7 +1,15 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
3
  import type { Completion } from "@codemirror/autocomplete";
4
+ import { createStore } from "jotai";
4
5
  import { beforeEach, describe, expect, it } from "vitest";
6
+ import { MockNotebook } from "@/__mocks__/notebook";
7
+ import { notebookAtom } from "@/core/cells/cells";
8
+ import { CellId as CellIdClass } from "@/core/cells/ids";
9
+ import {
10
+ type ErrorContextItem,
11
+ ErrorContextProvider,
12
+ } from "../providers/error";
5
13
  import {
6
14
  type AIContextItem,
7
15
  AIContextProvider,
@@ -513,4 +521,150 @@ describe("AIContextRegistry", () => {
513
521
  expect(provider).toBe(mockProvider);
514
522
  });
515
523
  });
524
+
525
+ describe("Integration with ErrorContextProvider", () => {
526
+ let errorProvider: ErrorContextProvider;
527
+ let store: ReturnType<typeof createStore>;
528
+ let registry: AIContextRegistry<ErrorContextItem>;
529
+
530
+ beforeEach(() => {
531
+ store = createStore();
532
+
533
+ // Create mock notebook with errors using the new MockNotebook utilities
534
+ const cellId1 = CellIdClass.create();
535
+ const cellId2 = CellIdClass.create();
536
+
537
+ const notebookState = MockNotebook.notebookStateWithErrors([
538
+ {
539
+ cellId: cellId1,
540
+ cellName: "Cell 1",
541
+ errorData: [
542
+ MockNotebook.errors.syntax("Invalid syntax"),
543
+ MockNotebook.errors.exception("Runtime error"),
544
+ ],
545
+ },
546
+ {
547
+ cellId: cellId2,
548
+ cellName: "Cell 2",
549
+ errorData: [MockNotebook.errors.cycle()],
550
+ },
551
+ ]);
552
+
553
+ store.set(notebookAtom, notebookState);
554
+ errorProvider = new ErrorContextProvider(store);
555
+ registry = new AIContextRegistry<ErrorContextItem>().register(
556
+ errorProvider,
557
+ );
558
+ });
559
+
560
+ it("should register and provide error context items", () => {
561
+ const provider = registry.getProvider("error");
562
+ expect(provider).toBe(errorProvider);
563
+
564
+ const items = registry.getAllItems();
565
+ expect(items).toHaveLength(1);
566
+ expect(items[0].type).toBe("error");
567
+ expect(items[0].name).toBe("Errors");
568
+ });
569
+
570
+ it("should parse error context IDs correctly", () => {
571
+ const contextIds = registry.parseAllContextIds(
572
+ "Use @error://all to analyze errors",
573
+ );
574
+ expect(contextIds).toEqual(["error://all"]);
575
+
576
+ const contextInfo = registry.getContextInfo(contextIds);
577
+ expect(contextInfo).toHaveLength(1);
578
+ expect(contextInfo[0].type).toBe("error");
579
+ });
580
+
581
+ it("should format error context for AI", () => {
582
+ const contextIds = ["error://all"] as ContextLocatorId[];
583
+ const formattedContext = registry.formatContextForAI(contextIds);
584
+
585
+ expect(formattedContext).toContain("<error");
586
+ expect(formattedContext).toContain("Cell 1");
587
+ expect(formattedContext).toContain("Cell 2");
588
+ expect(formattedContext).toContain("Invalid syntax");
589
+ expect(formattedContext).toContain("This cell is in a cycle");
590
+ });
591
+
592
+ it("should provide error completions", () => {
593
+ // Get the error provider and test its completion directly
594
+ const provider = registry.getProvider("error");
595
+ expect(provider).toBeDefined();
596
+
597
+ const items = provider!.getItems();
598
+ expect(items).toHaveLength(1);
599
+
600
+ const completion = provider!.formatCompletion(items[0]);
601
+ expect(completion.label).toBe("@Errors");
602
+ expect(completion.type).toBe("error");
603
+ });
604
+
605
+ it("should handle empty errors gracefully", () => {
606
+ // Create store with no errors using MockNotebook
607
+ const emptyStore = createStore();
608
+ const emptyNotebookState = MockNotebook.notebookStateWithErrors([]);
609
+ emptyStore.set(notebookAtom, emptyNotebookState);
610
+
611
+ const emptyErrorProvider = new ErrorContextProvider(emptyStore);
612
+ const emptyRegistry = new AIContextRegistry().register(
613
+ emptyErrorProvider,
614
+ );
615
+
616
+ const items = emptyRegistry.getAllItems();
617
+ expect(items).toHaveLength(0);
618
+ });
619
+
620
+ it("should work with mixed providers", () => {
621
+ const mixedRegistry = new AIContextRegistry<
622
+ ErrorContextItem | MockContextItem
623
+ >()
624
+ .register(errorProvider)
625
+ .register(mockProvider);
626
+
627
+ const items = mixedRegistry.getAllItems();
628
+ expect(items.length).toBeGreaterThan(1);
629
+
630
+ // Should have both error and mock items
631
+ const errorItems = items.filter((item) => item.type === "error");
632
+ const mockItems = items.filter((item) => item.type === "mock");
633
+
634
+ expect(errorItems).toHaveLength(1);
635
+ expect(mockItems).toHaveLength(3);
636
+ });
637
+
638
+ it("should parse mixed context IDs", () => {
639
+ const mixedRegistry = new AIContextRegistry<
640
+ ErrorContextItem | MockContextItem
641
+ >()
642
+ .register(errorProvider)
643
+ .register(mockProvider);
644
+
645
+ const input = "Use @error://all and @mock://item1 for analysis";
646
+ const contextIds = mixedRegistry.parseAllContextIds(input);
647
+
648
+ expect(contextIds).toContain("error://all");
649
+ expect(contextIds).toContain("mock://item1");
650
+ expect(contextIds).toHaveLength(2);
651
+ });
652
+
653
+ it("should format mixed context for AI", () => {
654
+ const mixedRegistry = new AIContextRegistry<
655
+ ErrorContextItem | MockContextItem
656
+ >()
657
+ .register(errorProvider)
658
+ .register(mockProvider);
659
+
660
+ const contextIds = ["error://all", "mock://item1"] as ContextLocatorId[];
661
+ const formattedContext = mixedRegistry.formatContextForAI(contextIds);
662
+
663
+ // Should contain both error and mock context
664
+ expect(formattedContext).toContain("<error");
665
+ expect(formattedContext).toContain("Mock:");
666
+ expect(formattedContext).toContain("Cell 1");
667
+ expect(formattedContext).toContain("Item 1");
668
+ });
669
+ });
516
670
  });