Skip to content

Commit

Permalink
Rename endpoint to credential
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz committed Jul 25, 2023
1 parent 5e82f69 commit e2862c5
Show file tree
Hide file tree
Showing 30 changed files with 273 additions and 267 deletions.
55 changes: 31 additions & 24 deletions src/cli-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -58,25 +65,25 @@ function help() {
Options:
-h show help
-l list all API endpoints
--endpoint <name> chat with the specified API endpoint
-h show help
-l list all API credentials
--credential <name> 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.
Expand Down
96 changes: 48 additions & 48 deletions src/controller/api-manager.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand All @@ -20,35 +20,35 @@ export type APIRecord = {
description?: string,
priority?: number,
params?: Param[],
login?: () => Promise<Partial<APIEndpoint>>;
refresh?: () => Promise<Partial<APIEndpoint>>;
login?: () => Promise<Partial<APICredential>>;
refresh?: () => Promise<Partial<APICredential>>;
};

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<string, APIRecord> = {};
#endpoints: Record<string, APIEndpoint> = {};
#credentials: Record<string, APICredential> = {};

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;
}

Expand All @@ -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();
}
Expand All @@ -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}));
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/controller/assistant-manager.ts
Original file line number Diff line number Diff line change
@@ -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, {
Expand Down Expand Up @@ -92,8 +92,8 @@ export class AssistantManager extends ConfigStoreItem {
return data;
}

createAssistant(name: string, serviceName: string, endpoint: APIEndpoint, viewClass: BaseViewType, options?: Partial<WebServiceOptions>) {
const service = serviceManager.createService(name, serviceName, endpoint, options);
createAssistant(name: string, serviceName: string, credential: APICredential, viewClass: BaseViewType, options?: Partial<WebServiceOptions>) {
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)))
Expand Down
12 changes: 6 additions & 6 deletions src/controller/service-manager.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -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<WebServiceOptions>): WebService {
createService(name: string, serviceName: string, credential: APICredential, options?: Partial<WebServiceOptions>): 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);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/exports/chie.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand Down
12 changes: 6 additions & 6 deletions src/extensions/bingchat/bingchat-api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import WebSocket from 'ws';
import crypto from 'node:crypto';
import {
APIEndpoint,
APICredential,
APIError,
AbortError,
ChatAPIOptions,
Expand Down Expand Up @@ -34,10 +34,10 @@ export default class BingChatAPI extends ChatConversationAPI<SessionData> {
#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) {
Expand Down Expand Up @@ -71,7 +71,7 @@ export default class BingChatAPI extends ChatConversationAPI<SessionData> {
signal: options.signal,
headers: {
...edgeBrowserHeaders,
cookie: this.endpoint.cookie,
cookie: this.credential.cookie,
},
});
const body = await response.json();
Expand Down
Loading

0 comments on commit e2862c5

Please sign in to comment.