Skip to content

Commit 656d9eb

Browse files
committed
connect api
1 parent b87a4ab commit 656d9eb

File tree

3 files changed

+83
-14
lines changed

3 files changed

+83
-14
lines changed

packages/xl-ai-server/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { cors } from "hono/cors";
55
import { existsSync, readFileSync } from "node:fs";
66
import { createSecureServer } from "node:http2";
77
import { Agent, setGlobalDispatcher } from "undici";
8+
import { autocompleteRoute } from "./routes/autocomplete.js";
89
import { modelPlaygroundRoute } from "./routes/model-playground/index.js";
910
import { objectGenerationRoute } from "./routes/objectGeneration.js";
1011
import { proxyRoute } from "./routes/proxy.js";
@@ -37,6 +38,7 @@ app.route("/ai/proxy", proxyRoute);
3738
app.route("/ai/object-generation", objectGenerationRoute);
3839
app.route("/ai/server-promptbuilder", serverPromptbuilderRoute);
3940
app.route("/ai/model-playground", modelPlaygroundRoute);
41+
app.route("/ai/autocomplete", autocompleteRoute);
4042

4143
const http2 = existsSync("localhost.pem");
4244
serve(
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { createGroq } from "@ai-sdk/groq";
2+
import { generateText } from "ai";
3+
import { Hono } from "hono";
4+
5+
export const autocompleteRoute = new Hono();
6+
7+
// Setup your model
8+
// const model = createOpenAI({
9+
// apiKey: process.env.OPENAI_API_KEY,
10+
// })("gpt-4.1-nano");
11+
12+
const model = createGroq({
13+
apiKey: process.env.GROQ_API_KEY,
14+
})("openai/gpt-oss-20b");
15+
16+
// Use `streamText` to stream text responses from the LLM
17+
autocompleteRoute.post("/generateText", async (c) => {
18+
const { text } = await c.req.json();
19+
20+
const result = await generateText({
21+
model,
22+
system: `You are a writing assistant. Predict and generate the most likely next part of the text.
23+
- separate suggestions by newlines
24+
- max 3 suggestions
25+
- keep it short, max 5 words per suggestion
26+
- don't include other text (or explanations)
27+
- ONLY return the text to be appended. Your suggestion will EXACTLY replace [SUGGESTION_HERE].
28+
- DONT include the original text / characters (prefix)
29+
- add a space (or other relevant punctuation) before the suggestion if starting a new word`,
30+
messages: [
31+
{
32+
role: "user",
33+
content: `Complete the following text:
34+
${text}[SUGGESTION_HERE]`,
35+
},
36+
],
37+
abortSignal: c.req.raw.signal,
38+
});
39+
40+
return c.json({
41+
suggestions: result.text
42+
.split("\n")
43+
.map((suggestion) => suggestion.trimEnd())
44+
.filter((suggestion) => suggestion.trim().length > 0),
45+
});
46+
});

packages/xl-ai/src/plugins/AutoCompletePlugin.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,34 @@ async function fetchAutoCompleteSuggestions(
4646
state: EditorState,
4747
_signal: AbortSignal,
4848
) {
49-
console.log("fetch");
50-
return [
51-
{
52-
position: state.selection.from,
53-
suggestion: "Hello World",
54-
},
49+
// TODO: options to get block json until selection
50+
const text = state.doc.textBetween(
51+
state.selection.from - 300,
52+
state.selection.from,
53+
);
54+
55+
const response = await fetch(
56+
`https://localhost:3000/ai/autocomplete/generateText`,
5557
{
56-
position: state.selection.from,
57-
suggestion: "Hello Planet",
58+
method: "POST",
59+
body: JSON.stringify({ text }),
5860
},
59-
];
61+
);
62+
const data = await response.json();
63+
return data.suggestions.map((suggestion: string) => ({
64+
position: state.selection.from,
65+
suggestion: suggestion,
66+
}));
67+
// return [
68+
// {
69+
// position: state.selection.from,
70+
// suggestion: "Hello World",
71+
// },
72+
// {
73+
// position: state.selection.from,
74+
// suggestion: "Hello Planet",
75+
// },
76+
// ];
6077
}
6178

6279
function getMatchingSuggestions(
@@ -136,7 +153,10 @@ export class AutoCompleteProseMirrorPlugin<
136153
},
137154
);
138155

139-
constructor(private readonly editor: BlockNoteEditor<BSchema, I, S>) {
156+
constructor(
157+
private readonly editor: BlockNoteEditor<BSchema, I, S>,
158+
options: {},
159+
) {
140160
super();
141161

142162
// eslint-disable-next-line @typescript-eslint/no-this-alias
@@ -416,14 +436,15 @@ export interface DebouncedFunction<T extends any[], R> {
416436
// TODO: test with Collaboration edits
417437
// TODO: compare kilocode / cline etc
418438
// TODO: think about advanced scenarios (e.g.: multiple suggestions, etc.)
419-
439+
// TODO: double tap -> extra long
420440
/**
421441
* Create a new AIExtension instance, this can be passed to the BlockNote editor via the `extensions` option
422442
*/
423-
export function createAIAutoCompleteExtension() {
424-
// options: ConstructorParameters<typeof AIAutoCom>[1],
443+
export function createAIAutoCompleteExtension(
444+
options: ConstructorParameters<typeof AutoCompleteProseMirrorPlugin>[1],
445+
) {
425446
return (editor: BlockNoteEditor<any, any, any>) => {
426-
return new AutoCompleteProseMirrorPlugin(editor);
447+
return new AutoCompleteProseMirrorPlugin(editor, options);
427448
};
428449
}
429450

0 commit comments

Comments
 (0)