Fuse-MD / api /predict.py
Unit4IAI's picture
Upload 25 files
bd659a9 verified
Raw History Blame Contribute Delete
3.88 kB
import argparse
import json
import sys
import urllib.error
import urllib.parse
import urllib.request
import uuid
from pathlib import Path
from typing import Optional
from local_config import load_local_api_config
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Send a local prediction request to the Fuse-MD FastAPI server."
)
parser.add_argument(
"--image",
required=True,
help="Path to the image file to upload.",
)
parser.add_argument(
"--text",
required=True,
help="Text or OCR transcription for the meme.",
)
parser.add_argument(
"--threshold",
type=float,
help="Optional threshold override for this request.",
)
parser.add_argument(
"--url",
help="Prediction endpoint URL. Defaults to the local API URL from api/local_config.py.",
)
return parser.parse_args()
def default_predict_url() -> str:
config = load_local_api_config()
return f"http://{config.host}:{config.port}/predict"
def encode_multipart_form(
*,
text: str,
image_path: Path,
image_bytes: bytes,
threshold: Optional[float],
) -> tuple[bytes, str]:
boundary = f"fusemd-{uuid.uuid4().hex}"
lines: list[bytes] = []
def add_field(name: str, value: str) -> None:
lines.extend(
[
f"--{boundary}".encode("utf-8"),
f'Content-Disposition: form-data; name="{name}"'.encode("utf-8"),
b"",
value.encode("utf-8"),
]
)
add_field("text", text)
if threshold is not None:
add_field("threshold", str(threshold))
lines.extend(
[
f"--{boundary}".encode("utf-8"),
(
f'Content-Disposition: form-data; name="image"; filename="{image_path.name}"'
).encode("utf-8"),
b"Content-Type: application/octet-stream",
b"",
image_bytes,
f"--{boundary}--".encode("utf-8"),
b"",
]
)
body = b"\r\n".join(lines)
return body, boundary
def main() -> int:
args = parse_args()
image_path = Path(args.image)
if not image_path.is_absolute():
image_path = (Path.cwd() / image_path).resolve()
if not image_path.exists():
print(f"Image not found: {image_path}")
return 1
if not image_path.is_file():
print(f"Image path is not a file: {image_path}")
return 1
image_bytes = image_path.read_bytes()
url = args.url or default_predict_url()
body, boundary = encode_multipart_form(
text=args.text,
image_path=image_path,
image_bytes=image_bytes,
threshold=args.threshold,
)
request = urllib.request.Request(
url=url,
data=body,
method="POST",
headers={
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Content-Length": str(len(body)),
},
)
try:
with urllib.request.urlopen(request) as response:
payload = json.loads(response.read().decode("utf-8"))
print(json.dumps(payload, indent=2))
return 0
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
print(f"API request failed with status {exc.code}.")
print(detail)
return 1
except urllib.error.URLError as exc:
parsed = urllib.parse.urlparse(url)
base_url = f"{parsed.scheme}://{parsed.netloc}" if parsed.scheme and parsed.netloc else url
print(f"Could not connect to Fuse-MD API at {base_url}.")
print("Start it first with: python api/run_api.py")
print(f"Connection error: {exc.reason}")
return 1
if __name__ == "__main__":
sys.exit(main())