|
|
@@ -1,16 +1,39 @@
|
|
|
-import { Injectable } from '@nestjs/common'
|
|
|
+import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
|
|
import { InjectRepository } from '@nestjs/typeorm'
|
|
|
import { Org } from './entities/org.entity'
|
|
|
import { Repository } from 'typeorm'
|
|
|
import { PageRequest } from '../common/dto/page-request'
|
|
|
import { Pagination, paginate } from 'nestjs-typeorm-paginate'
|
|
|
+import { OrgUser } from './entities/org-user.entity'
|
|
|
+import { UsersService } from 'src/users/users.service'
|
|
|
+import * as randomstring from 'randomstring'
|
|
|
+import { Role } from '../model/role.enum'
|
|
|
+import { KnowledgeBaseService } from 'src/knowledge-base/knowledge-base.service'
|
|
|
+import { OpenAIApi, Configuration } from 'azure-openai'
|
|
|
+import * as dedent from 'dedent'
|
|
|
|
|
|
@Injectable()
|
|
|
export class OrgService {
|
|
|
+ private readonly openai: OpenAIApi
|
|
|
constructor(
|
|
|
@InjectRepository(Org)
|
|
|
- private readonly orgRepository: Repository<Org>
|
|
|
- ) {}
|
|
|
+ private readonly orgRepository: Repository<Org>,
|
|
|
+ @InjectRepository(OrgUser)
|
|
|
+ private readonly orgUserRepository: Repository<OrgUser>,
|
|
|
+ private readonly userService: UsersService,
|
|
|
+ private readonly knowledgeService: KnowledgeBaseService
|
|
|
+ ) {
|
|
|
+ this.openai = new OpenAIApi(
|
|
|
+ new Configuration({
|
|
|
+ apiKey: 'beb32e4625a94b65ba8bc0ba1688c4d2',
|
|
|
+ // add azure info into configuration
|
|
|
+ azure: {
|
|
|
+ apiKey: 'beb32e4625a94b65ba8bc0ba1688c4d2',
|
|
|
+ endpoint: 'https://zouma.openai.azure.com'
|
|
|
+ }
|
|
|
+ })
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
async findById(orgId: number): Promise<Org> {
|
|
|
return await this.orgRepository.findOneOrFail({
|
|
|
@@ -31,4 +54,72 @@ export class OrgService {
|
|
|
async update(org: Org): Promise<Org> {
|
|
|
return await this.orgRepository.save(org)
|
|
|
}
|
|
|
+
|
|
|
+ async findAllUsers(req: PageRequest<OrgUser>): Promise<Pagination<OrgUser>> {
|
|
|
+ return await paginate<OrgUser>(this.orgUserRepository, req.page, req.search)
|
|
|
+ }
|
|
|
+
|
|
|
+ async addUsers(orgUsers: OrgUser[]): Promise<void> {
|
|
|
+ const users = await this.userService.findByPhone(orgUsers.map((x) => x.phone))
|
|
|
+ for (let x of orgUsers) {
|
|
|
+ let user = users.find((y) => y.phone === x.phone)
|
|
|
+ if (!user) {
|
|
|
+ const name = '0x' + randomstring.generate({ length: 8, charset: 'alphanumeric' })
|
|
|
+ user = await this.userService.create({
|
|
|
+ name: name,
|
|
|
+ username: name,
|
|
|
+ phone: x.phone,
|
|
|
+ roles: [Role.User]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ await this.userService.updateUser(user.id, { orgId: x.orgId })
|
|
|
+ x.userId = user.id
|
|
|
+ await this.orgUserRepository.save(x)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async removeUser(orgUserId): Promise<void> {
|
|
|
+ const orgUser = await this.orgUserRepository.findOneOrFail({ where: { id: orgUserId } })
|
|
|
+ await this.orgUserRepository.delete(orgUserId)
|
|
|
+ await this.userService.updateUser(orgUser.userId, { orgId: null })
|
|
|
+ }
|
|
|
+
|
|
|
+ async ask(question: string, orgId: number, knowledgeId?: number, fileId?: number) {
|
|
|
+ const org = await this.findById(orgId)
|
|
|
+ const context = await this.knowledgeService.askKnowledge(question, orgId, knowledgeId, fileId)
|
|
|
+ let content
|
|
|
+ if (org.contextTemplate) {
|
|
|
+ content = org.contextTemplate.replace('${context}', context.join('\n'))
|
|
|
+ } else {
|
|
|
+ content = dedent`You are a helpful AI article assistant.
|
|
|
+ The following are the relevant article content fragments found from the article.
|
|
|
+ The relevance is sorted from high to low.
|
|
|
+ You can only answer according to the following content:
|
|
|
+ \`\`\`
|
|
|
+ ${context.join('\n')}
|
|
|
+ \`\`\`
|
|
|
+ You need to carefully consider your answer to ensure that it is based on the context.
|
|
|
+ If the context does not mention the content or it is uncertain whether it is correct,
|
|
|
+ please answer "Current context cannot provide effective information.
|
|
|
+ You must use Chinese to respond.`
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await this.openai.createChatCompletion({
|
|
|
+ model: 'gpt35',
|
|
|
+ messages: [
|
|
|
+ { role: 'system', content: org.systemPrompt },
|
|
|
+ { role: 'user', content },
|
|
|
+ { role: 'user', content: question }
|
|
|
+ ]
|
|
|
+ })
|
|
|
+ return { answer: response.data.choices[0].message.content }
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error(error.message)
|
|
|
+ if (error.response) {
|
|
|
+ Logger.error(error.response.data)
|
|
|
+ }
|
|
|
+ throw new InternalServerErrorException(error.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|