generated from coulomb/repo-seed
Copy markitect.llm module into standalone llm_connect package. All markitect.* imports replaced with llm_connect.* equivalents. LLMError base class inlined (no markitect.exceptions dependency). Verified: from llm_connect import create_adapter works. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
2.5 KiB
Python
87 lines
2.5 KiB
Python
"""
|
|
Thin synchronous HTTP helper built on :mod:`urllib.request`.
|
|
|
|
Translates HTTP errors into typed :mod:`markitect.llm.exceptions`.
|
|
"""
|
|
|
|
import json
|
|
import urllib.request
|
|
import urllib.error
|
|
from typing import Dict, Any, Optional
|
|
|
|
from llm_connect.exceptions import (
|
|
LLMAPIError,
|
|
LLMRateLimitError,
|
|
LLMTimeoutError,
|
|
)
|
|
|
|
|
|
def post_json(
|
|
url: str,
|
|
payload: Dict[str, Any],
|
|
headers: Optional[Dict[str, str]] = None,
|
|
timeout: int = 300,
|
|
) -> Dict[str, Any]:
|
|
"""POST *payload* as JSON and return the parsed response body.
|
|
|
|
Raises:
|
|
LLMRateLimitError: on HTTP 429
|
|
LLMAPIError: on other non-2xx responses
|
|
LLMTimeoutError: on socket / read timeout
|
|
"""
|
|
data = json.dumps(payload).encode()
|
|
req = urllib.request.Request(
|
|
url,
|
|
data=data,
|
|
headers={"Content-Type": "application/json", **(headers or {})},
|
|
method="POST",
|
|
)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
body = resp.read().decode()
|
|
try:
|
|
return json.loads(body)
|
|
except json.JSONDecodeError as exc:
|
|
preview = body[:300].replace("\n", "\\n")
|
|
raise LLMAPIError(
|
|
f"Invalid JSON response from {url}: {exc} — body preview: {preview!r}",
|
|
cause=exc,
|
|
) from exc
|
|
except urllib.error.HTTPError as exc:
|
|
body = ""
|
|
try:
|
|
body = exc.read().decode()
|
|
except Exception:
|
|
pass
|
|
|
|
if exc.code == 429:
|
|
raise LLMRateLimitError(
|
|
f"Rate limited (429) from {url}",
|
|
status_code=429,
|
|
response_body=body,
|
|
cause=exc,
|
|
) from exc
|
|
|
|
raise LLMAPIError(
|
|
f"HTTP {exc.code} from {url}",
|
|
status_code=exc.code,
|
|
response_body=body,
|
|
cause=exc,
|
|
) from exc
|
|
except urllib.error.URLError as exc:
|
|
if "timed out" in str(exc.reason):
|
|
raise LLMTimeoutError(
|
|
f"Request to {url} timed out after {timeout}s",
|
|
cause=exc,
|
|
) from exc
|
|
raise LLMAPIError(
|
|
f"URL error for {url}: {exc.reason}",
|
|
cause=exc,
|
|
) from exc
|
|
except TimeoutError as exc:
|
|
raise LLMTimeoutError(
|
|
f"Request to {url} timed out after {timeout}s",
|
|
cause=exc,
|
|
) from exc
|