Spaces:
Running
Running
Download api.mjs from Valen-Team/Valen-API: direct link, hf CLI and curl.
- Browser
- Download file 6.3 kB
-
https://huggingface.co/spaces/Valen-Team/Valen-API/resolve/main/api.mjs
- Command line
-
hf download hf://spaces/Valen-Team/Valen-API/api.mjs
-
curl -L -o api.mjs https://huggingface.co/spaces/Valen-Team/Valen-API/resolve/main/api.mjs
6.3 kB
| export const ENDPOINT = 'https://api.valen-team.workers.dev/v1/decisions'; | |
| export const MODELS = ['valen-2b', 'valen-4b', 'valen-0-8b', 'valen-preview-0923']; | |
| export function buildPayload({model, mode, question, context = '', options = '', image}) { | |
| if (!MODELS.includes(model) || !['predicate', 'choice', 'score'].includes(mode)) throw new Error('model'); | |
| question = question.trim(); context = context.trim(); | |
| if (!question || question.length > 1000) throw new Error('question'); | |
| if (context.length > 4000) throw new Error('context'); | |
| const q = {name: 'q0', type: mode, instructions: question}; | |
| if (mode !== 'predicate') { | |
| const lines = options.split('\n').map(s => s.trim()).filter(Boolean); | |
| if (lines.length < 2 || lines.length > 8 || lines.some(s => s.length > 300)) throw new Error('options'); | |
| if (mode === 'choice') q.choices = lines.map((description, i) => ({value: `option_${i+1}`, description})); | |
| else q.levels = lines.map((description, i) => ({label: String(i), description})); | |
| } | |
| const evidence = context || (image ? 'Answer using only the visible image.' : question); | |
| if (image && !/^data:image\/jpeg;base64,[A-Za-z0-9+/]+={0,2}$/.test(image)) throw new Error('image'); | |
| const input = image ? [{role: 'user', content: [{type: 'input_text', text: evidence}, {type: 'input_image', image_url: image}]}] : evidence; | |
| const payload = {model, input, questions: [q], temperature: 1.0}; | |
| if (new TextEncoder().encode(JSON.stringify(payload)).length > 4500000) throw new Error('size'); | |
| return payload; | |
| } | |
| export function previewPayload(payload) { | |
| const value = structuredClone(payload); | |
| if (Array.isArray(value.input)) value.input[0].content[1].image_url = '<JPEG data URL omitted from preview>'; | |
| return value; | |
| } | |
| export function pythonExample(payload, attachment) { | |
| const value = structuredClone(payload); | |
| const hasImage = Array.isArray(value.input); | |
| if (hasImage) value.input[0].content[1].image_url = 'IMAGE_DATA_URL'; | |
| const isVideo=hasImage&&attachment?.kind==='video'; | |
| const filename=JSON.stringify(attachment?.name||(isVideo?'video.mp4':'image.jpg')); | |
| const setup = isVideo ? `import base64 | |
| import io | |
| import av | |
| # Extract the middle frame, as in the Valen cookbook. | |
| video_path = ${filename} | |
| with av.open(video_path) as video: | |
| frame_count = sum(1 for _ in video.decode(video=0)) | |
| if frame_count == 0: | |
| raise ValueError("The video has no decodable frames") | |
| with av.open(video_path) as video: | |
| for index, frame in enumerate(video.decode(video=0)): | |
| if index == frame_count // 2: | |
| image = frame.to_image().convert("RGB") | |
| break | |
| image.thumbnail((1536, 1536)) | |
| buffer = io.BytesIO() | |
| image.save(buffer, format="JPEG", quality=85) | |
| image_url = "data:image/jpeg;base64," + base64.b64encode(buffer.getvalue()).decode("ascii") | |
| ` : hasImage ? `import base64 | |
| import io | |
| from PIL import Image, ImageOps | |
| # Use your selected image in the current directory. | |
| with Image.open(${filename}) as image: | |
| image = ImageOps.exif_transpose(image).convert("RGB") | |
| image.thumbnail((1536, 1536)) | |
| buffer = io.BytesIO() | |
| image.save(buffer, format="JPEG", quality=85) | |
| image_url = "data:image/jpeg;base64," + base64.b64encode(buffer.getvalue()).decode("ascii") | |
| ` : ''; | |
| // json.loads safely preserves quotes, Unicode and literal user text in Python. | |
| const literal = JSON.stringify(JSON.stringify(value)).replaceAll('\u2028', '\\u2028').replaceAll('\u2029', '\\u2029'); | |
| return `# pip install requests${hasImage ? ' Pillow' : ''}${isVideo ? ' av==16.1.0' : ''} | |
| import json | |
| import requests | |
| ${setup} | |
| payload = json.loads(${literal}) | |
| ${hasImage ? 'payload["input"][0]["content"][1]["image_url"] = image_url\n' : ''} | |
| response = requests.post( | |
| "${ENDPOINT}", | |
| headers={"Origin": "https://valen-team.github.io"}, | |
| json=payload, | |
| timeout=120, | |
| ) | |
| response.raise_for_status() | |
| result = response.json() | |
| print(result["answers"][0]) | |
| print("Model ID:", result.get("model_id")) | |
| `; | |
| } | |
| export function parseAnswer(data, payload) { | |
| const fail = () => {throw new Error('response');}; | |
| const q = payload.questions[0]; | |
| if (!Array.isArray(data?.answers) || data.answers.length !== 1) fail(); | |
| const a = data.answers[0]; | |
| const probability = value => typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1; | |
| if (!a || a.name !== 'q0' || a.type !== q.type || (a.confidence !== undefined && !probability(a.confidence))) fail(); | |
| if (q.type === 'predicate') { | |
| if (!probability(a.probability)) fail(); | |
| return {title: a.probability >= .5 ? 'yes' : 'no', confidence: a.confidence, | |
| rows: [{label: 'yes', probability: a.probability}, {label: 'no', probability: 1 - a.probability}]}; | |
| } | |
| const labels = q.type === 'choice' ? new Map(q.choices.map(c => [c.value, c.description])) : new Map(q.levels.map((l, i) => [i, l.description])); | |
| if (!Array.isArray(a.probabilities) || a.probabilities.length !== labels.size) fail(); | |
| const seen = new Set(); | |
| const rows = a.probabilities.map(p => { | |
| if (!p) fail(); | |
| const key = q.type === 'score' && typeof p.value === 'string' && /^\d+$/.test(p.value) ? Number(p.value) : p.value; | |
| if (!labels.has(key) || seen.has(key) || !probability(p.probability)) fail(); | |
| seen.add(key); | |
| return {label: labels.get(key), probability: p.probability}; | |
| }); | |
| if (Math.abs(rows.reduce((sum, row) => sum + row.probability, 0) - 1) > .02) fail(); | |
| let title; | |
| if (q.type === 'choice') {if (!labels.has(a.choice)) fail(); title = labels.get(a.choice);} | |
| else { | |
| if (typeof a.score !== 'number' || !Number.isFinite(a.score) || a.score < 0 || a.score > labels.size-1) fail(); | |
| title = `${a.score.toFixed(2)} / ${labels.size-1}`; | |
| } | |
| return {title, rows, confidence: a.confidence}; | |
| } | |
| export async function requestDecision(payload, signal, fetcher = fetch) { | |
| // The browser supplies the Space's real Origin. Never try to spoof it in JS. | |
| const response = await fetcher(ENDPOINT, {method: 'POST', headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify(payload), signal, credentials: 'omit', redirect: 'error'}); | |
| if (!response.ok) throw new Error(`http_${response.status}`); | |
| let data; | |
| try {data = await response.json();} catch {throw new Error('response');} | |
| return {data, answer: parseAnswer(data, payload)}; | |
| } | |