fix(backend): first pass respects refined env flag; add A/B override
Browse filesThe first pass was hardcoded to baseline, so CHALCHITRA_REFINED_PROMPT was
ignored except on retry. Now the first pass uses the configured default
(refined=None reads the env). Adds an optional per-request `refined` override
(API + interpret) for a single clean pass — used to A/B baseline vs refined on
the same image against the live model. The frontend never sends it.
- app.py +6 -1
- backend/oracle.py +12 -5
app.py
CHANGED
|
@@ -106,6 +106,7 @@ class Moment(BaseModel):
|
|
| 106 |
images: list[str] = Field(default_factory=list) # data URLs or raw base64 JPEG
|
| 107 |
fragment: str = ""
|
| 108 |
exclude: list[str] = Field(default_factory=list) # titles already shown this session
|
|
|
|
| 109 |
|
| 110 |
|
| 111 |
@app.post("/api/interpret")
|
|
@@ -114,7 +115,11 @@ async def api_interpret(moment: Moment):
|
|
| 114 |
return JSONResponse({"error": "Bring at least one image."}, status_code=400)
|
| 115 |
try:
|
| 116 |
return interpret(
|
| 117 |
-
moment.images,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
)
|
| 119 |
except InputError as exc:
|
| 120 |
# Bad/oversized/unsupported input — rejected before reaching the model.
|
|
|
|
| 106 |
images: list[str] = Field(default_factory=list) # data URLs or raw base64 JPEG
|
| 107 |
fragment: str = ""
|
| 108 |
exclude: list[str] = Field(default_factory=list) # titles already shown this session
|
| 109 |
+
refined: bool | None = None # optional A/B override of the prompt variant
|
| 110 |
|
| 111 |
|
| 112 |
@app.post("/api/interpret")
|
|
|
|
| 115 |
return JSONResponse({"error": "Bring at least one image."}, status_code=400)
|
| 116 |
try:
|
| 117 |
return interpret(
|
| 118 |
+
moment.images,
|
| 119 |
+
moment.fragment,
|
| 120 |
+
provider=_provider(),
|
| 121 |
+
exclude=moment.exclude,
|
| 122 |
+
refined=moment.refined,
|
| 123 |
)
|
| 124 |
except InputError as exc:
|
| 125 |
# Bad/oversized/unsupported input — rejected before reaching the model.
|
backend/oracle.py
CHANGED
|
@@ -148,6 +148,7 @@ def interpret(
|
|
| 148 |
fragment: str = "",
|
| 149 |
provider: Provider | None = None,
|
| 150 |
exclude: list[str] | None = None,
|
|
|
|
| 151 |
) -> dict[str, Any]:
|
| 152 |
"""Run the full flow. `images` are data URLs or raw base64 JPEG strings.
|
| 153 |
|
|
@@ -155,9 +156,10 @@ def interpret(
|
|
| 155 |
different films that still fit the moment (the "other films" re-roll).
|
| 156 |
Stateless — that list lives on the client and rides in the request.
|
| 157 |
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
|
|
|
| 161 |
"""
|
| 162 |
# Guardrails first: decode/validate/re-encode images and clean the inputs
|
| 163 |
# before anything reaches the model. InputError propagates to a clean 4xx.
|
|
@@ -168,10 +170,15 @@ def interpret(
|
|
| 168 |
provider = provider or get_provider()
|
| 169 |
content = _build_content(images, fragment, exclude)
|
| 170 |
|
| 171 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
first: dict[str, Any] | None = None
|
| 173 |
try:
|
| 174 |
-
first = _run(provider, content, refined=
|
| 175 |
if not _is_weak(first["interpretation"]):
|
| 176 |
return first
|
| 177 |
except OracleError:
|
|
|
|
| 148 |
fragment: str = "",
|
| 149 |
provider: Provider | None = None,
|
| 150 |
exclude: list[str] | None = None,
|
| 151 |
+
refined: bool | None = None,
|
| 152 |
) -> dict[str, Any]:
|
| 153 |
"""Run the full flow. `images` are data URLs or raw base64 JPEG strings.
|
| 154 |
|
|
|
|
| 156 |
different films that still fit the moment (the "other films" re-roll).
|
| 157 |
Stateless — that list lives on the client and rides in the request.
|
| 158 |
|
| 159 |
+
`refined` overrides the prompt variant for a single clean pass (no retry) —
|
| 160 |
+
used for A/B testing the baseline vs refined prompt on the same image. When
|
| 161 |
+
None (the normal path), the first pass uses the configured default and one
|
| 162 |
+
retry with the refined prompt covers a flat or unparseable reply.
|
| 163 |
"""
|
| 164 |
# Guardrails first: decode/validate/re-encode images and clean the inputs
|
| 165 |
# before anything reaches the model. InputError propagates to a clean 4xx.
|
|
|
|
| 170 |
provider = provider or get_provider()
|
| 171 |
content = _build_content(images, fragment, exclude)
|
| 172 |
|
| 173 |
+
# Explicit override (A/B / debugging): one clean pass with the chosen variant.
|
| 174 |
+
if refined is not None:
|
| 175 |
+
return _run(provider, content, refined=refined)
|
| 176 |
+
|
| 177 |
+
# First pass with the configured default (refined=None reads the env flag).
|
| 178 |
+
# If it parses and reads well, ship it.
|
| 179 |
first: dict[str, Any] | None = None
|
| 180 |
try:
|
| 181 |
+
first = _run(provider, content, refined=None)
|
| 182 |
if not _is_weak(first["interpretation"]):
|
| 183 |
return first
|
| 184 |
except OracleError:
|