From bd57267b279a0f88de4b42451c9aa6e93ffd7a5b Mon Sep 17 00:00:00 2001 From: Zhiming Ma Date: Tue, 4 Jun 2024 20:30:55 +0800 Subject: [PATCH] chore: extract tabby-openapi. (#2342) --- clients/tabby-agent/openapi/tabby.json | 376 ------------------ clients/tabby-agent/package.json | 1 + clients/tabby-agent/src/Agent.ts | 2 +- clients/tabby-agent/src/CompletionContext.ts | 2 +- clients/tabby-agent/src/CompletionSolution.ts | 2 +- clients/tabby-agent/src/TabbyAgent.ts | 2 +- clients/tabby-agent/src/stream.ts | 2 +- clients/tabby-openapi/.gitignore | 1 + clients/tabby-openapi/compatible/index.d.ts | 35 ++ .../lib/index.d.ts} | 105 +++-- clients/tabby-openapi/openapi.json | 1 + clients/tabby-openapi/package.json | 18 + ee/tabby-ui/lib/types/chat.ts | 31 +- ee/tabby-ui/package.json | 1 + pnpm-lock.yaml | 30 ++ 15 files changed, 167 insertions(+), 442 deletions(-) delete mode 100644 clients/tabby-agent/openapi/tabby.json create mode 100644 clients/tabby-openapi/.gitignore create mode 100644 clients/tabby-openapi/compatible/index.d.ts rename clients/{tabby-agent/src/types/tabbyApi.d.ts => tabby-openapi/lib/index.d.ts} (84%) create mode 100644 clients/tabby-openapi/openapi.json create mode 100644 clients/tabby-openapi/package.json diff --git a/clients/tabby-agent/openapi/tabby.json b/clients/tabby-agent/openapi/tabby.json deleted file mode 100644 index 34177202d156..000000000000 --- a/clients/tabby-agent/openapi/tabby.json +++ /dev/null @@ -1,376 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Tabby Server", - "description": "\n[![tabby stars](https://img.shields.io/github/stars/TabbyML/tabby)](https://github.com/TabbyML/tabby)\n[![Join Slack](https://shields.io/badge/Join-Tabby%20Slack-red?logo=slack)](https://links.tabbyml.com/join-slack)\n\nInstall following IDE / Editor extensions to get started with [Tabby](https://github.com/TabbyML/tabby).\n* [VSCode Extension](https://github.com/TabbyML/tabby/tree/main/clients/vscode) – Install from the [marketplace](https://marketplace.visualstudio.com/items?itemName=TabbyML.vscode-tabby), or [open-vsx.org](https://open-vsx.org/extension/TabbyML/vscode-tabby)\n* [VIM Extension](https://github.com/TabbyML/tabby/tree/main/clients/vim)\n* [IntelliJ Platform Plugin](https://github.com/TabbyML/tabby/tree/main/clients/intellij) – Install from the [marketplace](https://plugins.jetbrains.com/plugin/22379-tabby)\n", - "contact": { "name": "TabbyML Team" }, - "license": { "name": "Apache 2.0", "url": "https://github.com/TabbyML/tabby/blob/main/LICENSE" }, - "version": "0.11.0" - }, - "servers": [{ "url": "/", "description": "Server" }], - "paths": { - "/v1/chat/completions": { - "post": { - "tags": ["v1"], - "operationId": "chat_completions", - "requestBody": { - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatCompletionRequest" } } }, - "required": true - }, - "responses": { - "200": { - "description": "Success", - "content": { "text/event-stream": { "schema": { "$ref": "#/components/schemas/ChatCompletionChunk" } } } - }, - "405": { "description": "When chat model is not specified, the endpoint returns 405 Method Not Allowed" }, - "422": { "description": "When the prompt is malformed, the endpoint returns 422 Unprocessable Entity" } - }, - "security": [{ "token": [] }] - } - }, - "/v1/completions": { - "post": { - "tags": ["v1"], - "operationId": "completion", - "requestBody": { - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompletionRequest" } } }, - "required": true - }, - "responses": { - "200": { - "description": "Success", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompletionResponse" } } } - }, - "400": { "description": "Bad Request" } - }, - "security": [{ "token": [] }] - } - }, - "/v1/events": { - "post": { - "tags": ["v1"], - "operationId": "event", - "requestBody": { - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LogEventRequest" } } }, - "required": true - }, - "responses": { "200": { "description": "Success" }, "400": { "description": "Bad Request" } }, - "security": [{ "token": [] }] - } - }, - "/v1/health": { - "get": { - "tags": ["v1"], - "operationId": "health", - "responses": { - "200": { - "description": "Success", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthState" } } } - } - }, - "security": [{ "token": [] }] - } - }, - "/v1beta/search": { - "get": { - "tags": ["v1beta"], - "operationId": "search", - "parameters": [ - { "name": "q", "in": "query", "required": true, "schema": { "type": "string", "default": "get" } }, - { - "name": "limit", - "in": "query", - "required": false, - "schema": { "type": "integer", "default": 20, "nullable": true, "minimum": 0 } - }, - { - "name": "offset", - "in": "query", - "required": false, - "schema": { "type": "integer", "default": 0, "nullable": true, "minimum": 0 } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResponse" } } } - }, - "501": { "description": "When code search is not enabled, the endpoint will returns 501 Not Implemented" } - }, - "security": [{ "token": [] }] - } - }, - "/v1beta/server_setting": { - "get": { - "tags": ["v1beta"], - "operationId": "config", - "responses": { - "200": { - "description": "Success", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServerSetting" } } } - } - }, - "security": [{ "token": [] }] - } - } - }, - "components": { - "schemas": { - "ChatCompletionChoice": { - "type": "object", - "required": ["index", "delta"], - "properties": { - "index": { "type": "integer", "minimum": 0 }, - "logprobs": { "type": "string", "nullable": true }, - "finish_reason": { "type": "string", "nullable": true }, - "delta": { "$ref": "#/components/schemas/ChatCompletionDelta" } - } - }, - "ChatCompletionChunk": { - "type": "object", - "required": ["id", "created", "system_fingerprint", "object", "model", "choices"], - "properties": { - "id": { "type": "string" }, - "created": { "type": "integer", "format": "int64", "minimum": 0 }, - "system_fingerprint": { "type": "string" }, - "object": { "type": "string" }, - "model": { "type": "string" }, - "choices": { "type": "array", "items": { "$ref": "#/components/schemas/ChatCompletionChoice" } } - } - }, - "ChatCompletionDelta": { - "type": "object", - "required": ["content"], - "properties": { "content": { "type": "string" } } - }, - "ChatCompletionRequest": { - "type": "object", - "required": ["messages"], - "properties": { - "messages": { "type": "array", "items": { "$ref": "#/components/schemas/Message" } }, - "temperature": { "type": "number", "format": "float", "nullable": true }, - "seed": { "type": "integer", "format": "int64", "nullable": true, "minimum": 0 } - }, - "example": { - "messages": [ - { "content": "What is tail recursion?", "role": "user" }, - { "content": "It's a kind of optimization in compiler?", "role": "assistant" }, - { "content": "Could you share more details?", "role": "user" } - ] - } - }, - "Choice": { - "type": "object", - "required": ["index", "text"], - "properties": { "index": { "type": "integer", "format": "int32", "minimum": 0 }, "text": { "type": "string" } } - }, - "CompletionRequest": { - "type": "object", - "properties": { - "language": { - "type": "string", - "description": "Language identifier, full list is maintained at\nhttps://code.visualstudio.com/docs/languages/identifiers", - "example": "python", - "nullable": true - }, - "segments": { "allOf": [{ "$ref": "#/components/schemas/Segments" }], "nullable": true }, - "user": { - "type": "string", - "description": "A unique identifier representing your end-user, which can help Tabby to monitor & generating\nreports.", - "nullable": true - }, - "debug_options": { "allOf": [{ "$ref": "#/components/schemas/DebugOptions" }], "nullable": true }, - "temperature": { - "type": "number", - "format": "float", - "description": "The temperature parameter for the model, used to tune variance and \"creativity\" of the model output", - "nullable": true - }, - "seed": { - "type": "integer", - "format": "int64", - "description": "The seed used for randomly selecting tokens", - "nullable": true, - "minimum": 0 - } - }, - "example": { - "language": "python", - "segments": { "prefix": "def fib(n):\n ", "suffix": "\n return fib(n - 1) + fib(n - 2)" } - } - }, - "CompletionResponse": { - "type": "object", - "required": ["id", "choices"], - "properties": { - "id": { "type": "string" }, - "choices": { "type": "array", "items": { "$ref": "#/components/schemas/Choice" } }, - "debug_data": { "allOf": [{ "$ref": "#/components/schemas/DebugData" }], "nullable": true } - }, - "example": { "choices": [{ "index": 0, "text": "string" }], "id": "string" } - }, - "DebugData": { - "type": "object", - "properties": { - "snippets": { "type": "array", "items": { "$ref": "#/components/schemas/Snippet" }, "nullable": true }, - "prompt": { "type": "string", "nullable": true } - } - }, - "DebugOptions": { - "type": "object", - "properties": { - "raw_prompt": { - "type": "string", - "description": "When `raw_prompt` is specified, it will be passed directly to the inference engine for completion. `segments` field in `CompletionRequest` will be ignored.\n\nThis is useful for certain requests that aim to test the tabby's e2e quality.", - "nullable": true - }, - "return_snippets": { "type": "boolean", "description": "When true, returns `snippets` in `debug_data`." }, - "return_prompt": { "type": "boolean", "description": "When true, returns `prompt` in `debug_data`." }, - "disable_retrieval_augmented_code_completion": { - "type": "boolean", - "description": "When true, disable retrieval augmented code completion." - } - } - }, - "Declaration": { - "type": "object", - "description": "A snippet of declaration code that is relevant to the current completion request.", - "required": ["filepath", "body"], - "properties": { - "filepath": { - "type": "string", - "description": "Filepath of the file where the snippet is from.\n- When the file belongs to the same workspace as the current file,\nthis is a relative filepath, use the same rule as [Segments::filepath].\n- When the file located outside the workspace, such as in a dependency package,\nthis is a file URI with an absolute filepath." - }, - "body": { "type": "string", "description": "Body of the snippet." } - } - }, - "HealthState": { - "type": "object", - "required": ["device", "arch", "cpu_info", "cpu_count", "cuda_devices", "version"], - "properties": { - "model": { "type": "string", "nullable": true }, - "chat_model": { "type": "string", "nullable": true }, - "chat_device": { "type": "string", "nullable": true }, - "device": { "type": "string" }, - "arch": { "type": "string" }, - "cpu_info": { "type": "string" }, - "cpu_count": { "type": "integer", "minimum": 0 }, - "cuda_devices": { "type": "array", "items": { "type": "string" } }, - "version": { "$ref": "#/components/schemas/Version" }, - "webserver": { "type": "boolean", "nullable": true } - } - }, - "Hit": { - "type": "object", - "required": ["score", "doc", "id"], - "properties": { - "score": { "type": "number", "format": "float" }, - "doc": { "$ref": "#/components/schemas/HitDocument" }, - "id": { "type": "integer", "format": "int32", "minimum": 0 } - } - }, - "HitDocument": { - "type": "object", - "required": ["body", "filepath", "git_url", "language"], - "properties": { - "body": { "type": "string" }, - "filepath": { "type": "string" }, - "git_url": { "type": "string" }, - "language": { "type": "string" } - } - }, - "LogEventRequest": { - "type": "object", - "required": ["type", "completion_id", "choice_index"], - "properties": { - "type": { - "type": "string", - "description": "Event type, should be `view`, `select` or `dismiss`.", - "example": "view" - }, - "completion_id": { "type": "string" }, - "choice_index": { "type": "integer", "format": "int32", "minimum": 0 }, - "view_id": { "type": "string", "nullable": true }, - "elapsed": { "type": "integer", "format": "int32", "nullable": true, "minimum": 0 } - } - }, - "Message": { - "type": "object", - "required": ["role", "content"], - "properties": { "role": { "type": "string" }, "content": { "type": "string" } } - }, - "SearchResponse": { - "type": "object", - "required": ["num_hits", "hits"], - "properties": { - "num_hits": { "type": "integer", "minimum": 0 }, - "hits": { "type": "array", "items": { "$ref": "#/components/schemas/Hit" } } - } - }, - "Segments": { - "type": "object", - "required": ["prefix"], - "properties": { - "prefix": { "type": "string", "description": "Content that appears before the cursor in the editor window." }, - "suffix": { - "type": "string", - "description": "Content that appears after the cursor in the editor window.", - "nullable": true - }, - "filepath": { - "type": "string", - "description": "The relative path of the file that is being edited.\n- When [Segments::git_url] is set, this is the path of the file in the git repository.\n- When [Segments::git_url] is empty, this is the path of the file in the workspace.", - "nullable": true - }, - "git_url": { - "type": "string", - "description": "The remote URL of the current git repository.\nLeave this empty if the file is not in a git repository,\nor the git repository does not have a remote URL.", - "nullable": true - }, - "declarations": { - "type": "array", - "items": { "$ref": "#/components/schemas/Declaration" }, - "description": "The relevant declaration code snippets provided by the editor's LSP,\ncontain declarations of symbols extracted from [Segments::prefix].", - "nullable": true - }, - "relevant_snippets_from_changed_files": { - "type": "array", - "items": { "$ref": "#/components/schemas/Snippet" }, - "description": "The relevant code snippets extracted from recently edited files.\nThese snippets are selected from candidates found within code chunks\nbased on the edited location.\nThe current editing file is excluded from the search candidates.\n\nWhen provided alongside [Segments::declarations], the snippets have\nalready been deduplicated to ensure no duplication with entries\nin [Segments::declarations].\n\nSorted in descending order of [Snippet::score].", - "nullable": true - }, - "clipboard": { - "type": "string", - "description": "Clipboard content when requesting code completion.", - "nullable": true - } - } - }, - "ServerSetting": { - "type": "object", - "required": ["disable_client_side_telemetry"], - "properties": { "disable_client_side_telemetry": { "type": "boolean" } } - }, - "Snippet": { - "type": "object", - "required": ["filepath", "body", "score"], - "properties": { - "filepath": { "type": "string" }, - "body": { "type": "string" }, - "score": { "type": "number", "format": "float" } - } - }, - "Version": { - "type": "object", - "required": ["build_date", "build_timestamp", "git_sha", "git_describe"], - "properties": { - "build_date": { "type": "string" }, - "build_timestamp": { "type": "string" }, - "git_sha": { "type": "string" }, - "git_describe": { "type": "string" } - } - } - }, - "securitySchemes": { "token": { "type": "http", "scheme": "bearer", "bearerFormat": "token" } } - } -} diff --git a/clients/tabby-agent/package.json b/clients/tabby-agent/package.json index b6a4859a3003..1319a8bbd537 100644 --- a/clients/tabby-agent/package.json +++ b/clients/tabby-agent/package.json @@ -78,6 +78,7 @@ "randomstring": "^1.3.0", "semver": "^7.6.0", "stats-logscale": "^1.0.9", + "tabby-openapi": "workspace:*", "toml": "^3.0.0", "ts-node": "^10.9.1", "tsc-watch": "^6.2.0", diff --git a/clients/tabby-agent/src/Agent.ts b/clients/tabby-agent/src/Agent.ts index 9cc732b97d0c..735956d8b4fd 100644 --- a/clients/tabby-agent/src/Agent.ts +++ b/clients/tabby-agent/src/Agent.ts @@ -1,4 +1,4 @@ -import type { components as ApiComponents } from "./types/tabbyApi"; +import type { components as ApiComponents } from "tabby-openapi/compatible"; import type { AgentConfig, PartialAgentConfig } from "./AgentConfig"; import type { DataStore } from "./dataStore"; import type { Logger } from "./logger"; diff --git a/clients/tabby-agent/src/CompletionContext.ts b/clients/tabby-agent/src/CompletionContext.ts index 404eb0e76d7b..b8bacfd35be0 100644 --- a/clients/tabby-agent/src/CompletionContext.ts +++ b/clients/tabby-agent/src/CompletionContext.ts @@ -1,7 +1,7 @@ import path from "path"; import hashObject from "object-hash"; import { splitLines, isBlank, regOnlyAutoClosingCloseChars } from "./utils"; -import type { components as TabbyApiComponents } from "./types/tabbyApi"; +import type { components as TabbyApiComponents } from "tabby-openapi/compatible"; import type { AgentConfig } from "./AgentConfig"; export type CompletionRequest = { diff --git a/clients/tabby-agent/src/CompletionSolution.ts b/clients/tabby-agent/src/CompletionSolution.ts index a9f4542ae113..61ac650b55a0 100644 --- a/clients/tabby-agent/src/CompletionSolution.ts +++ b/clients/tabby-agent/src/CompletionSolution.ts @@ -1,5 +1,5 @@ import { splitLines, isBlank } from "./utils"; -import type { components as TabbyApiComponents } from "./types/tabbyApi"; +import type { components as TabbyApiComponents } from "tabby-openapi/compatible"; import type { CompletionContext } from "./CompletionContext"; export type InlineCompletionItem = { diff --git a/clients/tabby-agent/src/TabbyAgent.ts b/clients/tabby-agent/src/TabbyAgent.ts index 6d577497bcef..7acb6cd580c9 100644 --- a/clients/tabby-agent/src/TabbyAgent.ts +++ b/clients/tabby-agent/src/TabbyAgent.ts @@ -7,7 +7,7 @@ import createClient from "openapi-fetch"; import type { ParseAs } from "openapi-fetch"; import * as semver from "semver"; import { Readable } from "node:stream"; -import type { paths as TabbyApi, components as TabbyApiComponents } from "./types/tabbyApi"; +import type { paths as TabbyApi, components as TabbyApiComponents } from "tabby-openapi/compatible"; import type { Agent, AgentStatus, diff --git a/clients/tabby-agent/src/stream.ts b/clients/tabby-agent/src/stream.ts index ed2472e3968b..65015ad87762 100644 --- a/clients/tabby-agent/src/stream.ts +++ b/clients/tabby-agent/src/stream.ts @@ -1,7 +1,7 @@ import { Readable } from "node:stream"; import type { ReadableStream as NodeReadableStream } from "node:stream/web"; import { EventSourceParserStream, ParsedEvent } from "eventsource-parser/stream"; -import type { components as TabbyApiComponents } from "./types/tabbyApi"; +import type { components as TabbyApiComponents } from "tabby-openapi/compatible"; import { getLogger } from "./logger"; const logger = getLogger("StreamParser"); diff --git a/clients/tabby-openapi/.gitignore b/clients/tabby-openapi/.gitignore new file mode 100644 index 000000000000..3c3629e647f5 --- /dev/null +++ b/clients/tabby-openapi/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/clients/tabby-openapi/compatible/index.d.ts b/clients/tabby-openapi/compatible/index.d.ts new file mode 100644 index 000000000000..71049a51a1fb --- /dev/null +++ b/clients/tabby-openapi/compatible/index.d.ts @@ -0,0 +1,35 @@ +import { + paths as _paths, + webhooks as _webhooks, + components as _components, + $defs as _$defs, + external as _external, + operations as _operations, +} from "../lib"; + +export interface paths extends _paths { + "/v1/health": _paths["/v1/health"] & { + // backward compatible for Tabby server 0.2.x and earlier + post: operations["health"]; + }; + // backward compatible for Tabby server 0.10.x and earlier + "/v1beta/chat/completions": { + post: operations["chat_completions"]; + }; +} + +export type webhooks = _webhooks; +export type components = _components; +export type $defs = _$defs; +export type external = _external; + +export interface operations extends _operations { + event: _operations["event"] & { + // Add a query parameter to specify the select kind + parameters: { + query: { + select_kind?: string | null; + }; + }; + }; +} diff --git a/clients/tabby-agent/src/types/tabbyApi.d.ts b/clients/tabby-openapi/lib/index.d.ts similarity index 84% rename from clients/tabby-agent/src/types/tabbyApi.d.ts rename to clients/tabby-openapi/lib/index.d.ts index 3a59ddd1a643..71de462cfb71 100644 --- a/clients/tabby-agent/src/types/tabbyApi.d.ts +++ b/clients/tabby-openapi/lib/index.d.ts @@ -1,3 +1,14 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + + +/** OneOf type helpers */ +type Without = { [P in Exclude]?: never }; +type XOR = (T | U) extends object ? (Without & U) | (Without & T) : T | U; +type OneOf = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR, ...Rest]> : never; + export interface paths { "/v1/chat/completions": { post: operations["chat_completions"]; @@ -10,15 +21,9 @@ export interface paths { }; "/v1/health": { get: operations["health"]; - // back compatible for Tabby server 0.2.x and earlier - post: operations["health"]; }; - // back compatible for Tabby server 0.10.x and earlier - "/v1beta/chat/completions": { - post: operations["chat_completions"]; - }; - "/v1beta/search": { - get: operations["search"]; + "/v1beta/answer": { + post: operations["answer"]; }; "/v1beta/server_setting": { get: operations["config"]; @@ -29,6 +34,32 @@ export type webhooks = Record; export interface components { schemas: { + /** + * @example { + * "messages": [ + * { + * "content": "What is tail recursion?", + * "role": "user" + * } + * ] + * } + */ + AnswerRequest: { + user?: string | null; + messages: components["schemas"]["Message"][]; + code_query?: components["schemas"]["CodeSearchQuery"] | null; + doc_query?: boolean; + generate_relevant_questions?: boolean; + }; + AnswerResponseChunk: OneOf<[{ + relevant_code: components["schemas"]["CodeSearchDocument"][]; + }, { + relevant_documents: components["schemas"]["DocSearchDocument"][]; + }, { + relevant_questions: string[]; + }, { + answer_delta: string; + }]>; ChatCompletionChoice: { index: number; logprobs?: string | null; @@ -66,17 +97,33 @@ export interface components { * } */ ChatCompletionRequest: { + user?: string | null; messages: components["schemas"]["Message"][]; /** Format: float */ temperature?: number | null; /** Format: int64 */ seed?: number | null; + /** Format: float */ + presence_penalty?: number | null; }; Choice: { /** Format: int32 */ index: number; text: string; }; + CodeSearchDocument: { + body: string; + filepath: string; + git_url: string; + language: string; + start_line: number; + }; + CodeSearchQuery: { + git_url: string; + filepath?: string | null; + language: string; + content: string; + }; /** * @example { * "language": "python", @@ -158,6 +205,11 @@ export interface components { /** @description Body of the snippet. */ body: string; }; + DocSearchDocument: { + title: string; + link: string; + snippet: string; + }; HealthState: { model?: string | null; chat_model?: string | null; @@ -170,19 +222,6 @@ export interface components { version: components["schemas"]["Version"]; webserver?: boolean | null; }; - Hit: { - /** Format: float */ - score: number; - doc: components["schemas"]["HitDocument"]; - /** Format: int32 */ - id: number; - }; - HitDocument: { - body: string; - filepath: string; - git_url: string; - language: string; - }; LogEventRequest: { /** * @description Event type, should be `view`, `select` or `dismiss`. @@ -200,10 +239,6 @@ export interface components { role: string; content: string; }; - SearchResponse: { - num_hits: number; - hits: components["schemas"]["Hit"][]; - }; Segments: { /** @description Content that appears before the cursor in the editor window. */ prefix: string; @@ -270,6 +305,7 @@ export type $defs = Record; export type external = Record; export interface operations { + chat_completions: { requestBody: { content: { @@ -313,11 +349,6 @@ export interface operations { }; }; event: { - parameters: { - query: { - select_kind?: string | null; - }; - }; requestBody: { content: { "application/json": components["schemas"]["LogEventRequest"]; @@ -344,22 +375,20 @@ export interface operations { }; }; }; - search: { - parameters: { - query: { - q: string; - limit?: number | null; - offset?: number | null; + answer: { + requestBody: { + content: { + "application/json": components["schemas"]["AnswerRequest"]; }; }; responses: { /** @description Success */ 200: { content: { - "application/json": components["schemas"]["SearchResponse"]; + "text/event-stream": components["schemas"]["AnswerResponseChunk"]; }; }; - /** @description When code search is not enabled, the endpoint will returns 501 Not Implemented */ + /** @description When answer search is not enabled, the endpoint will returns 501 Not Implemented */ 501: { content: never; }; diff --git a/clients/tabby-openapi/openapi.json b/clients/tabby-openapi/openapi.json new file mode 100644 index 000000000000..a7d6193d4054 --- /dev/null +++ b/clients/tabby-openapi/openapi.json @@ -0,0 +1 @@ +{"openapi":"3.0.3","info":{"title":"Tabby Server","description":"\n[![tabby stars](https://img.shields.io/github/stars/TabbyML/tabby)](https://github.com/TabbyML/tabby)\n[![Join Slack](https://shields.io/badge/Join-Tabby%20Slack-red?logo=slack)](https://links.tabbyml.com/join-slack)\n\nInstall following IDE / Editor extensions to get started with [Tabby](https://github.com/TabbyML/tabby).\n* [VSCode Extension](https://github.com/TabbyML/tabby/tree/main/clients/vscode) – Install from the [marketplace](https://marketplace.visualstudio.com/items?itemName=TabbyML.vscode-tabby), or [open-vsx.org](https://open-vsx.org/extension/TabbyML/vscode-tabby)\n* [VIM Extension](https://github.com/TabbyML/tabby/tree/main/clients/vim)\n* [IntelliJ Platform Plugin](https://github.com/TabbyML/tabby/tree/main/clients/intellij) – Install from the [marketplace](https://plugins.jetbrains.com/plugin/22379-tabby)\n","contact":{"name":"TabbyML Team"},"license":{"name":"Apache 2.0","url":"https://github.com/TabbyML/tabby/blob/main/LICENSE"},"version":"0.12.0-dev.0"},"servers":[{"url":"/","description":"Server"}],"paths":{"/v1/chat/completions":{"post":{"tags":["v1"],"operationId":"chat_completions","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/ChatCompletionChunk"}}}},"405":{"description":"When chat model is not specified, the endpoint returns 405 Method Not Allowed"},"422":{"description":"When the prompt is malformed, the endpoint returns 422 Unprocessable Entity"}},"security":[{"token":[]}]}},"/v1/completions":{"post":{"tags":["v1"],"operationId":"completion","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompletionRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompletionResponse"}}}},"400":{"description":"Bad Request"}},"security":[{"token":[]}]}},"/v1/events":{"post":{"tags":["v1"],"operationId":"event","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogEventRequest"}}},"required":true},"responses":{"200":{"description":"Success"},"400":{"description":"Bad Request"}},"security":[{"token":[]}]}},"/v1/health":{"get":{"tags":["v1"],"operationId":"health","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthState"}}}}},"security":[{"token":[]}]}},"/v1beta/answer":{"post":{"tags":["v1beta"],"operationId":"answer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnswerRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/AnswerResponseChunk"}}}},"501":{"description":"When answer search is not enabled, the endpoint will returns 501 Not Implemented"}},"security":[{"token":[]}]}},"/v1beta/server_setting":{"get":{"tags":["v1beta"],"operationId":"config","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerSetting"}}}}},"security":[{"token":[]}]}}},"components":{"schemas":{"AnswerRequest":{"type":"object","required":["messages"],"properties":{"user":{"type":"string","nullable":true},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"code_query":{"allOf":[{"$ref":"#/components/schemas/CodeSearchQuery"}],"nullable":true},"doc_query":{"type":"boolean"},"generate_relevant_questions":{"type":"boolean"}},"example":{"messages":[{"content":"What is tail recursion?","role":"user"}]}},"AnswerResponseChunk":{"oneOf":[{"type":"object","required":["relevant_code"],"properties":{"relevant_code":{"type":"array","items":{"$ref":"#/components/schemas/CodeSearchDocument"}}}},{"type":"object","required":["relevant_documents"],"properties":{"relevant_documents":{"type":"array","items":{"$ref":"#/components/schemas/DocSearchDocument"}}}},{"type":"object","required":["relevant_questions"],"properties":{"relevant_questions":{"type":"array","items":{"type":"string"}}}},{"type":"object","required":["answer_delta"],"properties":{"answer_delta":{"type":"string"}}}]},"ChatCompletionChoice":{"type":"object","required":["index","delta"],"properties":{"index":{"type":"integer","minimum":0},"logprobs":{"type":"string","nullable":true},"finish_reason":{"type":"string","nullable":true},"delta":{"$ref":"#/components/schemas/ChatCompletionDelta"}}},"ChatCompletionChunk":{"type":"object","required":["id","created","system_fingerprint","object","model","choices"],"properties":{"id":{"type":"string"},"created":{"type":"integer","format":"int64","minimum":0},"system_fingerprint":{"type":"string"},"object":{"type":"string"},"model":{"type":"string"},"choices":{"type":"array","items":{"$ref":"#/components/schemas/ChatCompletionChoice"}}}},"ChatCompletionDelta":{"type":"object","required":["content"],"properties":{"content":{"type":"string"}}},"ChatCompletionRequest":{"type":"object","required":["messages"],"properties":{"user":{"type":"string","nullable":true},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"temperature":{"type":"number","format":"float","nullable":true},"seed":{"type":"integer","format":"int64","nullable":true,"minimum":0},"presence_penalty":{"type":"number","format":"float","nullable":true}},"example":{"messages":[{"content":"What is tail recursion?","role":"user"},{"content":"It's a kind of optimization in compiler?","role":"assistant"},{"content":"Could you share more details?","role":"user"}]}},"Choice":{"type":"object","required":["index","text"],"properties":{"index":{"type":"integer","format":"int32","minimum":0},"text":{"type":"string"}}},"CodeSearchDocument":{"type":"object","required":["body","filepath","git_url","language","start_line"],"properties":{"body":{"type":"string"},"filepath":{"type":"string"},"git_url":{"type":"string"},"language":{"type":"string"},"start_line":{"type":"integer","minimum":0}}},"CodeSearchQuery":{"type":"object","required":["git_url","language","content"],"properties":{"git_url":{"type":"string"},"filepath":{"type":"string","nullable":true},"language":{"type":"string"},"content":{"type":"string"}}},"CompletionRequest":{"type":"object","properties":{"language":{"type":"string","description":"Language identifier, full list is maintained at\nhttps://code.visualstudio.com/docs/languages/identifiers","example":"python","nullable":true},"segments":{"allOf":[{"$ref":"#/components/schemas/Segments"}],"nullable":true},"user":{"type":"string","description":"A unique identifier representing your end-user, which can help Tabby to monitor & generating\nreports.","nullable":true},"debug_options":{"allOf":[{"$ref":"#/components/schemas/DebugOptions"}],"nullable":true},"temperature":{"type":"number","format":"float","description":"The temperature parameter for the model, used to tune variance and \"creativity\" of the model output","nullable":true},"seed":{"type":"integer","format":"int64","description":"The seed used for randomly selecting tokens","nullable":true,"minimum":0}},"example":{"language":"python","segments":{"prefix":"def fib(n):\n ","suffix":"\n return fib(n - 1) + fib(n - 2)"}}},"CompletionResponse":{"type":"object","required":["id","choices"],"properties":{"id":{"type":"string"},"choices":{"type":"array","items":{"$ref":"#/components/schemas/Choice"}},"debug_data":{"allOf":[{"$ref":"#/components/schemas/DebugData"}],"nullable":true}},"example":{"choices":[{"index":0,"text":"string"}],"id":"string"}},"DebugData":{"type":"object","properties":{"snippets":{"type":"array","items":{"$ref":"#/components/schemas/Snippet"},"nullable":true},"prompt":{"type":"string","nullable":true}}},"DebugOptions":{"type":"object","properties":{"raw_prompt":{"type":"string","description":"When `raw_prompt` is specified, it will be passed directly to the inference engine for completion. `segments` field in `CompletionRequest` will be ignored.\n\nThis is useful for certain requests that aim to test the tabby's e2e quality.","nullable":true},"return_snippets":{"type":"boolean","description":"When true, returns `snippets` in `debug_data`."},"return_prompt":{"type":"boolean","description":"When true, returns `prompt` in `debug_data`."},"disable_retrieval_augmented_code_completion":{"type":"boolean","description":"When true, disable retrieval augmented code completion."}}},"Declaration":{"type":"object","description":"A snippet of declaration code that is relevant to the current completion request.","required":["filepath","body"],"properties":{"filepath":{"type":"string","description":"Filepath of the file where the snippet is from.\n- When the file belongs to the same workspace as the current file,\nthis is a relative filepath, use the same rule as [Segments::filepath].\n- When the file located outside the workspace, such as in a dependency package,\nthis is a file URI with an absolute filepath."},"body":{"type":"string","description":"Body of the snippet."}}},"DocSearchDocument":{"type":"object","required":["title","link","snippet"],"properties":{"title":{"type":"string"},"link":{"type":"string"},"snippet":{"type":"string"}}},"HealthState":{"type":"object","required":["device","arch","cpu_info","cpu_count","cuda_devices","version"],"properties":{"model":{"type":"string","nullable":true},"chat_model":{"type":"string","nullable":true},"chat_device":{"type":"string","nullable":true},"device":{"type":"string"},"arch":{"type":"string"},"cpu_info":{"type":"string"},"cpu_count":{"type":"integer","minimum":0},"cuda_devices":{"type":"array","items":{"type":"string"}},"version":{"$ref":"#/components/schemas/Version"},"webserver":{"type":"boolean","nullable":true}}},"LogEventRequest":{"type":"object","required":["type","completion_id","choice_index"],"properties":{"type":{"type":"string","description":"Event type, should be `view`, `select` or `dismiss`.","example":"view"},"completion_id":{"type":"string"},"choice_index":{"type":"integer","format":"int32","minimum":0},"view_id":{"type":"string","nullable":true},"elapsed":{"type":"integer","format":"int32","nullable":true,"minimum":0}}},"Message":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string"},"content":{"type":"string"}}},"Segments":{"type":"object","required":["prefix"],"properties":{"prefix":{"type":"string","description":"Content that appears before the cursor in the editor window."},"suffix":{"type":"string","description":"Content that appears after the cursor in the editor window.","nullable":true},"filepath":{"type":"string","description":"The relative path of the file that is being edited.\n- When [Segments::git_url] is set, this is the path of the file in the git repository.\n- When [Segments::git_url] is empty, this is the path of the file in the workspace.","nullable":true},"git_url":{"type":"string","description":"The remote URL of the current git repository.\nLeave this empty if the file is not in a git repository,\nor the git repository does not have a remote URL.","nullable":true},"declarations":{"type":"array","items":{"$ref":"#/components/schemas/Declaration"},"description":"The relevant declaration code snippets provided by the editor's LSP,\ncontain declarations of symbols extracted from [Segments::prefix].","nullable":true},"relevant_snippets_from_changed_files":{"type":"array","items":{"$ref":"#/components/schemas/Snippet"},"description":"The relevant code snippets extracted from recently edited files.\nThese snippets are selected from candidates found within code chunks\nbased on the edited location.\nThe current editing file is excluded from the search candidates.\n\nWhen provided alongside [Segments::declarations], the snippets have\nalready been deduplicated to ensure no duplication with entries\nin [Segments::declarations].\n\nSorted in descending order of [Snippet::score].","nullable":true},"clipboard":{"type":"string","description":"Clipboard content when requesting code completion.","nullable":true}}},"ServerSetting":{"type":"object","required":["disable_client_side_telemetry"],"properties":{"disable_client_side_telemetry":{"type":"boolean"}}},"Snippet":{"type":"object","required":["filepath","body","score"],"properties":{"filepath":{"type":"string"},"body":{"type":"string"},"score":{"type":"number","format":"float"}}},"Version":{"type":"object","required":["build_date","build_timestamp","git_sha","git_describe"],"properties":{"build_date":{"type":"string"},"build_timestamp":{"type":"string"},"git_sha":{"type":"string"},"git_describe":{"type":"string"}}}},"securitySchemes":{"token":{"type":"http","scheme":"bearer","bearerFormat":"token"}}}} \ No newline at end of file diff --git a/clients/tabby-openapi/package.json b/clients/tabby-openapi/package.json new file mode 100644 index 000000000000..3d502f9cbb1b --- /dev/null +++ b/clients/tabby-openapi/package.json @@ -0,0 +1,18 @@ +{ + "name": "tabby-openapi", + "version": "0.12.0-dev", + "description": "Tabby API schema for typescript.", + "files": [ + "lib/**/*.d.ts", + "compatible/**/*.d.ts" + ], + "types": "./lib/index.d.ts", + "scripts": { + "update-schema": "wget http://localhost:8080/api-docs/openapi.json -O ./openapi.json", + "codegen": "openapi-typescript ./openapi.json -o ./lib/index.d.ts" + }, + "devDependencies": { + "openapi-typescript": "^6.6.1", + "wget": "^0.0.1" + } +} diff --git a/ee/tabby-ui/lib/types/chat.ts b/ee/tabby-ui/lib/types/chat.ts index a0c1b92b579a..a23ee0bc0302 100644 --- a/ee/tabby-ui/lib/types/chat.ts +++ b/ee/tabby-ui/lib/types/chat.ts @@ -1,5 +1,5 @@ -import { Message } from 'ai' import type { ChatMessage } from 'tabby-chat-panel' +import type { components as TabbyOpenApiComponents } from 'tabby-openapi' export interface UserMessage extends ChatMessage { id: string @@ -50,29 +50,14 @@ export type SearchReponse = { export type MessageActionType = 'delete' | 'regenerate' -export type CodeSearchDocument = { - body: string - filepath: string - git_url: string - language: string - start_line: number -} +export type AnswerRequest = TabbyOpenApiComponents['schemas']['AnswerRequest'] -export type AnswerRequest = { - messages: Array - code_query?: { - git_url: string - filepath: string - language: string - content: string - } - doc_query?: boolean - generate_relevant_questions?: boolean -} +type AnswerResponseChunk = + TabbyOpenApiComponents['schemas']['AnswerResponseChunk'] export type AnswerResponse = { - relevant_code?: Array - relevant_documents?: Array - relevant_questions?: Array - answer_delta?: string + relevant_code?: AnswerResponseChunk['relevant_code'] + relevant_documents?: AnswerResponseChunk['relevant_code'] + relevant_questions?: AnswerResponseChunk['relevant_questions'] + answer_delta?: AnswerResponseChunk['answer_delta'] } diff --git a/ee/tabby-ui/package.json b/ee/tabby-ui/package.json index aead5e7d24dd..cfb56cff25be 100644 --- a/ee/tabby-ui/package.json +++ b/ee/tabby-ui/package.json @@ -133,6 +133,7 @@ "npm-run-all": "^4.1.5", "postcss": "^8.4.21", "prettier": "^2.7.1", + "tabby-openapi": "workspace:*", "tailwind-merge": "^1.12.0", "tailwindcss": "^3.3.1", "tailwindcss-animate": "^1.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 66630dd421c5..f91ad73957b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -180,6 +180,9 @@ importers: stats-logscale: specifier: ^1.0.9 version: 1.0.9 + tabby-openapi: + specifier: workspace:* + version: link:../tabby-openapi toml: specifier: ^3.0.0 version: 3.0.0 @@ -263,6 +266,15 @@ importers: specifier: ^1.5.2 version: 1.6.0(@types/node@20.12.12)(terser@5.31.0) + clients/tabby-openapi: + devDependencies: + openapi-typescript: + specifier: ^6.6.1 + version: 6.7.6 + wget: + specifier: ^0.0.1 + version: 0.0.1 + clients/vim: devDependencies: cpy-cli: @@ -720,6 +732,9 @@ importers: prettier: specifier: ^2.7.1 version: 2.8.8 + tabby-openapi: + specifier: workspace:* + version: link:../../clients/tabby-openapi tailwind-merge: specifier: ^1.12.0 version: 1.14.0 @@ -9471,6 +9486,10 @@ packages: tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + tunnel@0.0.2: + resolution: {integrity: sha512-PjBRT+4Y/UH3q/lnjXosoaB+hY1yNH2TXWMjPSF1bAIdZmUDAJ2lBPt5s8jTH1e7sgb3nleK889E9vyY4NXpYw==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + tunnel@0.0.6: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} @@ -9942,6 +9961,11 @@ packages: webpack-cli: optional: true + wget@0.0.1: + resolution: {integrity: sha512-iKDSrvontU6lAQq89bNn7me3HU/+Cau7NedEYz607TOS4n0AgksloCt2UwU+ZH5Kn0Lq+XbJ6STOpnhpjicvOQ==} + engines: {node: '>= 0.6.18'} + hasBin: true + whatwg-fetch@3.6.19: resolution: {integrity: sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==} @@ -21029,6 +21053,8 @@ snapshots: safe-buffer: 5.2.1 optional: true + tunnel@0.0.2: {} + tunnel@0.0.6: {} turbo-darwin-64@1.13.3: @@ -21567,6 +21593,10 @@ snapshots: - esbuild - uglify-js + wget@0.0.1: + dependencies: + tunnel: 0.0.2 + whatwg-fetch@3.6.19: {} whatwg-url@5.0.0: