Spaces:
Running
Running
File size: 6,303 Bytes
7646924 4f01dd5 7646924 4f01dd5 7646924 4f01dd5 7646924 4f01dd5 7646924 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 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)};
}
|