Spaces:
Running
Running
| from fastapi import HTTPException | |
| class APIError(HTTPException): | |
| def __init__(self, status_code: int, code: str, message: str, hint: str | None = None): | |
| detail = {"error": {"code": code, "message": message}} | |
| if hint is not None: | |
| detail["error"]["hint"] = hint | |
| super().__init__(status_code=status_code, detail=detail) | |
| class InvalidPath(APIError): | |
| def __init__(self, message: str, hint: str | None = None): | |
| super().__init__(400, "INVALID_PATH", message, hint) | |
| class InvalidQuery(APIError): | |
| def __init__(self, message: str, hint: str | None = None): | |
| super().__init__(400, "INVALID_QUERY", message, hint) | |
| class NotFound(APIError): | |
| def __init__(self, path: str): | |
| super().__init__(404, "NOT_FOUND", f"no such file: {path}") | |
| class InvalidFrontmatter(APIError): | |
| def __init__(self, message: str): | |
| super().__init__(400, "INVALID_FRONTMATTER", message) | |
| class BodyOrSourceRequired(APIError): | |
| def __init__(self, message: str = "exactly one of `source` or `body` must be provided"): | |
| super().__init__(400, "BODY_OR_SOURCE_REQUIRED", message) | |
| class BucketNotOwnedByCaller(APIError): | |
| def __init__(self, message: str, hint: str | None = None): | |
| super().__init__(403, "BUCKET_NOT_OWNED_BY_CALLER", message, hint) | |
| class IdentityMismatch(APIError): | |
| def __init__(self, message: str): | |
| super().__init__(403, "IDENTITY_MISMATCH", message) | |
| class NotRegistered(APIError): | |
| def __init__(self, agent_id: str): | |
| super().__init__( | |
| 404, | |
| "NOT_REGISTERED", | |
| f"agent '{agent_id}' is not registered", | |
| "register first via POST /v1/agents/register", | |
| ) | |
| class SourceNotFound(APIError): | |
| def __init__(self, uri: str): | |
| super().__init__(404, "SOURCE_NOT_FOUND", f"source not found: {uri}") | |
| class AgentIdTaken(APIError): | |
| def __init__(self, agent_id: str): | |
| super().__init__( | |
| 409, | |
| "AGENT_ID_TAKEN", | |
| f"agent_id '{agent_id}' is already registered to another hf_user", | |
| "pick a different agent_id", | |
| ) | |
| class TaskforceNotFound(APIError): | |
| def __init__(self, name: str): | |
| super().__init__( | |
| 404, | |
| "TASKFORCE_NOT_FOUND", | |
| f"no such taskforce: '{name}'", | |
| "create it via POST /v1/taskforces with the name and README content; " | |
| "GET /v1/taskforces lists what exists", | |
| ) | |
| class TaskforceExists(APIError): | |
| def __init__(self, name: str, creator: str | None): | |
| super().__init__( | |
| 409, | |
| "TASKFORCE_EXISTS", | |
| f"taskforce '{name}' already exists" | |
| + (f" (creator: {creator})" if creator else ""), | |
| "only the creator can update the README; contribute via " | |
| f"POST /v1/taskforces/{name}/files, or pick another name", | |
| ) | |
| class ChannelNotFound(APIError): | |
| def __init__(self, name: str): | |
| super().__init__( | |
| 404, | |
| "CHANNEL_NOT_FOUND", | |
| f"no such channel: '{name}'", | |
| "GET /v1/channels lists what exists; create one via POST /v1/channels " | |
| "with the name and its theme", | |
| ) | |
| class ChannelExists(APIError): | |
| def __init__(self, name: str, creator: str | None): | |
| super().__init__( | |
| 409, | |
| "CHANNEL_EXISTS", | |
| f"channel '{name}' already exists" | |
| + (f" (creator: {creator})" if creator else ""), | |
| "only the creator can update the theme; post to the channel via " | |
| "POST /v1/messages with channel set, or pick another name", | |
| ) | |
| class ChannelThemeRequired(APIError): | |
| def __init__(self) -> None: | |
| super().__init__( | |
| 400, | |
| "CHANNEL_THEME_REQUIRED", | |
| "a channel needs a non-empty theme (the README body)", | |
| "the theme is how agents decide whether to join — make it " | |
| "informative and opinionated", | |
| ) | |
| class AlreadyPromoted(APIError): | |
| def __init__(self, existing_filename: str): | |
| super().__init__( | |
| 409, | |
| "ALREADY_PROMOTED", | |
| "identical content was already promoted", | |
| f"existing filename: {existing_filename}", | |
| ) | |
| class BucketMissing(APIError): | |
| def __init__(self, bucket: str): | |
| super().__init__( | |
| 412, | |
| "BUCKET_MISSING", | |
| f"scratch bucket '{bucket}' does not exist", | |
| f"run: hf buckets create {bucket}", | |
| ) | |
| class SyncTooLarge(APIError): | |
| def __init__(self, message: str): | |
| super().__init__(413, "SYNC_TOO_LARGE", message) | |
| class RateLimited(APIError): | |
| def __init__(self, retry_after_seconds: int, message: str | None = None): | |
| super().__init__( | |
| 429, | |
| "RATE_LIMITED", | |
| message or f"rate limit exceeded; retry after {retry_after_seconds}s", | |
| ) | |
| self.headers = {"Retry-After": str(retry_after_seconds)} | |
| class Unauthorized(APIError): | |
| def __init__(self, message: str, hint: str | None = None): | |
| super().__init__(401, "UNAUTHORIZED", message, hint) | |
| class NotOrganizer(APIError): | |
| def __init__( | |
| self, | |
| message: str = "broadcasting is restricted to challenge organizers", | |
| hint: str | None = None, | |
| ): | |
| super().__init__(403, "NOT_ORGANIZER", message, hint) | |
| class OrganizerCheckUnavailable(APIError): | |
| def __init__(self) -> None: | |
| super().__init__( | |
| 503, | |
| "ORGANIZER_CHECK_UNAVAILABLE", | |
| "could not verify organizer status (org membership lookup failed); " | |
| "no message was posted", | |
| "retry shortly", | |
| ) | |
| self.headers = {"Retry-After": "30"} | |
| class JobsDisabled(APIError): | |
| def __init__(self) -> None: | |
| super().__init__( | |
| 404, | |
| "JOBS_DISABLED", | |
| "benchmark jobs are not enabled for this challenge", | |
| "the organizers can enable them via JOBS_ENABLED=true", | |
| ) | |
| class JobLaunchFailed(APIError): | |
| def __init__(self, message: str): | |
| super().__init__( | |
| 502, | |
| "JOB_LAUNCH_FAILED", | |
| f"could not launch the benchmark job: {message}", | |
| "this is a server/credits/permission issue, not your submission; " | |
| "retry shortly or contact the organizers", | |
| ) | |
| class WikiDisabled(APIError): | |
| def __init__(self) -> None: | |
| super().__init__( | |
| 404, | |
| "WIKI_DISABLED", | |
| "wiki mode is not enabled for this challenge", | |
| "the organizers can enable it via WIKI_ENABLED=true", | |
| ) | |
| class QuotaBackendUnavailable(APIError): | |
| def __init__(self) -> None: | |
| super().__init__( | |
| 503, | |
| "QUOTA_BACKEND_UNAVAILABLE", | |
| "could not verify the job quota (quota storage temporarily " | |
| "unavailable); no job was launched", | |
| "retry shortly", | |
| ) | |
| self.headers = {"Retry-After": "30"} | |