Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | /** * Subtask: Generate a postcard thumbnail image. * * Simple image generation — no review needed. Generates a small iconic * thumbnail and stores it directly. */ import { createTask, createChildTask, type TaskHandle } from '../task-manager' import { generateAndStoreImage } from '../image-generation' import type { PostcardThumbnailGenerateEvent } from './events' import { recordImageGenUsage } from '../ai-usage/helpers' import { AiFeature } from '../ai-usage/features' export interface PostcardThumbnailGenerateInput { postcardId: string prompt: string providerId: string modelId: string _userId?: string } export interface PostcardThumbnailGenerateOutput { postcardId: string thumbnailUrl: string sizeBytes: number } type Handler = ( handle: TaskHandle<PostcardThumbnailGenerateOutput, PostcardThumbnailGenerateEvent>, input: PostcardThumbnailGenerateInput ) => Promise<void> const handler: Handler = async (handle, config) => { const { postcardId, prompt, providerId, modelId } = config handle.emit({ type: 'thumbnail_generating', postcardId, provider: providerId, model: modelId }) handle.setProgress(10, 'Generating thumbnail...') try { const result = await generateAndStoreImage({ provider: providerId, model: modelId, prompt, imageOptions: { size: { width: 256, height: 192 } }, storageTarget: { type: 'persistent', category: 'postcards', filename: `${postcardId}-thumb.png`, }, }) if (config._userId) { recordImageGenUsage(providerId as 'openai' | 'gemini', modelId, { userId: config._userId, feature: AiFeature.IMAGE_POSTCARD_THUMB, backgroundTaskId: handle.id, }) } handle.emit({ type: 'thumbnail_generated', postcardId, thumbnailUrl: result.publicUrl, sizeBytes: result.sizeBytes ?? 0, }) handle.complete({ postcardId, thumbnailUrl: result.publicUrl, sizeBytes: result.sizeBytes ?? 0, }) } catch (err) { const error = err instanceof Error ? err.message : 'Thumbnail generation failed' handle.emit({ type: 'thumbnail_error', postcardId, error }) handle.fail(error) } } export async function startPostcardThumbnailGenerate( input: PostcardThumbnailGenerateInput, userId?: string, parentTaskId?: string ): Promise<string> { if (parentTaskId) { return createChildTask< PostcardThumbnailGenerateInput, PostcardThumbnailGenerateOutput, PostcardThumbnailGenerateEvent >(parentTaskId, 'postcard-thumbnail-generate', input, handler, userId) } return createTask< PostcardThumbnailGenerateInput, PostcardThumbnailGenerateOutput, PostcardThumbnailGenerateEvent >('postcard-thumbnail-generate', input, handler, userId) } |