A Cloudflare Worker which works in conjunction with https://github.com/indexxing/bsky-alt-text
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 apiKey: [], 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 // * Original cloud function used "gemini-2.0-flash", but I think the lite version should work fine too. 71 model: "gemini-2.0-flash-lite", 72 contents: [{ 73 parts: [ 74 { 75 text: 76 `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:`, 77 }, 78 { text: text }, 79 ], 80 }], 81 config: { 82 temperature: 0.2, 83 maxOutputTokens: 1024, 84 }, 85 }); 86 87 const condensedText = res.candidates?.[0]?.content?.parts?.[0] 88 ?.text; 89 if (!condensedText) { 90 return { 91 success: false, 92 error: "Failed to condense text.", 93 }; 94 } 95 96 return { 97 success: true, 98 altText: condensedText, 99 }; 100 } catch (e) { 101 return { 102 success: false, 103 message: e, 104 }; 105 } 106 } 107}