bongard-mini / editor.py
0xDing's picture
Simplify the playground to one Run action
4a85866 verified
Raw History Blame Contribute Delete
4.09 kB
"""Plain-language question editing and handoff to an app builder."""
import html
import json
from bongard.data import DataError, strict_loads, validate_question
KIND_LABELS = {"choice": "Choose one option", "noul": "Yes or no", "score": "Rate on a scale"}
def label(key):
text = key.replace("_", " ").strip()
return text[:1].upper() + text[1:]
def text_value(value):
return value if isinstance(value, str) else json.dumps(value, ensure_ascii=False)
def load_questions(text):
try:
questions = strict_loads(text or "")
except (ValueError, DataError) as exc:
raise DataError(f"The question JSON could not be read: {exc}") from None
if not isinstance(questions, dict) or not 1 <= len(questions) <= 8:
raise DataError("Include 1–8 questions in this request.")
for key, question in questions.items():
validate_question(question, key)
if question["type"] == "choice" and not 2 <= len(question["criteria"]) <= 32:
raise DataError("A choice question needs 2–32 answer options.")
return questions
def question_preview(text):
try:
questions = load_questions(text)
except DataError as exc:
if (text or "").strip() == "{}":
return '<div class="question-empty">Include at least one question in the JSON.</div>'
return f'<div class="inline-error" role="alert">{html.escape(str(exc))}</div>'
rows = []
for question in questions.values():
kind = question["type"]
detail = KIND_LABELS[kind]
if kind != "noul":
count = len(question["criteria"])
detail += f" · {count} {'options' if kind == 'choice' else 'levels'}"
rows.append(
f'<div class="question-summary"><span>{html.escape(detail)}</span>'
f"<p>{html.escape(text_value(question.get('instructions')))}</p></div>"
)
return (
f'<div class="question-list"><div class="questions-count">{len(questions)} questions · one request</div>'
+ "".join(rows)
+ "</div>"
)
def handoff(state_text, questions_text, has_image=False, calibrated=True, rotations=False):
questions = load_questions(questions_text)
image = 'handle_file("your-photo.png")' if has_image else "None"
code = (
"# Install once: pip install gradio_client\n"
"from gradio_client import Client, handle_file\n\n"
'client = Client("AgentBull/bongard-mini")\n'
f"state_text = {state_text!r}\n"
f"questions_text = {json.dumps(questions, ensure_ascii=False)!r}\n\n"
"_, _, response = client.predict(\n"
f" state_text, questions_text, {image}, {calibrated}, {rotations},\n"
' api_name="/decide",\n'
")\n"
'print(response["answers"])\n'
)
prompt = (
"Add Bongard-mini decision judgments to my app using the tested request below. "
"For a prototype, call the public Hugging Face Space AgentBull/bongard-mini with "
"the official Gradio client for my app's stack: gradio_client for Python or "
"@gradio/client for JavaScript. Use the '/decide' endpoint. The arguments, in order, "
"are state_text, questions_text "
f"(JSON string), image (optional), calibrated={calibrated}, rotations={rotations}. The third return "
"value (Python prediction[2], JavaScript result.data[2]) is the model response; "
"use its 'answers' dictionary and preserve its full "
"probability distributions. Handle response['error'] and waiting for a GPU. "
"Keep credentials on the server and do not bake a Hugging Face token into the frontend. "
"For deployment, use the downloadable model and public runtime at "
"https://huggingface.co/AgentBull/bongard-mini and https://github.com/AgentBull/bongard.\n\n"
f"Context:\n{state_text}\n\nQuestions:\n{json.dumps(questions, ensure_ascii=False, indent=2)}"
)
if has_image:
prompt += "\n\nThis request also requires an image. Let the user attach it as image 1."
return prompt, code