Skip to content

Commit

Permalink
chore: update for comopletion
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Dec 12, 2023
1 parent 842a4c7 commit 6f50a40
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 128 deletions.
265 changes: 137 additions & 128 deletions web/core/lib/editor/action/AiActionExecutor.ts
Original file line number Diff line number Diff line change
@@ -1,133 +1,142 @@
import { Editor } from "@tiptap/core";
import { ChangeForm, OutputForm, PromptAction } from "@/editor/defs/custom-action.type";
import { actionPosition, PromptCompiler } from "@/editor/action/PromptCompiler";
import { Editor } from '@tiptap/core';
import { ChangeForm, OutputForm, PromptAction } from '@/editor/defs/custom-action.type';
import { actionPosition, PromptCompiler } from '@/editor/action/PromptCompiler';

// @ts-ignore
import { MarkdownParser } from "@/../node_modules/tiptap-markdown/src/parse/MarkdownParser";
import { BuiltinFunctionExecutor } from "@/editor/action/BuiltinFunctionExecutor";
import { MarkdownParser } from '@/../node_modules/tiptap-markdown/src/parse/MarkdownParser';
import { BuiltinFunctionExecutor } from '@/editor/action/BuiltinFunctionExecutor';

export class AiActionExecutor {
private readonly editor: Editor;

constructor(editor: Editor) {
this.editor = editor;
}

private async handleStreaming(action: PromptAction, prompt: string) {
this.editor.setEditable(false);
const originalSelection = this.editor.state.selection;

const response = await fetch("/api/completion/yiyan", {
method: "POST",
body: JSON.stringify({ prompt: prompt }),
});

let allText = "";
let buffer = "";
await response.body?.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write: (chunk) => {
allText += chunk;
buffer = buffer.concat(chunk);

if (buffer.includes("\n")) {
const pos = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(pos, buffer).run();

// insert new line
const posInfo = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(posInfo, "\n").run();

buffer = "";
}
},
})
);

const pos = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(pos, buffer).run();

let markdownParser = new MarkdownParser(this.editor, {});
let markdownNode = markdownParser.parse(allText);

this.editor.chain().focus()?.deleteRange({
from: originalSelection.from,
to: this.editor.state.selection.to
}).run();

this.editor.chain().insertContentAt(this.editor.state.selection, markdownNode).run();

this.editor.setEditable(true);
}

private async handleTextOrDiff(_action: PromptAction, prompt: string): Promise<string | undefined> {
// @ts-ignore
this.editor.commands?.setTrackChangeStatus(true);

this.editor.setEditable(false);

const response = await fetch("/api/completion/yiyan", {
method: "POST",
body: JSON.stringify({ prompt: prompt }),
});

const text = await response.text();
this.editor.setEditable(true);

// @ts-ignore
this.editor.commands?.setTrackChangeStatus(false);
return text;
}

private async handleDefault(action: PromptAction, prompt: string) {
this.editor.setEditable(false);
const response = await fetch("/api/completion/yiyan", {
method: "POST",
body: JSON.stringify({ prompt: prompt }),
});

const msg = await response.text();
const posInfo = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(posInfo, msg).run();

this.editor.setEditable(true);
}

public async execute(action: PromptAction) {
console.log("execute action", action)
if (action.builtinFunction) {
let executor = new BuiltinFunctionExecutor(this.editor);
return await executor.execute(action);
}

const actionExecutor = new PromptCompiler(action, this.editor);
actionExecutor.compile();

let prompt = action.compiledTemplate;

if (prompt == null) {
throw Error("template is not been compiled yet! compile it first");
}

console.info("compiledTemplate: \n\n", prompt);

if (action.changeForm == ChangeForm.DIFF) {
// @ts-ignore
this.editor.commands?.setTrackChangeStatus(true);
}

switch (action.outputForm) {
case OutputForm.STREAMING:
await this.handleStreaming(action, prompt);
return undefined;

case OutputForm.DIFF:
case OutputForm.TEXT:
return await this.handleTextOrDiff(action, prompt);

default:
await this.handleDefault(action, prompt);
return undefined;
}
}
private readonly editor: Editor;

constructor(editor: Editor) {
this.editor = editor;
}

/**
* TODO: will according the {@link PromptAction.useModel} to return the endpoint in future
* @param action
*/
endpoint(action: PromptAction) {
const endpoint = '/api/completion/mock';
return endpoint;
}

private async handleStreaming(action: PromptAction, prompt: string) {
this.editor.setEditable(false);
const originalSelection = this.editor.state.selection;

const response = await fetch(this.endpoint(action), {
method: 'POST',
body: JSON.stringify({ prompt: prompt })
});

let allText = '';
let buffer = '';
await response.body?.pipeThrough(new TextDecoderStream()).pipeTo(
new WritableStream({
write: (chunk) => {
allText += chunk;
buffer = buffer.concat(chunk);

if (buffer.includes('\n')) {
const pos = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(pos, buffer).run();

// insert new line
const posInfo = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(posInfo, '\n').run();

buffer = '';
}
}
})
);

const pos = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(pos, buffer).run();

let markdownParser = new MarkdownParser(this.editor, {});
let markdownNode = markdownParser.parse(allText);

this.editor.chain().focus()?.deleteRange({
from: originalSelection.from,
to: this.editor.state.selection.to
}).run();

this.editor.chain().insertContentAt(this.editor.state.selection, markdownNode).run();

this.editor.setEditable(true);
}

private async handleTextOrDiff(action: PromptAction, prompt: string): Promise<string | undefined> {
// @ts-ignore
this.editor.commands?.setTrackChangeStatus(true);

this.editor.setEditable(false);

const response = await fetch(this.endpoint(action), {
method: 'POST',
body: JSON.stringify({ prompt: prompt })
});

const text = await response.text();
this.editor.setEditable(true);

// @ts-ignore
this.editor.commands?.setTrackChangeStatus(false);
return text;
}

private async handleDefault(action: PromptAction, prompt: string) {
this.editor.setEditable(false);
const response = await fetch(this.endpoint(action), {
method: 'POST',
body: JSON.stringify({ prompt: prompt })
});

const msg = await response.text();
const posInfo = actionPosition(action, this.editor.state.selection);
this.editor.chain().focus()?.insertContentAt(posInfo, msg).run();

this.editor.setEditable(true);
}

public async execute(action: PromptAction) {
console.log('execute action', action);
if (action.builtinFunction) {
let executor = new BuiltinFunctionExecutor(this.editor);
return await executor.execute(action);
}

const actionExecutor = new PromptCompiler(action, this.editor);
actionExecutor.compile();

let prompt = action.compiledTemplate;

if (prompt == null) {
throw Error('template is not been compiled yet! compile it first');
}

console.info('compiledTemplate: \n\n', prompt);

if (action.changeForm == ChangeForm.DIFF) {
// @ts-ignore
this.editor.commands?.setTrackChangeStatus(true);
}

switch (action.outputForm) {
case OutputForm.STREAMING:
await this.handleStreaming(action, prompt);
return undefined;

case OutputForm.DIFF:
case OutputForm.TEXT:
return await this.handleTextOrDiff(action, prompt);

default:
await this.handleDefault(action, prompt);
return undefined;
}
}
}
19 changes: 19 additions & 0 deletions web/studio/app/api/completion/mock/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { OpenAIStream, StreamingTextResponse } from 'ai';


export async function POST(req: Request) {
const { prompt } = await req.json();

// mock response to return text stream , "Hello World" is the response
const response = new ReadableStream({
start(controller) {
controller.enqueue("Hello World, this is from mock api");
controller.close();
}
});

// Convert the response into a friendly text-stream
const stream = OpenAIStream(new Response(response));
// Respond with the stream
return new StreamingTextResponse(stream);
}

0 comments on commit 6f50a40

Please sign in to comment.