A Cloudflare Worker which works in conjunction with https://github.com/indexxing/bsky-alt-text
at main 2.7 kB view raw
1import { Enumeration, Num, OpenAPIRoute } from "chanfana"; 2import { z } from "zod"; 3import { type AppContext } from "../types"; 4 5export class CondenseTextEndpoint extends OpenAPIRoute { 6 schema = { 7 tags: ["Processing"], 8 summary: "Condense a given text based on a directive", 9 security: [ 10 { 11 bearerAuth: [], 12 }, 13 ], 14 request: { 15 body: { 16 content: { 17 "application/json": { 18 schema: z.object({ 19 text: z.string({ 20 description: "The text to be condensed.", 21 required_error: 22 "Text is required for condensation.", 23 }).min(1, "Text cannot be empty."), 24 mediaType: z.enum(["video", "image"], { 25 description: 26 "The type of media being described", 27 required_error: "Media type is required.", 28 }), 29 }), 30 }, 31 }, 32 }, 33 }, 34 responses: { 35 "200": { 36 description: "Returns the condensed text", 37 content: { 38 "application/json": { 39 schema: z.object({ 40 success: z.boolean(), 41 condensedText: z.string().nullable(), 42 error: z.string().optional(), 43 }), 44 }, 45 }, 46 }, 47 "500": { 48 description: 49 "Internal Server Error - Issue with Cloud Function or API call", 50 content: { 51 "application/json": { 52 schema: z.object({ 53 success: z.boolean(), 54 message: z.string(), 55 }), 56 }, 57 }, 58 }, 59 }, 60 }; 61 62 async handle(c: AppContext) { 63 const data = await this.getValidatedData<typeof this.schema>(); 64 const { text, mediaType } = data.body; 65 66 const targetLength = c.env.MAX_ALT_TEXT_LENGTH - 100; 67 68 try { 69 const res = await c.var.gemini.models.generateContent({ 70 model: c.env.GEMINI_MODEL, 71 contents: [{ 72 parts: [ 73 { 74 text: 75 `You are an expert at writing concise, informative alt text. Please condense the following ${mediaType} description to be no more than ${targetLength} characters while preserving the most important details. The description needs to be accessible and useful for screen readers:`, 76 }, 77 { text: text }, 78 ], 79 }], 80 config: { 81 temperature: c.env.GEMINI_CONDENSE_TEMPERATURE, 82 maxOutputTokens: c.env.GEMINI_CONDENSE_MAX_OUTPUT_TOKENS, 83 }, 84 }); 85 86 const condensedText = res.candidates?.[0]?.content?.parts?.[0] 87 ?.text; 88 if (!condensedText) { 89 c.status(502); // Bad response from upstream API resulting in "Bad Gateway" status 90 91 return { 92 success: false, 93 error: "Failed to condense text.", 94 }; 95 } 96 97 return { 98 success: true, 99 text: condensedText, 100 tokens: res.usageMetadata.totalTokenCount ?? 0, 101 }; 102 } catch (e) { 103 c.status(500); 104 105 return { 106 success: false, 107 error: e, 108 }; 109 } 110 } 111}