@marimo-team/islands 0.23.14-dev19 → 0.23.14-dev21
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/{chat-ui-9wA4feLE.js → chat-ui-CO8WuXGb.js} +347 -143
- package/dist/{code-visibility-CxDvzm2S.js → code-visibility-CPWEmIAX.js} +163 -171
- package/dist/{html-to-image-DBHfshED.js → html-to-image-DLSOS-bE.js} +2298 -2267
- package/dist/main.js +333 -333
- package/dist/{process-output-Dw6We6B6.js → process-output-B9ysqk1y.js} +1 -1
- package/dist/{reveal-component-tJhA77Am.js → reveal-component-BJwt0B2_.js} +2 -2
- package/package.json +2 -2
- package/src/components/editor/errors/__tests__/auto-fix.test.ts +11 -107
- package/src/components/editor/errors/auto-fix.tsx +11 -31
- package/src/components/editor/output/MarimoTracebackOutput.tsx +2 -2
- package/src/core/ai/context/__tests__/registry.test.ts +133 -3
- package/src/core/ai/context/providers/__tests__/datasource.test.ts +3 -1
- package/src/core/ai/context/providers/__tests__/error.test.ts +200 -15
- package/src/core/ai/context/providers/datasource.ts +27 -2
- package/src/core/ai/context/providers/error.ts +226 -36
- package/src/core/ai/context/registry.ts +77 -43
- package/src/core/codemirror/ai/resources.ts +15 -10
- package/src/core/errors/errors.ts +2 -1
- package/src/core/mime.ts +11 -4
- package/src/core/ai/context/providers/__tests__/__snapshots__/error.test.ts.snap +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
2
2
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
3
|
-
import { at as parseHtmlContent, it as ansiToPlainText } from "./html-to-image-
|
|
3
|
+
import { at as parseHtmlContent, it as ansiToPlainText } from "./html-to-image-DLSOS-bE.js";
|
|
4
4
|
import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
|
|
5
5
|
import { t as Strings } from "./strings-Dq_j3Rxw.js";
|
|
6
6
|
import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
|
|
@@ -6,10 +6,10 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
|
6
6
|
import { _ as Logger, g as cn, h as Events, l as useEventListener, t as Button } from "./button-BacYv-bE.js";
|
|
7
7
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
8
8
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
9
|
-
import { lt as kioskModeAtom } from "./html-to-image-
|
|
9
|
+
import { lt as kioskModeAtom } from "./html-to-image-DLSOS-bE.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-BbqSm5gU.js";
|
|
11
11
|
import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
|
|
12
|
-
import { a as DEFAULT_SLIDE_TYPE, c as Slide, ct as PanelGroup, i as DEFAULT_DECK_TRANSITION, lt as PanelResizeHandle, on as EyeOff, s as SlideSidebar, sn as Expand, st as Panel, t as useNotebookCodeAvailable, un as Code } from "./code-visibility-
|
|
12
|
+
import { a as DEFAULT_SLIDE_TYPE, c as Slide, ct as PanelGroup, i as DEFAULT_DECK_TRANSITION, lt as PanelResizeHandle, on as EyeOff, s as SlideSidebar, sn as Expand, st as Panel, t as useNotebookCodeAvailable, un as Code } from "./code-visibility-CPWEmIAX.js";
|
|
13
13
|
import { X as useDebouncedCallback } from "./input-BSdZp5Ng.js";
|
|
14
14
|
import "./toDate-D1Z7ZXWh.js";
|
|
15
15
|
import "./react-dom-BTJzcVJ9.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.23.14-
|
|
3
|
+
"version": "0.23.14-dev21",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@lezer/python": "^1.1.18",
|
|
57
57
|
"@marimo-team/codemirror-ai": "^0.3.7",
|
|
58
58
|
"@marimo-team/codemirror-languageserver": "^1.16.12",
|
|
59
|
-
"@marimo-team/codemirror-mcp": "^0.1.
|
|
59
|
+
"@marimo-team/codemirror-mcp": "^0.1.7",
|
|
60
60
|
"@marimo-team/codemirror-sql": "^0.2.8",
|
|
61
61
|
"@marimo-team/llm-info": "workspace:*",
|
|
62
62
|
"@marimo-team/marimo-api": "workspace:*",
|
|
@@ -1,119 +1,23 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
4
|
import { cellId } from "@/__tests__/branded";
|
|
5
|
-
import
|
|
5
|
+
import { buildFixInChatPrompt } from "../auto-fix";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
const { buildFixPrompt, buildFixPromptFromText } = await import("../auto-fix");
|
|
14
|
-
|
|
15
|
-
describe("buildFixPromptFromText", () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
getDatasourceContext.mockReset();
|
|
18
|
-
getDatasourceContext.mockReturnValue(null);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("includes the cell id in the header when provided", () => {
|
|
22
|
-
const prompt = buildFixPromptFromText("boom", cellId("cell-1"));
|
|
23
|
-
expect(prompt).toBe(
|
|
24
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nboom",
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("uses a generic header when no cell id is provided", () => {
|
|
29
|
-
const prompt = buildFixPromptFromText("boom");
|
|
30
|
-
expect(prompt).toBe(
|
|
31
|
-
"My code gives the following error. Please fix it:\n\nboom",
|
|
7
|
+
describe("buildFixInChatPrompt", () => {
|
|
8
|
+
it("references the per-cell error context URI", () => {
|
|
9
|
+
expect(buildFixInChatPrompt(cellId("cell-1"))).toBe(
|
|
10
|
+
"@error://cell-1\n\nPlease fix this error.",
|
|
32
11
|
);
|
|
33
12
|
});
|
|
34
13
|
|
|
35
|
-
it("
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
includeDatasourceContext: true,
|
|
39
|
-
});
|
|
40
|
-
expect(prompt).toBe(
|
|
41
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nboom\n\nDatabase schema: @datasource://my_db",
|
|
14
|
+
it("falls back to inline error text when no cell id", () => {
|
|
15
|
+
expect(buildFixInChatPrompt(undefined, "ValueError: boom")).toBe(
|
|
16
|
+
"My code gives the following error. Please fix it:\n\nValueError: boom",
|
|
42
17
|
);
|
|
43
18
|
});
|
|
44
19
|
|
|
45
|
-
it("
|
|
46
|
-
|
|
47
|
-
const prompt = buildFixPromptFromText("boom", cellId("cell-1"));
|
|
48
|
-
expect(prompt).toBe(
|
|
49
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nboom",
|
|
50
|
-
);
|
|
51
|
-
expect(getDatasourceContext).not.toHaveBeenCalled();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("does not look up datasource context without a cell id", () => {
|
|
55
|
-
buildFixPromptFromText("boom", undefined, {
|
|
56
|
-
includeDatasourceContext: true,
|
|
57
|
-
});
|
|
58
|
-
expect(getDatasourceContext).not.toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe("buildFixPrompt", () => {
|
|
63
|
-
beforeEach(() => {
|
|
64
|
-
getDatasourceContext.mockReset();
|
|
65
|
-
getDatasourceContext.mockReturnValue(null);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("uses the error message when present", () => {
|
|
69
|
-
const errors: MarimoError[] = [
|
|
70
|
-
{ type: "sql-error", msg: "syntax error", sql_statement: "SELECT" },
|
|
71
|
-
];
|
|
72
|
-
expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
|
|
73
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nsyntax error",
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("joins multiple errors with newlines", () => {
|
|
78
|
-
const errors: MarimoError[] = [
|
|
79
|
-
{ type: "syntax", msg: "bad syntax" },
|
|
80
|
-
{
|
|
81
|
-
type: "exception",
|
|
82
|
-
exception_type: "ValueError",
|
|
83
|
-
msg: "bad value",
|
|
84
|
-
raising_cell: null,
|
|
85
|
-
},
|
|
86
|
-
];
|
|
87
|
-
expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
|
|
88
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nbad syntax\nbad value",
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("falls back to the error type when there is no message", () => {
|
|
93
|
-
const errors: MarimoError[] = [
|
|
94
|
-
{ type: "multiple-defs", name: "foo", cells: [cellId("foo")] },
|
|
95
|
-
];
|
|
96
|
-
expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
|
|
97
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nmultiple-defs",
|
|
98
|
-
);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("appends datasource context for SQL errors", () => {
|
|
102
|
-
getDatasourceContext.mockReturnValue("@datasource://my_db");
|
|
103
|
-
const errors: MarimoError[] = [
|
|
104
|
-
{ type: "sql-error", msg: "syntax error", sql_statement: "SELECT" },
|
|
105
|
-
];
|
|
106
|
-
expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
|
|
107
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nsyntax error\n\nDatabase schema: @datasource://my_db",
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("does not append datasource context for non-SQL errors", () => {
|
|
112
|
-
getDatasourceContext.mockReturnValue("@datasource://my_db");
|
|
113
|
-
const errors: MarimoError[] = [{ type: "syntax", msg: "bad syntax" }];
|
|
114
|
-
expect(buildFixPrompt(errors, cellId("cell-1"))).toBe(
|
|
115
|
-
"My cell (id: cell-1) produced the following error. Please fix it:\n\nbad syntax",
|
|
116
|
-
);
|
|
117
|
-
expect(getDatasourceContext).not.toHaveBeenCalled();
|
|
20
|
+
it("uses a generic prompt when no cell id and no error text", () => {
|
|
21
|
+
expect(buildFixInChatPrompt(undefined)).toBe("Please fix this error.");
|
|
118
22
|
});
|
|
119
23
|
});
|
|
@@ -21,44 +21,24 @@ import { aiCompletionCellAtom } from "@/core/ai/state";
|
|
|
21
21
|
import { notebookAtom, useCellActions } from "@/core/cells/cells";
|
|
22
22
|
import type { CellId } from "@/core/cells/ids";
|
|
23
23
|
import { aiFeaturesEnabledAtom } from "@/core/config/config";
|
|
24
|
-
import { getDatasourceContext } from "@/core/ai/context/providers/datasource";
|
|
25
24
|
import { getAutoFixes } from "@/core/errors/errors";
|
|
26
25
|
import type { MarimoError } from "@/core/kernel/messages";
|
|
27
26
|
import { cn } from "@/utils/cn";
|
|
28
27
|
import { useOpenAiAssistant } from "../chrome/wrapper/useOpenAiAssistant";
|
|
29
28
|
import { type FixMode, useFixMode } from "./fix-mode";
|
|
30
29
|
|
|
31
|
-
export function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
includeDatasourceContext = false,
|
|
36
|
-
}: { includeDatasourceContext?: boolean } = {},
|
|
30
|
+
export function buildFixInChatPrompt(
|
|
31
|
+
cellId: CellId | undefined,
|
|
32
|
+
fallbackErrorText?: string,
|
|
37
33
|
): string {
|
|
38
|
-
|
|
39
|
-
cellId
|
|
40
|
-
? `My cell (id: ${cellId}) produced the following error. Please fix it:`
|
|
41
|
-
: "My code gives the following error. Please fix it:";
|
|
42
|
-
let prompt = `${header}\n\n${errorText}`;
|
|
43
|
-
if (cellId != null && includeDatasourceContext) {
|
|
44
|
-
const datasourceContext = getDatasourceContext(cellId);
|
|
45
|
-
if (datasourceContext) {
|
|
46
|
-
prompt += `\n\nDatabase schema: ${datasourceContext}`;
|
|
47
|
-
}
|
|
34
|
+
if (cellId != null) {
|
|
35
|
+
return `@error://${cellId}\n\nPlease fix this error.`;
|
|
48
36
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.map((error) => ("msg" in error && error.msg ? error.msg : error.type))
|
|
55
|
-
.join("\n");
|
|
56
|
-
const includeDatasourceContext = errors.some(
|
|
57
|
-
(error) => error.type === "sql-error",
|
|
58
|
-
);
|
|
59
|
-
return buildFixPromptFromText(errorText, cellId, {
|
|
60
|
-
includeDatasourceContext,
|
|
61
|
-
});
|
|
37
|
+
const errorText = fallbackErrorText?.trim();
|
|
38
|
+
if (errorText) {
|
|
39
|
+
return `My code gives the following error. Please fix it:\n\n${errorText}`;
|
|
40
|
+
}
|
|
41
|
+
return "Please fix this error.";
|
|
62
42
|
}
|
|
63
43
|
|
|
64
44
|
export const AutoFixButton = ({
|
|
@@ -112,7 +92,7 @@ export const AutoFixButton = ({
|
|
|
112
92
|
|
|
113
93
|
const openAISidebar = () => {
|
|
114
94
|
openAiAssistant({
|
|
115
|
-
prompt:
|
|
95
|
+
prompt: buildFixInChatPrompt(cellId),
|
|
116
96
|
});
|
|
117
97
|
};
|
|
118
98
|
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
getTracebackInfo,
|
|
43
43
|
} from "@/utils/traceback";
|
|
44
44
|
import { useOpenAiAssistant } from "../chrome/wrapper/useOpenAiAssistant";
|
|
45
|
-
import { AIFixButton,
|
|
45
|
+
import { AIFixButton, buildFixInChatPrompt } from "../errors/auto-fix";
|
|
46
46
|
import { MangledSegments } from "../errors/mangled-local-chip";
|
|
47
47
|
import { CellLinkTraceback } from "../links/cell-link";
|
|
48
48
|
import type { OnRefactorWithAI } from "../Output";
|
|
@@ -101,7 +101,7 @@ export const MarimoTracebackOutput = ({
|
|
|
101
101
|
|
|
102
102
|
const openAISidebar = () => {
|
|
103
103
|
openAiAssistant({
|
|
104
|
-
prompt:
|
|
104
|
+
prompt: buildFixInChatPrompt(cellId, lastTracebackLine),
|
|
105
105
|
});
|
|
106
106
|
};
|
|
107
107
|
|
|
@@ -176,6 +176,33 @@ class FileContextProvider extends AIContextProvider<FileContextItem> {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
// Second mock provider with the same context type as MockContextProvider
|
|
180
|
+
class SecondaryMockProvider extends AIContextProvider<MockContextItem> {
|
|
181
|
+
readonly title = "Secondary Mock Items";
|
|
182
|
+
readonly mentionPrefix = "@";
|
|
183
|
+
readonly contextType = "mock";
|
|
184
|
+
|
|
185
|
+
getItems(): MockContextItem[] {
|
|
186
|
+
return [
|
|
187
|
+
{
|
|
188
|
+
type: "mock",
|
|
189
|
+
uri: this.asURI("secondary-item"),
|
|
190
|
+
name: "Secondary Item",
|
|
191
|
+
description: "From the second mock provider",
|
|
192
|
+
data: { value: "secondary" },
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
formatContext(item: MockContextItem): string {
|
|
198
|
+
return `Secondary: ${item.name}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
formatCompletion(item: MockContextItem): Completion {
|
|
202
|
+
return this.createBasicCompletion(item);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
179
206
|
describe("AIContextProvider", () => {
|
|
180
207
|
let provider: MockContextProvider;
|
|
181
208
|
|
|
@@ -411,6 +438,95 @@ describe("AIContextRegistry", () => {
|
|
|
411
438
|
});
|
|
412
439
|
});
|
|
413
440
|
|
|
441
|
+
describe("resolveItems", () => {
|
|
442
|
+
let mockGetItems: ReturnType<typeof vi.spyOn>;
|
|
443
|
+
let fileGetItems: ReturnType<typeof vi.spyOn>;
|
|
444
|
+
|
|
445
|
+
beforeEach(() => {
|
|
446
|
+
registry.register(mockProvider);
|
|
447
|
+
registry.register(fileProvider);
|
|
448
|
+
mockGetItems = vi.spyOn(mockProvider, "getItems");
|
|
449
|
+
fileGetItems = vi.spyOn(fileProvider, "getItems");
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it("should resolve valid IDs from the matching providers only", () => {
|
|
453
|
+
const resolved = registry.resolveItems([
|
|
454
|
+
"mock://item1",
|
|
455
|
+
"file://config.py",
|
|
456
|
+
] as ContextLocatorId[]);
|
|
457
|
+
|
|
458
|
+
expect(resolved).toHaveLength(2);
|
|
459
|
+
expect(resolved.map((item) => item.uri)).toEqual([
|
|
460
|
+
"mock://item1",
|
|
461
|
+
"file://config.py",
|
|
462
|
+
]);
|
|
463
|
+
expect(mockGetItems).toHaveBeenCalledTimes(1);
|
|
464
|
+
expect(fileGetItems).toHaveBeenCalledTimes(1);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("should not call unrelated providers", () => {
|
|
468
|
+
registry.resolveItems(["mock://item1"] as ContextLocatorId[]);
|
|
469
|
+
|
|
470
|
+
expect(mockGetItems).toHaveBeenCalledTimes(1);
|
|
471
|
+
expect(fileGetItems).not.toHaveBeenCalled();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it("should ignore unknown or invalid URIs", () => {
|
|
475
|
+
const resolved = registry.resolveItems([
|
|
476
|
+
"mock://nonexistent",
|
|
477
|
+
"unknown://item",
|
|
478
|
+
"malformed" as ContextLocatorId,
|
|
479
|
+
] as ContextLocatorId[]);
|
|
480
|
+
|
|
481
|
+
expect(resolved).toEqual([]);
|
|
482
|
+
expect(mockGetItems).toHaveBeenCalledTimes(1);
|
|
483
|
+
expect(fileGetItems).not.toHaveBeenCalled();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("should return empty array for empty input", () => {
|
|
487
|
+
expect(registry.resolveItems([])).toEqual([]);
|
|
488
|
+
expect(mockGetItems).not.toHaveBeenCalled();
|
|
489
|
+
expect(fileGetItems).not.toHaveBeenCalled();
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it("should preserve requested order when providers are interleaved", () => {
|
|
493
|
+
const resolved = registry.resolveItems([
|
|
494
|
+
"mock://item1",
|
|
495
|
+
"file://config.py",
|
|
496
|
+
"mock://item2",
|
|
497
|
+
"file://utils/helpers.py",
|
|
498
|
+
] as ContextLocatorId[]);
|
|
499
|
+
|
|
500
|
+
expect(resolved.map((item) => item.uri)).toEqual([
|
|
501
|
+
"mock://item1",
|
|
502
|
+
"file://config.py",
|
|
503
|
+
"mock://item2",
|
|
504
|
+
"file://utils/helpers.py",
|
|
505
|
+
]);
|
|
506
|
+
// Each provider is still queried only once, regardless of interleaving.
|
|
507
|
+
expect(mockGetItems).toHaveBeenCalledTimes(1);
|
|
508
|
+
expect(fileGetItems).toHaveBeenCalledTimes(1);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("should resolve items from all providers that share a context type", () => {
|
|
512
|
+
const secondaryProvider = new SecondaryMockProvider();
|
|
513
|
+
const secondaryGetItems = vi.spyOn(secondaryProvider, "getItems");
|
|
514
|
+
registry.register(secondaryProvider);
|
|
515
|
+
|
|
516
|
+
const resolved = registry.resolveItems([
|
|
517
|
+
"mock://item1",
|
|
518
|
+
"mock://secondary-item",
|
|
519
|
+
] as ContextLocatorId[]);
|
|
520
|
+
|
|
521
|
+
expect(resolved.map((item) => item.uri)).toEqual([
|
|
522
|
+
"mock://item1",
|
|
523
|
+
"mock://secondary-item",
|
|
524
|
+
]);
|
|
525
|
+
expect(mockGetItems).toHaveBeenCalledTimes(1);
|
|
526
|
+
expect(secondaryGetItems).toHaveBeenCalledTimes(1);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
414
530
|
describe("getContextInfo", () => {
|
|
415
531
|
beforeEach(() => {
|
|
416
532
|
registry.register(mockProvider);
|
|
@@ -531,6 +647,19 @@ describe("AIContextRegistry", () => {
|
|
|
531
647
|
expect(formatted).toContain("Mock: Item 1 (value1)");
|
|
532
648
|
expect(formatted.split("\n\n")).toHaveLength(1);
|
|
533
649
|
});
|
|
650
|
+
|
|
651
|
+
it("should format items from the provider that owns them", () => {
|
|
652
|
+
registry.register(new SecondaryMockProvider());
|
|
653
|
+
|
|
654
|
+
const formatted = registry.formatContextForAI([
|
|
655
|
+
"mock://item1",
|
|
656
|
+
"mock://secondary-item",
|
|
657
|
+
] as ContextLocatorId[]);
|
|
658
|
+
|
|
659
|
+
expect(formatted).toBe(
|
|
660
|
+
"Mock: Item 1 (value1)\n\nSecondary: Secondary Item",
|
|
661
|
+
);
|
|
662
|
+
});
|
|
534
663
|
});
|
|
535
664
|
|
|
536
665
|
describe("edge cases and error handling", () => {
|
|
@@ -621,7 +750,7 @@ describe("AIContextRegistry", () => {
|
|
|
621
750
|
expect(provider).toBe(errorProvider);
|
|
622
751
|
|
|
623
752
|
const items = registry.getAllItems();
|
|
624
|
-
expect(items).toHaveLength(
|
|
753
|
+
expect(items).toHaveLength(3);
|
|
625
754
|
expect(items[0].type).toBe("error");
|
|
626
755
|
expect(items[0].name).toBe("Errors");
|
|
627
756
|
});
|
|
@@ -654,10 +783,11 @@ describe("AIContextRegistry", () => {
|
|
|
654
783
|
expect(provider).toBeDefined();
|
|
655
784
|
|
|
656
785
|
const items = provider!.getItems();
|
|
657
|
-
expect(items).toHaveLength(
|
|
786
|
+
expect(items).toHaveLength(3);
|
|
658
787
|
|
|
659
788
|
const completion = provider!.formatCompletion(items[0]);
|
|
660
789
|
expect(completion.label).toBe("@Errors");
|
|
790
|
+
expect(completion.apply).toBe("@error://all");
|
|
661
791
|
expect(completion.type).toBe("error");
|
|
662
792
|
});
|
|
663
793
|
|
|
@@ -690,7 +820,7 @@ describe("AIContextRegistry", () => {
|
|
|
690
820
|
const errorItems = items.filter((item) => item.type === "error");
|
|
691
821
|
const mockItems = items.filter((item) => item.type === "mock");
|
|
692
822
|
|
|
693
|
-
expect(errorItems).toHaveLength(
|
|
823
|
+
expect(errorItems).toHaveLength(3);
|
|
694
824
|
expect(mockItems).toHaveLength(3);
|
|
695
825
|
});
|
|
696
826
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
|
|
4
|
+
import { createStore } from "jotai";
|
|
4
5
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
5
6
|
import { cellId, variableName } from "@/__tests__/branded";
|
|
6
7
|
import type {
|
|
@@ -634,7 +635,8 @@ describe("DatasourceContextProvider", () => {
|
|
|
634
635
|
|
|
635
636
|
describe("getDatasourceContext", () => {
|
|
636
637
|
it("should return null if no cell ID is found", () => {
|
|
637
|
-
const
|
|
638
|
+
const store = createStore();
|
|
639
|
+
const context = getDatasourceContext(cellId("1"), store);
|
|
638
640
|
expect(context).toBeNull();
|
|
639
641
|
});
|
|
640
642
|
});
|