feat: Agent update backend FIM completion api. (#218)

improve-workflow
Zhiming Ma 2023-06-08 00:11:31 +08:00 committed by GitHub
parent 1aaf29c968
commit c82cc38e9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 164 additions and 205 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -35,39 +35,23 @@ declare class CancelablePromise<T> implements Promise<T> {
get isCancelled(): boolean;
}
/**
* An enumeration.
*/
declare enum EventType {
COMPLETION = "completion",
VIEW = "view",
SELECT = "select"
}
type ChoiceEvent = {
type: EventType;
completion_id: string;
choice_index: number;
};
type Choice = {
index: number;
text: string;
};
type CompletionEvent = {
type: EventType;
type CompletionResponse$1 = {
id: string;
language: string;
prompt: string;
created: number;
choices: Array<Choice>;
};
type CompletionResponse$1 = {
id: string;
created: number;
choices: Array<Choice>;
type LogEventRequest$1 = {
/**
* Event type, should be `view` or `select`.
*/
type: string;
completion_id: string;
choice_index: number;
};
type ApiResult = {
@ -87,16 +71,6 @@ declare class ApiError extends Error {
constructor(request: ApiRequestOptions, response: ApiResult, message: string);
}
type ValidationError = {
loc: Array<(string | number)>;
msg: string;
type: string;
};
type HTTPValidationError = {
detail?: Array<ValidationError>;
};
type AgentConfig = {
server?: {
endpoint?: string;
@ -120,13 +94,14 @@ type CompletionRequest = {
position: number;
};
type CompletionResponse = CompletionResponse$1;
type LogEventRequest = LogEventRequest$1;
interface AgentFunction {
initialize(options?: AgentInitOptions): boolean;
updateConfig(config: AgentConfig): boolean;
getConfig(): AgentConfig;
getStatus(): "connecting" | "ready" | "disconnected";
getCompletions(request: CompletionRequest): CancelablePromise<CompletionResponse>;
postEvent(event: ChoiceEvent | CompletionEvent): CancelablePromise<boolean>;
postEvent(event: LogEventRequest): CancelablePromise<boolean>;
}
type StatusChangedEvent = {
event: "statusChanged";
@ -154,13 +129,13 @@ declare class TabbyAgent extends EventEmitter implements Agent {
private changeStatus;
private ping;
private callApi;
private createPrompt;
private createSegments;
initialize(params: AgentInitOptions): boolean;
updateConfig(config: AgentConfig): boolean;
getConfig(): AgentConfig;
getStatus(): "connecting" | "ready" | "disconnected";
getCompletions(request: CompletionRequest): CancelablePromise<CompletionResponse>;
postEvent(request: ChoiceEvent | CompletionEvent): CancelablePromise<boolean>;
postEvent(request: LogEventRequest): CancelablePromise<boolean>;
}
export { Agent, AgentConfig, AgentEvent, AgentFunction, ApiError, CancelError, CancelablePromise, Choice, ChoiceEvent, CompletionEvent, CompletionRequest, CompletionResponse, EventType, HTTPValidationError, StatusChangedEvent, TabbyAgent, ValidationError, agentEventNames };
export { Agent, AgentConfig, AgentEvent, AgentFunction, ApiError, CancelError, CancelablePromise, Choice, CompletionRequest, CompletionResponse, StatusChangedEvent, TabbyAgent, agentEventNames };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -50,7 +50,6 @@ __export(src_exports, {
ApiError: () => ApiError,
CancelError: () => CancelError,
CancelablePromise: () => CancelablePromise,
EventType: () => EventType,
TabbyAgent: () => TabbyAgent,
agentEventNames: () => agentEventNames
});
@ -425,42 +424,40 @@ var AxiosHttpRequest = class extends BaseHttpRequest {
}
};
// src/generated/services/DefaultService.ts
var DefaultService = class {
// src/generated/services/V1Service.ts
var V1Service = class {
constructor(httpRequest) {
this.httpRequest = httpRequest;
}
/**
* Completions
* @param requestBody
* @returns CompletionResponse Successful Response
* @returns CompletionResponse Success
* @throws ApiError
*/
completionsV1CompletionsPost(requestBody) {
completion(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/v1/completions",
body: requestBody,
mediaType: "application/json",
errors: {
422: `Validation Error`
400: `Bad Request`
}
});
}
/**
* Events
* @param requestBody
* @returns any Successful Response
* @returns any Success
* @throws ApiError
*/
eventsV1EventsPost(requestBody) {
event(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/v1/events",
body: requestBody,
mediaType: "application/json",
errors: {
422: `Validation Error`
400: `Bad Request`
}
});
}
@ -470,7 +467,7 @@ var DefaultService = class {
var TabbyApi = class {
constructor(config, HttpRequest = AxiosHttpRequest) {
this.request = new HttpRequest({
BASE: config?.BASE ?? "",
BASE: config?.BASE ?? "https://app.tabbyml.com/api/workspace/tabbyml/tabby",
VERSION: config?.VERSION ?? "0.1.0",
WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
CREDENTIALS: config?.CREDENTIALS ?? "include",
@ -480,18 +477,10 @@ var TabbyApi = class {
HEADERS: config?.HEADERS,
ENCODE_PATH: config?.ENCODE_PATH
});
this.default = new DefaultService(this.request);
this.v1 = new V1Service(this.request);
}
};
// src/generated/models/EventType.ts
var EventType = /* @__PURE__ */ ((EventType2) => {
EventType2["COMPLETION"] = "completion";
EventType2["VIEW"] = "view";
EventType2["SELECT"] = "select";
return EventType2;
})(EventType || {});
// src/utils.ts
function sleep(milliseconds) {
return new Promise((r) => setTimeout(r, milliseconds));
@ -711,12 +700,15 @@ var TabbyAgent = class extends import_events.EventEmitter {
}
callApi(api, request2) {
this.logger.debug({ api: api.name, request: request2 }, "API request");
const promise = api.call(this.api.default, request2);
const promise = api.call(this.api.v1, request2);
return cancelable(
promise.then((response) => {
this.logger.debug({ api: api.name, response }, "API response");
this.changeStatus("ready");
return response;
}).catch((error) => {
this.logger.debug({ api: api.name }, "API request canceled");
throw error;
}).catch((error) => {
this.logger.error({ api: api.name, error }, "API error");
this.changeStatus("disconnected");
@ -727,13 +719,16 @@ var TabbyAgent = class extends import_events.EventEmitter {
}
);
}
createPrompt(request2) {
createSegments(request2) {
const maxLines = 20;
const prefix = request2.text.slice(0, request2.position);
const lines = splitLines(prefix);
const cutoff = Math.max(lines.length - maxLines, 0);
const prompt = lines.slice(cutoff).join("");
return prompt;
const prefixLines = splitLines(prefix);
const suffix = request2.text.slice(request2.position);
const suffixLines = splitLines(suffix);
return {
prefix: prefixLines.slice(Math.max(prefixLines.length - maxLines, 0)).join(""),
suffix: suffixLines.slice(0, maxLines).join("")
};
}
initialize(params) {
if (params.config) {
@ -769,20 +764,19 @@ var TabbyAgent = class extends import_events.EventEmitter {
resolve2(this.completionCache.get(request2));
});
}
const prompt = this.createPrompt(request2);
if (isBlank(prompt)) {
this.logger.debug("Prompt is blank, returning empty completion response");
const segments = this.createSegments(request2);
if (isBlank(segments.prefix)) {
this.logger.debug("Segment prefix is blank, returning empty completion response");
return new CancelablePromise((resolve2) => {
resolve2({
id: "agent-" + (0, import_uuid.v4)(),
created: (/* @__PURE__ */ new Date()).getTime(),
choices: []
});
});
}
const promise = this.callApi(this.api.default.completionsV1CompletionsPost, {
prompt,
language: request2.language
const promise = this.callApi(this.api.v1.completion, {
language: request2.language,
segments
});
return cancelable(
promise.then((response) => {
@ -795,7 +789,7 @@ var TabbyAgent = class extends import_events.EventEmitter {
);
}
postEvent(request2) {
return this.callApi(this.api.default.eventsV1EventsPost, request2);
return this.callApi(this.api.v1.event, request2);
}
};
@ -806,7 +800,6 @@ var agentEventNames = ["statusChanged", "configUpdated"];
ApiError,
CancelError,
CancelablePromise,
EventType,
TabbyAgent,
agentEventNames
});

File diff suppressed because one or more lines are too long

View File

@ -12629,47 +12629,45 @@ var AxiosHttpRequest = class extends BaseHttpRequest {
}
};
// src/generated/services/DefaultService.ts
// src/generated/services/V1Service.ts
init_global();
init_dirname();
init_filename();
init_buffer2();
init_process2();
var DefaultService = class {
var V1Service = class {
constructor(httpRequest) {
this.httpRequest = httpRequest;
}
/**
* Completions
* @param requestBody
* @returns CompletionResponse Successful Response
* @returns CompletionResponse Success
* @throws ApiError
*/
completionsV1CompletionsPost(requestBody) {
completion(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/v1/completions",
body: requestBody,
mediaType: "application/json",
errors: {
422: `Validation Error`
400: `Bad Request`
}
});
}
/**
* Events
* @param requestBody
* @returns any Successful Response
* @returns any Success
* @throws ApiError
*/
eventsV1EventsPost(requestBody) {
event(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/v1/events",
body: requestBody,
mediaType: "application/json",
errors: {
422: `Validation Error`
400: `Bad Request`
}
});
}
@ -12679,7 +12677,7 @@ var DefaultService = class {
var TabbyApi = class {
constructor(config2, HttpRequest = AxiosHttpRequest) {
this.request = new HttpRequest({
BASE: config2?.BASE ?? "",
BASE: config2?.BASE ?? "https://app.tabbyml.com/api/workspace/tabbyml/tabby",
VERSION: config2?.VERSION ?? "0.1.0",
WITH_CREDENTIALS: config2?.WITH_CREDENTIALS ?? false,
CREDENTIALS: config2?.CREDENTIALS ?? "include",
@ -12689,7 +12687,7 @@ var TabbyApi = class {
HEADERS: config2?.HEADERS,
ENCODE_PATH: config2?.ENCODE_PATH
});
this.default = new DefaultService(this.request);
this.v1 = new V1Service(this.request);
}
};
@ -12700,19 +12698,6 @@ init_filename();
init_buffer2();
init_process2();
// src/generated/models/EventType.ts
init_global();
init_dirname();
init_filename();
init_buffer2();
init_process2();
var EventType = /* @__PURE__ */ ((EventType2) => {
EventType2["COMPLETION"] = "completion";
EventType2["VIEW"] = "view";
EventType2["SELECT"] = "select";
return EventType2;
})(EventType || {});
// src/utils.ts
init_global();
init_dirname();
@ -32383,12 +32368,15 @@ var TabbyAgent = class extends EventEmitter {
}
callApi(api, request2) {
this.logger.debug({ api: api.name, request: request2 }, "API request");
const promise = api.call(this.api.default, request2);
const promise = api.call(this.api.v1, request2);
return cancelable(
promise.then((response) => {
this.logger.debug({ api: api.name, response }, "API response");
this.changeStatus("ready");
return response;
}).catch((error) => {
this.logger.debug({ api: api.name }, "API request canceled");
throw error;
}).catch((error) => {
this.logger.error({ api: api.name, error }, "API error");
this.changeStatus("disconnected");
@ -32399,13 +32387,16 @@ var TabbyAgent = class extends EventEmitter {
}
);
}
createPrompt(request2) {
createSegments(request2) {
const maxLines = 20;
const prefix = request2.text.slice(0, request2.position);
const lines = splitLines(prefix);
const cutoff = Math.max(lines.length - maxLines, 0);
const prompt = lines.slice(cutoff).join("");
return prompt;
const prefixLines = splitLines(prefix);
const suffix = request2.text.slice(request2.position);
const suffixLines = splitLines(suffix);
return {
prefix: prefixLines.slice(Math.max(prefixLines.length - maxLines, 0)).join(""),
suffix: suffixLines.slice(0, maxLines).join("")
};
}
initialize(params) {
if (params.config) {
@ -32441,20 +32432,19 @@ var TabbyAgent = class extends EventEmitter {
resolve4(this.completionCache.get(request2));
});
}
const prompt = this.createPrompt(request2);
if (isBlank(prompt)) {
this.logger.debug("Prompt is blank, returning empty completion response");
const segments = this.createSegments(request2);
if (isBlank(segments.prefix)) {
this.logger.debug("Segment prefix is blank, returning empty completion response");
return new CancelablePromise((resolve4) => {
resolve4({
id: "agent-" + v4_default(),
created: (/* @__PURE__ */ new Date()).getTime(),
choices: []
});
});
}
const promise = this.callApi(this.api.default.completionsV1CompletionsPost, {
prompt,
language: request2.language
const promise = this.callApi(this.api.v1.completion, {
language: request2.language,
segments
});
return cancelable(
promise.then((response) => {
@ -32467,7 +32457,7 @@ var TabbyAgent = class extends EventEmitter {
);
}
postEvent(request2) {
return this.callApi(this.api.default.eventsV1EventsPost, request2);
return this.callApi(this.api.v1.event, request2);
}
};
@ -32482,7 +32472,6 @@ export {
ApiError,
CancelError,
CancelablePromise,
EventType,
TabbyAgent,
agentEventNames
};

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@
"browser": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"scripts": {
"openapi-codegen": "rimraf ./src/generated && openapi --input ../../docs/openapi.json --output ./src/generated --client axios --name TabbyApi --indent 2",
"openapi-codegen": "rimraf ./src/generated && openapi --input ../../website/static/openapi.json --output ./src/generated --client axios --name TabbyApi --indent 2",
"dev": "tsup --watch",
"build": "tsup"
},

View File

@ -1,7 +1,6 @@
import {
CancelablePromise,
ChoiceEvent,
CompletionEvent,
LogEventRequest as ApiLogEventRequest,
CompletionResponse as ApiCompletionResponse,
} from "./generated";
@ -21,13 +20,15 @@ export type CompletionRequest = {
export type CompletionResponse = ApiCompletionResponse;
export type LogEventRequest = ApiLogEventRequest;
export interface AgentFunction {
initialize(options?: AgentInitOptions): boolean;
updateConfig(config: AgentConfig): boolean;
getConfig(): AgentConfig;
getStatus(): "connecting" | "ready" | "disconnected";
getCompletions(request: CompletionRequest): CancelablePromise<CompletionResponse>;
postEvent(event: ChoiceEvent | CompletionEvent): CancelablePromise<boolean>;
postEvent(event: LogEventRequest): CancelablePromise<boolean>;
}
export type StatusChangedEvent = {

View File

@ -3,9 +3,9 @@ import { EventEmitter } from "events";
import { v4 as uuid } from "uuid";
import deepEqual from "deep-equal";
import deepMerge from "deepmerge";
import { TabbyApi, CancelablePromise, ApiError, ChoiceEvent, CompletionEvent } from "./generated";
import { TabbyApi, CancelablePromise, ApiError, CancelError } from "./generated";
import { sleep, cancelable, splitLines, isBlank } from "./utils";
import { Agent, AgentEvent, AgentInitOptions, CompletionRequest, CompletionResponse } from "./Agent";
import { Agent, AgentEvent, AgentInitOptions, CompletionRequest, CompletionResponse, LogEventRequest } from "./Agent";
import { AgentConfig, defaultAgentConfig } from "./AgentConfig";
import { CompletionCache } from "./CompletionCache";
import { rootLogger, allLoggers } from "./logger";
@ -59,7 +59,7 @@ export class TabbyAgent extends EventEmitter implements Agent {
request: Request
): CancelablePromise<Response> {
this.logger.debug({ api: api.name, request }, "API request");
const promise = api.call(this.api.default, request);
const promise = api.call(this.api.v1, request);
return cancelable(
promise
.then((response: Response) => {
@ -67,6 +67,10 @@ export class TabbyAgent extends EventEmitter implements Agent {
this.changeStatus("ready");
return response;
})
.catch((error: CancelError) => {
this.logger.debug({ api: api.name }, "API request canceled");
throw error;
})
.catch((error: ApiError) => {
this.logger.error({ api: api.name, error }, "API error");
this.changeStatus("disconnected");
@ -78,13 +82,17 @@ export class TabbyAgent extends EventEmitter implements Agent {
);
}
private createPrompt(request: CompletionRequest): string {
private createSegments(request: CompletionRequest): { prefix: string; suffix: string } {
// max to 20 lines in prefix and max to 20 lines in suffix
const maxLines = 20;
const prefix = request.text.slice(0, request.position);
const lines = splitLines(prefix);
const cutoff = Math.max(lines.length - maxLines, 0);
const prompt = lines.slice(cutoff).join("");
return prompt;
const prefixLines = splitLines(prefix);
const suffix = request.text.slice(request.position);
const suffixLines = splitLines(suffix);
return {
prefix: prefixLines.slice(Math.max(prefixLines.length - maxLines, 0)).join(""),
suffix: suffixLines.slice(0, maxLines).join(""),
};
}
public initialize(params: AgentInitOptions): boolean {
@ -127,20 +135,19 @@ export class TabbyAgent extends EventEmitter implements Agent {
resolve(this.completionCache.get(request));
});
}
const prompt = this.createPrompt(request);
if (isBlank(prompt)) {
this.logger.debug("Prompt is blank, returning empty completion response");
const segments = this.createSegments(request);
if (isBlank(segments.prefix)) {
this.logger.debug("Segment prefix is blank, returning empty completion response");
return new CancelablePromise((resolve) => {
resolve({
id: "agent-" + uuid(),
created: new Date().getTime(),
choices: [],
});
});
}
const promise = this.callApi(this.api.default.completionsV1CompletionsPost, {
prompt,
const promise = this.callApi(this.api.v1.completion, {
language: request.language,
segments,
});
return cancelable(
promise.then((response: CompletionResponse) => {
@ -153,7 +160,7 @@ export class TabbyAgent extends EventEmitter implements Agent {
);
}
public postEvent(request: ChoiceEvent | CompletionEvent): CancelablePromise<boolean> {
return this.callApi(this.api.default.eventsV1EventsPost, request);
public postEvent(request: LogEventRequest): CancelablePromise<boolean> {
return this.callApi(this.api.v1.event, request);
}
}

View File

@ -13,10 +13,5 @@ export {
CancelablePromise,
CancelError,
ApiError,
HTTPValidationError,
ValidationError,
Choice,
ChoiceEvent,
CompletionEvent,
EventType,
} from "./generated";

View File

@ -9,7 +9,6 @@ import {
} from "vscode";
import { strict as assert } from "assert";
import { Duration } from "@sapphire/duration";
import { ChoiceEvent } from "tabby-agent";
import { Agent } from "./Agent";
const target = ConfigurationTarget.Global;
@ -131,7 +130,7 @@ const openSettings: Command = {
const agent = Agent.getInstance();
const emitEvent: Command = {
command: "tabby.emitEvent",
callback: (event: ChoiceEvent) => {
callback: (event) => {
console.debug("Emit Event: ", event);
agent.postEvent(event);
},

View File

@ -10,7 +10,7 @@ import {
TextDocument,
workspace,
} from "vscode";
import { CompletionResponse, EventType, ChoiceEvent, CancelablePromise } from "tabby-agent";
import { CompletionResponse, CancelablePromise } from "tabby-agent";
import { Agent } from "./Agent";
import { sleep } from "./utils";
@ -82,8 +82,8 @@ export class TabbyCompletionProvider implements InlineCompletionItemProvider {
private toInlineCompletions(tabbyCompletion: CompletionResponse | null, range: Range): InlineCompletionItem[] {
return (
tabbyCompletion?.choices?.map((choice: any) => {
let event: ChoiceEvent = {
type: EventType.SELECT,
let event = {
type: "select",
completion_id: tabbyCompletion.id,
choice_index: choice.index,
};