Commit 66813230 authored by nanahira's avatar nanahira

add davinci fallback

parent cc44f8dc
...@@ -7,4 +7,5 @@ accounts: ...@@ -7,4 +7,5 @@ accounts:
password: 'wwww' password: 'wwww'
pro: false pro: false
loginType: 'default' loginType: 'default'
proxies: [] proxies: []
\ No newline at end of file apiKeys: []
\ No newline at end of file
...@@ -14,9 +14,10 @@ ...@@ -14,9 +14,10 @@
"@nestjs/core": "^9.0.0", "@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/swagger": "^6.2.1", "@nestjs/swagger": "^6.2.1",
"aragami": "^1.1.2", "aragami": "^1.1.3",
"better-lock": "^2.0.3", "better-lock": "^2.0.3",
"chatgpt3": "npm:chatgpt@^3.5.2", "chatgpt3": "npm:chatgpt@^3.5.2",
"chatgpt4": "npm:chatgpt@^4.3.2",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.14.0",
"nestjs-aragami": "^1.0.0", "nestjs-aragami": "^1.0.0",
...@@ -2747,9 +2748,9 @@ ...@@ -2747,9 +2748,9 @@
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
}, },
"node_modules/aragami": { "node_modules/aragami": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/aragami/-/aragami-1.1.2.tgz", "resolved": "https://registry.npmjs.org/aragami/-/aragami-1.1.3.tgz",
"integrity": "sha512-asrmtKkLOgtXMYt3TeM/lWsmbKdJwjUPDV9yb+h62FBBMFx0h+lrEQN1XL6ZIkfyggtyC0G5gv0f9G+Ul0cTUg==", "integrity": "sha512-k/VdRFoNTgpCmFkuJL1P0NohIai7b+EPj1JzykrmTj1az71PSgNofkXxZ2M38FWD/+iRC7XJGAHhaDpzlSAWHw==",
"dependencies": { "dependencies": {
"@nanahira/redlock": "^1.0.0", "@nanahira/redlock": "^1.0.0",
"better-lock": "^2.0.3", "better-lock": "^2.0.3",
...@@ -3273,6 +3274,34 @@ ...@@ -3273,6 +3274,34 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/chatgpt4": {
"name": "chatgpt",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-4.3.2.tgz",
"integrity": "sha512-5+Kh0mdP/pDJTL3kA6C6Dp43EO8T8sILPJbbQwOu3J4dK+n/tYpSXsmG6VZkmitAVOffeTonXTGDJwslc66orw==",
"dependencies": {
"eventsource-parser": "^0.0.5",
"gpt-3-encoder": "^1.1.4",
"keyv": "^4.5.2",
"p-timeout": "^6.0.0",
"quick-lru": "^6.1.1",
"uuid": "^9.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/chatgpt4/node_modules/p-timeout": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz",
"integrity": "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "3.5.3", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
...@@ -4992,6 +5021,11 @@ ...@@ -4992,6 +5021,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/gpt-3-encoder": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/gpt-3-encoder/-/gpt-3-encoder-1.1.4.tgz",
"integrity": "sha512-fSQRePV+HUAhCn7+7HL7lNIXNm6eaFWFbNLOOGtmSJ0qJycyQvj60OvRlH7mee8xAMjBDNRdMXlMwjAbMTDjkg=="
},
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.10", "version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
...@@ -6366,6 +6400,11 @@ ...@@ -6366,6 +6400,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"node_modules/json-parse-even-better-errors": { "node_modules/json-parse-even-better-errors": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
...@@ -6412,6 +6451,14 @@ ...@@ -6412,6 +6451,14 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"node_modules/keyv": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
"integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
"dependencies": {
"json-buffer": "3.0.1"
}
},
"node_modules/kind-of": { "node_modules/kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
...@@ -8232,6 +8279,17 @@ ...@@ -8232,6 +8279,17 @@
} }
] ]
}, },
"node_modules/quick-lru": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz",
"integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/random": { "node_modules/random": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz", "resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz",
...@@ -12425,9 +12483,9 @@ ...@@ -12425,9 +12483,9 @@
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
}, },
"aragami": { "aragami": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/aragami/-/aragami-1.1.2.tgz", "resolved": "https://registry.npmjs.org/aragami/-/aragami-1.1.3.tgz",
"integrity": "sha512-asrmtKkLOgtXMYt3TeM/lWsmbKdJwjUPDV9yb+h62FBBMFx0h+lrEQN1XL6ZIkfyggtyC0G5gv0f9G+Ul0cTUg==", "integrity": "sha512-k/VdRFoNTgpCmFkuJL1P0NohIai7b+EPj1JzykrmTj1az71PSgNofkXxZ2M38FWD/+iRC7XJGAHhaDpzlSAWHw==",
"requires": { "requires": {
"@nanahira/redlock": "^1.0.0", "@nanahira/redlock": "^1.0.0",
"better-lock": "^2.0.3", "better-lock": "^2.0.3",
...@@ -12810,6 +12868,26 @@ ...@@ -12810,6 +12868,26 @@
} }
} }
}, },
"chatgpt4": {
"version": "npm:chatgpt@4.3.2",
"resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-4.3.2.tgz",
"integrity": "sha512-5+Kh0mdP/pDJTL3kA6C6Dp43EO8T8sILPJbbQwOu3J4dK+n/tYpSXsmG6VZkmitAVOffeTonXTGDJwslc66orw==",
"requires": {
"eventsource-parser": "^0.0.5",
"gpt-3-encoder": "^1.1.4",
"keyv": "^4.5.2",
"p-timeout": "^6.0.0",
"quick-lru": "^6.1.1",
"uuid": "^9.0.0"
},
"dependencies": {
"p-timeout": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.1.tgz",
"integrity": "sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w=="
}
}
},
"chokidar": { "chokidar": {
"version": "3.5.3", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
...@@ -14103,6 +14181,11 @@ ...@@ -14103,6 +14181,11 @@
"slash": "^3.0.0" "slash": "^3.0.0"
} }
}, },
"gpt-3-encoder": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/gpt-3-encoder/-/gpt-3-encoder-1.1.4.tgz",
"integrity": "sha512-fSQRePV+HUAhCn7+7HL7lNIXNm6eaFWFbNLOOGtmSJ0qJycyQvj60OvRlH7mee8xAMjBDNRdMXlMwjAbMTDjkg=="
},
"graceful-fs": { "graceful-fs": {
"version": "4.2.10", "version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
...@@ -15122,6 +15205,11 @@ ...@@ -15122,6 +15205,11 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true "dev": true
}, },
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"json-parse-even-better-errors": { "json-parse-even-better-errors": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
...@@ -15160,6 +15248,14 @@ ...@@ -15160,6 +15248,14 @@
"universalify": "^2.0.0" "universalify": "^2.0.0"
} }
}, },
"keyv": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
"integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
"requires": {
"json-buffer": "3.0.1"
}
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
...@@ -16344,6 +16440,11 @@ ...@@ -16344,6 +16440,11 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true "dev": true
}, },
"quick-lru": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz",
"integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q=="
},
"random": { "random": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz", "resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz",
......
...@@ -30,7 +30,7 @@ export class AccountPoolService ...@@ -30,7 +30,7 @@ export class AccountPoolService
account: OpenAIAccount, account: OpenAIAccount,
retry = true, retry = true,
): Promise<boolean> { ): Promise<boolean> {
const proxy = this.proxyPoolService.getProxy(); const proxy = await this.proxyPoolService.getProxy();
const state = new AccountState( const state = new AccountState(
account, account,
new this.ChatGPTAPIBrowserConstructor({ new this.ChatGPTAPIBrowserConstructor({
...@@ -64,7 +64,6 @@ export class AccountPoolService ...@@ -64,7 +64,6 @@ export class AccountPoolService
this.ChatGPTAPIBrowserConstructor = ( this.ChatGPTAPIBrowserConstructor = (
await eval("import('chatgpt3')") await eval("import('chatgpt3')")
).ChatGPTAPIBrowser; ).ChatGPTAPIBrowser;
await this.proxyPoolService.init();
this.accountInfos = await this.accountProvider.getAccounts(); this.accountInfos = await this.accountProvider.getAccounts();
this.accountInfos.forEach((a) => this.initAccount(a)); this.accountInfos.forEach((a) => this.initAccount(a));
} }
...@@ -98,6 +97,10 @@ export class AccountPoolService ...@@ -98,6 +97,10 @@ export class AccountPoolService
return useAccount; return useAccount;
} }
hasAccount(email: string) {
return this.accounts.has(email);
}
getAccount(email: string, exclude: string[] = []) { getAccount(email: string, exclude: string[] = []) {
if (!email || exclude.includes(email)) { if (!email || exclude.includes(email)) {
return this.randomAccount(exclude); return this.randomAccount(exclude);
......
...@@ -6,7 +6,7 @@ import { OpenAIAccount } from '../utility/config'; ...@@ -6,7 +6,7 @@ import { OpenAIAccount } from '../utility/config';
export class AccountProviderService { export class AccountProviderService {
constructor(private config: ConfigService) {} constructor(private config: ConfigService) {}
async getAccounts() { async getAccounts() {
return this.config.get<OpenAIAccount[]>('accounts'); return this.config.get<OpenAIAccount[]>('accounts') || [];
} }
async writeBack( async writeBack(
......
...@@ -19,7 +19,7 @@ import { ...@@ -19,7 +19,7 @@ import {
ApiOperation, ApiOperation,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { TalkDto } from './chatgpt/talk.dto'; import { TalkDto } from './chatgpt/talk.dto';
import { ResetConversationDto } from './conversation/reset-conversation.dto'; import { SessionDto } from './conversation/session.dto';
import { ConversationService } from './conversation/conversation.service'; import { ConversationService } from './conversation/conversation.service';
import { AuthService } from './auth/auth.service'; import { AuthService } from './auth/auth.service';
import { AccountPoolStatusDto } from './account-pool/account-pool-status.dto'; import { AccountPoolStatusDto } from './account-pool/account-pool-status.dto';
...@@ -57,7 +57,7 @@ export class AppController { ...@@ -57,7 +57,7 @@ export class AppController {
@Post('reset-conversation') @Post('reset-conversation')
@ApiOperation({ summary: 'Reset conversation' }) @ApiOperation({ summary: 'Reset conversation' })
@ApiBody({ type: ResetConversationDto }) @ApiBody({ type: SessionDto })
@ApiCreatedResponse({ type: BlankReturnMessageDto }) @ApiCreatedResponse({ type: BlankReturnMessageDto })
async resetConversation( async resetConversation(
@Body( @Body(
...@@ -66,7 +66,7 @@ export class AppController { ...@@ -66,7 +66,7 @@ export class AppController {
transformOptions: { enableImplicitConversion: true }, transformOptions: { enableImplicitConversion: true },
}), }),
) )
dto: ResetConversationDto, dto: SessionDto,
@Headers('Authorization') header: string, @Headers('Authorization') header: string,
) { ) {
await this.authService.auth(header); await this.authService.auth(header);
......
...@@ -10,6 +10,8 @@ import { ProxyPoolService } from './proxy-pool/proxy-pool.service'; ...@@ -10,6 +10,8 @@ import { ProxyPoolService } from './proxy-pool/proxy-pool.service';
import { AccountPoolService } from './account-pool/account-pool.service'; import { AccountPoolService } from './account-pool/account-pool.service';
import { AccountProviderService } from './account-provider/account-provider.service'; import { AccountProviderService } from './account-provider/account-provider.service';
import { ProxyProviderService } from './proxy-provider/proxy-provider.service'; import { ProxyProviderService } from './proxy-provider/proxy-provider.service';
import { KeyProviderService } from './key-provider/key-provider.service';
import { DavinciService } from './davinci/davinci.service';
@Module({ @Module({
imports: [ imports: [
...@@ -43,6 +45,8 @@ import { ProxyProviderService } from './proxy-provider/proxy-provider.service'; ...@@ -43,6 +45,8 @@ import { ProxyProviderService } from './proxy-provider/proxy-provider.service';
AccountPoolService, AccountPoolService,
AccountProviderService, AccountProviderService,
ProxyProviderService, ProxyProviderService,
KeyProviderService,
DavinciService,
], ],
}) })
export class AppModule {} export class AppModule {}
...@@ -6,12 +6,14 @@ import { ConversationDto } from '../conversation/conversation.dto'; ...@@ -6,12 +6,14 @@ import { ConversationDto } from '../conversation/conversation.dto';
import { BetterLock } from 'better-lock/dist/better_lock'; import { BetterLock } from 'better-lock/dist/better_lock';
import { BlankReturnMessageDto } from '../dto/ReturnMessage.dto'; import { BlankReturnMessageDto } from '../dto/ReturnMessage.dto';
import { AccountPoolService } from '../account-pool/account-pool.service'; import { AccountPoolService } from '../account-pool/account-pool.service';
import { DavinciService } from '../davinci/davinci.service';
@Injectable() @Injectable()
export class ChatgptService extends ConsoleLogger { export class ChatgptService extends ConsoleLogger {
constructor( constructor(
private conversationService: ConversationService, private conversationService: ConversationService,
private accountPoolService: AccountPoolService, private accountPoolService: AccountPoolService,
private davinciService: DavinciService,
) { ) {
super('ChatGPT'); super('ChatGPT');
} }
...@@ -32,10 +34,17 @@ export class ChatgptService extends ConsoleLogger { ...@@ -32,10 +34,17 @@ export class ChatgptService extends ConsoleLogger {
!previousConversation || !previousConversation ||
account.getEmail() !== previousConversation.account; account.getEmail() !== previousConversation.account;
if (!account) { if (!account) {
throw new BlankReturnMessageDto( /*throw new BlankReturnMessageDto(
500, 500,
'No available accounts', 'No available accounts',
).toException(); ).toException();*/
// fallback to davinci
const davinciResponse = await this.davinciService.chat(question.text);
const dto = new ConversationDto();
dto.session = '';
dto.isNewConversation = true;
dto.text = davinciResponse.text.replace(/^<!--(.*)-->$/gm, '');
return dto;
} }
this.log(`Processing chat for ${session} with ${account.getEmail()}`); this.log(`Processing chat for ${session} with ${account.getEmail()}`);
const result = await account.sendMessage(question.text, { const result = await account.sendMessage(question.text, {
...@@ -72,4 +81,13 @@ export class ChatgptService extends ConsoleLogger { ...@@ -72,4 +81,13 @@ export class ChatgptService extends ConsoleLogger {
? this.sessionLock.acquire(dto.session, () => this.chatProcess(dto)) ? this.sessionLock.acquire(dto.session, () => this.chatProcess(dto))
: this.chatProcess(dto); : this.chatProcess(dto);
} }
async hasConversation(session: string) {
const conversation = await this.conversationService.getConversation(
session,
);
return (
conversation && this.accountPoolService.hasAccount(conversation.account)
);
}
} }
...@@ -8,6 +8,10 @@ import type { ChatResponse } from 'chatgpt3'; ...@@ -8,6 +8,10 @@ import type { ChatResponse } from 'chatgpt3';
export class ConversationService { export class ConversationService {
constructor(@InjectAragami() private readonly aragami: Aragami) {} constructor(@InjectAragami() private readonly aragami: Aragami) {}
async hasConversation(session: string) {
return this.aragami.has(ConversationStorage, session);
}
async getConversation(session: string) { async getConversation(session: string) {
return this.aragami.get(ConversationStorage, session); return this.aragami.get(ConversationStorage, session);
} }
......
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator'; import { IsNotEmpty, IsString } from 'class-validator';
export class ResetConversationDto { export class SessionDto {
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@ApiProperty({ description: 'Session identifier.' }) @ApiProperty({ description: 'Session identifier.' })
......
import { Test, TestingModule } from '@nestjs/testing';
import { DavinciService } from './davinci.service';
describe('DavinciService', () => {
let service: DavinciService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [DavinciService],
}).compile();
service = module.get<DavinciService>(DavinciService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { ConsoleLogger, Injectable, OnModuleInit } from '@nestjs/common';
import { KeyProviderService } from '../key-provider/key-provider.service';
import { ChatGPTAPI, ChatMessage } from 'chatgpt4';
import { BlankReturnMessageDto } from '../dto/ReturnMessage.dto';
@Injectable()
export class DavinciService extends ConsoleLogger implements OnModuleInit {
constructor(private keyProvider: KeyProviderService) {
super('Davinci');
}
private ChatGPTApiConstructor: typeof ChatGPTAPI;
async onModuleInit() {
const { ChatGPTAPI } = await eval("import('chatgpt4')");
this.ChatGPTApiConstructor = ChatGPTAPI;
}
private pointer = 0;
private async getKey(exclude: string[] = []) {
const excludeSet = new Set(exclude);
const keys = (await this.keyProvider.getKeys()).filter(
(key) => !excludeSet.has(key),
);
if (!keys.length) {
throw new BlankReturnMessageDto(
500,
'No available accounts.',
).toException();
}
const index = this.pointer++ % keys.length;
return keys[index];
}
getInstance(apiKey: string) {
return new this.ChatGPTApiConstructor({
apiKey,
completionParams: {
model: 'text-davinci-003',
},
getMessageById: async () => undefined,
upsertMessage: async () => undefined,
});
}
async chat(text: string, excludeKeys: string[] = []): Promise<ChatMessage> {
const key = await this.getKey(excludeKeys);
const api = this.getInstance(key);
try {
return await api.sendMessage(text);
} catch (e) {
this.error(`Error with key ${key}}`);
this.error(e);
return this.chat(text, [...excludeKeys, key]);
} finally {
// clean off the message store
await api['_messageStore'].disconnect();
delete api['_messageStore'];
}
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { KeyProviderService } from './key-provider.service';
describe('KeyProviderService', () => {
let service: KeyProviderService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [KeyProviderService],
}).compile();
service = module.get<KeyProviderService>(KeyProviderService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class KeyProviderService {
constructor(private config: ConfigService) {}
async getKeys() {
return this.config.get<string[]>('apiKeys') || [];
}
}
...@@ -4,20 +4,16 @@ import { ProxyProviderService } from '../proxy-provider/proxy-provider.service'; ...@@ -4,20 +4,16 @@ import { ProxyProviderService } from '../proxy-provider/proxy-provider.service';
@Injectable() @Injectable()
export class ProxyPoolService { export class ProxyPoolService {
private pointer = 0; private pointer = 0;
private proxies: string[] = [];
constructor(private proxyProvider: ProxyProviderService) {} constructor(private proxyProvider: ProxyProviderService) {}
async init() { async getProxy() {
this.proxies = await this.proxyProvider.getProxies(); const proxies = await this.proxyProvider.getProxies();
} if (!proxies?.length) {
getProxy() {
if (!this.proxies?.length) {
return; return;
} }
if (this.pointer >= this.proxies.length) { if (this.pointer >= proxies.length) {
this.pointer = 0; this.pointer = 0;
} }
return this.proxies[this.pointer++]; return proxies[this.pointer++];
} }
} }
...@@ -5,6 +5,6 @@ import { ConfigService } from '@nestjs/config'; ...@@ -5,6 +5,6 @@ import { ConfigService } from '@nestjs/config';
export class ProxyProviderService { export class ProxyProviderService {
constructor(private config: ConfigService) {} constructor(private config: ConfigService) {}
async getProxies() { async getProxies() {
return this.config.get<string[]>('proxies'); return this.config.get<string[]>('proxies') || [];
} }
} }
...@@ -15,6 +15,7 @@ const defaultConfig = { ...@@ -15,6 +15,7 @@ const defaultConfig = {
redisUrl: '', redisUrl: '',
token: '', token: '',
proxies: [] as string[], proxies: [] as string[],
apiKeys: [] as string[],
}; };
export type Config = typeof defaultConfig; export type Config = typeof defaultConfig;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment