feat: add multimodal client artifact handling

parent 30f105dc
......@@ -3,6 +3,7 @@ from tools.manual_multimodal_test_client import (
build_request_spec,
build_parser,
choose_mode_interactively,
handle_response_payload,
parse_args,
resolve_mode_config,
)
......@@ -353,3 +354,53 @@ def test_build_request_spec_for_transcription_requires_existing_audio_file(tmp_p
with pytest.raises(FileNotFoundError, match=rf"File not found: {missing_path}\. Supply --audio-file\."):
build_request_spec(config)
class DummyResponse:
def __init__(self, payload, status_code=200):
self._payload = payload
self.status_code = status_code
self.text = "payload-text"
def json(self):
return self._payload
def raise_for_status(self):
if self.status_code >= 400:
raise RuntimeError(f"HTTP {self.status_code}")
def test_handle_response_payload_returns_llm_text_without_artifact(tmp_path):
response = DummyResponse({
"choices": [{"message": {"content": "hello from model"}}]
})
result = handle_response_payload("llm", response, tmp_path)
assert result["text"] == "hello from model"
assert result["artifact_path"] is None
def test_handle_response_payload_downloads_url_artifact(monkeypatch, tmp_path):
response = DummyResponse({
"data": [{"url": "http://example.invalid/audio.wav"}]
})
monkeypatch.setattr(
"tools.manual_multimodal_test_client._download_artifact",
lambda url: b"wave-bytes",
)
result = handle_response_payload("audio-generation", response, tmp_path)
assert result["artifact_path"].suffix == ".wav"
assert result["artifact_path"].read_bytes() == b"wave-bytes"
def test_handle_response_payload_decodes_base64_artifact(tmp_path):
response = DummyResponse({
"data": [{"b64_json": "aGVsbG8="}]
})
result = handle_response_payload("audio-generation", response, tmp_path)
assert result["artifact_path"].read_bytes() == b"hello"
from __future__ import annotations
import argparse
import base64
import json
import time
from pathlib import Path
import requests
MODES = [
"llm",
......@@ -192,3 +197,57 @@ def choose_mode_interactively() -> str:
if selected < 1 or selected > len(MODES):
raise ValueError(f"Invalid mode selection: {raw}")
return MODES[selected - 1]
def _download_artifact(url: str) -> bytes:
response = requests.get(url, timeout=60)
response.raise_for_status()
return response.content
def _artifact_suffix_for_mode(mode: str) -> str:
if mode == "audio-generation":
return ".wav"
if mode == "video-generation":
return ".mp4"
return ".bin"
def _write_artifact(output_dir: Path, mode: str, payload: bytes) -> Path:
output_dir.mkdir(parents=True, exist_ok=True)
artifact_path = output_dir / f"{mode}-{int(time.time() * 1000)}{_artifact_suffix_for_mode(mode)}"
artifact_path.write_bytes(payload)
return artifact_path
def handle_response_payload(mode: str, response, output_dir: Path) -> dict:
response.raise_for_status()
payload = response.json()
if mode in {"llm", "video-doubt", "music-audio-doubt"}:
text = payload["choices"][0]["message"]["content"]
return {"text": text, "artifact_path": None, "payload": payload}
if mode == "transcription":
text = payload.get("text") or payload.get("transcript") or json.dumps(payload)
return {"text": text, "artifact_path": None, "payload": payload}
if mode in {"audio-generation", "video-generation"}:
first = payload["data"][0]
text = first.get("text") or first.get("caption") or ""
if "url" in first:
artifact_bytes = _download_artifact(first["url"])
elif "b64_json" in first:
artifact_bytes = base64.b64decode(first["b64_json"])
else:
raise ValueError(f"No artifact found in generation response for mode: {mode}")
artifact_path = _write_artifact(output_dir, mode, artifact_bytes)
return {"text": text, "artifact_path": artifact_path, "payload": payload}
raise ValueError(f"Unsupported response mode: {mode}")
def execute_request(spec: dict):
method = spec["method"]
kwargs = {key: value for key, value in spec.items() if key not in {"method", "url"}}
return requests.request(method, spec["url"], timeout=300, **kwargs)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment