-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(wip 2) calling the language model api
- Loading branch information
Showing
1 changed file
with
92 additions
and
26 deletions.
There are no files selected for viewing
118 changes: 92 additions & 26 deletions
118
packages/vscode-extension/src/common/completionProvider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,104 @@ | ||
/* eslint-disable no-empty */ | ||
/* eslint-disable no-unused-vars */ | ||
|
||
import { | ||
InlineCompletionContext, | ||
InlineCompletionItem, | ||
InlineCompletionItemProvider, | ||
LanguageModelChatMessage, | ||
lm, | ||
Position, | ||
Range, | ||
TextDocument, | ||
} from 'vscode'; | ||
import { CancellationToken } from 'vscode-languageclient'; | ||
import { CancellationToken, CancellationTokenSource } from 'vscode-languageclient'; | ||
|
||
const ANNOTATION_PROMPT = `You are a code tutor who helps liquid developers learn to use modern Liquid features. | ||
Your job is to evaluate a block of code and suggest opportunities to use newer Liquid filters and tags that could improve the code. Look specifically for: | ||
1. For loops that could be simplified using the new 'find' filter | ||
2. Array operations that could use 'map', 'where', or other newer filters | ||
3. Complex logic that could be simplified with 'case/when' | ||
4. Instead of "array | where: field, value | first", use "array | find: field, value" | ||
5. Your response must be a parsable json | ||
Format your response as a JSON object with the following structure: | ||
{ | ||
"range": { | ||
"start": {"line": <line number>, "character": <character number>}, | ||
"end": {"line": <line number>, "character": <character number>} | ||
}, | ||
"newCode": "The suggested code that will replace the current code", | ||
"line": <line number>, | ||
"suggestion": "Friendly explanation of how and why to use the new feature" | ||
} | ||
Example respons (): | ||
{ | ||
"range": { | ||
"start": {"line": 5, "character": 0}, | ||
"end": {"line": 7, "character": 42} | ||
}, | ||
"newCode": "{% assign first_product = products | first %}", | ||
"line": 5, | ||
"suggestion": "Instead of using a for loop to get the first item, you could use the 'first' filter. This is more concise and clearly shows your intent." | ||
} | ||
`; | ||
|
||
export default class LiquidCompletionProvider implements InlineCompletionItemProvider { | ||
provideInlineCompletionItems( | ||
async provideInlineCompletionItems( | ||
document: TextDocument, | ||
position: Position, | ||
context: InlineCompletionContext, | ||
token: CancellationToken, | ||
): InlineCompletionItem[] { | ||
console.error('[SERVER]!!! inline completion >>>'); | ||
console.error( | ||
'[SERVER]!!! inline completion document -', | ||
JSON.stringify(document as any, undefined, 2), | ||
); | ||
console.error( | ||
'[SERVER]!!! inline completion position -', | ||
JSON.stringify(position as any, undefined, 2), | ||
); | ||
console.error( | ||
'[SERVER]!!! inline completion context -', | ||
JSON.stringify(context as any, undefined, 2), | ||
); | ||
console.error( | ||
'[SERVER]!!! inline completion token -', | ||
JSON.stringify(token as any, undefined, 2), | ||
); | ||
console.error('[SERVER]!!! inline completion <<<'); | ||
|
||
return [new InlineCompletionItem('Hello from the inline completion provider')]; | ||
_position: Position, | ||
_context: InlineCompletionContext, | ||
_token: CancellationToken, | ||
) { | ||
console.error('[SERVER] inline completion'); | ||
|
||
let [model] = await lm.selectChatModels({ | ||
vendor: 'copilot', | ||
family: 'gpt-4o', | ||
}); | ||
|
||
function getVisibleCodeWithLineNumbers(document: TextDocument) { | ||
let code = ''; | ||
const lines = document.getText().split('\n'); | ||
for (let i = 0; i < lines.length; i++) { | ||
code += `${i + 1}: ${lines[i]}\n`; | ||
} | ||
return code; | ||
} | ||
|
||
const codeWithLineNumbers = getVisibleCodeWithLineNumbers(document); | ||
|
||
const messages = [ | ||
LanguageModelChatMessage.User(ANNOTATION_PROMPT), | ||
LanguageModelChatMessage.User(codeWithLineNumbers), | ||
]; | ||
|
||
let accumulatedResponse = ''; | ||
let annotation: any = {}; | ||
|
||
if (model) { | ||
let chatResponse = await model.sendRequest(messages, {}, new CancellationTokenSource().token); | ||
|
||
for await (const fragment of chatResponse.text) { | ||
accumulatedResponse += fragment; | ||
|
||
if (fragment.includes('}')) { | ||
try { | ||
annotation = JSON.parse(accumulatedResponse.replace('```json', '')); | ||
accumulatedResponse = ''; | ||
} catch (e) {} | ||
} | ||
} | ||
} | ||
|
||
// const range = new Range( | ||
// new Position(annotation.range.start.line - 1, annotation.range.start.character), | ||
// new Position(annotation.range.end.line - 1, annotation.range.end.character), | ||
// ); | ||
|
||
return [new InlineCompletionItem(annotation.newCode)]; | ||
} | ||
} |