From e2862c5bf3c472bbce6c0288f214fb3addb22404 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 25 Jul 2023 17:15:41 +0900 Subject: [PATCH] Rename endpoint to credential --- src/cli-main.ts | 55 ++++++----- src/controller/api-manager.ts | 96 +++++++++---------- src/controller/assistant-manager.ts | 6 +- src/controller/service-manager.ts | 12 +-- src/exports/chie.ts | 4 +- src/extensions/bingchat/bingchat-api.ts | 12 +-- src/extensions/chatgpt-web/chatgpt-web-api.ts | 16 ++-- src/extensions/chatgpt/chatgpt-api.ts | 18 ++-- .../{api-endpoint.ts => api-credential.ts} | 14 +-- src/model/chat-api.ts | 12 +-- src/model/web-api.ts | 16 ++-- src/model/web-service.ts | 6 +- src/view/api-params-view.ts | 24 ++--- src/view/chat-view.ts | 8 +- src/view/dashboard-window.ts | 4 +- src/view/new-api-window.ts | 56 +++++------ src/view/new-assistant-window.ts | 43 ++++----- src/view/settings-window.ts | 22 ++--- src/view/welcome-board.ts | 14 +-- test-ui/app-tray-test.ts | 4 +- test-ui/chat-view-test.ts | 4 +- test-ui/chat-window-test.ts | 8 +- test-ui/dashboard-window-test.ts | 22 ++--- test-ui/multi-chats-view-test.ts | 4 +- test-ui/window-menu-bar-test.ts | 4 +- test/api-manager-test.ts | 20 ++-- test/assistant-manager-test.ts | 10 +- test/chat-service-test.ts | 4 +- test/multi-chats-service-test.ts | 8 +- test/setup.ts | 14 +-- 30 files changed, 273 insertions(+), 267 deletions(-) rename src/model/{api-endpoint.ts => api-credential.ts} (59%) diff --git a/src/cli-main.ts b/src/cli-main.ts index 49ec80f..552ad43 100644 --- a/src/cli-main.ts +++ b/src/cli-main.ts @@ -23,32 +23,39 @@ async function cliMain() { extensionManager.activateBuiltinExtensions(); - let endpointName; + let credentialName; for (let i = 2; i < process.argv.length; ++i) { const arg = process.argv[i]; if (arg == '-h') help(); if (arg == '-l') list(); - if (arg == '--endpoint') - endpointName = process.argv[i + 1]; + if (arg == '--credential') + credentialName = process.argv[i + 1]; } - // Find an endpoint that supports chat service. - const endpoint = apiManager.getEndpoints() - // Only search from chatable endpoints. - .filter(endpoint => { - const {apiClass} = apiManager.getAPIRecord(endpoint.type); - return matchClass(ChatCompletionAPI, apiClass) || - matchClass(ChatConversationAPI, apiClass); + // Find an credential that supports chat service. + const credential = apiManager.getCredentials() + // Only search from chatable credentials. + .filter(credential => { + try { + const {apiClass} = apiManager.getAPIRecord(credential.type); + return matchClass(ChatCompletionAPI, apiClass) || + matchClass(ChatConversationAPI, apiClass); + } catch (error) { + if (error.message.includes('not exist')) + return false; + else + throw error; + } }) // Return the one matches name. - .find(endpoint => endpointName ? endpoint.name == endpointName : true); - if (!endpoint) - throw new Error('Can not find an API endpoint that supports chatting.'); + .find(credential => credentialName ? credential.name == credentialName : true); + if (!credential) + throw new Error('Can not find an API credential that supports chatting.'); // Chat and exit. - await enterConversation(endpoint); + await enterConversation(credential); process.exit(0); } @@ -58,25 +65,25 @@ function help() { Options: - -h show help - -l list all API endpoints - --endpoint chat with the specified API endpoint + -h show help + -l list all API credentials + --credential chat with the specified API credential `); process.exit(0); } function list() { - console.log('API Endpoints:'); - for (const endpoint of apiManager.getEndpoints()) - console.log(' ' + endpoint.name); + console.log('API Credentials:'); + for (const credential of apiManager.getCredentials()) + console.log(' ' + credential.name); process.exit(0); } -async function enterConversation(endpoint) { - console.log('Start chatting with', endpoint.name + ':'); +async function enterConversation(credential) { + console.log('Start chatting with', credential.name + ':'); const chat = new ChatService({ - name: endpoint.name, - api: apiManager.createAPIForEndpoint(endpoint) as ChatServiceSupportedAPIs, + name: credential.name, + api: apiManager.createAPIForCredential(credential) as ChatServiceSupportedAPIs, }); // Create terminal chat interface. diff --git a/src/controller/api-manager.ts b/src/controller/api-manager.ts index 65466de..808ba08 100644 --- a/src/controller/api-manager.ts +++ b/src/controller/api-manager.ts @@ -1,6 +1,6 @@ import {Signal} from 'typed-signals'; -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import Icon from '../model/icon'; import Param from '../model/param'; import WebAPI from '../model/web-api'; @@ -9,7 +9,7 @@ import {ConfigStoreItem} from '../model/config-store'; import {Selection} from '../model/param'; import {getNextId} from '../util/id-generator'; -type WebAPIType = new (endpoint: APIEndpoint) => WebAPI; +type WebAPIType = new (credential: APICredential) => WebAPI; export type APIRecord = { name: string, @@ -20,35 +20,35 @@ export type APIRecord = { description?: string, priority?: number, params?: Param[], - login?: () => Promise>; - refresh?: () => Promise>; + login?: () => Promise>; + refresh?: () => Promise>; }; export class APIManager extends ConfigStoreItem { - onAddEndpoint: Signal<(endpoint: APIEndpoint) => void> = new Signal(); - onUpdateEndpoint: Signal<(endpoint: APIEndpoint) => void> = new Signal(); - onRemoveEndpoint: Signal<(endpoint: APIEndpoint) => void> = new Signal(); + onAddCredential: Signal<(credential: APICredential) => void> = new Signal(); + onUpdateCredential: Signal<(credential: APICredential) => void> = new Signal(); + onRemoveCredential: Signal<(credential: APICredential) => void> = new Signal(); #apis: Record = {}; - #endpoints: Record = {}; + #credentials: Record = {}; deserialize(data: object) { if (!data) // accepts empty config data = {}; if (typeof data != 'object') throw new Error(`Unknown data for "apis": ${data}.`); - this.#endpoints = {}; + this.#credentials = {}; for (const id in data) { - const endpoint = APIEndpoint.deserialize(data[id]); - endpoint.id = id; - this.#endpoints[id] = endpoint; + const credential = APICredential.deserialize(data[id]); + credential.id = id; + this.#credentials[id] = credential; } } serialize() { const data = {}; - for (const id in this.#endpoints) - data[id] = this.#endpoints[id].serialize(); + for (const id in this.#credentials) + data[id] = this.#credentials[id].serialize(); return data; } @@ -62,8 +62,8 @@ export class APIManager extends ConfigStoreItem { unregisterAPI(name: string) { if (!(name in this.#apis)) throw new Error(`There is no API named "${name}".`); - if (this.getEndpoints().find(e => e.type == name)) - throw new Error(`Can not unregister API "${name}" because there is an API endpoint using it.`); + if (this.getCredentials().find(e => e.type == name)) + throw new Error(`Can not unregister API "${name}" because there is an API credential using it.`); delete this.#apis[name]; this.saveConfig(); } @@ -78,58 +78,58 @@ export class APIManager extends ConfigStoreItem { return this.#apis[name]; } - createAPIForEndpoint(endpoint: APIEndpoint) { - if (!(endpoint.type in this.#apis)) - throw new Error(`Unable to find API implementation for endpoint ${endpoint.type}.`); - return new (this.#apis[endpoint.type].apiClass)(endpoint); + createAPIForCredential(credential: APICredential) { + if (!(credential.type in this.#apis)) + throw new Error(`Unable to find API implementation for credential ${credential.type}.`); + return new (this.#apis[credential.type].apiClass)(credential); } - addEndpoint(endpoint: APIEndpoint) { - if (endpoint.id) - throw new Error('Re-adding a managed APIEndpoint.'); - endpoint.id = getNextId(endpoint.name, Object.keys(this.#endpoints)); - this.#endpoints[endpoint.id] = endpoint; - this.onAddEndpoint.emit(endpoint); + addCredential(credential: APICredential) { + if (credential.id) + throw new Error('Re-adding a managed APICredential.'); + credential.id = getNextId(credential.name, Object.keys(this.#credentials)); + this.#credentials[credential.id] = credential; + this.onAddCredential.emit(credential); this.saveConfig(); - return endpoint.id; + return credential.id; } - updateEndpoint(endpoint: APIEndpoint) { - this.onUpdateEndpoint.emit(endpoint); + updateCredential(credential: APICredential) { + this.onUpdateCredential.emit(credential); this.saveConfig(); } - removeEndpointById(id: string) { - if (!(id in this.#endpoints)) + removeCredentialById(id: string) { + if (!(id in this.#credentials)) throw new Error(`Removing unknown API id: ${id}.`); - const assistant = assistantManager.getAssistants().find(a => a.service.api.endpoint.id == id); + const assistant = assistantManager.getAssistants().find(a => a.service.api.credential.id == id); if (assistant) - throw new Error(`Can not remove API endpoint because assistant "${assistant.service.name}" is using it.`); - const endpoint = this.#endpoints[id]; - delete this.#endpoints[id]; - this.onRemoveEndpoint.emit(endpoint); + throw new Error(`Can not remove API credential because assistant "${assistant.service.name}" is using it.`); + const credential = this.#credentials[id]; + delete this.#credentials[id]; + this.onRemoveCredential.emit(credential); this.saveConfig(); - endpoint.id = null; + credential.id = null; } - getEndpoints() { - return Object.values(this.#endpoints); + getCredentials() { + return Object.values(this.#credentials); } - getEndpointById(id: string) { - if (!(id in this.#endpoints)) + getCredentialById(id: string) { + if (!(id in this.#credentials)) throw new Error(`Getting unknown API id: ${id}.`); - return this.#endpoints[id]; + return this.#credentials[id]; } - getEndpointsByType(type: string): APIEndpoint[] { - return Object.keys(this.#endpoints) - .filter(k => this.#endpoints[k].type == type) - .map(k => this.#endpoints[k]); + getCredentialsByType(type: string): APICredential[] { + return Object.keys(this.#credentials) + .filter(k => this.#credentials[k].type == type) + .map(k => this.#credentials[k]); } - getEndpointSelections(): Selection[] { - return Object.values(this.#endpoints).map(v => ({name: v.name, value: v})); + getCredentialSelections(): Selection[] { + return Object.values(this.#credentials).map(v => ({name: v.name, value: v})); } } diff --git a/src/controller/assistant-manager.ts b/src/controller/assistant-manager.ts index c78c504..9bf13f3 100644 --- a/src/controller/assistant-manager.ts +++ b/src/controller/assistant-manager.ts @@ -1,6 +1,6 @@ import {Signal} from 'typed-signals'; -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import Assistant from '../model/assistant'; import Icon from '../model/icon'; import WebService, { @@ -92,8 +92,8 @@ export class AssistantManager extends ConfigStoreItem { return data; } - createAssistant(name: string, serviceName: string, endpoint: APIEndpoint, viewClass: BaseViewType, options?: Partial) { - const service = serviceManager.createService(name, serviceName, endpoint, options); + createAssistant(name: string, serviceName: string, credential: APICredential, viewClass: BaseViewType, options?: Partial) { + const service = serviceManager.createService(name, serviceName, credential, options); // Do runtime check of API type compatibility. const {viewClasses} = serviceManager.getServiceByName(serviceName); if (!viewClasses.find(V => matchClass(V, viewClass))) diff --git a/src/controller/service-manager.ts b/src/controller/service-manager.ts index f6093c3..1df99fb 100644 --- a/src/controller/service-manager.ts +++ b/src/controller/service-manager.ts @@ -1,4 +1,4 @@ -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import BaseView, {BaseViewType} from '../view/base-view'; import Param from '../model/param'; import WebAPI from '../model/web-api'; @@ -7,7 +7,7 @@ import apiManager, {sortByPriority} from '../controller/api-manager'; import {Selection} from '../model/param'; import {deepAssign, matchClass} from '../util/object-utils'; -type WebAPIType = (new (endpoint) => WebAPI) | (abstract new (endpoint) => WebAPI); +type WebAPIType = (new (credential) => WebAPI) | (abstract new (credential) => WebAPI); export type ServiceRecord = { name?: string, @@ -86,16 +86,16 @@ export class ServiceManager { return this.#views.map(v => ({name: v.name, value: v})); } - createService(name: string, serviceName: string, endpoint: APIEndpoint, options?: Partial): WebService { + createService(name: string, serviceName: string, credential: APICredential, options?: Partial): WebService { if (!(serviceName in this.#services)) throw new Error(`Service with name "${serviceName}" does not exist.`); // Do runtime check of API type compatibility. - const {icon, apiClass} = apiManager.getAPIRecord(endpoint.type); + const {icon, apiClass} = apiManager.getAPIRecord(credential.type); const {apiClasses, serviceClass} = this.#services[serviceName]; if (!apiClasses.find(A => matchClass(A, apiClass))) - throw new Error(`Service "${serviceName}" does not support API type "${endpoint.type}".`); + throw new Error(`Service "${serviceName}" does not support API type "${credential.type}".`); // Create service. - const serviceOptions = deepAssign({name, api: new apiClass(endpoint), icon}, options); + const serviceOptions = deepAssign({name, api: new apiClass(credential), icon}, options); return new serviceClass(serviceOptions); } } diff --git a/src/exports/chie.ts b/src/exports/chie.ts index 4efb30b..47eb6fa 100644 --- a/src/exports/chie.ts +++ b/src/exports/chie.ts @@ -1,4 +1,4 @@ -export * from '../model/api-endpoint'; +export * from '../model/api-credential'; export * from '../model/base-chat-service'; export * from '../model/base-multi-chats-service'; export * from '../model/chat-api'; @@ -7,7 +7,7 @@ export * from '../model/errors'; export * from '../model/multi-chats-service'; export * from '../model/tool'; export * from '../view/base-view'; -export {default as APIEndpoint} from '../model/api-endpoint'; +export {default as APICredential} from '../model/api-credential'; export {default as BaseChatService} from '../model/base-chat-service'; export {default as BaseMultiChatsService} from '../model/base-multi-chats-service'; export {default as BaseView} from '../view/base-view'; diff --git a/src/extensions/bingchat/bingchat-api.ts b/src/extensions/bingchat/bingchat-api.ts index 93b3d5d..83a6131 100644 --- a/src/extensions/bingchat/bingchat-api.ts +++ b/src/extensions/bingchat/bingchat-api.ts @@ -1,7 +1,7 @@ import WebSocket from 'ws'; import crypto from 'node:crypto'; import { - APIEndpoint, + APICredential, APIError, AbortError, ChatAPIOptions, @@ -34,10 +34,10 @@ export default class BingChatAPI extends ChatConversationAPI { #lastContentLength: number; #lastLinks: {name: string, url: string}[]; - constructor(endpoint: APIEndpoint) { - if (endpoint.type != 'BingChat') - throw new Error('Expect BingChat API endpoint in BingChatAPI.'); - super(endpoint); + constructor(credential: APICredential) { + if (credential.type != 'BingChat') + throw new Error('Expect BingChat API credential in BingChatAPI.'); + super(credential); } async sendMessage(text: string, options: ChatAPIOptions) { @@ -71,7 +71,7 @@ export default class BingChatAPI extends ChatConversationAPI { signal: options.signal, headers: { ...edgeBrowserHeaders, - cookie: this.endpoint.cookie, + cookie: this.credential.cookie, }, }); const body = await response.json(); diff --git a/src/extensions/chatgpt-web/chatgpt-web-api.ts b/src/extensions/chatgpt-web/chatgpt-web-api.ts index ba34721..8c44456 100644 --- a/src/extensions/chatgpt-web/chatgpt-web-api.ts +++ b/src/extensions/chatgpt-web/chatgpt-web-api.ts @@ -2,7 +2,7 @@ import crypto from 'node:crypto'; import {createParser} from 'eventsource-parser'; import { - APIEndpoint, + APICredential, APIError, ChatAPIOptions, ChatConversationAPI, @@ -21,10 +21,10 @@ export default class ChatGPTWebAPI extends ChatConversationAPI { static canRemoveFromServer = true; static canRemoveMessagesAfter = true; - constructor(endpoint: APIEndpoint) { - if (endpoint.type != 'ChatGPT Web') - throw new Error('Expect ChatGPT Web API endpoint in ChatGPTWebAPI.'); - super(endpoint); + constructor(credential: APICredential) { + if (credential.type != 'ChatGPT Web') + throw new Error('Expect ChatGPT Web API credential in ChatGPTWebAPI.'); + super(credential); } async sendMessage(text: string, options: ChatAPIOptions) { @@ -32,7 +32,7 @@ export default class ChatGPTWebAPI extends ChatConversationAPI { this.session = {messageIds: [ crypto.randomUUID() ]}; const messageId = crypto.randomUUID(); - const response = await fetch(this.endpoint.url, { + const response = await fetch(this.credential.url, { body: JSON.stringify({ action: 'variant', model: this.getParam('model'), @@ -76,7 +76,7 @@ export default class ChatGPTWebAPI extends ChatConversationAPI { async removeFromServer() { if (!this.session?.conversationId) return; - const response = await fetch(`${this.endpoint.url}/${this.session.conversationId}`, { + const response = await fetch(`${this.credential.url}/${this.session.conversationId}`, { body: JSON.stringify({is_visible: false}), method: 'PATCH', headers: this.#getHeaders(), @@ -94,7 +94,7 @@ export default class ChatGPTWebAPI extends ChatConversationAPI { 'Content-Type': 'application/json', 'User-Agent': this.getParam('userAgent'), Authorization: `Bearer ${this.getParam('token')}`, - cookie: this.endpoint.cookie, + cookie: this.credential.cookie, referer: 'https://chat.openai.com/', authority: 'chat.openai.com', }; diff --git a/src/extensions/chatgpt/chatgpt-api.ts b/src/extensions/chatgpt/chatgpt-api.ts index 807f6a3..8df947a 100644 --- a/src/extensions/chatgpt/chatgpt-api.ts +++ b/src/extensions/chatgpt/chatgpt-api.ts @@ -1,6 +1,6 @@ import {createParser} from 'eventsource-parser'; import { - APIEndpoint, + APICredential, APIError, ChatCompletionAPI, ChatCompletionAPIOptions, @@ -10,23 +10,23 @@ import { } from 'chie'; export default class ChatGPTAPI extends ChatCompletionAPI { - constructor(endpoint: APIEndpoint) { - if (endpoint.type != 'ChatGPT API') - throw new Error('Expect ChatGPT API endpoint in ChatGPTAPI.'); - super(endpoint); + constructor(credential: APICredential) { + if (credential.type != 'ChatGPT API') + throw new Error('Expect ChatGPT API credential in ChatGPTAPI.'); + super(credential); } async sendConversation(history: ChatMessage[], options: ChatCompletionAPIOptions) { // Start request. const headers = {'Content-Type': 'application/json'}; - if (this.endpoint.key) - headers['Authorization'] = `Bearer ${this.endpoint.key}`; - const response = await fetch(this.endpoint.url, { + if (this.credential.key) + headers['Authorization'] = `Bearer ${this.credential.key}`; + const response = await fetch(this.credential.url, { body: JSON.stringify(this.#getRequestBody(history, options)), method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.endpoint.key}`, + 'Authorization': `Bearer ${this.credential.key}`, }, signal: options.signal, }); diff --git a/src/model/api-endpoint.ts b/src/model/api-credential.ts similarity index 59% rename from src/model/api-endpoint.ts rename to src/model/api-credential.ts index 04429a9..b5c5e00 100644 --- a/src/model/api-endpoint.ts +++ b/src/model/api-credential.ts @@ -1,6 +1,6 @@ import Serializable from './serializable'; -export default class APIEndpoint implements Serializable { +export default class APICredential implements Serializable { id?: string; type: string; name: string; @@ -9,26 +9,26 @@ export default class APIEndpoint implements Serializable { cookie?: string; params?: Record; - static deserialize(data: Partial): APIEndpoint { + static deserialize(data: Partial): APICredential { if (!data || typeof data != 'object' || typeof data.type != 'string' || typeof data.name != 'string') { - throw new Error(`Invalid APIEndpoint data: ${JSON.stringify(data)}`); + throw new Error(`Invalid APICredential data: ${JSON.stringify(data)}`); } if ('params' in data) { if (typeof data.params != 'object') - throw new Error('The params of APIEndpoint must be Record'); + throw new Error('The params of APICredential must be Record'); } - return new APIEndpoint(data); + return new APICredential(data); } - constructor(init: Partial) { + constructor(init: Partial) { Object.assign(this, init); } serialize() { - const data: Partial = {type: this.type, name: this.name, url: this.url}; + const data: Partial = {type: this.type, name: this.name, url: this.url}; if (this.url) data.url = this.url; if (this.key) diff --git a/src/model/chat-api.ts b/src/model/chat-api.ts index 64dc5e5..eef4519 100644 --- a/src/model/chat-api.ts +++ b/src/model/chat-api.ts @@ -1,4 +1,4 @@ -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import Tool from '../model/tool'; import WebAPI from '../model/web-api'; @@ -62,8 +62,8 @@ export interface ChatCompletionAPIOptions extends ChatAPIOptions { } export abstract class ChatCompletionAPI extends WebAPI { - constructor(endpoint: APIEndpoint) { - super(endpoint); + constructor(credential: APICredential) { + super(credential); } // Send the whole conversation history and get reply. @@ -73,8 +73,8 @@ export abstract class ChatCompletionAPI extends WebAPI { export abstract class ChatConversationAPI extends WebAPI { session?: T; - constructor(endpoint: APIEndpoint) { - super(endpoint); + constructor(credential: APICredential) { + super(credential); } // Send a single user message and get reply. @@ -93,7 +93,7 @@ export abstract class ChatConversationAPI extends WebAPI { } // Defines static properties on ChatConversationAPI. -type ChatConversationAPIConstructorType = new (endpoint: APIEndpoint) => ChatConversationAPI; +type ChatConversationAPIConstructorType = new (credential: APICredential) => ChatConversationAPI; export interface ChatConversationAPIType extends ChatConversationAPIConstructorType { isHighlyRateLimited?: boolean; diff --git a/src/model/web-api.ts b/src/model/web-api.ts index c20d51b..2a3f088 100644 --- a/src/model/web-api.ts +++ b/src/model/web-api.ts @@ -1,24 +1,24 @@ -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; export default abstract class WebAPI { - endpoint: APIEndpoint; - // Override the params of endpoint. + credential: APICredential; + // Override the params of credential. params?: Record; - constructor(endpoint: APIEndpoint) { - this.endpoint = endpoint; + constructor(credential: APICredential) { + this.credential = credential; } getParam(name: string) { if (this.params && name in this.params) return this.params[name]; - if (this.endpoint.params) - return this.endpoint.params[name]; + if (this.credential.params) + return this.credential.params[name]; return undefined; } clone() { const apiManager = require('../controller/api-manager').default; - return apiManager.createAPIForEndpoint(this.endpoint); + return apiManager.createAPIForCredential(this.credential); } } diff --git a/src/model/web-service.ts b/src/model/web-service.ts index 73e5ec6..9b61acf 100644 --- a/src/model/web-service.ts +++ b/src/model/web-service.ts @@ -40,8 +40,8 @@ export default class WebService = {name: data.name, api}; if (typeof data.icon == 'string') options.icon = new Icon({chieURL: data.icon}); @@ -66,7 +66,7 @@ export default class WebService { - const result: Partial = {}; + readCredential(): Partial { + const result: Partial = {}; if (this.showAuthParams) { if (this.apiRecord.url) result.url = this.getValue('url'); diff --git a/src/view/chat-view.ts b/src/view/chat-view.ts index f9a5016..7f91b95 100644 --- a/src/view/chat-view.ts +++ b/src/view/chat-view.ts @@ -258,7 +258,7 @@ export default class ChatView extends BaseView { } } } - const apiRecord = apiManager.getAPIRecord(this.service.api.endpoint.type); + const apiRecord = apiManager.getAPIRecord(this.service.api.credential.type); if (apiRecord?.params) { for (const param of apiRecord.params) { if (param.hasSwitcher) { @@ -486,9 +486,9 @@ export default class ChatView extends BaseView { async #refreshToken(action: 'refresh' | 'login') { try { - const record = apiManager.getAPIRecord(this.service.api.endpoint.type); - deepAssign(this.service.api.endpoint, await record[action]()); - apiManager.updateEndpoint(this.service.api.endpoint); + const record = apiManager.getAPIRecord(this.service.api.credential.type); + deepAssign(this.service.api.credential, await record[action]()); + apiManager.updateCredential(this.service.api.credential); this.service.regenerateLastResponse(); } catch (error) { if (error.name != 'CancelledError') diff --git a/src/view/dashboard-window.ts b/src/view/dashboard-window.ts index cf94a63..38b7a1e 100644 --- a/src/view/dashboard-window.ts +++ b/src/view/dashboard-window.ts @@ -268,9 +268,9 @@ export default class DashboardWindow extends BaseWindow { } }, { - label: 'Edit API endpoint...', + label: 'Edit API credential...', onClick: () => { - const win = new NewAPIWindow(view.assistant.service.api.endpoint); + const win = new NewAPIWindow(view.assistant.service.api.credential); win.window.center(); win.window.activate(); } diff --git a/src/view/new-api-window.ts b/src/view/new-api-window.ts index 98a8c2d..fb77c6d 100644 --- a/src/view/new-api-window.ts +++ b/src/view/new-api-window.ts @@ -1,6 +1,6 @@ import gui from 'gui'; -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import APIParamsView from './api-params-view'; import BaseWindow from './base-window'; import ButtonsArea from './buttons-area'; @@ -15,7 +15,7 @@ import windowManager from '../controller/window-manager'; import {deepAssign, matchClass} from '../util/object-utils'; export default class NewAPIWindow extends BaseWindow { - endpoint?: APIEndpoint; + credential?: APICredential; apiSelector: ParamsView; apiParams?: APIParamsView; @@ -23,9 +23,9 @@ export default class NewAPIWindow extends BaseWindow { submitButton: gui.Button; loginButton?: gui.Button; - constructor(endpoint?: APIEndpoint) { + constructor(credential?: APICredential) { super({pressEscToClose: true}); - this.endpoint = endpoint; + this.credential = credential; this.contentView.setStyle({ gap: basicStyle.padding / 2, @@ -39,31 +39,31 @@ export default class NewAPIWindow extends BaseWindow { name: 'name', type: 'string', displayName: 'Name', - value: endpoint?.name, + value: credential?.name, }, { name: 'type', type: 'selection', displayName: 'API Type', - selected: endpoint?.type, + selected: credential?.type, selections: apiManager.getAPISelections(), }, ], {nullable: false}); this.apiSelector.onActivate.connect(this.#onSubmit.bind(this)); this.contentView.addChildView(this.apiSelector.view); - if (!endpoint) { + if (!credential) { this.createCheckbox = gui.Button.create({ type: 'checkbox', - title: 'Create an assistant for this API endpoint...' + title: 'Create an assistant for this API credential...' }); this.createCheckbox.setChecked(true); this.createCheckbox.setStyle({marginLeft: valueMarginLeft}); this.apiSelector.view.addChildViewAt(this.createCheckbox, 1); } - this.#updateAPIParamsView(endpoint); - if (endpoint) + this.#updateAPIParamsView(credential); + if (credential) this.apiSelector.getRow('type').editor.setEnabled(false); else this.apiSelector.getRow('type').subscribeOnChange(this.#updateAPIParamsView.bind(this)); @@ -71,7 +71,7 @@ export default class NewAPIWindow extends BaseWindow { const buttonsArea = new ButtonsArea(); buttonsArea.view.setStyle({flex: 1, paddingTop: basicStyle.padding / 2}); this.contentView.addChildView(buttonsArea.view); - this.submitButton = buttonsArea.addButton(endpoint ? 'OK' : 'Add'); + this.submitButton = buttonsArea.addButton(credential ? 'OK' : 'Add'); this.submitButton.makeDefault(); this.submitButton.onClick = this.#onSubmit.bind(this); buttonsArea.addCloseButton(); @@ -79,7 +79,7 @@ export default class NewAPIWindow extends BaseWindow { this.apiSelector.getRow('name').editor.focus(); this.resizeToFitContentView({width: 540}); - this.window.setTitle(endpoint ? `Edit API Endpoint: ${endpoint.name}` : 'Add New API Endpoint'); + this.window.setTitle(credential ? `Edit API Credential: ${credential.name}` : 'Add New API Credential'); } destructor() { @@ -92,7 +92,7 @@ export default class NewAPIWindow extends BaseWindow { return null; // do not remember state } - #updateAPIParamsView(endpoint?: APIEndpoint) { + #updateAPIParamsView(credential?: APICredential) { if (this.apiParams) this.contentView.removeChildView(this.apiParams.view); if (this.loginButton) @@ -101,8 +101,8 @@ export default class NewAPIWindow extends BaseWindow { apiRecord: this.apiSelector.getValue('type'), showAuthParams: true, }); - if (endpoint) - this.apiParams.fillEndpoint(endpoint); + if (credential) + this.apiParams.fillCredential(credential); this.contentView.addChildViewAt(this.apiParams.view, 1); // Show a login button. if (this.apiParams.apiRecord.auth == 'login') { @@ -144,24 +144,24 @@ export default class NewAPIWindow extends BaseWindow { this.apiSelector.requestAttention('name'); return; } - if (this.endpoint) { - // Edit endpoint. - this.endpoint.name = name; - deepAssign(this.endpoint, this.apiParams.readEndpoint()); - apiManager.updateEndpoint(this.endpoint); + if (this.credential) { + // Edit credential. + this.credential.name = name; + deepAssign(this.credential, this.apiParams.readCredential()); + apiManager.updateCredential(this.credential); } else { - if (apiManager.getEndpoints().find(e => e.name == name)) { - alert('There is already an API endpoint with the same name.'); + if (apiManager.getCredentials().find(e => e.name == name)) { + alert('There is already an API credential with the same name.'); this.apiSelector.requestAttention('name'); return; } - // Create a new endpoint. + // Create a new credential. const apiRecord = this.apiSelector.getValue('type'); - const endpoint = new APIEndpoint(deepAssign({ + const credential = new APICredential(deepAssign({ name, type: apiRecord.name, - }, this.apiParams.readEndpoint())); - apiManager.addEndpoint(endpoint); + }, this.apiParams.readCredential())); + apiManager.addCredential(credential); if (this.createCheckbox?.isChecked()) { // Find a service type supporting the API. const serviceRecord = serviceManager.getRegisteredServices().find(service => { @@ -172,9 +172,9 @@ export default class NewAPIWindow extends BaseWindow { return false; }); if (!serviceRecord) - throw new Error('Unable to find a service type for the endpoint.'); + throw new Error('Unable to find a service type for the credential.'); // Create a new assistant. - assistantManager.createAssistant(name, serviceRecord.serviceClass.name, endpoint, serviceRecord.viewClasses[0]); + assistantManager.createAssistant(name, serviceRecord.serviceClass.name, credential, serviceRecord.viewClasses[0]); // Close new assistant window since it is no longer needed. windowManager.getNamedWindow('newAssistant')?.close(); // Show the added assistant. diff --git a/src/view/new-assistant-window.ts b/src/view/new-assistant-window.ts index 074c33e..2536a27 100644 --- a/src/view/new-assistant-window.ts +++ b/src/view/new-assistant-window.ts @@ -1,6 +1,6 @@ import gui from 'gui'; -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import APIParamsView from '../view/api-params-view'; import Assistant from '../model/assistant'; import BaseWindow from '../view/base-window'; @@ -16,7 +16,6 @@ import basicStyle from '../view/basic-style'; import serviceManager, {ServiceRecord} from '../controller/service-manager'; import windowManager from '../controller/window-manager'; import {BaseViewType} from '../view/base-view'; -import {ChatCompletionAPI} from '../model/chat-api'; import {WebServiceOptions} from '../model/web-service'; import {isEmptyObject, matchClass} from '../util/object-utils'; @@ -71,8 +70,8 @@ export default class NewAssistantWindow extends BaseWindow { name: 'api', type: 'selection', displayName: 'API', - selected: assistant?.service.api.endpoint.name, - selections: apiManager.getEndpointSelections(), + selected: assistant?.service.api.credential.name, + selections: apiManager.getCredentialSelections(), }, { name: 'service', @@ -81,8 +80,8 @@ export default class NewAssistantWindow extends BaseWindow { selected: assistant?.service.constructor.name, selections: serviceManager.getServiceSelections(), constrainedBy: 'api', - constrain: (endpoint: APIEndpoint, record: ServiceRecord) => { - const apiClass = apiManager.getAPIRecord(endpoint.type).apiClass; + constrain: (credential: APICredential, record: ServiceRecord) => { + const apiClass = apiManager.getAPIRecord(credential.type).apiClass; for (const A of record.apiClasses) { if (matchClass(A, apiClass)) return true; @@ -109,17 +108,17 @@ export default class NewAssistantWindow extends BaseWindow { this.serviceSelector.onActivate.connect(this.#onSubmit.bind(this)); this.contentView.addChildView(this.serviceSelector.view); - // Create helper button to add or edit api endpoint. + // Create helper button to add or edit api credential. let apiButton: gui.Button; if (assistant) { - apiButton = gui.Button.create('Edit API endpoint...'); + apiButton = gui.Button.create('Edit API credential...'); apiButton.onClick = () => { - const win = new NewAPIWindow(assistant.service.api.endpoint); + const win = new NewAPIWindow(assistant.service.api.credential); win.window.center(); win.window.activate(); }; } else { - apiButton = gui.Button.create('Create new API endpoint...'); + apiButton = gui.Button.create('Create new API credential...'); apiButton.onClick = () => windowManager.showNamedWindow('newAPI'); } apiButton.setStyle({ @@ -196,10 +195,10 @@ export default class NewAssistantWindow extends BaseWindow { } #hasCustomAPIParams() { - const endpoint = this.serviceSelector.getValue('api'); - if (!endpoint) + const credential = this.serviceSelector.getValue('api'); + if (!credential) return false; - const apiRecord = apiManager.getAPIRecord(endpoint.type); + const apiRecord = apiManager.getAPIRecord(credential.type); return apiRecord?.params?.length > 0; } @@ -216,8 +215,8 @@ export default class NewAssistantWindow extends BaseWindow { throw new Error('Can not create duplicate APIParamsView.'); // Create params view and fill with service.params. - const endpoint = this.serviceSelector.getValue('api'); - const apiRecord = apiManager.getAPIRecord(endpoint.type); + const credential = this.serviceSelector.getValue('api'); + const apiRecord = apiManager.getAPIRecord(credential.type); this.apiParams = new APIParamsView({ apiRecord, showAuthParams: false, @@ -252,8 +251,8 @@ export default class NewAssistantWindow extends BaseWindow { } // Filter supported service params. - const endpoint = this.serviceSelector.getValue('api'); - const apiClass = apiManager.getAPIRecord(endpoint.type).apiClass; + const credential = this.serviceSelector.getValue('api'); + const apiClass = apiManager.getAPIRecord(credential.type).apiClass; const serviceRecord = this.serviceSelector.getValue('service'); const params = serviceRecord?.params?.filter(param => { if (param.requiredAPIClass) @@ -281,11 +280,11 @@ export default class NewAssistantWindow extends BaseWindow { } #updateDefaultIcon() { - // Get default icon of endpoint. - const endpoint = this.serviceSelector.getValue('api'); - if (!endpoint) + // Get default icon of credential. + const credential = this.serviceSelector.getValue('api'); + if (!credential) return; - const icon = apiManager.getAPIRecord(endpoint.type).icon; + const icon = apiManager.getAPIRecord(credential.type).icon; // Update the icon view. const row = this.serviceSelector.getRow('icon') as IconParamRow; if (!row.hasCustomIcon()) @@ -331,7 +330,7 @@ export default class NewAssistantWindow extends BaseWindow { const assistant = assistantManager.createAssistant( name, (this.serviceSelector.getValue('service') as ServiceRecord).serviceClass.name, - (this.serviceSelector.getValue('api') as APIEndpoint), + (this.serviceSelector.getValue('api') as APICredential), (this.serviceSelector.getValue('view') as BaseViewType), options); assistant.setShortcut(this.serviceSelector.getValue('shortcut')); diff --git a/src/view/settings-window.ts b/src/view/settings-window.ts index 43be979..1feb55e 100644 --- a/src/view/settings-window.ts +++ b/src/view/settings-window.ts @@ -1,6 +1,6 @@ import gui from 'gui'; -import APIEndpoint from '../model/api-endpoint'; +import APICredential from '../model/api-credential'; import BaseWindow from '../view/base-window'; import EditableTable from '../view/editable-table'; import Extension from '../model/extension'; @@ -15,7 +15,7 @@ import windowManager from '../controller/window-manager'; export default class SettingsWindow extends BaseWindow { tab: gui.Tab; - apisTable: EditableTable; + apisTable: EditableTable; extensionsTable: EditableTable; shortcutEditor: ShortcutEditor; @@ -92,7 +92,7 @@ export default class SettingsWindow extends BaseWindow { } #createAPISetting() { - this.apisTable = new EditableTable([ + this.apisTable = new EditableTable([ {title: 'Type', key: 'type'}, {title: 'Name', key: 'name'}, ]); @@ -101,9 +101,9 @@ export default class SettingsWindow extends BaseWindow { padding: basicStyle.padding, }); - this.apisTable.onRemoveRow.connect((index, endpoint) => apiManager.removeEndpointById(endpoint.id)); - this.apisTable.onEditRow.connect((index, endpoint) => { - const win = new NewAPIWindow(endpoint); + this.apisTable.onRemoveRow.connect((index, credential) => apiManager.removeCredentialById(credential.id)); + this.apisTable.onEditRow.connect((index, credential) => { + const win = new NewAPIWindow(credential); win.window.center(); win.window.activate(); }); @@ -111,12 +111,12 @@ export default class SettingsWindow extends BaseWindow { const addButton = this.apisTable.buttonsArea.addButton('Add'); addButton.onClick = () => windowManager.showNamedWindow('newAPI'); - // Fill table with existing endpoints. - const update = () => this.apisTable.setData(apiManager.getEndpoints()); + // Fill table with existing credentials. + const update = () => this.apisTable.setData(apiManager.getCredentials()); update(); - this.connections.add(apiManager.onAddEndpoint.connect(update)); - this.connections.add(apiManager.onUpdateEndpoint.connect(update)); - this.connections.add(apiManager.onRemoveEndpoint.connect(update)); + this.connections.add(apiManager.onAddCredential.connect(update)); + this.connections.add(apiManager.onUpdateCredential.connect(update)); + this.connections.add(apiManager.onRemoveCredential.connect(update)); return this.apisTable.view; } diff --git a/src/view/welcome-board.ts b/src/view/welcome-board.ts index ab27038..91e52db 100644 --- a/src/view/welcome-board.ts +++ b/src/view/welcome-board.ts @@ -5,9 +5,9 @@ import apiManager from '../controller/api-manager'; import assistantManager from '../controller/assistant-manager'; import windowManager from '../controller/window-manager'; -const noEndpointText = 'Start by adding an API endpoint first:'; +const noCredentialText = 'Start by adding an API credential first:'; const noAssistantText = 'Start by creating an assistant:'; -const addEndpointLabel = 'Add API endpoint...'; +const addCredentialLabel = 'Add API credential...'; const addAssistantLabel = 'New assistant...'; export default class WelcomeBoard extends AppearanceAware { @@ -19,17 +19,17 @@ export default class WelcomeBoard extends AppearanceAware { alignItems: 'center', }); - const hasEndpoint = apiManager.getEndpoints().length > 0; + const hasCredential = apiManager.getCredentials().length > 0; const hasAssistant = assistantManager.getAssistants().length > 0; - if (hasEndpoint && hasAssistant) // should not happen + if (hasCredential && hasAssistant) // should not happen return; - const label = gui.Label.create(hasEndpoint ? noAssistantText: noEndpointText); + const label = gui.Label.create(hasCredential ? noAssistantText: noCredentialText); label.setStyle({marginBottom: 10}); this.view.addChildView(label); - const button = gui.Button.create(hasEndpoint ? addAssistantLabel : addEndpointLabel); + const button = gui.Button.create(hasCredential ? addAssistantLabel : addCredentialLabel); this.view.addChildView(button); - button.onClick = () => windowManager.showNamedWindow(hasEndpoint ? 'newAssistant' : 'newAPI'); + button.onClick = () => windowManager.showNamedWindow(hasCredential ? 'newAssistant' : 'newAPI'); } } diff --git a/test-ui/app-tray-test.ts b/test-ui/app-tray-test.ts index 732246d..8ae29df 100644 --- a/test-ui/app-tray-test.ts +++ b/test-ui/app-tray-test.ts @@ -19,8 +19,8 @@ describe('AppTray', () => { let collected = false; (() => { const tray = new AppTray(); - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', credential, ChatView); addFinalizer(tray, () => collected = true); assistantManager.removeAssistantById(assistant.id); tray.destructor(); diff --git a/test-ui/chat-view-test.ts b/test-ui/chat-view-test.ts index 760e928..18fe650 100644 --- a/test-ui/chat-view-test.ts +++ b/test-ui/chat-view-test.ts @@ -9,8 +9,8 @@ import {addFinalizer, gcUntil} from './util'; describe('ChatView', () => { let service: ChatService; beforeEach(() => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const api = apiManager.createAPIForEndpoint(endpoint) as ChatCompletionAPI; + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const api = apiManager.createAPIForCredential(credential) as ChatCompletionAPI; service = new ChatService({name: 'Test', api}); }); afterEach(() => { diff --git a/test-ui/chat-window-test.ts b/test-ui/chat-window-test.ts index 05ebe5f..58c51c8 100644 --- a/test-ui/chat-window-test.ts +++ b/test-ui/chat-window-test.ts @@ -15,8 +15,8 @@ describe('ChatWindow', () => { it('can be garbage collected', async () => { let collected = false; (() => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat', 'ChatService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat', 'ChatService', credential, ChatView); const chatWindow = new ChatWindow(assistant); addFinalizer(chatWindow, () => collected = true); chatWindow.window.close(); @@ -27,8 +27,8 @@ describe('ChatWindow', () => { it('can be garbage collected after sending message', async () => { let collected = false; await (async () => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat', 'ChatService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat', 'ChatService', credential, ChatView); const chatWindow = new ChatWindow(assistant); await (assistant.service as ChatService).sendMessage({role: ChatRole.User, content: 'message'}); addFinalizer(chatWindow, () => collected = true); diff --git a/test-ui/dashboard-window-test.ts b/test-ui/dashboard-window-test.ts index 981878c..2beca03 100644 --- a/test-ui/dashboard-window-test.ts +++ b/test-ui/dashboard-window-test.ts @@ -18,9 +18,9 @@ describe('DashboardWindow', async () => { it('can be garbage collected', async () => { let collected = false; (() => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - assistantManager.createAssistant('TestChat 1', 'MultiChatsService', endpoint, ChatView); - assistantManager.createAssistant('TestChat 2', 'MultiChatsService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + assistantManager.createAssistant('TestChat 1', 'MultiChatsService', credential, ChatView); + assistantManager.createAssistant('TestChat 2', 'MultiChatsService', credential, ChatView); const dashboard = new DashboardWindow(); addFinalizer(dashboard, () => collected = true); dashboard.switchTo(1); @@ -32,8 +32,8 @@ describe('DashboardWindow', async () => { it('can be garbage collected after sending message', async () => { let collected = false; await (async () => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', credential, ChatView); const dashboard = new DashboardWindow(); dashboard.restoreState({}); await (assistant.service as MultiChatsService).chats[0].sendMessage({role: ChatRole.User, content: 'message'}); @@ -44,12 +44,12 @@ describe('DashboardWindow', async () => { }); it('keep sane when adding/removing assistants', async () => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; const dashboard = new DashboardWindow(); assert.equal(dashboard.views.length, 0); - const i1 = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', endpoint, ChatView); - const i2 = assistantManager.createAssistant('TestChat 2', 'MultiChatsService', endpoint, ChatView); - const i3 = assistantManager.createAssistant('TestChat 3', 'MultiChatsService', endpoint, ChatView); + const i1 = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', credential, ChatView); + const i2 = assistantManager.createAssistant('TestChat 2', 'MultiChatsService', credential, ChatView); + const i3 = assistantManager.createAssistant('TestChat 3', 'MultiChatsService', credential, ChatView); assert.equal(dashboard.views.length, 3); assert.equal(dashboard.selectedView, dashboard.views[2]); assistantManager.removeAssistantById(i2.id); @@ -67,8 +67,8 @@ describe('DashboardWindow', async () => { let collected = false; const dashboard = new DashboardWindow(); (() => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', credential, ChatView); addFinalizer(assistant.service, () => collected = true); assistantManager.removeAssistantById(assistant.id); })(); diff --git a/test-ui/multi-chats-view-test.ts b/test-ui/multi-chats-view-test.ts index 4aba630..97df4a2 100644 --- a/test-ui/multi-chats-view-test.ts +++ b/test-ui/multi-chats-view-test.ts @@ -10,8 +10,8 @@ import {addFinalizer, gcUntil} from './util'; describe('MultiChatsView', function() { let service: MultiChatsService; beforeEach(() => { - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const api = apiManager.createAPIForEndpoint(endpoint) as ChatCompletionAPI; + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const api = apiManager.createAPIForCredential(credential) as ChatCompletionAPI; service = new MultiChatsService({name: 'Test', api}); }); afterEach(() => { diff --git a/test-ui/window-menu-bar-test.ts b/test-ui/window-menu-bar-test.ts index bcdf748..bd1d23c 100644 --- a/test-ui/window-menu-bar-test.ts +++ b/test-ui/window-menu-bar-test.ts @@ -23,8 +23,8 @@ describe('WindowMenuBar', () => { (() => { const win = new BaseWindow(); const menubar = new WindowMenuBar(win); - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat 1', 'MultiChatsService', credential, ChatView); addFinalizer(menubar, () => collected = true); assistantManager.removeAssistantById(assistant.id); menubar.destructor(); diff --git a/test/api-manager-test.ts b/test/api-manager-test.ts index fe358ed..041f5a3 100644 --- a/test/api-manager-test.ts +++ b/test/api-manager-test.ts @@ -1,6 +1,6 @@ import {assert} from 'chai'; -import APIEndpoint from '../src/model/api-endpoint'; +import APICredential from '../src/model/api-credential'; import {APIManager} from '../src/controller/api-manager'; describe('APIManager', () => { @@ -9,29 +9,29 @@ describe('APIManager', () => { it('deserializes from config', () => { const apiManager = new APIManager(); apiManager.deserialize({'tiananmen-8964': apiConfig}); - assert.notEqual(apiManager.getEndpointById('tiananmen-8964'), null); + assert.notEqual(apiManager.getCredentialById('tiananmen-8964'), null); }); it('generates id increasingly', () => { const apiManager = new APIManager(); - const id1 = apiManager.addEndpoint(APIEndpoint.deserialize(apiConfig)); + const id1 = apiManager.addCredential(APICredential.deserialize(apiConfig)); assert.equal(id1, 'tiananmen-1'); - const id2 = apiManager.addEndpoint(APIEndpoint.deserialize(apiConfig)); + const id2 = apiManager.addCredential(APICredential.deserialize(apiConfig)); assert.equal(id2, 'tiananmen-2'); - const id3 = apiManager.addEndpoint(APIEndpoint.deserialize(apiConfig)); + const id3 = apiManager.addCredential(APICredential.deserialize(apiConfig)); assert.equal(id3, 'tiananmen-3'); - apiManager.removeEndpointById(id1); - apiManager.removeEndpointById(id2); - const id4 = apiManager.addEndpoint(APIEndpoint.deserialize(apiConfig)); + apiManager.removeCredentialById(id1); + apiManager.removeCredentialById(id2); + const id4 = apiManager.addCredential(APICredential.deserialize(apiConfig)); assert.equal(id4, 'tiananmen-4'); }); it('handles invalid id', () => { const apiManager = new APIManager(); apiManager.deserialize({'tiananmen-invalid': apiConfig}); - const id1 = apiManager.addEndpoint(APIEndpoint.deserialize(apiConfig)); + const id1 = apiManager.addCredential(APICredential.deserialize(apiConfig)); assert.equal(id1, 'tiananmen-1'); - const id2 = apiManager.addEndpoint(APIEndpoint.deserialize(apiConfig)); + const id2 = apiManager.addCredential(APICredential.deserialize(apiConfig)); assert.equal(id2, 'tiananmen-2'); }); }); diff --git a/test/assistant-manager-test.ts b/test/assistant-manager-test.ts index 185979f..ad16f5c 100644 --- a/test/assistant-manager-test.ts +++ b/test/assistant-manager-test.ts @@ -1,6 +1,6 @@ import {assert} from 'chai'; -import APIEndpoint from '../src/model/api-endpoint'; +import APICredential from '../src/model/api-credential'; import ChatView from '../src/view/chat-view'; import ChatService from '../src/model/chat-service'; import apiManager from '../src/controller/api-manager'; @@ -14,19 +14,19 @@ describe('AssistantManager', () => { }); it('createAssistant checks API compatibility', () => { - const endpoint = APIEndpoint.deserialize({ + const credential = APICredential.deserialize({ name: 'Some API', type: 'DummyConversationAPI', url: '', }); assert.throws( - () => assistantManager.createAssistant('TestChat', 'MultiChatsService', endpoint, ChatView), + () => assistantManager.createAssistant('TestChat', 'MultiChatsService', credential, ChatView), 'Service "MultiChatsService" does not support API type "DummyConversationAPI".'); }); it('serialize and restore assistants', () => { - const endpoint = apiManager.getEndpointsByType('DummyConversationAPI')[0]; - const assistant = assistantManager.createAssistant('TestChat', 'ChatService', endpoint, ChatView); + const credential = apiManager.getCredentialsByType('DummyConversationAPI')[0]; + const assistant = assistantManager.createAssistant('TestChat', 'ChatService', credential, ChatView); assert.equal(assistant, assistantManager.getAssistants()[0]); // Force a re-initialization by deserializing from serialized data. assistantManager.deserialize(assistantManager.serialize()); diff --git a/test/chat-service-test.ts b/test/chat-service-test.ts index cfb1154..8199208 100644 --- a/test/chat-service-test.ts +++ b/test/chat-service-test.ts @@ -10,8 +10,8 @@ describe('ChatService', () => { let service: ChatService; beforeEach(() => { config.inMemory = false; - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const api = apiManager.createAPIForEndpoint(endpoint) as ChatCompletionAPI; + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const api = apiManager.createAPIForCredential(credential) as ChatCompletionAPI; service = new ChatService({name: 'Test', api}); }); afterEach(() => { diff --git a/test/multi-chats-service-test.ts b/test/multi-chats-service-test.ts index fc2516a..7bc3503 100644 --- a/test/multi-chats-service-test.ts +++ b/test/multi-chats-service-test.ts @@ -10,8 +10,8 @@ describe('MultiChatsService', () => { let service: MultiChatsService; beforeEach(() => { config.inMemory = false; - const endpoint = apiManager.getEndpointsByType('DummyCompletionAPI')[0]; - const api = apiManager.createAPIForEndpoint(endpoint) as ChatCompletionAPI; + const credential = apiManager.getCredentialsByType('DummyCompletionAPI')[0]; + const api = apiManager.createAPIForCredential(credential) as ChatCompletionAPI; service = new MultiChatsService({name: 'Test', api}); }); afterEach(() => { @@ -24,7 +24,7 @@ describe('MultiChatsService', () => { await chat.sendMessage({content: 'Message'}); assert.deepEqual(service.serialize(), { name: 'Test', - api: service.api.endpoint.id, + api: service.api.credential.id, icon: 'chie://app-file/assets/icons/bot.png', chats: [ {moment: chat.moment} ], }); @@ -43,7 +43,7 @@ describe('MultiChatsService', () => { await historyKeeper.flush(); const data = { name: 'Test', - api: service.api.endpoint.id, + api: service.api.credential.id, icon: 'chie://app-file/assets/icons/bot.png', chats: [ {moment, title: 'Test from disk'} ], }; diff --git a/test/setup.ts b/test/setup.ts index 645b82f..41b0d2a 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,4 +1,4 @@ -import APIEndpoint from '../src/model/api-endpoint'; +import APICredential from '../src/model/api-credential'; import ChatService from '../src/model/chat-service'; import ChatView from '../src/view/chat-view'; import MultiChatsService from '../src/model/multi-chats-service'; @@ -13,8 +13,8 @@ import { } from '../src/model/chat-api'; class DummyCompletionAPI extends ChatCompletionAPI { - constructor(endpoint) { - super(endpoint); + constructor(credential) { + super(credential); } async sendConversation(history, {onMessageDelta}) { onMessageDelta({role: ChatRole.Assistant, content: 'Reply'}, {pending: false}); @@ -22,8 +22,8 @@ class DummyCompletionAPI extends ChatCompletionAPI { } class DummyConversationAPI extends ChatConversationAPI { - constructor(endpoint) { - super(endpoint); + constructor(credential) { + super(credential); } async sendMessage(text, {onMessageDelta}) { onMessageDelta({role: ChatRole.Assistant, content: 'Reply'}, {pending: false}); @@ -66,11 +66,11 @@ export const mochaHooks = { apiClasses: [ChatCompletionAPI, ChatConversationAPI], viewClasses: [ChatView], }); - apiManager.addEndpoint(new APIEndpoint({ + apiManager.addCredential(new APICredential({ name: 'API 1', type: 'DummyCompletionAPI', })); - apiManager.addEndpoint(new APIEndpoint({ + apiManager.addCredential(new APICredential({ name: 'API 2', type: 'DummyConversationAPI', }));