From ec68856543955dd43beb81a2c3c5299412c68ec8 Mon Sep 17 00:00:00 2001 From: seven Date: Fri, 29 Mar 2024 19:19:04 +0800 Subject: [PATCH] wip: code suggestion #33 Signed-off-by: seven --- src/common/httpClient.ts | 63 ++++++++++++++++++++++++++++++++++++++ src/views/editor/index.vue | 42 ++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/common/httpClient.ts b/src/common/httpClient.ts index 11c1d770..ad073471 100644 --- a/src/common/httpClient.ts +++ b/src/common/httpClient.ts @@ -101,3 +101,66 @@ export const loadHttpClient = (con: { ssl: con.sslCertVerification, }), }); + +const MODEL = 'gpt-3.5-turbo-0125'; +let assistant = null; +export const loadAiClient = async () => { + const headers = { + 'Content-Type': 'application/json', + 'OpenAI-Beta': 'assistants=v1', + Authorization: `Bearer ${OPENAI_API_KEY}`, + }; + const { data, status, details } = await fetchApi.fetch('https://api.openai.com/v1/assistants', { + method: 'POST', + headers, + body: JSON.stringify({ + instructions: 'You are a personal math tutor. Write and run code to answer math questions.', + name: 'Math Tutor', + tools: [{ type: 'code_interpreter' }], + model: MODEL, + }), + }); + assistant = data as { id: string }; + const { data: thread, status: threadStatus } = await fetchApi.fetch( + `https://api.openai.com/v1/assistants/${assistant.id}/threads`, + { + method: 'POST', + headers, + body: JSON.stringify({ + messages: [ + { + role: 'system', + content: 'You are a personal math tutor. Write and run code to answer math questions.', + }, + ], + }), + }, + ); + + console.log(`gpt assistant: ${assistant}, thread ${thread}`); + if (status !== 200) { + throw new CustomError(status, details); + } + return { + suggest: async (fileContent: string, currentLineNumber: number) => { + const { data, status, details } = await fetchApi.fetch( + `https://api.openai.com/v1/threads/${(thread as { id: string }).id}/messages`, + { + method: 'POST', + headers, + body: JSON.stringify({ + messages: [ + { + role: 'system', + content: fileContent, + current_line_number: currentLineNumber, + }, + ], + }), + }, + ); + console.log(`gpt suggest: ${data}, status: ${status}, details: ${details}`); + return (data as { choices: Array<{ text: string }> }).choices[0].text.trim(); + }, + }; +}; diff --git a/src/views/editor/index.vue b/src/views/editor/index.vue index 6afe8a6e..87dfa503 100644 --- a/src/views/editor/index.vue +++ b/src/views/editor/index.vue @@ -19,6 +19,7 @@ import { } from '../../common'; import { useAppStore, useConnectionStore, useSourceFileStore } from '../../store'; import { useLang } from '../../lang'; +import { loadAiClient } from '../../common/httpClient'; type Editor = ReturnType; @@ -191,7 +192,12 @@ const executeQueryAction = async ( } }; -const setupQueryEditor = (code: string) => { +const { fetchApi } = window; +let aiClient: { + suggest: (text: string, rangeLength: number) => Promise; +} | null = null; + +const setupQueryEditor = async (code: string) => { queryEditor = monaco.editor.create(queryEditorRef.value, { automaticLayout: true, theme: getEditorTheme(), @@ -199,6 +205,10 @@ const setupQueryEditor = (code: string) => { language: 'search', }); + if (!aiClient) { + aiClient = await loadAiClient(); + } + autoIndentCmdId = queryEditor.addCommand( 0, ( @@ -263,6 +273,35 @@ const setupQueryEditor = (code: string) => { executeQueryAction(queryEditor, displayEditor, target.position); } }); + + // Event listener for user input + queryEditor.onDidChangeModelContent(async ({ range, rangeLength, text }) => { + const suggestion = await aiClient.suggest(text, 0); + + const { status, data, details } = await fetchApi.fetch( + 'http://your-backend-service/api/suggest', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ code: text }), + }, + ); + if (status !== 200) { + message.error(details, { + closable: true, + keepAliveOnHover: true, + }); + return; + } + queryEditor.suggest( + (data as Array<{ label: string; kind: number }>).map(suggestion => ({ + label: suggestion.label, + kind: monaco.languages.CompletionItemKind[suggestion.kind], + })), + ); + }); }; const toggleEditor = (editorRef: Ref, display: string) => { editorRef.value.style.display = display; @@ -280,6 +319,7 @@ const setupJsonEditor = () => { minimap: { enabled: false }, }); }; + onMounted(async () => { await readSourceFromFile(); const code = defaultFile.value;