Commit 9b76c374 by hometown

feat: chat api done

parent 6c68b8cf
{
// Visuals
"workbench.colorTheme": "Vitesse Dark",
"workbench.colorTheme": "Vitesse Light",
"workbench.editor.tabCloseButton": "left",
"workbench.fontAliasing": "antialiased",
"workbench.iconTheme": "file-icons",
......
......@@ -21,6 +21,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^7.5.5",
"@nestjs/core": "^7.5.5",
"@nestjs/graphql": "^7.9.1",
......@@ -30,10 +31,11 @@
"@nestjs/swagger": "^4.7.5",
"@nestjs/typeorm": "^7.1.5",
"apollo-server-express": "^2.19.0",
"axios": "^1.6.2",
"bcrypt": "^5.0.0",
"body-parser-xml": "^2.0.1",
"class-transformer": "^0.3.1",
"class-validator": "^0.12.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"dayjs": "^1.9.7",
"express-rate-limit": "^5.2.3",
"graphql": "^15.4.0",
......
import { ArgumentMetadata, HttpException, HttpStatus, Injectable, PipeTransform, Type, } from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import { ValidationError, validate } from 'class-validator';
function formatErrors(errors: ValidationError[], errorMessage?: any, parentField?: string) {
const message = errorMessage || {}
let errorField = ''
let validationList
errors.forEach(error => {
errorField = parentField ? `${parentField}.${error.property}` : error?.property
if(!error?.constraints && error?.children?.length) {
formatErrors(error.children, message, errorField)
} else {
validationList = Object.values(error.constraints)
message[errorField] = validationList.length > 0 ? validationList.pop() : 'Invalid Value'!
}
})
console.log(message,'message')
return message
}
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
......@@ -10,17 +27,9 @@ export class ValidationPipe implements PipeTransform<any> {
const object = plainToClass(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
// const errObj = {};
// errors.forEach(err => {
// const {
// property,
// constraints,
// } = err;
// errObj[property] = Object.values(constraints);
// });
const errObj = Object.values(errors[0].constraints)[0];
const errorObj = formatErrors(errors)
throw new HttpException(
{ message: '请求参数验证失败 ', error: errObj },
{ message: '请求参数验证失败 ', error: errorObj },
HttpStatus.BAD_REQUEST,
);
}
......
......@@ -4,11 +4,11 @@ import { map } from 'rxjs/operators';
import { classToPlain } from 'class-transformer';
import systemConfig from "../config/system";
const transformValue = (result: any, code: number = 0, message: string = '请求成功', ok = true) => {
const transformValue = (result: any, code: number, message: string = '请求成功', ok = true) => {
const { returnFormat } = systemConfig
return {
[returnFormat.result]: classToPlain(result),
[returnFormat.code]: code,
[returnFormat.code]: code || 200,
[returnFormat.message]: message,
[returnFormat.ok]: ok,
}
......
import { ChatService } from './chat.service';
import { Body, Controller, HttpException, HttpStatus, Post, Req } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { sendMessageDto } from './dto/sendMessage.dto';
import { ChatQueryDto } from './dto/chatQuery.dto';
@ApiTags('chat')
@Controller('chat')
export class ChatController {
constructor(private readonly chatService: ChatService) {}
@ApiOperation({ summary: '发送消息', description: '向ERNIE Bot模型发送消息得到答案' })
@Post('sendMessage')
sendMessage(@Body() body: sendMessageDto) {
@ApiOperation({ summary: '查询答案', description: '向ERNIE Bot模型发送消息得到答案' })
@Post('query')
query(@Body() body: ChatQueryDto) {
try {
return this.chatService.sendMessage(body.messages)
return this.chatService.query(body)
} catch (error) {
new HttpException({ message: '服务端错误 ', error: error }, HttpStatus.BAD_REQUEST)
}
......
import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { ChatController } from './chat.controller';
import { ChatService } from './chat.service';;
@Module({
imports: [HttpModule],
controllers: [ChatController],
providers: [ChatService],
})
......
import { Injectable } from '@nestjs/common';
import type { Message } from './dto/sendMessage.dto';
import { Injectable, Logger } from '@nestjs/common';
import axios from 'axios';
import type { ErnieBotAccessToken, ErnieBotChatResponse } from './types';
import { ChatQueryDto } from './dto/chatQuery.dto';
@Injectable()
export class ChatService {
sendMessage(messages: Message[]) {
return messages
async query(body: ChatQueryDto) {
return await this.chat(body)
}
private async chat({query, history}: ChatQueryDto) {
const url = `https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/plugin/8yrkkifr8gccsvck/?access_token=${await this.getAccessToken()}`
const data = JSON.stringify({
query,
plugins: ['uuid-zhishiku'],
verbose: true,
history,
})
const res = await axios<ErnieBotChatResponse>(url, { method: 'post', headers: { 'Content-Type': 'application/json' }, data })
return res.data.result || res.data.error_msg
}
private async getAccessToken() {
const AK = 'mXQIDW260qBbNgQRF0p1YFgn'
const SK = 'hzriOtzGiDkd1S0wFwiyr7WlarfufTdi'
const res = await axios.post<ErnieBotAccessToken>(`https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${AK}&client_secret=${SK}`)
return res.data.access_token
}
}
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsArray, IsNotEmpty, ValidateNested, IsString, IsIn, IsDefined, IsObject } from "class-validator"
class HistoryItemDto {
@IsDefined()
@IsString()
@IsNotEmpty()
@IsIn(['user', 'assistant'])
@ApiPropertyOptional({description: '角色 user assistant'})
role: number;
@IsDefined()
@IsString()
@IsNotEmpty()
@ApiPropertyOptional({description: '内容'})
content: string;
}
export class ChatQueryDto {
@IsArray()
@IsObject({ each: true })
@ValidateNested({each: true})
@Type(() => HistoryItemDto)
@ApiPropertyOptional({description: '聊天记录', type: [HistoryItemDto]})
public readonly history: HistoryItemDto[]
@IsNotEmpty()
@IsString()
@ApiPropertyOptional({description: '查询信息'})
public readonly query: string
}
import { ApiPropertyOptional } from '@nestjs/swagger';
import { IsArray, ArrayMinSize } from "class-validator"
export interface Message {
role: 'user' | 'admin'
content: string
}
export class sendMessageDto {
@IsArray({message: '消息数组必须是数组'})
@ArrayMinSize(1, {message: '消息数组长度最小为1'})
@ApiPropertyOptional({ description: '消息数组', example: [{ role: 'user', content: '你好' }] })
public readonly messages: Message[]
}
export type HistoryItemRole = 'user' | 'assistant'
export interface HistoryItem {
role: HistoryItemRole
content: string
}
export type History = HistoryItem[]
interface ErnieBotChatUsage {
prompt_tokens: number
completion_tokens: number
total_tokens: number
}
export interface ErnieBotChatResponse {
id: string
object: string
created: number
result: string
is_truncated: boolean
need_clear_history: boolean
finish_reason: string
usage: ErnieBotChatUsage
error_msg: string
}
export interface ErnieBotAccessToken {
refresh_token: string
expires_in: number
session_key: string
access_token: string
scope: string
session_secret: string
}
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