| 1 | import { createZodDto, PaginationDtoSchema } from '@yikart/common' |
| 2 | import { z } from 'zod' |
| 3 | import { config } from '../../config' |
| 4 | |
| 5 | // 文本内容块 |
| 6 | const TextContentBlockSchema = z.object({ |
| 7 | type: z.literal('text'), |
| 8 | text: z.string().min(1), |
| 9 | }) |
| 10 | |
| 11 | // URL 图片源 |
| 12 | const URLImageSourceSchema = z.object({ |
| 13 | type: z.literal('url'), |
| 14 | url: z.url(), |
| 15 | }) |
| 16 | |
| 17 | // 图片内容块 |
| 18 | const ImageContentBlockSchema = z.object({ |
| 19 | type: z.literal('image'), |
| 20 | source: URLImageSourceSchema, |
| 21 | }) |
| 22 | |
| 23 | // URL 视频源 |
| 24 | const URLVideoSourceSchema = z.object({ |
| 25 | type: z.literal('url'), |
| 26 | url: z.url(), |
| 27 | }) |
| 28 | |
| 29 | // 视频内容块 |
| 30 | const VideoContentBlockSchema = z.object({ |
| 31 | type: z.literal('video'), |
| 32 | source: URLVideoSourceSchema, |
| 33 | }) |
| 34 | |
| 35 | // URL PDF 源 |
| 36 | const URLPDFSourceSchema = z.object({ |
| 37 | type: z.literal('url'), |
| 38 | url: z.url(), |
| 39 | }) |
| 40 | |
| 41 | // Plain Text 源 |
| 42 | const PlainTextSourceSchema = z.object({ |
| 43 | type: z.literal('text'), |
| 44 | media_type: z.literal('text/plain'), |
| 45 | data: z.string(), |
| 46 | }) |
| 47 | |
| 48 | // Content Block 源(递归引用) |
| 49 | const ContentBlockSourceSchema = z.object({ |
| 50 | type: z.literal('content'), |
| 51 | content: z.union([ |
| 52 | z.string(), |
| 53 | z.array(z.union([TextContentBlockSchema, ImageContentBlockSchema])), |
| 54 | ]), |
| 55 | }) |
| 56 | |
| 57 | // 文档内容块 |
| 58 | const DocumentBlockSchema = z.object({ |
| 59 | type: z.literal('document'), |
| 60 | source: z.union([ |
| 61 | PlainTextSourceSchema, |
| 62 | ContentBlockSourceSchema, |
| 63 | URLPDFSourceSchema, |
| 64 | ]), |
| 65 | cache_control: z.object({ type: z.literal('ephemeral') }).optional(), |
| 66 | }) |
| 67 | |
| 68 | // 联合类型:文本、图片、视频或文档 |
| 69 | const ContentBlockSchema = z.discriminatedUnion('type', [ |
| 70 | TextContentBlockSchema, |
| 71 | ImageContentBlockSchema, |
| 72 | VideoContentBlockSchema, |
| 73 | DocumentBlockSchema, |
| 74 | ]) |
| 75 | |
| 76 | // 导出 ContentBlock 类型供其他模块使用 |
| 77 | export type ContentBlock = z.infer<typeof ContentBlockSchema> |
| 78 | |
| 79 | // Prompt 可以是字符串或内容块数组 |
| 80 | const PromptSchema = z.union([ |
| 81 | z.string().min(1).max(4000), |
| 82 | z.array(ContentBlockSchema).min(1).max(20), |
| 83 | ]) |
| 84 | |
| 85 | export const AllowedModelSchema = z.enum(config.agent.models as [string, ...string[]]).default(config.agent.defaultModel) |
| 86 | |
| 87 | // 创建内容生成任务 DTO |
| 88 | export const CreateContentGenerationTaskSchema = z.object({ |
| 89 | prompt: PromptSchema.describe('提示词(字符串或内容块数组)'), |
| 90 | model: AllowedModelSchema.describe('使用的模型'), |
| 91 | includePartialMessages: z.boolean().optional().default(false).describe('是否包含部分消息(流式)'), |
| 92 | taskId: z.string().transform(val => val.trim() === '' ? undefined : val).optional().describe('任务ID(恢复对话时使用)'), |
| 93 | }) |
| 94 | export class CreateContentGenerationTaskDto extends createZodDto(CreateContentGenerationTaskSchema, 'CreateContentGenerationTaskDto') { } |
| 95 | |
| 96 | export const GetContentGenerationTaskSchema = z.object({ |
| 97 | taskId: z.string().min(1).max(50).describe('任务ID'), |
| 98 | }) |
| 99 | export class GetContentGenerationTaskDto extends createZodDto(GetContentGenerationTaskSchema) { } |
| 100 | |
| 101 | export const ListContentGenerationTaskDtoSchema = PaginationDtoSchema.extend({ |
| 102 | keyword: z.string().max(100).optional().describe('搜索关键词(匹配标题和对话内容)'), |
| 103 | favoriteOnly: z.coerce.boolean().optional().default(false).describe('仅显示收藏的任务'), |
| 104 | }) |
| 105 | export class ListContentGenerationTaskDto extends createZodDto(ListContentGenerationTaskDtoSchema, 'ListContentGenerationTaskDto') { } |
| 106 | |
| 107 | export const UpdateContentGenerationTaskTitleDtoSchema = z.object({ |
| 108 | title: z.string().min(1).max(200).describe('对话标题'), |
| 109 | }) |
| 110 | export class UpdateContentGenerationTaskTitleDto extends createZodDto(UpdateContentGenerationTaskTitleDtoSchema, 'UpdateContentGenerationTaskTitleDto') { } |
| 111 | |
| 112 | export const GetTaskMessagesQueryDtoSchema = z.object({ |
| 113 | lastMessageId: z.string().optional().describe('上次获取的最后一条消息 UUID'), |
| 114 | }) |
| 115 | export class GetTaskMessagesQueryDto extends createZodDto(GetTaskMessagesQueryDtoSchema, 'GetTaskMessagesQueryDto') { } |
| 116 | |
| 117 | export const CreateContentGenerationTaskRatingDtoSchema = z.object({ |
| 118 | rating: z.number().int().min(1).max(5).describe('评分 (1-5)'), |
| 119 | comment: z.string().max(1000).optional().describe('评论文本'), |
| 120 | }) |
| 121 | export class CreateContentGenerationTaskRatingDto extends createZodDto(CreateContentGenerationTaskRatingDtoSchema, 'CreateContentGenerationTaskRatingDto') { } |
| 122 | |
| 123 | // DTO: 转发任务到另一个用户 |
| 124 | export const ForwardContentGenerationTaskDtoSchema = z.object({ |
| 125 | targetUserId: z.string().min(1).describe('接收转发的用户ID'), |
| 126 | }) |
| 127 | export class ForwardContentGenerationTaskDto extends createZodDto(ForwardContentGenerationTaskDtoSchema, 'ForwardContentGenerationTaskDto') { } |
| 128 | |
| 129 | // DTO: 创建公开分享链接 |
| 130 | export const CreatePublicShareDtoSchema = z.object({ |
| 131 | ttlSeconds: z.number().int().min(60).max(60 * 60 * 24 * 30).optional().describe('分享链接有效期(秒),最小60秒,最大30天'), |
| 132 | }) |
| 133 | export class CreatePublicShareDto extends createZodDto(CreatePublicShareDtoSchema, 'CreatePublicShareDto') { } |
| 134 |