tools: print the error body the Library endpoint returns
The pairing came back perfectly clean, and it is the whole diagnosis:
has library_file_id → refused (11/11)
no library_file_id → deleted (7/7)
And /files/{libfile_id}/download answered 200 — not 403, not 404 — with
{status, error_code, error_type, error_message}. That endpoint accepts the
Library id; it is returning an application-level error inside an HTTP 200.
The probe printed only the keys and dropped the message, which is the one
thing that says what the call is missing.
- show() now prints the full body for every response, and detects a
non-JSON 200 as possible raw bytes.
- Added routes worth ruling in or out: the same call as POST, with a
conversation_id, /files/{lib}/content, /content?asset_pointer=, and four
Library listing endpoints — a listing usually reveals both the id form
the UI uses and the route that actually serves bytes.
- Probes a second Library id too, so one odd file cannot mislead us.
This commit is contained in:
@@ -80,7 +80,7 @@ def main() -> None:
|
||||
return
|
||||
|
||||
provider = ChatGPTProvider()
|
||||
pairs: list[tuple[str, str]] = []
|
||||
pairs: list[tuple[str, str, str]] = []
|
||||
|
||||
print("=" * 78)
|
||||
print("Pairing refused assets with their Library IDs")
|
||||
@@ -98,61 +98,78 @@ def main() -> None:
|
||||
source = (att or {}).get("source")
|
||||
print(f" {file_id[:34]}… library={str(lib)[:34]:<36} source={source}")
|
||||
if lib:
|
||||
pairs.append((file_id, lib))
|
||||
pairs.append((file_id, lib, conv_id))
|
||||
|
||||
if not pairs:
|
||||
print("\n No refused asset carries a library_file_id — different cause.")
|
||||
return
|
||||
|
||||
file_id, lib_id = pairs[0]
|
||||
file_id, lib_id, conv_id = pairs[0]
|
||||
print()
|
||||
print("=" * 78)
|
||||
print(f"Endpoint hunt for {lib_id}")
|
||||
print(f" (sediment id {file_id})")
|
||||
print("=" * 78)
|
||||
|
||||
def show(label: str, url: str) -> bool:
|
||||
def show(label: str, url: str, method: str = "GET") -> bool:
|
||||
"""Print status AND body. A 200 carrying an error envelope says what
|
||||
the endpoint wants — printing only the keys threw that away."""
|
||||
try:
|
||||
provider._pace()
|
||||
resp = provider._session.request("GET", url, timeout=30)
|
||||
resp = provider._session.request(method, url, timeout=30)
|
||||
except Exception as e: # noqa: BLE001 - diagnostic
|
||||
print(f" {label:<50} error {type(e).__name__}")
|
||||
return False
|
||||
note = ""
|
||||
|
||||
hit = False
|
||||
if resp.status_code == 200:
|
||||
try:
|
||||
body = resp.json()
|
||||
if isinstance(body, dict) and body.get("download_url"):
|
||||
note = " ← DOWNLOAD URL"
|
||||
hit = True
|
||||
else:
|
||||
keys = list(body)[:8] if isinstance(body, dict) else type(body).__name__
|
||||
note = f" 200 keys={keys}"
|
||||
except Exception: # noqa: BLE001 - diagnostic
|
||||
note = f" 200 non-JSON ({len(resp.content)} bytes)"
|
||||
hit = True
|
||||
print(f" {label:<50} {resp.status_code}{note}")
|
||||
try:
|
||||
body = resp.json()
|
||||
except Exception: # noqa: BLE001 - diagnostic
|
||||
preview = (resp.text or "").strip()[:200]
|
||||
if resp.status_code == 200 and resp.content:
|
||||
print(f" {label:<50} 200 non-JSON ({len(resp.content)} bytes) ← BYTES?")
|
||||
return True
|
||||
print(f" {label:<50} {resp.status_code} {preview}")
|
||||
return False
|
||||
|
||||
if isinstance(body, dict) and body.get("download_url"):
|
||||
print(f" {label:<50} {resp.status_code} ← DOWNLOAD URL")
|
||||
print(f" {json.dumps(body, default=str)[:300]}")
|
||||
return True
|
||||
|
||||
print(f" {label:<50} {resp.status_code}")
|
||||
print(f" {json.dumps(body, default=str)[:400]}")
|
||||
return hit
|
||||
|
||||
candidates = [
|
||||
# This one already returns 200 with an error envelope — read it.
|
||||
("/files/{lib}/download", f"{BASE_URL}/files/{lib_id}/download"),
|
||||
("/files/{lib}", f"{BASE_URL}/files/{lib_id}"),
|
||||
("/library/files/{lib}/download", f"{BASE_URL}/library/files/{lib_id}/download"),
|
||||
("/library/files/{lib}", f"{BASE_URL}/library/files/{lib_id}"),
|
||||
("/library/{lib}", f"{BASE_URL}/library/{lib_id}"),
|
||||
(
|
||||
"/files/{sediment}/download?library_file_id=",
|
||||
f"{BASE_URL}/files/{file_id}/download?library_file_id={lib_id}",
|
||||
),
|
||||
("/files/{lib}/download?use_case=gizmo", f"{BASE_URL}/files/{lib_id}/download?use_case=gizmo"),
|
||||
("/files/{lib}/download?conversation_id=", f"{BASE_URL}/files/{lib_id}/download?conversation_id={conv_id}"),
|
||||
("/files/{lib}/download (POST)", f"{BASE_URL}/files/{lib_id}/download"),
|
||||
# How does the UI enumerate the Library? A listing tends to reveal both
|
||||
# the id form it uses and the route that serves bytes.
|
||||
("/library/files?limit=3", f"{BASE_URL}/library/files?limit=3"),
|
||||
("/library?limit=3", f"{BASE_URL}/library?limit=3"),
|
||||
("/files?limit=3", f"{BASE_URL}/files?limit=3"),
|
||||
("/my_files?limit=3", f"{BASE_URL}/my_files?limit=3"),
|
||||
# Content routes that serve the asset rather than a signed URL.
|
||||
("/files/{lib}/content", f"{BASE_URL}/files/{lib_id}/content"),
|
||||
("/content?asset_pointer=sediment://{sediment}", f"{BASE_URL}/content?asset_pointer=sediment://{file_id}"),
|
||||
]
|
||||
|
||||
winners = []
|
||||
for label, url in candidates:
|
||||
if show(label, url):
|
||||
method = "POST" if "(POST)" in label else "GET"
|
||||
if show(label, url, method):
|
||||
winners.append((label, url))
|
||||
|
||||
# If a second file behaves differently, the first one was the anomaly.
|
||||
if len(pairs) > 1:
|
||||
_other_file, other_lib, _other_conv = pairs[1]
|
||||
print()
|
||||
print(f" --- second sample: {other_lib} ---")
|
||||
show("/files/{lib}/download", f"{BASE_URL}/files/{other_lib}/download")
|
||||
|
||||
print()
|
||||
print("=" * 78)
|
||||
print("Result")
|
||||
|
||||
Reference in New Issue
Block a user