runapi-core 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ """RunAPI Python SDK — shared core.
2
+
3
+ Configuration, errors, models, HTTP transport, polling, and the file upload
4
+ client shared by every per-model-line package.
5
+ """
6
+
7
+ from . import config, errors, polling
8
+ from .auth import resolve_api_key
9
+ from .config import configure
10
+ from .contract_gen import CONTRACT
11
+ from .errors import (
12
+ AuthenticationError,
13
+ ConflictError,
14
+ Error,
15
+ InsufficientCreditsError,
16
+ NetworkError,
17
+ NotFoundError,
18
+ RateLimitError,
19
+ ServerError,
20
+ ServiceUnavailableError,
21
+ TaskFailedError,
22
+ TaskTimeoutError,
23
+ TimeoutError,
24
+ ValidationError,
25
+ error_from_response,
26
+ )
27
+ from .files import FilesClient, UploadResponse
28
+ from .http_client import HttpClient
29
+ from .models import BaseModel, DynamicModel, TaskResponse, optional, required
30
+ from .multipart import MultipartBody, MultipartFile
31
+ from .options import ClientOptions, PollingOptions, RequestOptions
32
+ from .resource import Resource
33
+ from .version import __version__
34
+
35
+ __all__ = [
36
+ "__version__",
37
+ "config",
38
+ "configure",
39
+ "errors",
40
+ "polling",
41
+ "CONTRACT",
42
+ "resolve_api_key",
43
+ "Error",
44
+ "AuthenticationError",
45
+ "InsufficientCreditsError",
46
+ "NotFoundError",
47
+ "ConflictError",
48
+ "ValidationError",
49
+ "RateLimitError",
50
+ "ServiceUnavailableError",
51
+ "ServerError",
52
+ "NetworkError",
53
+ "TimeoutError",
54
+ "TaskTimeoutError",
55
+ "TaskFailedError",
56
+ "error_from_response",
57
+ "ClientOptions",
58
+ "RequestOptions",
59
+ "PollingOptions",
60
+ "BaseModel",
61
+ "DynamicModel",
62
+ "TaskResponse",
63
+ "required",
64
+ "optional",
65
+ "MultipartBody",
66
+ "MultipartFile",
67
+ "HttpClient",
68
+ "Resource",
69
+ "FilesClient",
70
+ "UploadResponse",
71
+ ]
runapi/core/auth.py ADDED
@@ -0,0 +1,36 @@
1
+ """API key resolution."""
2
+
3
+ import os
4
+ from typing import Optional
5
+
6
+ from . import config
7
+ from .errors import AuthenticationError
8
+
9
+ ENV_VAR_NAME = "RUNAPI_API_KEY"
10
+
11
+ MISSING_KEY_MESSAGE = (
12
+ "API key is required. Pass api_key or set the RUNAPI_API_KEY environment variable."
13
+ )
14
+
15
+
16
+ def resolve_api_key(explicit: Optional[str] = None) -> str:
17
+ """Resolve the API key, in priority order:
18
+
19
+ 1. the explicit argument
20
+ 2. the global ``runapi.core`` configuration (``config.api_key``)
21
+ 3. the ``RUNAPI_API_KEY`` environment variable
22
+
23
+ All sources are trimmed; blank values are treated as missing. Raises
24
+ :class:`AuthenticationError` when no source yields a value.
25
+ """
26
+ resolved = _normalize(explicit) or _normalize(config.api_key) or _normalize(os.environ.get(ENV_VAR_NAME))
27
+ if not resolved:
28
+ raise AuthenticationError(MISSING_KEY_MESSAGE)
29
+ return resolved
30
+
31
+
32
+ def _normalize(value: Optional[str]) -> Optional[str]:
33
+ if not isinstance(value, str):
34
+ return None
35
+ trimmed = value.strip()
36
+ return trimmed or None
runapi/core/config.py ADDED
@@ -0,0 +1,23 @@
1
+ """Module-level configuration shared across RunAPI clients.
2
+
3
+ Mirrors Ruby's global ``RunApi.api_key`` / ``RunApi.base_url``. Values are read
4
+ at request time, so ``configure()`` (or assigning these attributes directly)
5
+ affects clients constructed afterwards.
6
+ """
7
+
8
+ import sys
9
+ from typing import Optional
10
+
11
+ from .constants import DEFAULT_BASE_URL
12
+
13
+ api_key: Optional[str] = None
14
+ base_url: str = DEFAULT_BASE_URL
15
+
16
+
17
+ def configure(api_key: Optional[str] = None, base_url: Optional[str] = None) -> None:
18
+ """Set global configuration. Only non-None arguments are applied."""
19
+ module = sys.modules[__name__]
20
+ if api_key is not None:
21
+ module.api_key = api_key
22
+ if base_url is not None:
23
+ module.base_url = base_url
@@ -0,0 +1,17 @@
1
+ from .version import __version__
2
+
3
+ HTTP_REQUEST_TIMEOUT = 900
4
+ POLLING_INTERVAL = 2
5
+ POLLING_MAX_WAIT = 900
6
+
7
+ MAX_RETRIES = 2
8
+ RETRY_BASE_DELAY = 0.5
9
+ RETRY_MAX_DELAY = 5.0
10
+
11
+ DEFAULT_BASE_URL = "https://runapi.ai"
12
+
13
+ SDK_USER_AGENT = f"runapi-sdk-python/{__version__}"
14
+
15
+ IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "PUT", "DELETE", "OPTIONS"})
16
+
17
+ RETRYABLE_STATUS_CODES = frozenset({429, 500, 502, 503, 504})