2025-02-04 19:18:58 -03:00
|
|
|
"""Async Client implementation for the base Codex API."""
|
|
|
|
|
|
2025-01-31 15:49:58 -03:00
|
|
|
from abc import ABC, abstractmethod
|
2025-02-04 19:18:58 -03:00
|
|
|
|
|
|
|
|
from contextlib import asynccontextmanager
|
2025-02-18 15:41:29 -03:00
|
|
|
from typing import IO, AsyncIterator, AsyncGenerator, Optional
|
2025-01-30 18:34:32 -03:00
|
|
|
|
|
|
|
|
import aiohttp
|
2025-02-18 15:41:29 -03:00
|
|
|
from aiohttp import ClientTimeout
|
2025-01-30 18:34:32 -03:00
|
|
|
from urllib3.util import Url
|
|
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
from benchmarks.codex.client.common import Manifest, Cid
|
2025-01-31 15:49:58 -03:00
|
|
|
from benchmarks.core.utils.streams import BaseStreamReader
|
|
|
|
|
|
2025-01-30 18:34:32 -03:00
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
class AsyncCodexClient(ABC):
|
2025-01-31 15:49:58 -03:00
|
|
|
@abstractmethod
|
2025-02-18 15:41:29 -03:00
|
|
|
async def upload(
|
|
|
|
|
self,
|
|
|
|
|
name: str,
|
|
|
|
|
mime_type: str,
|
|
|
|
|
content: IO,
|
|
|
|
|
timeout: Optional[ClientTimeout] = None,
|
|
|
|
|
) -> Cid:
|
2025-01-31 15:49:58 -03:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
2025-02-04 19:18:58 -03:00
|
|
|
async def manifest(self, cid: Cid) -> Manifest:
|
2025-01-31 15:49:58 -03:00
|
|
|
pass
|
|
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
@asynccontextmanager
|
2025-01-31 15:49:58 -03:00
|
|
|
@abstractmethod
|
2025-02-18 15:41:29 -03:00
|
|
|
def download(
|
|
|
|
|
self, cid: Cid, timeout: Optional[ClientTimeout] = None
|
|
|
|
|
) -> AsyncGenerator[BaseStreamReader, None]:
|
2025-01-31 15:49:58 -03:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
class AsyncCodexClientImpl(AsyncCodexClient):
|
2025-01-30 18:34:32 -03:00
|
|
|
"""A lightweight async wrapper built around the Codex REST API."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, codex_api_url: Url):
|
|
|
|
|
self.codex_api_url = codex_api_url
|
|
|
|
|
|
2025-02-18 15:41:29 -03:00
|
|
|
async def upload(
|
|
|
|
|
self,
|
|
|
|
|
name: str,
|
|
|
|
|
mime_type: str,
|
|
|
|
|
content: IO,
|
|
|
|
|
timeout: Optional[ClientTimeout] = None,
|
|
|
|
|
) -> Cid:
|
|
|
|
|
async with aiohttp.ClientSession(timeout=ClientTimeout()) as session:
|
2025-01-30 18:34:32 -03:00
|
|
|
response = await session.post(
|
|
|
|
|
self.codex_api_url._replace(path="/api/codex/v1/data").url,
|
|
|
|
|
headers={
|
|
|
|
|
aiohttp.hdrs.CONTENT_TYPE: mime_type,
|
|
|
|
|
aiohttp.hdrs.CONTENT_DISPOSITION: f'attachment; filename="{name}"',
|
|
|
|
|
},
|
|
|
|
|
data=content,
|
2025-02-18 15:41:29 -03:00
|
|
|
timeout=timeout,
|
2025-01-30 18:34:32 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
return await response.text()
|
|
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
async def manifest(self, cid: Cid) -> Manifest:
|
2025-01-30 18:34:32 -03:00
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
|
response = await session.get(
|
|
|
|
|
self.codex_api_url._replace(
|
|
|
|
|
path=f"/api/codex/v1/data/{cid}/network/manifest"
|
|
|
|
|
).url,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
response_contents = await response.json()
|
|
|
|
|
|
2025-02-12 19:21:34 -03:00
|
|
|
return Manifest.from_codex_api_response(response_contents)
|
2025-01-31 15:49:58 -03:00
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
@asynccontextmanager
|
2025-02-18 15:41:29 -03:00
|
|
|
async def download(
|
|
|
|
|
self, cid: Cid, timeout: Optional[ClientTimeout] = None
|
|
|
|
|
) -> AsyncIterator[BaseStreamReader]:
|
|
|
|
|
async with aiohttp.ClientSession(timeout=ClientTimeout()) as session:
|
2025-01-31 15:49:58 -03:00
|
|
|
response = await session.get(
|
2025-02-04 19:18:58 -03:00
|
|
|
self.codex_api_url._replace(path=f"/api/codex/v1/data/{cid}").url,
|
2025-02-18 15:41:29 -03:00
|
|
|
timeout=timeout,
|
2025-01-31 15:49:58 -03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
2025-02-04 19:18:58 -03:00
|
|
|
yield response.content
|