Skip to content

Commit 3a278a1

Browse files
committed
update PR
1 parent 02757d5 commit 3a278a1

File tree

7 files changed

+213
-155
lines changed

7 files changed

+213
-155
lines changed

docs/content/docs/ai/reference.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ type LLMRequestOptions = {
206206
* @default { add: true, update: true, delete: true }
207207
*/
208208
defaultStreamTools?: {
209-
/** Enable the add tool (default: true) */
209+
/** Enable the add tool (default: false) */
210210
add?: boolean;
211-
/** Enable the update tool (default: true) */
211+
/** Enable the update tool (default: false) */
212212
update?: boolean;
213-
/** Enable the delete tool (default: true) */
213+
/** Enable the delete tool (default: false) */
214214
delete?: boolean;
215215
};
216216
/**

docs/content/docs/features/ai/reference.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ type LLMRequestOptions = {
206206
* @default { add: true, update: true, delete: true }
207207
*/
208208
defaultStreamTools?: {
209-
/** Enable the add tool (default: true) */
209+
/** Enable the add tool (default: false) */
210210
add?: boolean;
211-
/** Enable the update tool (default: true) */
211+
/** Enable the update tool (default: false) */
212212
update?: boolean;
213-
/** Enable the delete tool (default: true) */
213+
/** Enable the delete tool (default: false) */
214214
delete?: boolean;
215215
};
216216
/**
Lines changed: 84 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
1-
import { BlockNoteEditor, filterSuggestionItems } from "@blocknote/core";
21
import "@blocknote/core/fonts/inter.css";
32
import { en } from "@blocknote/core/locales";
43
import { BlockNoteView } from "@blocknote/mantine";
54
import "@blocknote/mantine/style.css";
5+
import { useCreateBlockNote } from "@blocknote/react";
66
import {
7-
FormattingToolbar,
8-
FormattingToolbarController,
9-
SuggestionMenuController,
10-
getDefaultReactSlashMenuItems,
11-
getFormattingToolbarItems,
12-
useCreateBlockNote,
13-
} from "@blocknote/react";
14-
import {
15-
AIToolbarButton,
167
StreamToolExecutor,
178
createAIExtension,
189
getAIExtension,
19-
getAISlashMenuItems,
2010
llmFormats,
2111
} from "@blocknote/xl-ai";
2212
import { en as aiEn } from "@blocknote/xl-ai/locales";
@@ -65,35 +55,33 @@ export default function App() {
6555
// Renders the editor instance using a React component.
6656
return (
6757
<div>
68-
<BlockNoteView
69-
editor={editor}
70-
// // We're disabling some default UI elements
71-
// formattingToolbar={false}
72-
// slashMenu={false}
73-
/>
74-
75-
{/* <AIMenuController />
76-
<FormattingToolbarWithAI />
77-
<SuggestionMenuWithAI editor={editor} />
78-
</BlockNoteView> */}
58+
<BlockNoteView editor={editor} />
59+
7960
<div className={"edit-buttons"}>
8061
{/*Inserts a new block at start of document.*/}
8162
<button
8263
className={"edit-button"}
8364
onClick={async () => {
8465
const blockToChange = editor.document[1].id;
85-
const tools = llmFormats.html.streamTools.update(editor, {
86-
idsSuffixed: true,
87-
withDelays: true,
88-
});
8966

90-
const executor = new StreamToolExecutor([tools]);
67+
// Let's get the stream tools so we can invoke them manually
68+
// In this case, we're using the default stream tools, which allow all operations
69+
const tools = llmFormats.html.getStreamTools(editor, true);
70+
71+
// Create an executor that can execute StreamToolCalls
72+
const executor = new StreamToolExecutor(tools);
73+
74+
// Use `executeOne` to invoke a single, non-streaming StreamToolCall
9175
await executor.executeOne({
9276
type: "update",
9377
id: blockToChange,
9478
block: "<p>Open source software is cool</p>",
9579
});
80+
81+
// make sure the executor is done
9682
await executor.waitTillEnd();
83+
84+
// accept the changes after 1 second
9785
await new Promise((resolve) => setTimeout(resolve, 1000));
9886
await getAIExtension(editor).acceptChanges();
9987
}}
@@ -104,21 +92,32 @@ export default function App() {
10492
className={"edit-button"}
10593
onClick={async () => {
10694
const blockToChange = editor.document[1].id;
107-
const tools = llmFormats.html.streamTools.update(editor, {
108-
idsSuffixed: true,
109-
withDelays: true,
95+
96+
// Let's get the stream tools so we can invoke them manually
97+
// In this case, we choose to only get the "update" tool
98+
const tools = llmFormats.html.getStreamTools(editor, true, {
99+
// only allow "update" operations
100+
update: true,
110101
});
111102

112-
const executor = new StreamToolExecutor([tools]);
103+
// Create an executor that can execute StreamToolCalls
104+
const executor = new StreamToolExecutor(tools);
105+
106+
// We'll stream two updates: a partial update and a full update
107+
// to use streaming operations, we need to get a writer
113108
const writer = executor.writable.getWriter();
109+
110+
// write a partial update
114111
writer.write({
115112
operation: {
116113
type: "update",
117114
id: blockToChange,
118115
block:
119116
"<p>This Open source software like Hello World refers to computer programs, this is a longer update, let's write a first sentence that's quite long long long long here.",
120117
},
118+
// this is not an update to an earlier "update" StreamToolCall
121119
isUpdateToPreviousOperation: false,
120+
// this operation is a partial update and will be "completed" by the next update
122121
isPossiblyPartial: true,
123122
});
124123
await new Promise((resolve) => setTimeout(resolve, 3000));
@@ -129,55 +128,73 @@ export default function App() {
129128
block:
130129
"<p>This Open source software like Hello World refers to computer programs, this is a longer update, let's write a first sentence that's quite long long long long here. And now let's write a second sentence.</p>",
131130
},
131+
// this is an update to an earlier "update" StreamToolCall
132132
isUpdateToPreviousOperation: true,
133+
// this operation is not a partial update, we've received the entire invocation
133134
isPossiblyPartial: false,
134135
});
136+
137+
// close the writer
135138
writer.close();
136139

140+
// wait till the executor is done
137141
await executor.waitTillEnd();
142+
143+
// accept the changes after 1 second
138144
await new Promise((resolve) => setTimeout(resolve, 1000));
139145
await getAIExtension(editor).acceptChanges();
140146
}}
141147
>
142148
Update first block (streaming)
143149
</button>
144-
</div>
145-
</div>
146-
);
147-
}
150+
<button
151+
className={"edit-button"}
152+
onClick={async () => {
153+
const blockToChange = editor.document[1].id;
148154

149-
// Formatting toolbar with the `AIToolbarButton` added
150-
function FormattingToolbarWithAI() {
151-
return (
152-
<FormattingToolbarController
153-
formattingToolbar={() => (
154-
<FormattingToolbar>
155-
{...getFormattingToolbarItems()}
156-
{/* Add the AI button */}
157-
<AIToolbarButton />
158-
</FormattingToolbar>
159-
)}
160-
/>
161-
);
162-
}
155+
// Let's get the stream tools so we can invoke them manually
156+
// In this case, we choose to only get the "update" tool
157+
const tools = llmFormats.html.getStreamTools(editor, true, {
158+
// only allow "update" operations
159+
update: true,
160+
});
163161

164-
// Slash menu with the AI option added
165-
function SuggestionMenuWithAI(props: {
166-
editor: BlockNoteEditor<any, any, any>;
167-
}) {
168-
return (
169-
<SuggestionMenuController
170-
triggerCharacter="/"
171-
getItems={async (query) =>
172-
filterSuggestionItems(
173-
[
174-
...getDefaultReactSlashMenuItems(props.editor),
175-
// add the default AI slash menu items, or define your own
176-
...getAISlashMenuItems(props.editor),
177-
],
178-
query,
179-
)
180-
}
181-
/>
162+
// Create an executor that can execute StreamToolCalls
163+
const executor = new StreamToolExecutor(tools);
164+
165+
// We'll stream two updates: a partial update and a full update
166+
// to use streaming operations, we need to get a writer
167+
const writer = executor.writable.getWriter();
168+
169+
// write a partial update, notice how the JSON is cut off (simulating a streaming json response)
170+
writer.write(
171+
`{
172+
"type": "update",
173+
"id": ${JSON.stringify(blockToChange + "$")},
174+
"block": "<p>This Open source software like Hello World refers to computer programs, this is a longer update, let's write a first sentence that's quite long long long long here.`,
175+
);
176+
await new Promise((resolve) => setTimeout(resolve, 3000));
177+
writer.write(`{
178+
"type": "update",
179+
"id": ${JSON.stringify(blockToChange + "$")},
180+
"block":
181+
"<p>This Open source software like Hello World refers to computer programs, this is a longer update, let's write a first sentence that's quite long long long long here. And now let's write a second sentence.</p>"
182+
}`);
183+
184+
// close the writer
185+
writer.close();
186+
187+
// wait till the executor is done
188+
await executor.waitTillEnd();
189+
190+
// accept the changes after 1 second
191+
await new Promise((resolve) => setTimeout(resolve, 1000));
192+
await getAIExtension(editor).acceptChanges();
193+
}}
194+
>
195+
Update first block (streaming strings)
196+
</button>
197+
</div>
198+
</div>
182199
);
183200
}

packages/xl-ai/src/api/LLMRequest.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ export type LLMRequestOptions = {
6262
* @default { add: true, update: true, delete: true }
6363
*/
6464
defaultStreamTools?: {
65-
/** Enable the add tool (default: true) */
65+
/** Enable the add tool (default: false) */
6666
add?: boolean;
67-
/** Enable the update tool (default: true) */
67+
/** Enable the update tool (default: false) */
6868
update?: boolean;
69-
/** Enable the delete tool (default: true) */
69+
/** Enable the delete tool (default: false) */
7070
delete?: boolean;
7171
};
7272
/**

packages/xl-ai/src/api/formats/base-tools/createUpdateBlockTool.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,9 @@ export function createUpdateBlockTool<T>(config: {
196196
}
197197

198198
const operation = chunk.operation as UpdateBlockToolCall<T>;
199-
console.log("first op");
200199
if (chunk.isPossiblyPartial) {
201200
const size = JSON.stringify(operation.block).length;
202201
if (size < minSize) {
203-
console.log("skipping", size, minSize);
204202
continue;
205203
} else {
206204
// increase minSize for next chunk
@@ -225,7 +223,6 @@ export function createUpdateBlockTool<T>(config: {
225223

226224
const jsonToolCall = await config.toJSONToolCall(editor, chunk);
227225
if (!jsonToolCall) {
228-
console.log("no jsonToolCall");
229226
continue;
230227
}
231228

@@ -244,7 +241,6 @@ export function createUpdateBlockTool<T>(config: {
244241
// if there's only a single replace step to be done and we're partial, let's wait for more content
245242

246243
// REC: unit test this and see if it's still needed even if we pass `dontReplaceContentAtEnd` to `updateToReplaceSteps`
247-
console.log("skipping", steps.length, chunk.isPossiblyPartial);
248244
continue;
249245
}
250246

packages/xl-ai/src/api/formats/html-blocks/htmlBlocks.ts

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,48 @@ import {
88
} from "./htmlPromptData.js";
99
import { tools } from "./tools/index.js";
1010

11-
function getStreamTools(
11+
// Import the tool call types
12+
import { AddBlocksToolCall } from "../base-tools/createAddBlocksTool.js";
13+
import { UpdateBlockToolCall } from "../base-tools/createUpdateBlockTool.js";
14+
import { DeleteBlockToolCall } from "../base-tools/delete.js";
15+
16+
// Define the tool types
17+
export type AddTool = StreamTool<AddBlocksToolCall<string>>;
18+
export type UpdateTool = StreamTool<UpdateBlockToolCall<string>>;
19+
export type DeleteTool = StreamTool<DeleteBlockToolCall>;
20+
21+
// Create a conditional type that maps boolean flags to tool types
22+
export type StreamToolsConfig = {
23+
add?: boolean;
24+
update?: boolean;
25+
delete?: boolean;
26+
};
27+
28+
export type StreamToolsResult<T extends StreamToolsConfig> = [
29+
...(T extends { update: true } ? [UpdateTool] : []),
30+
...(T extends { add: true } ? [AddTool] : []),
31+
...(T extends { delete: true } ? [DeleteTool] : []),
32+
];
33+
34+
function getStreamTools<
35+
T extends StreamToolsConfig = { add: true; update: true; delete: true },
36+
>(
1237
editor: BlockNoteEditor<any, any, any>,
1338
withDelays: boolean,
14-
defaultStreamTools?: {
15-
/** Enable the add tool (default: true) */
16-
add?: boolean;
17-
/** Enable the update tool (default: true) */
18-
update?: boolean;
19-
/** Enable the delete tool (default: true) */
20-
delete?: boolean;
21-
},
39+
defaultStreamTools?: T,
2240
selectionInfo?: {
2341
from: number;
2442
to: number;
2543
},
2644
onBlockUpdate?: (blockId: string) => void,
27-
) {
28-
const mergedStreamTools = {
29-
add: true,
30-
update: true,
31-
delete: true,
32-
...defaultStreamTools,
33-
};
45+
): StreamToolsResult<T> {
46+
const mergedStreamTools =
47+
defaultStreamTools ??
48+
({
49+
add: true,
50+
update: true,
51+
delete: true,
52+
} as T);
3453

3554
const streamTools: StreamTool<any>[] = [
3655
...(mergedStreamTools.update
@@ -51,16 +70,14 @@ function getStreamTools(
5170
: []),
5271
];
5372

54-
return streamTools;
73+
return streamTools as StreamToolsResult<T>;
5574
}
5675

5776
export const htmlBlockLLMFormat = {
5877
/**
5978
* Function to get the stream tools that can apply HTML block updates to the editor
6079
*/
6180
getStreamTools,
62-
63-
streamTools: tools,
6481
/**
6582
* The default PromptBuilder that determines how a userPrompt is converted to an array of
6683
* LLM Messages (CoreMessage[])

0 commit comments

Comments
 (0)