Bluesky app fork with some witchin' additions 馃挮
witchsky.app
bluesky
fork
client
1import {
2 DEFAULT_ALT_TEXT_AI_MODEL,
3 DEFAULT_ALT_TEXT_AI_PROMPT,
4 MAX_ALT_TEXT,
5} from '#/lib/constants'
6import {logger} from '#/logger'
7
8export async function generateAltText(
9 apiKey: string,
10 model: string,
11 imageBase64: string,
12 imageMimeType: string,
13 customPrompt?: string,
14): Promise<string> {
15 const response = await fetch(
16 'https://openrouter.ai/api/v1/chat/completions',
17 {
18 method: 'POST',
19 headers: {
20 Authorization: `Bearer ${apiKey}`,
21 'Content-Type': 'application/json',
22 'HTTP-Referer': 'https://witchsky.app',
23 'X-Title': 'Witchsky',
24 },
25 body: JSON.stringify({
26 model: model || DEFAULT_ALT_TEXT_AI_MODEL,
27 messages: [
28 {
29 role: 'user',
30 content: [
31 {
32 type: 'text',
33 text: customPrompt || DEFAULT_ALT_TEXT_AI_PROMPT,
34 },
35 {
36 type: 'image_url',
37 image_url: {
38 url: `data:${imageMimeType};base64,${imageBase64}`,
39 },
40 },
41 ],
42 },
43 ],
44 max_tokens: MAX_ALT_TEXT,
45 }),
46 },
47 )
48
49 if (!response.ok) {
50 const errorText = await response.text()
51 logger.error('OpenRouter API error', {
52 status: response.status,
53 error: errorText,
54 })
55 throw new Error(`OpenRouter API error: ${response.status}`)
56 }
57
58 const data = await response.json()
59 const altText = data.choices?.[0]?.message?.content?.trim()
60
61 if (!altText) {
62 throw new Error('No alt text generated')
63 }
64
65 return altText
66}