Spaces:
Paused
Paused
File size: 2,765 Bytes
107df28 810f920 107df28 c3434e7 107df28 810f920 107df28 810f920 107df28 810f920 107df28 c3434e7 107df28 810f920 107df28 c3434e7 107df28 810f920 107df28 4221b2e 107df28 | 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 | import base64
import re
import io
from PIL import Image
from huggingface_hub import InferenceClient
from config import EVALUATION_HEADING, REMOTE_MODEL, REMOTE_PROVIDER, remote_prompt, REMOTE_MAX_TOKENS
from critique import Critique
SCORE_PATTERN = re.compile(r"##\s*Score:\s*(\d+)\s*/\s*100")
HEADING_PATTERN = re.compile(rf"###\s*{re.escape(EVALUATION_HEADING)}\s*", re.IGNORECASE)
MAX_IMAGE_SIDE = 768
def image_as_data_url(image_path: str) -> str:
# downscale before sending to model to reduce tokens
with Image.open(image_path) as source:
image = source.convert("RGB")
if max(image.size) > MAX_IMAGE_SIDE:
image.thumbnail((MAX_IMAGE_SIDE, MAX_IMAGE_SIDE))
buffer = io.BytesIO()
image.save(buffer, format="JPEG", quality=88)
encoded = base64.b64encode(buffer.getvalue()).decode("utf-8")
return f"data:image/jpeg;base64,{encoded}"
def parse_response(text: str) -> tuple[int | None, str]:
# split the model markdown into (score, evaluation body)
# notice the Qwen3 output can be duplicated sometime
# so we only take the first part of the output
headings = list(SCORE_PATTERN.finditer(text))
if len(headings) > 1:
text = text[: headings[1].start()]
score = int(headings[0].group(1)) if headings else None
body = HEADING_PATTERN.split(text, maxsplit=1)
evaluation = body[1].strip() if len(body) > 1 else text.strip()
repeat = HEADING_PATTERN.search(evaluation)
if repeat:
evaluation = evaluation[: repeat.start()].strip()
return score, evaluation
def remote_critique(image_path, aspect, temperature, top_p, hf_token) -> Critique:
client = InferenceClient(provider=REMOTE_PROVIDER, token=hf_token)
response = client.chat.completions.create(
model=REMOTE_MODEL,
messages=[
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": image_as_data_url(image_path)}},
{"type": "text", "text": remote_prompt(aspect)},
],
}
],
max_tokens=REMOTE_MAX_TOKENS,
temperature=temperature,
top_p=top_p,
)
choice = response.choices[0]
content = (choice.message.content or "").strip()
# catch the case where the reasoning exhausts the token budget
if not content:
raise RuntimeError(
f"Hosted model returned no visible content (finish_reason="
f"{choice.finish_reason}); the token budget was likely exhausted by "
"internal reasoning."
)
score, evaluation = parse_response(content)
return Critique(score=score, evaluation=evaluation, model_name=REMOTE_MODEL, route="Hosted")
|