| 1 | import fetch from 'node-fetch'; |
| 2 | import express from 'express'; |
| 3 | |
| 4 | import { readSecret, SECRET_KEYS } from './secrets.js'; |
| 5 | |
| 6 | export const router = express.Router(); |
| 7 | |
| 8 | router.post('/caption-image', async (request, response) => { |
| 9 | try { |
| 10 | const mimeType = request.body.image.split(';')[0].split(':')[1]; |
| 11 | const base64Data = request.body.image.split(',')[1]; |
| 12 | const baseUrl = request.body.reverse_proxy ? request.body.reverse_proxy : 'https://api.anthropic.com/v1'; |
| 13 | const url = `${baseUrl}/messages`; |
| 14 | const body = { |
| 15 | model: request.body.model, |
| 16 | messages: [ |
| 17 | { |
| 18 | 'role': 'user', 'content': [ |
| 19 | { |
| 20 | 'type': 'image', |
| 21 | 'source': { |
| 22 | 'type': 'base64', |
| 23 | 'media_type': mimeType, |
| 24 | 'data': base64Data, |
| 25 | }, |
| 26 | }, |
| 27 | { 'type': 'text', 'text': request.body.prompt }, |
| 28 | ], |
| 29 | }, |
| 30 | ], |
| 31 | max_tokens: 4096, |
| 32 | }; |
| 33 | |
| 34 | console.debug('Multimodal captioning request', body); |
| 35 | |
| 36 | const result = await fetch(url, { |
| 37 | body: JSON.stringify(body), |
| 38 | method: 'POST', |
| 39 | headers: { |
| 40 | 'Content-Type': 'application/json', |
| 41 | 'anthropic-version': '2023-06-01', |
| 42 | 'x-api-key': request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE), |
| 43 | }, |
| 44 | }); |
| 45 | |
| 46 | if (!result.ok) { |
| 47 | const text = await result.text(); |
| 48 | console.warn(`Claude API returned error: ${result.status} ${result.statusText}`, text); |
| 49 | return response.status(result.status).send({ error: true }); |
| 50 | } |
| 51 | |
| 52 | /** @type {any} */ |
| 53 | const generateResponseJson = await result.json(); |
| 54 | const caption = generateResponseJson.content[0].text; |
| 55 | console.debug('Claude response:', generateResponseJson); |
| 56 | |
| 57 | if (!caption) { |
| 58 | return response.status(500).send('No caption found'); |
| 59 | } |
| 60 | |
| 61 | return response.json({ caption }); |
| 62 | } catch (error) { |
| 63 | console.error(error); |
| 64 | response.status(500).send('Internal server error'); |
| 65 | } |
| 66 | }); |