Recipes

Read a screenshot

Sending an image and asking a question about it — a stack trace, an invoice, a chart, a design comparison.

The call#

vision.py
import base64
from openai import OpenAI
 
client = OpenAI(base_url="https://ai.upfyn.com/v1", api_key=KEY, max_retries=0)
 
 
def read_screenshot(path: str, question: str) -> str:
with open(path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
 
response = client.chat.completions.create(
model="upfyn-yuva", # Bala is text-only and would 400 here
messages=[{
"role": "user",
"content": [
{"type": "text", "text": question},
{"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{b64}"}},
],
}],
max_tokens=600,
temperature=0,
)
return response.choices[0].message.content
 
 
print(read_screenshot(
"error.png",
"What is the exact error message, and which line of the stack trace is in our own code?",
))

Getting good answers#

Ask something specific

“Describe this image” produces a paragraph you have to read. “What is the exact error message, and which line is in our own code?” produces the two facts you wanted. The question does more for accuracy than the model choice does.

Do not shrink it

Downscaling is the most common cause of a wrong answer

Compressing a dense screenshot until the text blurs means the model is guessing at characters. If you are reading small text, send it at full size — the extra tokens cost less than a wrong answer.

Ask for fields, not prose

If you are going to parse the answer, get it structured in the first place.

Vision plus a schema
SCHEMA = {
"type": "object",
"properties": {
"error_message": {"type": "string"},
"error_type": {"type": "string"},
"file": {"type": "string"},
"line": {"type": ["integer", "null"]},
"is_our_code": {"type": "boolean"},
},
"required": ["error_message", "error_type", "file", "line", "is_our_code"],
"additionalProperties": False,
}
 
response = client.chat.completions.create(
model="upfyn-yuva",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Read this stack trace."},
{"type": "image_url", "image_url": {"url": data_uri}},
],
}],
response_format={"type": "json_schema",
"json_schema": {"name": "trace", "schema": SCHEMA, "strict": True}},
)

Note "line": {"type": ["integer", "null"]} — sometimes there is no line number, and a nullable field is honest where a required integer forces the model to invent one.

Several images at once

Comparing two images
content = [
{"type": "text",
"text": "The first image is the design, the second is what shipped. "
"List every visual difference."},
{"type": "image_url", "image_url": {"url": design_uri}},
{"type": "image_url", "image_url": {"url": built_uri}},
]

Say in the text which image is which. Order alone is not a reliable label.

Which model#

  • Yuva for almost everything — images, PDFs, documents.
  • Rishi when the reasoning about the image is hard, or the input is video.
  • Bala cannot see. Attaching an image returns 400 media_not_supported rather than being ignored.

Reusing an image#

Base64 counts toward every request that carries it. If you will ask several questions about the same file, upload it once and reference the file_id — see Multimodal input.