Compare commits

..

6 commits

Author SHA1 Message Date
igardev
f7525ce0f1 Update documentation 2026-04-29 20:55:16 +03:00
igardev
0c3908876d Refactor the generated code to comply with the standard approach.
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 20:19:25 +03:00
igardev
78bafe0f8f Use Tools model from llama.cpp instead of chat mode, fix the authentication problem
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 19:00:17 +03:00
copilot-swe-agent[bot]
582f16fb88 Add llama.cpp model provider for GitHub Copilot Chat
Co-authored-by: ggerganov <1991296+ggerganov@users.noreply.github.com>
2026-03-04 12:00:42 +00:00
copilot-swe-agent[bot]
fe1b58886a Initial plan for llama.cpp model provider for GitHub Copilot Chat
Co-authored-by: ggerganov <1991296+ggerganov@users.noreply.github.com>
2026-03-04 11:55:39 +00:00
copilot-swe-agent[bot]
a582118c6f Initial plan 2026-03-04 11:20:05 +00:00
4 changed files with 25 additions and 255 deletions

View file

@ -2,7 +2,7 @@
"name": "llama-vscode",
"displayName": "llama-vscode",
"description": "Local LLM-assisted text completion using llama.cpp",
"version": "0.0.47",
"version": "0.0.45",
"publisher": "ggml-org",
"repository": "https://github.com/ggml-org/llama.vscode",
"engines": {

View file

@ -79,36 +79,7 @@ export const PREDEFINED_LISTS = new Map<string, any>([
"endpoint": "http://127.0.0.1:8010"
}
]],
[PREDEFINED_LISTS_KEYS.TOOLS,
[
{
"name": "Qwen3.5-2B-GGUF:Q8_0 (LOCAL) (CPU)",
"localStartCommand": "llama-server -hf unsloth/Qwen3.5-2B-GGUF:Q8_0 --jinja -c 0 -ub 1024 -b 1024 --cache-reuse 256 --port 8009 --host 127.0.0.1",
"endpoint": "http://localhost:8009",
"aiModel": "",
"isKeyRequired": false
},
{
"name": "Qwen3.5-2B-GGUF:Q8_0 (LOCAL) (VRAM>3GB)",
"localStartCommand": "llama-server -hf unsloth/Qwen3.5-2B-GGUF:Q8_0 --jinja -ngl 99 -c 0 -ub 1024 -b 1024 --cache-reuse 256 --port 8009 --host 127.0.0.1",
"endpoint": "http://localhost:8009",
"aiModel": "",
"isKeyRequired": false
},
{
"name": "Qwen3.5-4B-GGUF:Q8_0 (LOCAL) (VRAM>6GB)",
"localStartCommand": "llama-server -hf unsloth/Qwen3.5-4B-GGUF:Q8_0 --jinja -c 0 -ub 1024 -b 1024 --cache-reuse 256 --port 8009 --host 127.0.0.1",
"endpoint": "http://localhost:8009",
"aiModel": "",
"isKeyRequired": false
},
{
"name": "Qwen3.5-9B-GGUF:Q8_0 (LOCAL) (VRAM>12GB)",
"localStartCommand": "llama-server -hf unsloth/Qwen3.5-9B-GGUF:Q8_0 --jinja -c 0 -ub 1024 -b 1024 --cache-reuse 256 --port 8009 --host 127.0.0.1",
"endpoint": "http://localhost:8009",
"aiModel": "",
"isKeyRequired": false
},
[PREDEFINED_LISTS_KEYS.TOOLS, [
{
"name": "OpenAI gpt-oss 20B (LOCAL) (> 19GB VRAM)",
"localStartCommand": "llama-server -hf ggml-org/gpt-oss-20b-GGUF -c 0 --jinja --reasoning-format none -np 2 --port 8009",

View file

@ -44,13 +44,13 @@ export class OpenAiCompModelStrategy implements IAddStrategy {
prompt: 'example: http://localhost:8080 or https://openrauter.ai/api'
})??""
isKeyRequired = await Utils.confirmAction(`Is API key required for this endpoint (${endpoint})?`, "");
}
}
if (!endpoint){
vscode.window.showWarningMessage("Endpoint is not provided!")
return;
}
const providerModels: QuickPickItem[] = [];
const models = await this.getModels(endpoint, isKeyRequired);
const models = await this.getModels(endpoint);
if (models.length == 0) {
vscode.window.showInformationMessage("No models are found.")
return
@ -108,50 +108,30 @@ export class OpenAiCompModelStrategy implements IAddStrategy {
}
}
private async getModels(endpoint: string, isKeyRequired: boolean): Promise<OpenAiCompModel[]> {
const hfEndpoint = Utils.trimTrailingSlash(endpoint) + "/v1/models";
// Create a request configuration
let requestConfig: any = {};
if (isKeyRequired) {
// We get the saved key for this specific endpoint
const apiKey = this.app.persistence.getApiKey(endpoint);
if (apiKey) {
requestConfig = {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
};
}
}
private async getModels(endpoint: string): Promise<OpenAiCompModel[]> {
let hfEndpoint = Utils.trimTrailingSlash(endpoint) +"/v1/models";
try {
const result = await axios.default.get(
`${Utils.trimTrailingSlash(hfEndpoint)}`,
requestConfig
let result = await axios.default.get(
`${Utils.trimTrailingSlash(hfEndpoint)}`
);
let models: OpenAiCompModel[] = [];
let modelsList: OpenAiCompModel[] = [];
if (result && result.data && result.data.models) modelsList = result.data.models;
else if (result && result.data && result.data.data) modelsList = result.data.data;
if (modelsList.length > 0) {
for (let mdl of modelsList) {
models.push(mdl);
let modelsList: OpenAiCompModel[] = []
if (result && result.data && result.data.models) modelsList = result.data.models
else if (result && result.data && result.data.data) modelsList = result.data.data
if (modelsList.length > 0){
for(let mdl of modelsList){
models.push(mdl)
}
}
return models;
} catch (error) {
vscode.window.showErrorMessage("Error getting provider models: " + error);
} catch (error){
vscode.window.showErrorMessage("Error getting provider models): " + error)
return [];
}
}
private sanitizeInput(input: string): string {
return input ? input.trim() : '';
}

View file

@ -26,192 +26,6 @@ export class TextEditor {
vscode.commands.executeCommand('setContext', 'textEditSuggestionVisible', visible);
}
private escapeWebviewAttr(value: string): string {
return value
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;');
}
/**
* Multiline instructions (webview); resolves undefined if cancelled or closed.
*/
private showMultilineEditPrompt(): Promise<string | undefined> {
const title =
this.app.configuration.getUiText('How would you like to modify the selected text?') ??
'How would you like to modify the selected text?';
const placeholder =
this.app.configuration.getUiText('Enter your instructions for editing the text...') ??
'Enter your instructions for editing the text...';
const submitLabel = this.app.configuration.getUiText('Submit') ?? 'Submit';
const cancelLabel = this.app.configuration.getUiText('Cancel') ?? 'Cancel';
const emptyHint =
this.app.configuration.getUiText('Please enter editing instructions.') ??
'Please enter editing instructions.';
return new Promise((resolve) => {
let settled = false;
const panel = vscode.window.createWebviewPanel(
'editWithAiMultilinePrompt',
title,
{ viewColumn: vscode.ViewColumn.Beside, preserveFocus: false },
{ enableScripts: true }
);
const finish = (value: string | undefined) => {
if (settled) {
return;
}
settled = true;
resolve(value);
panel.dispose();
};
const cspSource = panel.webview.cspSource;
panel.webview.html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${cspSource} 'unsafe-inline'; script-src 'unsafe-inline' ${cspSource};">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
box-sizing: border-box;
margin: 0;
padding: 12px;
height: 100vh;
display: flex;
flex-direction: column;
font-family: var(--vscode-font-family);
font-size: var(--vscode-font-size);
color: var(--vscode-foreground);
background-color: var(--vscode-editor-background);
}
label {
margin-bottom: 8px;
}
textarea {
flex: 1;
min-height: 120px;
resize: vertical;
padding: 8px;
border: 1px solid var(--vscode-input-border);
background: var(--vscode-input-background);
color: var(--vscode-input-foreground);
font-family: var(--vscode-editor-font-family);
font-size: var(--vscode-editor-font-size);
}
textarea:focus {
outline: 1px solid var(--vscode-focusBorder);
}
.actions {
margin-top: 12px;
display: flex;
gap: 8px;
justify-content: flex-end;
}
/* DOM order is Submit then Cancel (Tab: textarea → Submit → Cancel); flex order keeps Cancel left, Submit right. */
.actions .secondary {
order: 1;
}
.actions .primary {
order: 2;
}
button {
padding: 6px 14px;
border: none;
cursor: pointer;
font-size: var(--vscode-font-size);
}
.primary {
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
}
.primary:hover {
background: var(--vscode-button-hoverBackground);
}
.secondary {
background: var(--vscode-button-secondaryBackground);
color: var(--vscode-button-secondaryForeground);
}
.secondary:hover {
background: var(--vscode-button-secondaryHoverBackground);
}
</style>
</head>
<body>
<label for="prompt">${this.escapeWebviewAttr(title)}</label>
<textarea id="prompt" placeholder="${this.escapeWebviewAttr(placeholder)}" autofocus></textarea>
<div class="actions">
<button type="button" class="primary" id="submit">${this.escapeWebviewAttr(submitLabel)}</button>
<button type="button" class="secondary" id="cancel">${this.escapeWebviewAttr(cancelLabel)}</button>
</div>
<script>
const vscode = acquireVsCodeApi();
const ta = document.getElementById('prompt');
function focusPrompt() {
if (!ta) {
return;
}
ta.focus();
const len = ta.value.length;
ta.setSelectionRange(len, len);
}
window.addEventListener('load', focusPrompt);
requestAnimationFrame(focusPrompt);
setTimeout(focusPrompt, 0);
setTimeout(focusPrompt, 100);
window.addEventListener('message', (event) => {
const data = event.data;
if (data && data.command === 'focusPrompt') {
focusPrompt();
}
});
document.getElementById('submit').addEventListener('click', () => {
vscode.postMessage({ command: 'submit', text: ta.value });
});
document.getElementById('cancel').addEventListener('click', () => {
vscode.postMessage({ command: 'cancel' });
});
</script>
</body>
</html>`;
const requestPromptFocus = () => {
void panel.webview.postMessage({ command: 'focusPrompt' });
};
panel.onDidChangeViewState((e) => {
if (e.webviewPanel.visible) {
requestPromptFocus();
}
});
requestPromptFocus();
setTimeout(requestPromptFocus, 50);
setTimeout(requestPromptFocus, 200);
panel.webview.onDidReceiveMessage((message) => {
if (message.command === 'submit') {
const text = typeof message.text === 'string' ? message.text : '';
if (!text.trim()) {
void vscode.window.showInformationMessage(emptyHint);
return;
}
finish(text);
} else if (message.command === 'cancel') {
finish(undefined);
}
});
panel.onDidDispose(() => {
if (!settled) {
settled = true;
resolve(undefined);
}
});
});
}
async showEditPrompt(editor: vscode.TextEditor) {
let chatUrl = this.app.configuration.endpoint_chat
if (!chatUrl) chatUrl = this.app.configuration.endpoint_tools;
@ -250,7 +64,12 @@ export class TextEditor {
const contextRange = new vscode.Range(startLine, 0, endLine, editor.document.lineAt(endLine).text.length);
const context = editor.document.getText(contextRange);
const prompt = await this.showMultilineEditPrompt();
// Create and show input box
const prompt = await vscode.window.showInputBox({
placeHolder: 'Enter your instructions for editing the text...',
prompt: 'How would you like to modify the selected text?',
ignoreFocusOut: true
});
if (!prompt) {
return;