@purpleschool/gptbot 0.14.27 → 0.14.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/api/controllers/http/avatar-studio.ts +23 -12
  2. package/api/routes.ts +68 -19
  3. package/build/api/controllers/http/avatar-studio.js +22 -12
  4. package/build/api/routes.js +41 -10
  5. package/build/commands/avatar-studio/generation-job/generate-avatar-studio-frame.command.js +2 -1
  6. package/build/commands/avatar-studio/generation-job/generate-avatar-studio-image.command.js +23 -0
  7. package/build/commands/avatar-studio/generation-job/generate-avatar-studio-scene-voice.command.js +4 -1
  8. package/build/commands/avatar-studio/generation-job/index.js +1 -3
  9. package/build/commands/avatar-studio/media/create-avatar-studio-media-from-file.command.js +6 -9
  10. package/build/commands/avatar-studio/media/find-avatar-studio-media.query.js +7 -5
  11. package/build/commands/avatar-studio/media/update-avatar-studio-media.command.js +1 -4
  12. package/build/commands/avatar-studio/scene/apply-avatar-studio-voice-sample-to-all-scenes.command.js +15 -0
  13. package/build/commands/avatar-studio/scene/index.js +2 -0
  14. package/build/commands/avatar-studio/scene/set-avatar-studio-scene-slot.command.js +21 -0
  15. package/build/commands/avatar-studio/scriptwriter/calculate-avatar-studio-scriptwriter-price.command.js +13 -6
  16. package/build/commands/avatar-studio/scriptwriter/create-avatar-studio-scriptwriter-message.command.js +23 -1
  17. package/build/commands/avatar-studio/shared/avatar-studio-common.schema.js +2 -1
  18. package/build/commands/avatar-studio/shared/get-avatar-studio-config.query.js +28 -18
  19. package/build/commands/avatar-studio/voice/design-avatar-studio-voice.command.js +5 -7
  20. package/build/commands/avatar-studio/voice/index.js +0 -3
  21. package/build/constants/avatar-studio/enums/avatar-studio-job-type.enum.js +0 -3
  22. package/build/constants/avatar-studio/enums/avatar-studio-slot.enum.js +10 -7
  23. package/build/constants/avatar-studio/enums/avatar-studio-unavailable-reason.enum.js +16 -0
  24. package/build/constants/avatar-studio/enums/index.js +1 -5
  25. package/build/models/avatar-studio/avatar-studio.schema.js +60 -68
  26. package/commands/avatar-studio/generation-job/generate-avatar-studio-frame.command.ts +2 -1
  27. package/commands/avatar-studio/generation-job/{generate-avatar-studio-media-image.command.ts → generate-avatar-studio-image.command.ts} +5 -1
  28. package/commands/avatar-studio/generation-job/generate-avatar-studio-scene-voice.command.ts +4 -1
  29. package/commands/avatar-studio/generation-job/index.ts +1 -3
  30. package/commands/avatar-studio/media/create-avatar-studio-media-from-file.command.ts +7 -16
  31. package/commands/avatar-studio/media/find-avatar-studio-media.query.ts +9 -7
  32. package/commands/avatar-studio/media/update-avatar-studio-media.command.ts +1 -4
  33. package/commands/avatar-studio/scene/apply-avatar-studio-voice-sample-to-all-scenes.command.ts +20 -0
  34. package/commands/avatar-studio/scene/index.ts +2 -0
  35. package/commands/avatar-studio/{generation-job/generate-avatar-studio-voice-timings.command.ts → scene/set-avatar-studio-scene-slot.command.ts} +8 -5
  36. package/commands/avatar-studio/scriptwriter/calculate-avatar-studio-scriptwriter-price.command.ts +14 -7
  37. package/commands/avatar-studio/scriptwriter/create-avatar-studio-scriptwriter-message.command.ts +26 -1
  38. package/commands/avatar-studio/shared/avatar-studio-common.schema.ts +2 -1
  39. package/commands/avatar-studio/shared/get-avatar-studio-config.query.ts +31 -19
  40. package/commands/avatar-studio/voice/design-avatar-studio-voice.command.ts +6 -14
  41. package/commands/avatar-studio/voice/index.ts +0 -3
  42. package/constants/avatar-studio/enums/avatar-studio-job-type.enum.ts +0 -3
  43. package/constants/avatar-studio/enums/avatar-studio-slot.enum.ts +10 -7
  44. package/constants/avatar-studio/enums/avatar-studio-unavailable-reason.enum.ts +12 -0
  45. package/constants/avatar-studio/enums/index.ts +1 -5
  46. package/models/avatar-studio/avatar-studio.schema.ts +59 -75
  47. package/package.json +1 -1
  48. package/build/commands/avatar-studio/generation-job/generate-avatar-studio-library-image.command.js +0 -20
  49. package/build/commands/avatar-studio/generation-job/generate-avatar-studio-media-image.command.js +0 -19
  50. package/build/commands/avatar-studio/generation-job/generate-avatar-studio-voice-timings.command.js +0 -18
  51. package/build/commands/avatar-studio/voice/find-avatar-studio-clip-voices.query.js +0 -14
  52. package/build/commands/avatar-studio/voice/find-avatar-studio-voices.query.js +0 -19
  53. package/build/commands/avatar-studio/voice/update-avatar-studio-clip-voice.command.js +0 -19
  54. package/build/constants/avatar-studio/enums/avatar-studio-job-phase.enum.js +0 -12
  55. package/build/constants/avatar-studio/enums/avatar-studio-media-role.enum.js +0 -14
  56. package/build/constants/avatar-studio/enums/avatar-studio-media-source.enum.js +0 -13
  57. package/build/constants/avatar-studio/enums/avatar-studio-scene-kind.enum.js +0 -7
  58. package/build/constants/avatar-studio/enums/avatar-studio-voice-source.enum.js +0 -10
  59. package/commands/avatar-studio/generation-job/generate-avatar-studio-library-image.command.ts +0 -25
  60. package/commands/avatar-studio/voice/find-avatar-studio-clip-voices.query.ts +0 -21
  61. package/commands/avatar-studio/voice/find-avatar-studio-voices.query.ts +0 -24
  62. package/commands/avatar-studio/voice/update-avatar-studio-clip-voice.command.ts +0 -21
  63. package/constants/avatar-studio/enums/avatar-studio-job-phase.enum.ts +0 -8
  64. package/constants/avatar-studio/enums/avatar-studio-media-role.enum.ts +0 -10
  65. package/constants/avatar-studio/enums/avatar-studio-media-source.enum.ts +0 -9
  66. package/constants/avatar-studio/enums/avatar-studio-scene-kind.enum.ts +0 -3
  67. package/constants/avatar-studio/enums/avatar-studio-voice-source.enum.ts +0 -6
@@ -1,20 +1,23 @@
1
1
  import { z } from 'zod';
2
- import { AvatarStudioGenerationJobSchema } from '../../../models';
2
+ import { AVATAR_STUDIO_SLOT } from '../../../constants';
3
+ import { AvatarStudioSceneSchema } from '../../../models';
3
4
  import { AvatarStudioSceneIdParamsSchema } from '../shared/avatar-studio-common.schema';
4
5
 
5
- export namespace GenerateAvatarStudioVoiceTimingsCommand {
6
- export const ParamsSchema = AvatarStudioSceneIdParamsSchema;
6
+ export namespace SetAvatarStudioSceneSlotCommand {
7
+ export const ParamsSchema = AvatarStudioSceneIdParamsSchema.extend({
8
+ slot: z.nativeEnum(AVATAR_STUDIO_SLOT),
9
+ });
7
10
  export type Params = z.infer<typeof ParamsSchema>;
8
11
 
9
12
  export const RequestSchema = z
10
13
  .object({
11
- mediaId: z.string().uuid().optional(),
14
+ mediaId: z.string().uuid(),
12
15
  })
13
16
  .strict();
14
17
  export type Request = z.infer<typeof RequestSchema>;
15
18
 
16
19
  export const ResponseSchema = z.object({
17
- data: AvatarStudioGenerationJobSchema,
20
+ data: AvatarStudioSceneSchema,
18
21
  });
19
22
  export type Response = z.infer<typeof ResponseSchema>;
20
23
  }
@@ -1,21 +1,28 @@
1
1
  import { z } from 'zod';
2
- import { AvatarStudioClipIdParamsSchema } from '../shared/avatar-studio-common.schema';
3
2
 
4
- export namespace CalculateAvatarStudioScriptwriterPriceCommand {
5
- export const ParamsSchema = AvatarStudioClipIdParamsSchema;
6
- export type Params = z.infer<typeof ParamsSchema>;
3
+ /** Сама цена, без транспортной обёртки: её считает и сервис сценариста. */
4
+ export const AvatarStudioScriptwriterPriceSchema = z.object({
5
+ price: z.number(),
6
+ charsUntilNextPriceIncrease: z.number().int().nonnegative().nullable(),
7
+ charsUntilNextPriceDecrease: z.number().int().nonnegative().nullable(),
8
+ });
9
+ export type AvatarStudioScriptwriterPrice = z.infer<typeof AvatarStudioScriptwriterPriceSchema>;
7
10
 
11
+ export namespace CalculateAvatarStudioScriptwriterPriceCommand {
12
+ /**
13
+ * Клип опционален: цену спрашивают ещё до его создания. Без клипа считаем по одной длине
14
+ * ввода, без контекста прошлого диалога — история в цену и не входит, если её нет.
15
+ */
8
16
  export const RequestSchema = z
9
17
  .object({
10
18
  inputLength: z.number().int().nonnegative(),
19
+ clipId: z.string().uuid().optional(),
11
20
  })
12
21
  .strict();
13
22
  export type Request = z.infer<typeof RequestSchema>;
14
23
 
15
24
  export const ResponseSchema = z.object({
16
- price: z.number(),
17
- charsUntilNextPriceIncrease: z.number().int().nonnegative().nullable(),
18
- charsUntilNextPriceDecrease: z.number().int().nonnegative().nullable(),
25
+ data: AvatarStudioScriptwriterPriceSchema,
19
26
  });
20
27
  export type Response = z.infer<typeof ResponseSchema>;
21
28
  }
@@ -50,10 +50,35 @@ export namespace CreateAvatarStudioScriptwriterMessageCommand {
50
50
 
51
51
  export type ScenePlan = z.infer<typeof ScenePlanSchema>;
52
52
 
53
+ export const ModelOutputScenePlanSchema = z
54
+ .object({
55
+ voiceConfig: z.object({
56
+ text: z.string().trim().min(1),
57
+ trimStart: z.number().nonnegative().nullable(),
58
+ trimEnd: z.number().nonnegative().nullable(),
59
+ }),
60
+ imageConfig: z.object({
61
+ prompt: z.string().trim().min(1),
62
+ style: z.nativeEnum(AVATAR_STUDIO_IMAGE_STYLE),
63
+ }),
64
+ videoConfig: z.object({
65
+ prompt: z.string().trim().min(1),
66
+ durationSeconds: z.number().positive(),
67
+ videoMode: z.nativeEnum(AVATAR_STUDIO_VIDEO_MODE),
68
+ quality: z.nativeEnum(AVATAR_STUDIO_VIDEO_QUALITY),
69
+ sound: z.boolean(),
70
+ trimStart: z.number().nonnegative().nullable(),
71
+ trimEnd: z.number().nonnegative().nullable(),
72
+ }),
73
+ })
74
+ .strict();
75
+
76
+ export type ModelOutputScenePlan = z.infer<typeof ModelOutputScenePlanSchema>;
77
+
53
78
  export const ModelOutputSchema = z
54
79
  .object({
55
80
  message: z.string().trim().min(1),
56
- scenes: z.array(ScenePlanSchema).min(1).max(20),
81
+ scenes: z.array(ModelOutputScenePlanSchema).min(1).max(20),
57
82
  })
58
83
  .strict();
59
84
  export type ModelOutput = z.infer<typeof ModelOutputSchema>;
@@ -15,7 +15,8 @@ export const AvatarStudioSceneIdParamsSchema = AvatarStudioClipIdParamsSchema.ex
15
15
  sceneId: z.string().uuid(),
16
16
  });
17
17
 
18
- export const AvatarStudioMediaIdParamsSchema = AvatarStudioClipIdParamsSchema.extend({
18
+ // Медиа принадлежит пользователю, а не клипу, поэтому clipId в его параметрах нет.
19
+ export const AvatarStudioMediaIdParamsSchema = z.object({
19
20
  mediaId: z.string().uuid(),
20
21
  });
21
22
 
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  import {
3
3
  AVATAR_STUDIO_IMAGE_STYLE,
4
4
  AVATAR_STUDIO_MEDIA_KIND,
5
- AVATAR_STUDIO_MEDIA_ROLE,
5
+ AVATAR_STUDIO_UNAVAILABLE_REASON,
6
6
  AVATAR_STUDIO_VIDEO_MODE,
7
7
  AVATAR_STUDIO_VIDEO_QUALITY,
8
8
  } from '../../../constants';
@@ -21,13 +21,32 @@ const AvatarStudioGenerationPriceSchema = z
21
21
  })
22
22
  .strict();
23
23
 
24
+ /**
25
+ * Картинки генерирует одна модель, с референсами и без. Наценка за референсы плоская:
26
+ * её берёт сам инструмент за факт вложения, количество картинок значения не имеет.
27
+ */
28
+ const AvatarStudioImagePricingSchema = z
29
+ .object({
30
+ base: AvatarStudioGenerationPriceSchema.nullable(),
31
+ referenceSurcharge: z.number().nonnegative(),
32
+ })
33
+ .strict();
34
+
24
35
  const AvatarStudioConfigSectionBaseSchema = z
25
36
  .object({
26
37
  available: z.boolean(),
27
- unavailableReason: z.string().optional(),
38
+ /** Присутствует только когда available = false. */
39
+ unavailableReason: z.nativeEnum(AVATAR_STUDIO_UNAVAILABLE_REASON).optional(),
28
40
  })
29
41
  .strict();
30
42
 
43
+ /**
44
+ * Сценарист доступен не всем: у анрега нет доступа к его модели. Цены здесь нет: она зависит
45
+ * от длины ввода и считается на лету — POST scriptwriter/price. Конфиг отвечает только на
46
+ * вопрос «можно ли пользоваться».
47
+ */
48
+ const AvatarStudioScriptwriterConfigSectionSchema = AvatarStudioConfigSectionBaseSchema;
49
+
31
50
  const AvatarStudioTextFieldSchema = z
32
51
  .object({
33
52
  kind: z.literal('text'),
@@ -50,7 +69,6 @@ const AvatarStudioMediaFieldSchema = z
50
69
  kind: z.literal('media'),
51
70
  required: z.boolean(),
52
71
  mediaKind: z.nativeEnum(AVATAR_STUDIO_MEDIA_KIND),
53
- mediaRole: z.nativeEnum(AVATAR_STUDIO_MEDIA_ROLE),
54
72
  })
55
73
  .strict();
56
74
 
@@ -59,6 +77,8 @@ const AvatarStudioMediaArrayFieldSchema = z
59
77
  kind: z.literal('media-array'),
60
78
  required: z.boolean(),
61
79
  supported: z.boolean(),
80
+ /** Предел задаёт модель (maxAttachedImages), поэтому он приезжает сюда, а не зашит в контракт. */
81
+ maxCount: z.number().int().nonnegative(),
62
82
  })
63
83
  .strict();
64
84
 
@@ -93,10 +113,11 @@ const AvatarStudioAudioConfigSectionSchema = AvatarStudioConfigSectionBaseSchema
93
113
  text: AvatarStudioTextFieldSchema.extend({
94
114
  required: z.boolean(),
95
115
  }),
96
- mediaId: AvatarStudioMediaFieldSchema.extend({
116
+ // Образец голоса — вход генерации наравне с текстом, поэтому живёт
117
+ // в voiceConfig и ставится через PATCH .../scenes/:sceneId.
118
+ voiceSampleMediaId: AvatarStudioMediaFieldSchema.extend({
97
119
  required: z.boolean(),
98
120
  mediaKind: z.literal(AVATAR_STUDIO_MEDIA_KIND.AUDIO),
99
- mediaRole: z.literal(AVATAR_STUDIO_MEDIA_ROLE.VOICE_SAMPLE),
100
121
  }),
101
122
  })
102
123
  .strict(),
@@ -116,7 +137,7 @@ const AvatarStudioFrameConfigSectionSchema = AvatarStudioConfigSectionBaseSchema
116
137
  style: AvatarStudioSelectFieldSchema(z.nativeEnum(AVATAR_STUDIO_IMAGE_STYLE)).extend({
117
138
  required: z.boolean(),
118
139
  }),
119
- referenceMediaIds: AvatarStudioMediaArrayFieldSchema.extend({
140
+ references: AvatarStudioMediaArrayFieldSchema.extend({
120
141
  required: z.boolean(),
121
142
  }),
122
143
  aspectRatio: AvatarStudioFixedFieldSchema(z.literal('9:16')),
@@ -125,12 +146,7 @@ const AvatarStudioFrameConfigSectionSchema = AvatarStudioConfigSectionBaseSchema
125
146
  defaults: AvatarStudioSceneImageConfigSchema.extend({
126
147
  aspectRatio: z.literal('9:16'),
127
148
  }),
128
- pricing: z
129
- .object({
130
- withoutReference: AvatarStudioGenerationPriceSchema.nullable(),
131
- withReference: AvatarStudioGenerationPriceSchema.nullable(),
132
- })
133
- .strict(),
149
+ pricing: AvatarStudioImagePricingSchema,
134
150
  });
135
151
 
136
152
  const AvatarStudioVideoConfigSectionSchema = AvatarStudioConfigSectionBaseSchema.extend({
@@ -183,7 +199,7 @@ const AvatarStudioLibraryImageConfigSectionSchema = AvatarStudioConfigSectionBas
183
199
  fields: z
184
200
  .object({
185
201
  prompt: AvatarStudioTextFieldSchema.extend({ required: z.boolean() }),
186
- referenceMediaIds: AvatarStudioMediaArrayFieldSchema.extend({
202
+ references: AvatarStudioMediaArrayFieldSchema.extend({
187
203
  required: z.boolean(),
188
204
  }),
189
205
  aspectRatio: AvatarStudioSelectFieldSchema(AvatarStudioAspectRatioSchema).extend({
@@ -197,12 +213,7 @@ const AvatarStudioLibraryImageConfigSectionSchema = AvatarStudioConfigSectionBas
197
213
  aspectRatio: AvatarStudioAspectRatioSchema,
198
214
  })
199
215
  .strict(),
200
- pricing: z
201
- .object({
202
- withoutReference: AvatarStudioGenerationPriceSchema.nullable(),
203
- withReference: AvatarStudioGenerationPriceSchema.nullable(),
204
- })
205
- .strict(),
216
+ pricing: AvatarStudioImagePricingSchema,
206
217
  });
207
218
 
208
219
  export namespace GetAvatarStudioConfigQuery {
@@ -213,6 +224,7 @@ export namespace GetAvatarStudioConfigQuery {
213
224
  frame: AvatarStudioFrameConfigSectionSchema,
214
225
  video: AvatarStudioVideoConfigSectionSchema,
215
226
  libraryImage: AvatarStudioLibraryImageConfigSectionSchema,
227
+ scriptwriter: AvatarStudioScriptwriterConfigSectionSchema,
216
228
  })
217
229
  .strict(),
218
230
  });
@@ -1,15 +1,7 @@
1
1
  import { z } from 'zod';
2
- import {
3
- AvatarStudioGenerationJobSchema,
4
- AvatarStudioMediaSchema,
5
- AvatarStudioVoiceSchema,
6
- } from '../../../models';
7
- import { AvatarStudioClipIdParamsSchema } from '../shared/avatar-studio-common.schema';
2
+ import { AvatarStudioGenerationJobSchema } from '../../../models';
8
3
 
9
4
  export namespace DesignAvatarStudioVoiceCommand {
10
- export const ParamsSchema = AvatarStudioClipIdParamsSchema;
11
- export type Params = z.infer<typeof ParamsSchema>;
12
-
13
5
  export const RequestSchema = z
14
6
  .object({
15
7
  title: z.string().trim().min(1).optional(),
@@ -19,12 +11,12 @@ export namespace DesignAvatarStudioVoiceCommand {
19
11
  .strict();
20
12
  export type Request = z.infer<typeof RequestSchema>;
21
13
 
14
+ /**
15
+ * Отдаём job, как и все остальные генерации: созданный голос лежит в job.resultMedia.
16
+ * Отдельного поля media нет — форма ответа одинакова у всех роутов генерации.
17
+ */
22
18
  export const ResponseSchema = z.object({
23
- data: z.object({
24
- job: AvatarStudioGenerationJobSchema,
25
- voice: AvatarStudioVoiceSchema,
26
- media: AvatarStudioMediaSchema,
27
- }),
19
+ data: AvatarStudioGenerationJobSchema,
28
20
  });
29
21
  export type Response = z.infer<typeof ResponseSchema>;
30
22
  }
@@ -1,4 +1 @@
1
1
  export * from './design-avatar-studio-voice.command';
2
- export * from './find-avatar-studio-clip-voices.query';
3
- export * from './find-avatar-studio-voices.query';
4
- export * from './update-avatar-studio-clip-voice.command';
@@ -2,11 +2,8 @@ export enum AVATAR_STUDIO_JOB_TYPE {
2
2
  FRAME_IMAGE = 'frame_image',
3
3
  LIBRARY_IMAGE = 'library_image',
4
4
  SCENE_VIDEO = 'scene_video',
5
- SCENE_VOICE = 'scene_voice',
6
5
  VOICE_TTS = 'voice_tts',
7
6
  VOICE_STT = 'voice_stt',
8
- VOICE_TIMINGS = 'voice_timings',
9
7
  TALKING_AVATAR = 'talking_avatar',
10
8
  VOICE_DESIGN = 'voice_design',
11
- SCRIPTWRITER = 'scriptwriter',
12
9
  }
@@ -1,11 +1,14 @@
1
+ /**
2
+ * Слоты-результаты сцены: в каждом ровно одно медиа за раз.
3
+ *
4
+ * Здесь только то, что действительно является слотом сцены:
5
+ * - аватар и b-roll — два способа получить VIDEO, а не два слота (режим в videoConfig.videoMode);
6
+ * - образец голоса — вход сцены (scene.voiceSampleMediaId), а не её результат;
7
+ * - референсы — несколько упорядоченных медиа, у них своя join-таблица;
8
+ * - «медиатека» слотом никогда не была: у таких job'ов targetSlot = null.
9
+ */
1
10
  export enum AVATAR_STUDIO_SLOT {
11
+ VOICE = 'voice',
2
12
  FRAME = 'frame',
3
13
  VIDEO = 'video',
4
- VOICE = 'voice',
5
- AVATAR = 'avatar',
6
- LIBRARY = 'library',
7
- VOICE_TIMINGS = 'voice_timings',
8
- VOICE_SAMPLE = 'voice_sample',
9
- SCRIPTWRITER = 'scriptwriter',
10
- EXPORT = 'export',
11
14
  }
@@ -0,0 +1,12 @@
1
+ export enum AVATAR_STUDIO_UNAVAILABLE_REASON {
2
+ /**
3
+ * Функция есть, но этому пользователю не положена: анрег либо нет подходящей подписки.
4
+ * Фронт вправе звать зарегистрироваться или улучшить тариф.
5
+ */
6
+ NO_ACCESS = 'no_access',
7
+ /**
8
+ * Модель выключена, не найдена или проверка доступа не удалась. Пользователь ни при чём,
9
+ * звать его никуда не нужно — это наша проблема.
10
+ */
11
+ MODEL_UNAVAILABLE = 'model_unavailable',
12
+ }
@@ -1,16 +1,12 @@
1
1
  export * from './avatar-studio-export-status.enum';
2
2
  export * from './avatar-studio-image-style.enum';
3
- export * from './avatar-studio-job-phase.enum';
4
3
  export * from './avatar-studio-job-status.enum';
5
4
  export * from './avatar-studio-job-type.enum';
6
5
  export * from './avatar-studio-media-kind.enum';
7
- export * from './avatar-studio-media-role.enum';
8
- export * from './avatar-studio-media-source.enum';
9
- export * from './avatar-studio-scene-kind.enum';
10
6
  export * from './avatar-studio-scriptwriter-role.enum';
11
7
  export * from './avatar-studio-slot.enum';
12
8
  export * from './avatar-studio-subtitle-font.enum';
9
+ export * from './avatar-studio-unavailable-reason.enum';
13
10
  export * from './avatar-studio-subtitle-mode.enum';
14
11
  export * from './avatar-studio-video-mode.enum';
15
12
  export * from './avatar-studio-video-quality.enum';
16
- export * from './avatar-studio-voice-source.enum';
@@ -2,19 +2,15 @@ import { z } from 'zod';
2
2
  import {
3
3
  AVATAR_STUDIO_EXPORT_STATUS,
4
4
  AVATAR_STUDIO_IMAGE_STYLE,
5
- AVATAR_STUDIO_JOB_PHASE,
6
5
  AVATAR_STUDIO_JOB_STATUS,
7
6
  AVATAR_STUDIO_JOB_TYPE,
8
7
  AVATAR_STUDIO_MEDIA_KIND,
9
- AVATAR_STUDIO_MEDIA_ROLE,
10
- AVATAR_STUDIO_MEDIA_SOURCE,
11
8
  AVATAR_STUDIO_SCRIPTWRITER_ROLE,
12
9
  AVATAR_STUDIO_SLOT,
13
10
  AVATAR_STUDIO_SUBTITLE_FONT,
14
11
  AVATAR_STUDIO_SUBTITLE_MODE,
15
12
  AVATAR_STUDIO_VIDEO_MODE,
16
13
  AVATAR_STUDIO_VIDEO_QUALITY,
17
- AVATAR_STUDIO_VOICE_SOURCE,
18
14
  } from '../../constants';
19
15
  import { FileSchema } from '../file.schema';
20
16
  import { JsonObjectSchema, JsonValueSchema } from '../json-value.schema';
@@ -45,34 +41,31 @@ export type AvatarStudioWordTiming = z.infer<typeof AvatarStudioWordTimingSchema
45
41
 
46
42
  export const AvatarStudioMediaSchema = z.object({
47
43
  uuid: z.string().uuid(),
44
+ // Медиа принадлежит пользователю, а не клипу. Оба поля NULL — общее системное медиа (пресет).
48
45
  userId: z.string().uuid().nullable(),
49
- clipId: z.string().uuid().nullable(),
50
- sceneId: z.string().uuid().nullable(),
46
+ unregisteredUserId: z.string().uuid().nullable(),
51
47
  fileId: z.string().uuid(),
52
48
  kind: z.nativeEnum(AVATAR_STUDIO_MEDIA_KIND),
53
- source: z.nativeEnum(AVATAR_STUDIO_MEDIA_SOURCE),
54
- role: z.nativeEnum(AVATAR_STUDIO_MEDIA_ROLE),
55
49
  title: z.string().nullable(),
56
- prompt: z.string().nullable(),
50
+ // Чем медиа сгенерировали, помнит job: media.toolJobId ⇢ job.request.
57
51
  toolJobId: z.string().uuid().nullable(),
58
52
  durationSeconds: z.number().nullable(),
53
+ /**
54
+ * Расшифровка аудио: «что сказано» — вход zero-shot TTS, когда медиа используют образцом.
55
+ * NULL — расшифровка ещё идёт или не удалась; таким медиа озвучивать нельзя.
56
+ */
59
57
  transcript: z.string().nullable(),
58
+ /** «Когда сказано», по словам: из этого рисуются субтитры. Свойство файла, не сцены. */
59
+ wordTimings: z.array(AvatarStudioWordTimingSchema).nullable(),
60
+ /** Готовые артефакты субтитров от провайдера STT. */
61
+ subtitles: JsonValueSchema.nullable(),
62
+ deletedAt: z.date().nullable(),
60
63
  file: FileSchema.optional(),
61
64
  createdAt: z.date(),
62
65
  updatedAt: z.date(),
63
66
  });
64
67
  export type AvatarStudioMedia = z.infer<typeof AvatarStudioMediaSchema>;
65
68
 
66
- export const AvatarStudioSceneReferenceMediaSchema = z.object({
67
- uuid: z.string().uuid(),
68
- sceneId: z.string().uuid(),
69
- mediaId: z.string().uuid(),
70
- position: z.number().int().min(0),
71
- media: AvatarStudioMediaSchema.optional(),
72
- createdAt: z.date(),
73
- });
74
- export type AvatarStudioSceneReferenceMedia = z.infer<typeof AvatarStudioSceneReferenceMediaSchema>;
75
-
76
69
  export const AvatarStudioTrimConfigFields = {
77
70
  trimStart: z.number().nonnegative().nullable().default(null),
78
71
  trimEnd: z.number().nonnegative().nullable().default(null),
@@ -91,9 +84,13 @@ export const validateAvatarStudioTrim = (
91
84
  }
92
85
  };
93
86
 
87
+ /**
88
+ * Образец голоса — вход генерации наравне с текстом, поэтому лежит здесь, а не колонкой
89
+ * сцены: колонки сцены хранят результаты в слотах, конфиги — то, из чего их делают.
90
+ */
94
91
  export const AvatarStudioSceneVoiceConfigBaseSchema = z.object({
95
92
  text: z.string().default(''),
96
- mediaId: z.string().uuid().nullable().default(null),
93
+ voiceSampleMediaId: z.string().uuid().nullable().default(null),
97
94
  ...AvatarStudioTrimConfigFields,
98
95
  });
99
96
 
@@ -101,9 +98,17 @@ export const AvatarStudioSceneVoiceConfigSchema =
101
98
  AvatarStudioSceneVoiceConfigBaseSchema.superRefine(validateAvatarStudioTrim);
102
99
  export type AvatarStudioSceneVoiceConfig = z.infer<typeof AvatarStudioSceneVoiceConfigSchema>;
103
100
 
101
+ /**
102
+ * Референсы — вход генерации кадра, наравне с prompt и style, поэтому лежат здесь,
103
+ * а не отдельной связью сцены: больше они ни для чего не нужны.
104
+ *
105
+ * Верхнюю границу числа референсов задаёт модель (maxAttachedImages), а не контракт:
106
+ * она разная у разных моделей и приезжает в конфиге.
107
+ */
104
108
  export const AvatarStudioSceneImageConfigSchema = z.object({
105
109
  prompt: z.string().nullable().default(null),
106
110
  style: z.nativeEnum(AVATAR_STUDIO_IMAGE_STYLE).default(AVATAR_STUDIO_IMAGE_STYLE.UGC),
111
+ references: z.array(z.string().uuid()).default([]),
107
112
  });
108
113
  export type AvatarStudioSceneImageConfig = z.infer<typeof AvatarStudioSceneImageConfigSchema>;
109
114
 
@@ -123,8 +128,8 @@ export type AvatarStudioSceneVideoConfig = z.infer<typeof AvatarStudioSceneVideo
123
128
  export const AvatarStudioSceneGenerationJobSchema = z.object({
124
129
  jobId: z.string().uuid(),
125
130
  type: z.nativeEnum(AVATAR_STUDIO_JOB_TYPE),
126
- phase: z.nativeEnum(AVATAR_STUDIO_JOB_PHASE).nullable(),
127
- targetSlot: z.nativeEnum(AVATAR_STUDIO_SLOT),
131
+ /** NULL — генерация не целится ни в один слот сцены. */
132
+ targetSlot: z.nativeEnum(AVATAR_STUDIO_SLOT).nullable(),
128
133
  status: z.nativeEnum(AVATAR_STUDIO_JOB_STATUS),
129
134
  resultMediaId: z.string().uuid().nullable(),
130
135
  error: z.string().nullable(),
@@ -140,66 +145,39 @@ export const AvatarStudioSceneSchema = z.object({
140
145
  voiceConfig: AvatarStudioSceneVoiceConfigSchema,
141
146
  imageConfig: AvatarStudioSceneImageConfigSchema,
142
147
  videoConfig: AvatarStudioSceneVideoConfigSchema,
143
- frameMediaId: z.string().uuid().nullable(),
144
- sceneVideoMediaId: z.string().uuid().nullable(),
148
+ // Что лежит в слотах сцены прямо сейчас.
145
149
  voiceMediaId: z.string().uuid().nullable(),
146
- avatarVideoMediaId: z.string().uuid().nullable(),
147
- frameStatus: z.nativeEnum(AVATAR_STUDIO_JOB_STATUS),
148
- videoStatus: z.nativeEnum(AVATAR_STUDIO_JOB_STATUS),
149
- voiceStatus: z.nativeEnum(AVATAR_STUDIO_JOB_STATUS),
150
- avatarStatus: z.nativeEnum(AVATAR_STUDIO_JOB_STATUS),
151
- frameError: z.string().nullable(),
152
- videoError: z.string().nullable(),
153
- voiceError: z.string().nullable(),
154
- avatarError: z.string().nullable(),
155
- wordTimings: z.array(AvatarStudioWordTimingSchema).nullable(),
156
- transcript: z.string().nullable(),
157
- subtitles: JsonValueSchema.nullable(),
158
- frameMedia: AvatarStudioMediaSchema.nullable().optional(),
159
- sceneVideoMedia: AvatarStudioMediaSchema.nullable().optional(),
150
+ frameMediaId: z.string().uuid().nullable(),
151
+ videoMediaId: z.string().uuid().nullable(),
160
152
  voiceMedia: AvatarStudioMediaSchema.nullable().optional(),
161
- avatarVideoMedia: AvatarStudioMediaSchema.nullable().optional(),
162
- referenceMedia: z.array(AvatarStudioSceneReferenceMediaSchema).optional(),
153
+ frameMedia: AvatarStudioMediaSchema.nullable().optional(),
154
+ videoMedia: AvatarStudioMediaSchema.nullable().optional(),
155
+ /**
156
+ * Ход генерации слотов. Статус слота — это статус свежайшего job'а с нужным targetSlot;
157
+ * сцена его не дублирует, чтобы не хранить одно и то же в двух местах.
158
+ */
163
159
  generationJobs: z.array(AvatarStudioSceneGenerationJobSchema).optional(),
164
160
  createdAt: z.date(),
165
161
  updatedAt: z.date(),
166
162
  });
167
163
  export type AvatarStudioScene = z.infer<typeof AvatarStudioSceneSchema>;
168
164
 
169
- export const AvatarStudioVoiceSchema = z.object({
170
- uuid: z.string().uuid(),
171
- userId: z.string().uuid().nullable(),
172
- clipId: z.string().uuid().nullable(),
173
- title: z.string(),
174
- source: z.nativeEnum(AVATAR_STUDIO_VOICE_SOURCE),
175
- presetKey: z.string().nullable(),
176
- gender: z.enum(['male', 'female']).nullable(),
177
- sortOrder: z.number().int().nullable(),
178
- sampleMediaId: z.string().uuid().nullable(),
179
- sampleText: z.string().nullable(),
180
- metadata: JsonObjectSchema.nullable(),
181
- sampleMedia: AvatarStudioMediaSchema.nullable().optional(),
182
- createdAt: z.date(),
183
- updatedAt: z.date(),
184
- deletedAt: z.date().nullable(),
185
- });
186
- export type AvatarStudioVoice = z.infer<typeof AvatarStudioVoiceSchema>;
187
-
188
165
  export const AvatarStudioGenerationJobBaseSchema = z.object({
189
166
  uuid: z.string().uuid(),
190
- userId: z.string().uuid(),
167
+ // Владелец ровно один: зарегистрированный пользователь либо анрег.
168
+ userId: z.string().uuid().nullable(),
169
+ unregisteredUserId: z.string().uuid().nullable(),
191
170
  clipId: z.string().uuid().nullable(),
192
171
  sceneId: z.string().uuid().nullable(),
193
- parentJobId: z.string().uuid().nullable(),
194
172
  toolJobId: z.string().uuid().nullable(),
195
173
  type: z.nativeEnum(AVATAR_STUDIO_JOB_TYPE),
196
- phase: z.nativeEnum(AVATAR_STUDIO_JOB_PHASE).nullable(),
197
- targetSlot: z.nativeEnum(AVATAR_STUDIO_SLOT),
174
+ /** NULL — генерация не целится ни в один слот сцены. */
175
+ targetSlot: z.nativeEnum(AVATAR_STUDIO_SLOT).nullable(),
198
176
  status: z.nativeEnum(AVATAR_STUDIO_JOB_STATUS),
199
177
  request: JsonObjectSchema.nullable(),
200
178
  resultMediaId: z.string().uuid().nullable(),
201
- tokenReservationUuid: z.string().uuid().nullable(),
202
- price: z.number().nullable(),
179
+ // tokenReservationUuid и price фронту не нужны: это внутренняя бухгалтерия резервирований.
180
+ // Колонки в БД остаются — по ним ведётся аудит списаний.
203
181
  error: z.string().nullable(),
204
182
  resultMedia: AvatarStudioMediaSchema.nullable().optional(),
205
183
  startedAt: z.date().nullable(),
@@ -208,14 +186,14 @@ export const AvatarStudioGenerationJobBaseSchema = z.object({
208
186
  updatedAt: z.date(),
209
187
  });
210
188
 
211
- export const AvatarStudioGenerationJobSchema = AvatarStudioGenerationJobBaseSchema.extend({
212
- children: z.array(AvatarStudioGenerationJobBaseSchema).optional(),
213
- });
189
+ export const AvatarStudioGenerationJobSchema = AvatarStudioGenerationJobBaseSchema;
214
190
  export type AvatarStudioGenerationJob = z.infer<typeof AvatarStudioGenerationJobSchema>;
215
191
 
216
192
  export const AvatarStudioExportJobSchema = z.object({
217
193
  uuid: z.string().uuid(),
218
- userId: z.string().uuid(),
194
+ // Владелец ровно один: зарегистрированный пользователь либо анрег.
195
+ userId: z.string().uuid().nullable(),
196
+ unregisteredUserId: z.string().uuid().nullable(),
219
197
  clipId: z.string().uuid(),
220
198
  status: z.nativeEnum(AVATAR_STUDIO_EXPORT_STATUS),
221
199
  resultMediaId: z.string().uuid().nullable(),
@@ -231,7 +209,9 @@ export type AvatarStudioExportJob = z.infer<typeof AvatarStudioExportJobSchema>;
231
209
 
232
210
  export const AvatarStudioScriptwriterMessageSchema = z.object({
233
211
  uuid: z.string().uuid(),
234
- userId: z.string().uuid(),
212
+ // Владелец ровно один: зарегистрированный пользователь либо анрег.
213
+ userId: z.string().uuid().nullable(),
214
+ unregisteredUserId: z.string().uuid().nullable(),
235
215
  clipId: z.string().uuid(),
236
216
  sceneId: z.string().uuid().nullable(),
237
217
  role: z.nativeEnum(AVATAR_STUDIO_SCRIPTWRITER_ROLE),
@@ -243,16 +223,20 @@ export type AvatarStudioScriptwriterMessage = z.infer<typeof AvatarStudioScriptw
243
223
 
244
224
  export const AvatarStudioClipSchema = z.object({
245
225
  uuid: z.string().uuid(),
246
- userId: z.string().uuid(),
226
+ // Владелец ровно один: зарегистрированный пользователь либо анрег.
227
+ userId: z.string().uuid().nullable(),
228
+ unregisteredUserId: z.string().uuid().nullable(),
247
229
  title: z.string(),
248
230
  subtitleSettings: AvatarStudioSubtitleSettingsSchema,
249
- selectedVoiceId: z.string().uuid().nullable(),
250
231
  exportMediaId: z.string().uuid().nullable(),
251
- selectedVoice: AvatarStudioVoiceSchema.nullable().optional(),
232
+ /**
233
+ * Обложка клипа: кадр первой по порядку сцены, у которой он есть. NULL — кадров нет ни у одной.
234
+ * Считается на лету из уже загруженных сцен, отдельного поля в БД нет.
235
+ */
236
+ previewUrl: z.string().nullable(),
252
237
  exportMedia: AvatarStudioMediaSchema.nullable().optional(),
253
238
  scenes: z.array(AvatarStudioSceneSchema).optional(),
254
- media: z.array(AvatarStudioMediaSchema).optional(),
255
- voices: z.array(AvatarStudioVoiceSchema).optional(),
239
+ // Медиатеки у клипа нет: она принадлежит пользователю — GET /media?owner=me.
256
240
  generationJobs: z.array(AvatarStudioGenerationJobSchema).optional(),
257
241
  exportJobs: z.array(AvatarStudioExportJobSchema).optional(),
258
242
  scriptwriterMessages: z.array(AvatarStudioScriptwriterMessageSchema).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purpleschool/gptbot",
3
- "version": "0.14.27",
3
+ "version": "0.14.28",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GenerateAvatarStudioLibraryImageCommand = void 0;
4
- const zod_1 = require("zod");
5
- const models_1 = require("../../../models");
6
- const avatar_studio_common_schema_1 = require("../shared/avatar-studio-common.schema");
7
- var GenerateAvatarStudioLibraryImageCommand;
8
- (function (GenerateAvatarStudioLibraryImageCommand) {
9
- GenerateAvatarStudioLibraryImageCommand.ParamsSchema = avatar_studio_common_schema_1.AvatarStudioClipIdParamsSchema;
10
- GenerateAvatarStudioLibraryImageCommand.RequestSchema = zod_1.z
11
- .object({
12
- prompt: zod_1.z.string().trim().min(1),
13
- referenceMediaIds: avatar_studio_common_schema_1.AvatarStudioUuidListSchema.optional(),
14
- aspectRatio: models_1.AvatarStudioAspectRatioSchema.default('9:16'),
15
- })
16
- .strict();
17
- GenerateAvatarStudioLibraryImageCommand.ResponseSchema = zod_1.z.object({
18
- data: models_1.AvatarStudioGenerationJobSchema,
19
- });
20
- })(GenerateAvatarStudioLibraryImageCommand || (exports.GenerateAvatarStudioLibraryImageCommand = GenerateAvatarStudioLibraryImageCommand = {}));
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GenerateAvatarStudioMediaImageCommand = void 0;
4
- const zod_1 = require("zod");
5
- const models_1 = require("../../../models");
6
- const avatar_studio_common_schema_1 = require("../shared/avatar-studio-common.schema");
7
- var GenerateAvatarStudioMediaImageCommand;
8
- (function (GenerateAvatarStudioMediaImageCommand) {
9
- GenerateAvatarStudioMediaImageCommand.RequestSchema = zod_1.z
10
- .object({
11
- prompt: zod_1.z.string().trim().min(1),
12
- referenceMediaIds: avatar_studio_common_schema_1.AvatarStudioUuidListSchema.optional(),
13
- aspectRatio: models_1.AvatarStudioAspectRatioSchema.default('9:16'),
14
- })
15
- .strict();
16
- GenerateAvatarStudioMediaImageCommand.ResponseSchema = zod_1.z.object({
17
- data: models_1.AvatarStudioGenerationJobSchema,
18
- });
19
- })(GenerateAvatarStudioMediaImageCommand || (exports.GenerateAvatarStudioMediaImageCommand = GenerateAvatarStudioMediaImageCommand = {}));