x1ongzhu пре 1 година
родитељ
комит
5a80df4b21

+ 29 - 0
src/story/entities/story-template.entitiy.ts

@@ -0,0 +1,29 @@
+import { JsonTransformer } from 'src/transformers/json.transformer'
+import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
+
+@Entity()
+export class StoryTemplate {
+    @PrimaryGeneratedColumn()
+    id: number
+
+    @CreateDateColumn()
+    createdAt: Date
+
+    @UpdateDateColumn()
+    updatedAt: Date
+
+    @Column()
+    name: string
+
+    @Column({ type: 'longtext'     })
+    firstPrompt: string
+
+    @Column({ type: 'longtext' })
+    continuePrompt: string
+
+    @Column({ type: 'longtext' })
+    endPrompt: string
+
+    @Column({ type: 'longtext', transformer: new JsonTransformer(), nullable: true })
+    variables: any
+}

+ 51 - 0
src/story/story.controller.ts

@@ -0,0 +1,51 @@
+import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'
+import { PageRequest } from 'src/common/dto/page-request'
+import { StoryTemplate } from './entities/story-template.entitiy'
+import { StoryService } from './story.service'
+import { Public } from 'src/auth/public.decorator'
+
+@Controller('story')
+export class StoryController {
+    constructor(private readonly storyService: StoryService) {}
+
+    @Post('/templates')
+    public async list(@Body() page: PageRequest<StoryTemplate>) {
+        return await this.storyService.findAllTemplates(page)
+    }
+
+    @Put('/templates')
+    public async create(@Body() dto) {
+        return await this.storyService.createTemplate(dto)
+    }
+
+    @Get('/templates/:id')
+    @Public()
+    public async get(@Param('id') id: string) {
+        return await this.storyService.findById(Number(id))
+    }
+
+    @Put('/templates/:id')
+    public async update(@Param('id') id: string, @Body() dto) {
+        return await this.storyService.updateTemplate(Number(id), dto)
+    }
+
+    @Delete('/templates/:id')
+    public async delete(@Param('id') id: string) {
+        return await this.storyService.deleteTemplate(Number(id))
+    }
+
+    @Post('/start')
+    public async start(@Body() body) {
+        return await this.storyService.start(body)
+    }
+
+    @Post('/next')
+    public async next(@Body() body) {
+        return await this.storyService.next(body)
+    }
+
+    @Post('/end')
+    public async end(@Body() body) {
+        return await this.storyService.end(body)
+    }
+}

+ 12 - 0
src/story/story.module.ts

@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common'
+import { StoryController } from './story.controller'
+import { StoryService } from './story.service'
+import { TypeOrmModule } from '@nestjs/typeorm'
+import { StoryTemplate } from './entities/story-template.entitiy'
+
+@Module({
+    imports: [TypeOrmModule.forFeature([StoryTemplate])],
+    controllers: [StoryController],
+    providers: [StoryService]
+})
+export class StoryModule {}

+ 105 - 0
src/story/story.service.ts

@@ -0,0 +1,105 @@
+import { Injectable, Logger } from '@nestjs/common'
+import { InjectRepository } from '@nestjs/typeorm'
+import { StoryTemplate } from './entities/story-template.entitiy'
+import { Repository } from 'typeorm'
+import { paginate } from 'nestjs-typeorm-paginate'
+import { ChatOpenAI } from 'langchain/chat_models/openai'
+import { CallbackManager } from 'langchain/callbacks'
+import { Serialized } from 'langchain/dist/load/serializable'
+import { LLMResult } from 'langchain/schema'
+
+@Injectable()
+export class StoryService {
+    private readonly logger = new Logger(StoryService.name)
+    constructor(
+        @InjectRepository(StoryTemplate)
+        private readonly storyTemplateRepository: Repository<StoryTemplate>
+    ) {}
+
+    public async findAllTemplates(req) {
+        return await paginate<StoryTemplate>(this.storyTemplateRepository, req.page, req.search)
+    }
+
+    public async createTemplate(dto) {
+        return await this.storyTemplateRepository.save(dto)
+    }
+
+    public async findById(id: number) {
+        return await this.storyTemplateRepository.findBy({ id })
+    }
+
+    public async updateTemplate(id: number, dto) {
+        return await this.storyTemplateRepository.update(id, dto)
+    }
+
+    public async deleteTemplate(id: number) {
+        return await this.storyTemplateRepository.delete(id)
+    }
+
+    public createLLM() {
+        const self = this
+        const cb = CallbackManager.fromHandlers({
+            async handleLLMStart(llm: Serialized, prompts: string[]) {
+                self.logger.log(`[LLM Start]LLM: ${JSON.stringify(llm)}`)
+                self.logger.log(`['LLM Start]Prompts: ${prompts.join('\n')}`)
+            },
+            async handleLLMEnd(output: LLMResult) {
+                self.logger.log(
+                    `[LLM End]${output.generations
+                        .reduce((acc, cur) => acc.concat(cur), [])
+                        .map((i) => i.text)
+                        .join('\n')}`
+                )
+                self.logger.log(`[LLM End]${JSON.stringify(output.llmOutput)}`)
+            },
+            async handleLLMError(error: Error) {
+                self.logger.error(error)
+            }
+        })
+
+        const onFailedAttempt = (error) => {
+            self.logger.error(error)
+        }
+
+        const baseParam = {
+            openAIApiKey: process.env.OPENAI_API_KEY,
+            modelName: 'gpt-3.5-turbo-0613',
+            callbackManager: cb,
+            onFailedAttempt,
+            timeout: 60000
+        }
+        return new ChatOpenAI({
+            ...baseParam,
+            modelName: 'gpt-3.5-turbo-0613',
+            configuration: {
+                baseURL: 'https://openai.c8c.top/v1'
+            }
+        })
+    }
+
+    public async start(body) {
+        const template = await this.storyTemplateRepository.findOne(body.templateId)
+        if (!template) throw new Error('Template not found')
+        const llm = this.createLLM().bind({
+            response_format: {
+                type: 'json_object' 
+            }
+        })
+        return (
+            await llm.invoke([
+                ['system', ''], 
+                ['human', '']
+            ])
+        ).content
+    }
+
+    public async next(body) {
+        const template = await this.storyTemplateRepository.findOne(body.templateId)
+        if (!template) throw new Error('Template not found')
+    }
+
+    public async end(body) {
+        const template = await this.storyTemplateRepository.findOne(body.templateId)
+        if (!template) throw new Error('Template not found')
+    }
+}