Spaces:
Running
Running
wave-48 backend deploy
Browse files
apex/instruct/__pycache__/docling_bridge.cpython-312.pyc
CHANGED
|
Binary files a/apex/instruct/__pycache__/docling_bridge.cpython-312.pyc and b/apex/instruct/__pycache__/docling_bridge.cpython-312.pyc differ
|
|
|
apex/instruct/docling_bridge.py
CHANGED
|
@@ -83,8 +83,14 @@ def extract_pdf_text(pdf_bytes: bytes) -> str:
|
|
| 83 |
) from exc
|
| 84 |
import io
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
if not text.strip():
|
| 89 |
raise CoaBridgeError(
|
| 90 |
"no extractable text in the PDF (likely a scanned image; needs OCR or the "
|
|
@@ -125,13 +131,24 @@ def coa_dict_from_text(text: str, generator: Callable[[str, int], str]) -> dict:
|
|
| 125 |
malformed or fails the existing `parse_coa_payload` schema validation.
|
| 126 |
"""
|
| 127 |
prompt = f"{_EXTRACTION_SYSTEM}\n\nDOCUMENT:\n{text[:_MAX_PROMPT_CHARS]}"
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
payload = _extract_json(raw)
|
| 130 |
if not isinstance(payload, dict):
|
| 131 |
raise CoaBridgeError("Granite extraction returned non-object JSON")
|
| 132 |
|
| 133 |
explicit = payload.get("simultaneity_permission_flag")
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
raise CoaBridgeUndetermined(
|
| 136 |
"COA simultaneity gate could not be determined from the PDF. Upload a "
|
| 137 |
"structured COA or a clearer certificate; APEX will not guess a "
|
|
|
|
| 83 |
) from exc
|
| 84 |
import io
|
| 85 |
|
| 86 |
+
try:
|
| 87 |
+
reader = PdfReader(io.BytesIO(pdf_bytes))
|
| 88 |
+
text = "\n".join((page.extract_text() or "") for page in reader.pages)
|
| 89 |
+
except Exception as exc: # noqa: BLE001 - pypdf raises PdfReadError/DependencyError on corrupt, encrypted, or truncated PDFs
|
| 90 |
+
raise CoaBridgeError(
|
| 91 |
+
"could not read the PDF (corrupt, encrypted, or unsupported format); "
|
| 92 |
+
"upload a structured COA or a clearer certificate"
|
| 93 |
+
) from exc
|
| 94 |
if not text.strip():
|
| 95 |
raise CoaBridgeError(
|
| 96 |
"no extractable text in the PDF (likely a scanned image; needs OCR or the "
|
|
|
|
| 131 |
malformed or fails the existing `parse_coa_payload` schema validation.
|
| 132 |
"""
|
| 133 |
prompt = f"{_EXTRACTION_SYSTEM}\n\nDOCUMENT:\n{text[:_MAX_PROMPT_CHARS]}"
|
| 134 |
+
try:
|
| 135 |
+
raw = generator(prompt, 0)
|
| 136 |
+
except Exception as exc: # noqa: BLE001 - external LLM/network boundary; re-raise generically so no upstream response text leaks
|
| 137 |
+
raise CoaBridgeError(
|
| 138 |
+
"COA extraction generator failed (upstream LLM or network error)"
|
| 139 |
+
) from exc
|
| 140 |
payload = _extract_json(raw)
|
| 141 |
if not isinstance(payload, dict):
|
| 142 |
raise CoaBridgeError("Granite extraction returned non-object JSON")
|
| 143 |
|
| 144 |
explicit = payload.get("simultaneity_permission_flag")
|
| 145 |
+
# Never-guess gate. Treat null AND an uncorroborated non-True value (e.g.
|
| 146 |
+
# the model emitting `false` for an ambiguous certificate instead of the
|
| 147 |
+
# instructed `null`) as undetermined when no approval anchor is present, so
|
| 148 |
+
# a hallucinated negative cannot silently close the gate on an adaptive
|
| 149 |
+
# driver. An affirmative `true` (or any flag backed by the approval anchor)
|
| 150 |
+
# passes here and is consistency-checked by parse_coa_payload below.
|
| 151 |
+
if explicit is not True and not _has_simultaneity_approval(payload):
|
| 152 |
raise CoaBridgeUndetermined(
|
| 153 |
"COA simultaneity gate could not be determined from the PDF. Upload a "
|
| 154 |
"structured COA or a clearer certificate; APEX will not guess a "
|
apex/orchestration/__pycache__/langgraph_runtime.cpython-312.pyc
CHANGED
|
Binary files a/apex/orchestration/__pycache__/langgraph_runtime.cpython-312.pyc and b/apex/orchestration/__pycache__/langgraph_runtime.cpython-312.pyc differ
|
|
|
apex/orchestration/langgraph_runtime.py
CHANGED
|
@@ -241,10 +241,14 @@ class LangGraphRuntime:
|
|
| 241 |
)
|
| 242 |
active_narrator = narrator or Narrator()
|
| 243 |
narrator_out = active_narrator.narrate(narrator_inputs)
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
steps.append(NodeExecutionTrace(
|
| 249 |
node="instruct",
|
| 250 |
status="ok",
|
|
|
|
| 241 |
)
|
| 242 |
active_narrator = narrator or Narrator()
|
| 243 |
narrator_out = active_narrator.narrate(narrator_inputs)
|
| 244 |
+
# The instruct node calls Narrator.narrate(), which composes the
|
| 245 |
+
# coaching report deterministically (derive_corner_insights) and does
|
| 246 |
+
# not invoke the wired text generator. Label the prose engine honestly
|
| 247 |
+
# as the deterministic floor regardless of whether a generator was
|
| 248 |
+
# supplied; the live Granite 4.1 8B narrative is produced by the
|
| 249 |
+
# frontend /api/coaching/narrate route, layered over these backend
|
| 250 |
+
# numbers (frontend provenance "backend-granite-live").
|
| 251 |
+
narrator_engine = "deterministic-floor"
|
| 252 |
steps.append(NodeExecutionTrace(
|
| 253 |
node="instruct",
|
| 254 |
status="ok",
|