Skip to content

Commit

Permalink
upgrade to v0.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
carusyte committed Feb 22, 2024
1 parent 4c820f7 commit 851dba6
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 42 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.1] - 2024-02-22

- Added configurable timeout setting and retry mechanism to accommodate Azure OpenAI / cloud service performance variation.
- Minor enhancement to code indentation in completion.
- Chat function can now take into account custom API parameters configured in settings.
- Update LICENSE template.

## [0.2.0] - 2024-02-07

- Support `2023-12-01-preview` API version and enable `response_format: {"type": "json_object"}` by default in code completion
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2024 carusyte

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
25 changes: 13 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "GAI Choy",
"description": "G̲enerative A̲I̲ empowered, C̲ode H̲elper O̲n Y̲our side. Yet another Copilot for coding, with built-in integration capability with Azure OpenAI models or, local LLM such as CodeShell.",
"publisher": "carusyte",
"version": "0.2.0",
"version": "0.2.1",
"icon": "assets/logo.png",
"pricing": "Free",
"keywords": [
Expand Down Expand Up @@ -63,7 +63,6 @@
"gpt-4",
"gpt-4-32k"
],
"when": "config.GAIChoy.RunEnvForLLMs == 'Azure OpenAI'",
"type": "string",
"order": 4
},
Expand All @@ -76,7 +75,6 @@
"gpt-4",
"gpt-4-32k"
],
"when": "config.GAIChoy.RunEnvForLLMs == 'Azure OpenAI'",
"type": "string",
"order": 5
},
Expand All @@ -85,7 +83,6 @@
"type": "null",
"scope": "application",
"markdownDescription": "[Set API Key](command:gaichoy.set_api_key)",
"when": "config.GAIChoy.RunEnvForLLMs == 'Azure OpenAI'",
"order": 6
},
"GAIChoy.ApiVersion": {
Expand All @@ -99,22 +96,26 @@
"2023-09-01-preview",
"2023-12-01-preview"
],
"when": "config.GAIChoy.RunEnvForLLMs == 'Azure OpenAI'",
"type": "string",
"order": 7
},
"GAIChoy.ApiParameters": {
"description": "The API parameters for Azure OpenAI. Format: key=value pairs delimited by semicolons.",
"default": "temperature=0.2",
"when": "config.GAIChoy.RunEnvForLLMs == 'Azure OpenAI'",
"type": "string",
"order": 8
},
"GAIChoy.ApiTimeout": {
"description": "The timeout in seconds before API call stops waiting or retrying.",
"type": "number",
"default": 60,
"order": 9
},
"GAIChoy.AutoTriggerCompletion": {
"description": "Whether or not to automatically trigger completion when typing.",
"default": false,
"type": "boolean",
"order": 9
"order": 10
},
"GAIChoy.AutoCompletionDelay": {
"description": "The delay in seconds before automatic code completion triggers.",
Expand All @@ -125,7 +126,7 @@
3
],
"default": 2,
"order": 10
"order": 11
},
"GAIChoy.CompletionMaxTokens": {
"description": "Maximum number of tokens for which suggestions will be displayed",
Expand All @@ -140,7 +141,7 @@
4096
],
"default": 64,
"order": 11
"order": 12
},
"GAIChoy.ChatMaxTokens": {
"description": "Maximum number of tokens for which chat messages will be displayed",
Expand All @@ -154,20 +155,20 @@
32768
],
"default": 2048,
"order": 12
"order": 13
},
"GAIChoy.EnableDebugMessage": {
"description": "Prints debug message to extension output.",
"type": "boolean",
"default": false,
"order": 13
"order": 14
},
"GAIChoy.ClearChatHistory": {
"description": "Clear the chat history",
"type": "null",
"scope": "application",
"markdownDescription": "[Clear chat history](command:gaichoy.clear_chat_history)",
"order": 14
"order": 15
}
}
},
Expand Down
7 changes: 7 additions & 0 deletions src/CodeShellCompletionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export class CodeShellCompletionProvider implements InlineCompletionItemProvider
if (index === 0){
return line;
}
// if the line contains leading whitespace with length equal or greater than indentation,
// that probably indicates the line has already been indented properly, and we can skip prepending
// excessive indentation in this case.
let leadingWhitespace = line.match(/^\s*/);
if (leadingWhitespace && leadingWhitespace[0].length >= indentation.length) {
return line;
}
return indentation + line;
});
return indentedLines.join("\n");
Expand Down
77 changes: 54 additions & 23 deletions src/FetchStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ExtensionResource from "./ExtensionResource";
export interface FetchStreamOptions {
url: string;
requestInit: RequestInit;
timeout: number;
onmessage: (data: string) => void;
ondone?: (data: Array<string>) => void;
onerror?: (response: Response) => void;
Expand All @@ -14,13 +15,15 @@ export interface FetchStreamOptions {
export class FetchStream {
url: string;
requestInit: RequestInit;
timeout: number;
onmessage: FetchStreamOptions["onmessage"];
ondone: FetchStreamOptions["ondone"];
onerror: FetchStreamOptions["onerror"];

constructor(options: FetchStreamOptions) {
this.url = options.url;
this.requestInit = options.requestInit;
this.timeout = options.timeout;
this.onmessage = options.onmessage;
this.ondone = options.ondone;
this.onerror = options.onerror;
Expand All @@ -35,28 +38,56 @@ export class FetchStream {
});
const xres = ExtensionResource.instance;
var responseMessage:Array<string> = [];
fetch(this.url, this.requestInit)
.then(response => {
if (response.status === 200) {
return response.body!;
} else {
return Promise.reject(response);
}
}).then(async (readableStream) => {
// read strings from readableStream chunk by chunk and feed into parser, push into responseMessage
for await (const chunk of readableStream) {
const chunkString = chunk.toString();
parser.feed(chunkString);
responseMessage.push(chunkString);
}
}).then(() => {
this.ondone?.(responseMessage);
}).catch(error => {
window.showErrorMessage(`${error}`);
window.setStatusBarMessage(`${error}`, 10000);
xres.errorMessage("caught error trying to fetch from " + this.url);
xres.errorMessage(error.message);
this.onerror?.(error);
});
// retry the following fetch action until success or this.timeout (seconds) is exceeded.
// each attempt to fetch should not exceed the remaining wait time, which equals this.timeout - total elapsed time.
const startTime = Date.now();
let elapsedTime = 0;

const fetchWithTimeout = () => {
const remainingTime = this.timeout * 1000 - elapsedTime;
if (remainingTime <= 0) {
this.onerror?.(new Response(null, { status: 408, statusText: 'FetchStream timeout exceeded.' }));
return;
}

const abortController = new AbortController();
const id = setTimeout(() => abortController.abort(), remainingTime);
this.requestInit.signal = abortController.signal;

fetch(this.url, this.requestInit)
.then(response => {
clearTimeout(id);
if (response.status === 200) {
return response.body!;
} else {
throw new Error(`HTTP error! status: ${response.status}`);
// return Promise.reject(response);
}
}).then(async (readableStream) => {
// read strings from readableStream chunk by chunk and feed into parser, push into responseMessage
for await (const chunk of readableStream) {
const chunkString = chunk.toString();
parser.feed(chunkString);
responseMessage.push(chunkString);
}
}).then(() => {
this.ondone?.(responseMessage);
}).catch(error => {
elapsedTime = Date.now() - startTime;
if (elapsedTime < this.timeout * 1000) {
xres.errorMessage("retrying error calling " + this.url);
xres.errorMessage(error.message);
fetchWithTimeout();
} else {
window.showErrorMessage(`${error}`);
window.setStatusBarMessage(`${error}`, 10000);
xres.errorMessage("caught error trying to fetch from " + this.url);
xres.errorMessage(error.message);
this.onerror?.(error);
}
});
};

fetchWithTimeout();
}
}
7 changes: 7 additions & 0 deletions src/RequestEventStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Agent } from "https";
import { workspace } from "vscode";
import { FetchStream } from "./FetchStream";
import { ChatItem } from "./ChatMemory";
import { AzureOAI } from "./llm/AzureOAI";
import AbortController from "abort-controller";
import ExtensionResource from "./ExtensionResource";

Expand All @@ -19,6 +20,8 @@ export async function postEventStream(prompt: string, chatList: Array<ChatItem>,
const maxtokens = workspace.getConfiguration("GAIChoy").get("ChatMaxTokens") as number;
const modelEnv = workspace.getConfiguration("GAIChoy").get("RunEnvForLLMs") as string;
const allowSelfSignedCert = workspace.getConfiguration("GAIChoy").get("AllowSelfSignedCert") as boolean
const parameters = workspace.getConfiguration("GAIChoy").get("ApiParameters") as string;
const timeout = workspace.getConfiguration("GAIChoy").get("ApiTimeout") as number;

// get API key from secret storage
let api_key = await ExtensionResource.instance.getApiKey();
Expand Down Expand Up @@ -88,6 +91,9 @@ export async function postEventStream(prompt: string, chatList: Array<ChatItem>,
// "max_tokens": maxtokens,
// "stop": ["|<end>|", "|end|", "<|endoftext|>", "## human"],
};

AzureOAI.mergeParameters(body, parameters)

for (let item of chatList) {
if (item.humanMessage.content.length > 0) {
// @ts-ignore
Expand Down Expand Up @@ -133,6 +139,7 @@ export async function postEventStream(prompt: string, chatList: Array<ChatItem>,
new FetchStream({
url: serverAddress + uri,
requestInit: requestInit,
timeout: timeout,
onmessage: msgCallback,
ondone: doneCallback,
onerror: errorCallback
Expand Down
33 changes: 27 additions & 6 deletions src/llm/AzureOAI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class AzureOAI {
const model = workspace.getConfiguration("GAIChoy").get("ChatModel") as string;
const api_version = workspace.getConfiguration("GAIChoy").get("ApiVersion") as string;
const parameters = workspace.getConfiguration("GAIChoy").get("ApiParameters") as string;
const timeout = workspace.getConfiguration("GAIChoy").get("ApiTimeout") as number;

// get API key from secret storage
let api_key = await ExtensionResource.instance.getApiKey();
Expand Down Expand Up @@ -120,12 +121,32 @@ Expected response in JSON format:

ExtensionResource.instance.debugMessage("request.data: " + data)
const uri = "/openai/deployments/" + model + "/chat/completions?api-version=" + api_version
const response = await axiosInstance.post<OpenAiCompletionResponse>(serverAddress + uri, data, { headers: headers });
ExtensionResource.instance.debugMessage("response.data: " + response.data)
const content = response.data.choices[0].message.content;
ExtensionResource.instance.debugMessage("response.data.choices[0].message.content: " + content)
let contentJSON = JSON.parse(this.trimTripleBackticks(content));
return contentJSON.generated_code;

try {
let startTime = Date.now();
let elapsedTime = 0;
do {
try {
// adjust the timeout parameter at each round of retry. deduct by the elapsedTime.
let postTimeout = Math.max(timeout * 1000 - elapsedTime, 0);
const response = await axiosInstance.post<OpenAiCompletionResponse>(serverAddress + uri, data, { headers: headers, timeout: postTimeout });
ExtensionResource.instance.debugMessage("response.data: " + response.data);
const content = response.data.choices[0].message.content;
ExtensionResource.instance.debugMessage("response.data.choices[0].message.content: " + content);
let contentJSON = JSON.parse(this.trimTripleBackticks(content));
return contentJSON.generated_code;
} catch (error) {
ExtensionResource.instance.debugMessage("Error in code completion: " + error);
if (Date.now() - startTime > timeout * 1000) {
throw new Error('API timeout exceeded');
}
}
elapsedTime = Date.now() - startTime;
} while (elapsedTime < timeout * 1000);
} catch (error) {
ExtensionResource.instance.debugMessage("Final Error: " + error);
throw error;
}
}

static trimTripleBackticks(str: string){
Expand Down

0 comments on commit 851dba6

Please sign in to comment.