simulacrum-sdk 0.2.0__py3-none-any.whl → 0.2.4__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.
Potentially problematic release.
This version of simulacrum-sdk might be problematic. Click here for more details.
- simulacrum/__init__.py +1 -1
- simulacrum/_errors.py +102 -0
- simulacrum/api.py +25 -28
- simulacrum/client.py +4 -12
- simulacrum/exceptions.py +96 -19
- simulacrum/models.py +40 -39
- {simulacrum_sdk-0.2.0.dist-info → simulacrum_sdk-0.2.4.dist-info}/METADATA +2 -2
- simulacrum_sdk-0.2.4.dist-info/RECORD +12 -0
- simulacrum_sdk-0.2.0.dist-info/RECORD +0 -11
- {simulacrum_sdk-0.2.0.dist-info → simulacrum_sdk-0.2.4.dist-info}/WHEEL +0 -0
- {simulacrum_sdk-0.2.0.dist-info → simulacrum_sdk-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {simulacrum_sdk-0.2.0.dist-info → simulacrum_sdk-0.2.4.dist-info}/top_level.txt +0 -0
simulacrum/__init__.py
CHANGED
simulacrum/_errors.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from .exceptions import (
|
|
7
|
+
ConflictError,
|
|
8
|
+
ForbiddenError,
|
|
9
|
+
HTTPClientError,
|
|
10
|
+
NotFoundError,
|
|
11
|
+
RateLimitError,
|
|
12
|
+
ServerError,
|
|
13
|
+
SimulacrumError,
|
|
14
|
+
UnauthorizedError,
|
|
15
|
+
ValidationError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _parse_retry_after(value: str) -> Optional[float]:
|
|
20
|
+
try:
|
|
21
|
+
return float(value)
|
|
22
|
+
except Exception:
|
|
23
|
+
try:
|
|
24
|
+
dt = datetime.strptime(value, "%a, %d %b %Y %H:%M:%S %Z")
|
|
25
|
+
return max(
|
|
26
|
+
0.0,
|
|
27
|
+
(dt.replace(tzinfo=timezone.utc) - datetime.now(timezone.utc)).total_seconds(),
|
|
28
|
+
)
|
|
29
|
+
except Exception:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def parse_error(response, *, service: str, endpoint: str) -> SimulacrumError:
|
|
34
|
+
status = getattr(response, "status_code")
|
|
35
|
+
headers = getattr(response, "headers", {}) or {}
|
|
36
|
+
req_id = headers.get("X-Request-ID")
|
|
37
|
+
|
|
38
|
+
error_type = "http_error"
|
|
39
|
+
message = f"HTTP {status}"
|
|
40
|
+
details = None
|
|
41
|
+
trace_id = None
|
|
42
|
+
|
|
43
|
+
# Try parse JSON body according to ErrorEnvelope
|
|
44
|
+
try:
|
|
45
|
+
data = response.json()
|
|
46
|
+
err = (data or {}).get("error") or {}
|
|
47
|
+
error_type = str(err.get("type") or error_type)
|
|
48
|
+
message = str(err.get("message") or message)
|
|
49
|
+
details = err.get("details")
|
|
50
|
+
trace_id = (data or {}).get("trace_id")
|
|
51
|
+
except Exception:
|
|
52
|
+
try:
|
|
53
|
+
text = response.text
|
|
54
|
+
except Exception:
|
|
55
|
+
text = None
|
|
56
|
+
if text:
|
|
57
|
+
message = str(text).strip()[:500]
|
|
58
|
+
|
|
59
|
+
common = dict(
|
|
60
|
+
service=service,
|
|
61
|
+
endpoint=endpoint,
|
|
62
|
+
status_code=status,
|
|
63
|
+
error_type=error_type,
|
|
64
|
+
message=message,
|
|
65
|
+
details=details,
|
|
66
|
+
trace_id_body=trace_id,
|
|
67
|
+
request_id_header=req_id,
|
|
68
|
+
response=response,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if status == 401:
|
|
72
|
+
return UnauthorizedError(**common)
|
|
73
|
+
if status == 403:
|
|
74
|
+
return ForbiddenError(**common)
|
|
75
|
+
if status == 404:
|
|
76
|
+
return NotFoundError(**common)
|
|
77
|
+
if status == 409:
|
|
78
|
+
return ConflictError(**common)
|
|
79
|
+
if status in (400, 422):
|
|
80
|
+
return ValidationError(**common)
|
|
81
|
+
if status == 429:
|
|
82
|
+
ra = headers.get("Retry-After")
|
|
83
|
+
return RateLimitError(**common, retry_after_seconds=_parse_retry_after(ra) if ra else None)
|
|
84
|
+
if 500 <= status <= 599:
|
|
85
|
+
return ServerError(**common)
|
|
86
|
+
if 400 <= status <= 499:
|
|
87
|
+
return HTTPClientError(**common)
|
|
88
|
+
return HTTPClientError(**common)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def is_retryable(exc: SimulacrumError) -> bool:
|
|
92
|
+
return exc.status_code in {429, 500, 502, 503, 504}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def retry_after(exc: RateLimitError) -> Optional[float]:
|
|
96
|
+
return getattr(exc, "retry_after_seconds", None)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def extract_trace_id(exc: SimulacrumError) -> Optional[str]:
|
|
100
|
+
return exc.trace_id()
|
|
101
|
+
|
|
102
|
+
|
simulacrum/api.py
CHANGED
|
@@ -2,47 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, Mapping, Optional
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import uuid
|
|
6
|
+
import httpx
|
|
6
7
|
|
|
7
|
-
from simulacrum.
|
|
8
|
+
from simulacrum._errors import parse_error
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _infer_service_from_url(url: str) -> str:
|
|
12
|
+
lowered = url.lower()
|
|
13
|
+
if "/onsiteiq/" in lowered:
|
|
14
|
+
return "onsiteiq"
|
|
15
|
+
if "/tempo/" in lowered:
|
|
16
|
+
return "tempo"
|
|
17
|
+
return "tempo"
|
|
8
18
|
|
|
9
19
|
|
|
10
20
|
def send_request(method: str, url: str, headers: Mapping[str, str], json: Optional[Mapping[str, Any]]) -> Dict[str, Any]:
|
|
11
|
-
"""Execute an HTTP request against the Simulacrum API and
|
|
21
|
+
"""Execute an HTTP request against the Simulacrum API and raise rich typed errors on failure.
|
|
12
22
|
|
|
13
23
|
Args:
|
|
14
|
-
method (str): HTTP method to invoke (
|
|
24
|
+
method (str): HTTP method to invoke ("GET", "POST", ...).
|
|
15
25
|
url (str): Fully-qualified endpoint URL.
|
|
16
26
|
headers (Mapping[str, str]): HTTP headers that include authorization and content type.
|
|
17
27
|
json (Mapping[str, Any] | None): JSON-serialisable payload for the request body.
|
|
18
28
|
|
|
19
29
|
Returns:
|
|
20
30
|
dict[str, Any]: Parsed JSON payload returned by the API.
|
|
21
|
-
|
|
22
|
-
Raises:
|
|
23
|
-
AuthError: Raised when the API reports an authentication failure.
|
|
24
|
-
ApiError: Raised for all other non-success responses or malformed data.
|
|
25
31
|
"""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
try:
|
|
30
|
-
data: Dict[str, Any] = response.json()
|
|
31
|
-
error_code: Optional[str] = data.get("error_code")
|
|
32
|
-
message: str = data.get("message", "Unknown error")
|
|
32
|
+
# Ensure we always send an X-Request-ID
|
|
33
|
+
merged_headers: Dict[str, str] = dict(headers)
|
|
34
|
+
merged_headers.setdefault("X-Request-ID", str(uuid.uuid4()))
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
with httpx.Client(timeout=30) as client:
|
|
37
|
+
response = client.request(method=method, url=url, headers=merged_headers, json=json)
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
raise ApiError(f"API error {response.status_code}: {message}")
|
|
41
|
-
|
|
42
|
-
except ValueError as exc:
|
|
43
|
-
raise ApiError(f"Unexpected API error: {response.text}") from exc
|
|
44
|
-
|
|
45
|
-
try:
|
|
39
|
+
if 200 <= response.status_code < 300:
|
|
40
|
+
# Let JSON decode errors propagate as ValueError to be consistent with prior behavior
|
|
46
41
|
return response.json() # type: ignore[return-value]
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
|
|
43
|
+
service = _infer_service_from_url(url)
|
|
44
|
+
endpoint = httpx.URL(url).path
|
|
45
|
+
raise parse_error(response, service=service, endpoint=endpoint)
|
simulacrum/client.py
CHANGED
|
@@ -41,7 +41,7 @@ class Simulacrum:
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
def forecast(
|
|
44
|
-
self, series: Sequence[float] | np.ndarray, horizon: int, model: str = "default"
|
|
44
|
+
self, series: Sequence[float] | np.ndarray, horizon: int | None = None, model: str = "default"
|
|
45
45
|
) -> np.ndarray:
|
|
46
46
|
"""Request a forecast for the provided time series.
|
|
47
47
|
|
|
@@ -86,6 +86,8 @@ class Simulacrum:
|
|
|
86
86
|
series=series_to_send, horizon=horizon, model=model
|
|
87
87
|
)
|
|
88
88
|
request_body: Dict[str, Any] = payload.model_dump()
|
|
89
|
+
# Exclude optional fields that are None (e.g., horizon for onsiteiq)
|
|
90
|
+
request_body = payload.model_dump(exclude_none=True)
|
|
89
91
|
response_data: Dict[str, Any] = send_request(
|
|
90
92
|
method="POST",
|
|
91
93
|
url=f"{self.base_url}/{model}/v1/forecast",
|
|
@@ -120,14 +122,4 @@ class Simulacrum:
|
|
|
120
122
|
json=None,
|
|
121
123
|
)
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
response_data
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
status_message = "valid" if validation.valid else "invalid"
|
|
128
|
-
print(
|
|
129
|
-
"Simulacrum API key validation: "
|
|
130
|
-
f"{status_message} (status={validation.status.value}, env={validation.env}) "
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
return validation
|
|
125
|
+
return ValidateAPIKeyResponse.model_validate(response_data)
|
simulacrum/exceptions.py
CHANGED
|
@@ -1,42 +1,119 @@
|
|
|
1
|
-
"""Custom exceptions raised by the Simulacrum SDK.
|
|
1
|
+
"""Custom exceptions raised by the Simulacrum SDK.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This module defines a rich, typed hierarchy aligned with Simulacrum services' ErrorEnvelope
|
|
4
|
+
and preserves backward compatibility with previously exported exception names.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, Optional, Type
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
class SimulacrumError(Exception):
|
|
7
|
-
"""Base exception for all SDK errors."""
|
|
13
|
+
"""Base exception for all SDK errors with structured fields."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
*,
|
|
18
|
+
service: str,
|
|
19
|
+
endpoint: str,
|
|
20
|
+
status_code: int,
|
|
21
|
+
error_type: str,
|
|
22
|
+
message: str,
|
|
23
|
+
details: Optional[Dict[str, Any]] = None,
|
|
24
|
+
trace_id_body: Optional[str] = None,
|
|
25
|
+
request_id_header: Optional[str] = None,
|
|
26
|
+
response: Any = None,
|
|
27
|
+
context: Optional[Dict[str, Any]] = None,
|
|
28
|
+
) -> None:
|
|
29
|
+
super().__init__(message)
|
|
30
|
+
self.service = service
|
|
31
|
+
self.endpoint = endpoint
|
|
32
|
+
self.status_code = status_code
|
|
33
|
+
self.error_type = error_type
|
|
34
|
+
self.message = message
|
|
35
|
+
self.details = details
|
|
36
|
+
self.trace_id_body = trace_id_body
|
|
37
|
+
self.request_id_header = request_id_header
|
|
38
|
+
self.response = response
|
|
39
|
+
self.context = context or {}
|
|
40
|
+
|
|
41
|
+
def trace_id(self) -> Optional[str]:
|
|
42
|
+
return self.trace_id_body or self.request_id_header
|
|
43
|
+
|
|
44
|
+
def __str__(self) -> str:
|
|
45
|
+
tid = self.trace_id()
|
|
46
|
+
tid_str = f" trace_id={tid}" if tid else ""
|
|
47
|
+
return f"[{self.service}] {self.status_code} {self.error_type}: {self.message}{tid_str}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UnauthorizedError(SimulacrumError):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ForbiddenError(SimulacrumError):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class NotFoundError(SimulacrumError):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ConflictError(SimulacrumError):
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ValidationError(SimulacrumError):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RateLimitError(SimulacrumError):
|
|
71
|
+
def __init__(
|
|
72
|
+
self, *args, retry_after_seconds: Optional[float] = None, **kwargs: Any
|
|
73
|
+
) -> None:
|
|
74
|
+
super().__init__(*args, **kwargs)
|
|
75
|
+
self.retry_after_seconds = retry_after_seconds
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ServerError(SimulacrumError):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class HTTPClientError(SimulacrumError):
|
|
83
|
+
pass
|
|
8
84
|
|
|
9
85
|
|
|
10
|
-
|
|
11
|
-
|
|
86
|
+
# Backward compatibility aliases/classes
|
|
87
|
+
class AuthError(UnauthorizedError):
|
|
88
|
+
"""Raised when authentication with the API fails (back-compat)."""
|
|
12
89
|
|
|
13
90
|
|
|
14
|
-
class ApiKeyExpiredError(
|
|
15
|
-
"""Raised when the API key has expired."""
|
|
91
|
+
class ApiKeyExpiredError(ForbiddenError):
|
|
92
|
+
"""Raised when the API key has expired (back-compat)."""
|
|
16
93
|
|
|
17
94
|
|
|
18
|
-
class ApiKeyInactiveError(
|
|
19
|
-
"""Raised when the API key has been deactivated."""
|
|
95
|
+
class ApiKeyInactiveError(ForbiddenError):
|
|
96
|
+
"""Raised when the API key has been deactivated (back-compat)."""
|
|
20
97
|
|
|
21
98
|
|
|
22
|
-
class ApiKeyInvalidError(
|
|
23
|
-
"""Raised when the API key is not recognised."""
|
|
99
|
+
class ApiKeyInvalidError(UnauthorizedError):
|
|
100
|
+
"""Raised when the API key is not recognised (back-compat)."""
|
|
24
101
|
|
|
25
102
|
|
|
26
|
-
class ForecastAlreadyRunningError(
|
|
27
|
-
"""Raised when a forecast job is already in progress."""
|
|
103
|
+
class ForecastAlreadyRunningError(ConflictError):
|
|
104
|
+
"""Raised when a forecast job is already in progress (back-compat)."""
|
|
28
105
|
|
|
29
106
|
|
|
30
|
-
class InvalidRequestError(
|
|
31
|
-
"""Raised when the request payload is malformed."""
|
|
107
|
+
class InvalidRequestError(ValidationError):
|
|
108
|
+
"""Raised when the request payload is malformed (back-compat)."""
|
|
32
109
|
|
|
33
110
|
|
|
34
|
-
class QuotaExceededError(
|
|
35
|
-
"""Raised when the API usage quota has been exhausted."""
|
|
111
|
+
class QuotaExceededError(RateLimitError):
|
|
112
|
+
"""Raised when the API usage quota has been exhausted (back-compat)."""
|
|
36
113
|
|
|
37
114
|
|
|
38
|
-
class ApiError(
|
|
39
|
-
"""Catch-all for unclassified API errors."""
|
|
115
|
+
class ApiError(HTTPClientError):
|
|
116
|
+
"""Catch-all for unclassified API errors (back-compat)."""
|
|
40
117
|
|
|
41
118
|
|
|
42
119
|
ERROR_CODE_MAP: Dict[str, Type[SimulacrumError]] = {
|
simulacrum/models.py
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import List, Optional, Sequence, Union
|
|
5
|
-
from enum import Enum
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
|
-
from pydantic import BaseModel, field_validator,
|
|
7
|
+
from pydantic import BaseModel, field_validator, model_validator
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class ForecastRequest(BaseModel):
|
|
@@ -13,7 +12,7 @@ class ForecastRequest(BaseModel):
|
|
|
13
12
|
|
|
14
13
|
Attributes:
|
|
15
14
|
series (list[float]): Historical observations used as forecast input.
|
|
16
|
-
horizon (int): Number of future periods to predict.
|
|
15
|
+
horizon (int | None): Number of future periods to predict. Optional for the ``"onsiteiq"`` model.
|
|
17
16
|
model (str | None): Identifier of the forecasting model, defaults to ``"default"``.
|
|
18
17
|
|
|
19
18
|
Example:
|
|
@@ -23,8 +22,9 @@ class ForecastRequest(BaseModel):
|
|
|
23
22
|
{'series': [1.0, 2.0, 3.0], 'horizon': 2, 'model': 'default'}
|
|
24
23
|
"""
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
# TODO: It must be List[List[float]] (num_steps, num_series) remove after refactoring
|
|
26
|
+
series: Union[List[float], List[List[float]]]
|
|
27
|
+
horizon: Optional[int] = None
|
|
28
28
|
model: Optional[str] = "default"
|
|
29
29
|
|
|
30
30
|
@field_validator("series", mode="before")
|
|
@@ -42,6 +42,23 @@ class ForecastRequest(BaseModel):
|
|
|
42
42
|
return value.astype(float).tolist()
|
|
43
43
|
return list(value)
|
|
44
44
|
|
|
45
|
+
@model_validator(mode="after")
|
|
46
|
+
def _validate_horizon_requirement(self):
|
|
47
|
+
# Horizon is required for all models except onsiteiq
|
|
48
|
+
model_name = (self.model or "").lower()
|
|
49
|
+
if model_name != "onsiteiq" and self.horizon is None:
|
|
50
|
+
raise ValueError("horizon is required and must be a positive integer for this model")
|
|
51
|
+
|
|
52
|
+
# When provided, horizon must be an integer >= 1
|
|
53
|
+
if self.horizon is not None:
|
|
54
|
+
if not isinstance(self.horizon, int):
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"horizon must be an integer (got {type(self.horizon).__name__})"
|
|
57
|
+
)
|
|
58
|
+
if self.horizon < 1:
|
|
59
|
+
raise ValueError("horizon must be >= 1")
|
|
60
|
+
return self
|
|
61
|
+
|
|
45
62
|
|
|
46
63
|
class ForecastResponse(BaseModel):
|
|
47
64
|
"""Forecast output returned by the API.
|
|
@@ -57,7 +74,7 @@ class ForecastResponse(BaseModel):
|
|
|
57
74
|
[4.2, 4.8]
|
|
58
75
|
"""
|
|
59
76
|
|
|
60
|
-
forecast: List[float]
|
|
77
|
+
forecast: Union[List[float], List[List[float]]]
|
|
61
78
|
|
|
62
79
|
def get_forecast(self) -> np.ndarray:
|
|
63
80
|
"""Return forecast values as a numpy array for downstream processing.
|
|
@@ -68,39 +85,23 @@ class ForecastResponse(BaseModel):
|
|
|
68
85
|
return np.array(self.forecast, dtype=float)
|
|
69
86
|
|
|
70
87
|
|
|
71
|
-
class ApiKeyStatus(str, Enum):
|
|
72
|
-
"""Possible statuses for an API key."""
|
|
73
|
-
|
|
74
|
-
active = "active"
|
|
75
|
-
deprecated = "deprecated"
|
|
76
|
-
disabled = "disabled"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class ApiKeyStatusHistoryEntry(BaseModel):
|
|
80
|
-
"""API key status history entry."""
|
|
81
|
-
|
|
82
|
-
status: ApiKeyStatus
|
|
83
|
-
changed_at: datetime
|
|
84
|
-
|
|
85
|
-
class Config:
|
|
86
|
-
extra = "forbid"
|
|
87
|
-
|
|
88
|
-
|
|
89
88
|
class ValidateAPIKeyResponse(BaseModel):
|
|
90
|
-
"""
|
|
89
|
+
"""Metadata describing the validity of an API key.
|
|
90
|
+
|
|
91
|
+
Attributes:
|
|
92
|
+
valid (bool): Indicates whether the API key is currently valid.
|
|
93
|
+
client (str): Identifier of the owning client account.
|
|
94
|
+
expires_at (datetime | None): Expiration timestamp if provided by the API.
|
|
95
|
+
"""
|
|
91
96
|
|
|
92
97
|
valid: bool
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
rate_limit_per_hour: int = Field(ge=1)
|
|
104
|
-
|
|
105
|
-
class Config:
|
|
106
|
-
extra = "forbid"
|
|
98
|
+
client: Optional[str] = None
|
|
99
|
+
key_id: Optional[str] = None
|
|
100
|
+
expires_at: Optional[datetime]
|
|
101
|
+
|
|
102
|
+
@model_validator(mode="after")
|
|
103
|
+
def _coalesce_client(self):
|
|
104
|
+
# Prefer explicit client; fall back to key_id when provided by API
|
|
105
|
+
if not self.client and self.key_id:
|
|
106
|
+
self.client = self.key_id
|
|
107
|
+
return self
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simulacrum-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Official Python SDK for accessing the Simulacrum API.
|
|
5
5
|
Author-email: "Simulacrum, Inc." <support@smlcrm.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
22
22
|
Requires-Python: >=3.8
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: httpx<1,>=0.27
|
|
26
26
|
Requires-Dist: pydantic<3,>=2.0
|
|
27
27
|
Requires-Dist: numpy>=1.24
|
|
28
28
|
Provides-Extra: dev
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
simulacrum/__init__.py,sha256=RUetyuqgZ35UgPPcKHJAxqyh8aNkuB8AMWIevCc3iew,437
|
|
2
|
+
simulacrum/_errors.py,sha256=lE6I8c4uL7HC6orHBJ5B1kz7x5NKDHd3mP2yjpQjeIg,2803
|
|
3
|
+
simulacrum/api.py,sha256=dj1xF6KfBLHMML0AicnoHElRzaNjPxooQxBARX219c0,1645
|
|
4
|
+
simulacrum/client.py,sha256=uV3OlCCetni-O3-ESYtQcT8SR3MMj5MercatbH6YkrI,5026
|
|
5
|
+
simulacrum/config.py,sha256=n3iN-cVUF3ucINS3iydDgMnFoMtVxDeVBIQwGiMKNk8,157
|
|
6
|
+
simulacrum/exceptions.py,sha256=YrqoQjaPcZI31PmXd5evOl4fQjzxmz2sc8a3dSzkAMI,3408
|
|
7
|
+
simulacrum/models.py,sha256=_lRcIc0kSwrlTkJmsfj2SK9O2fB7oQz9FACbS_s8B2o,3906
|
|
8
|
+
simulacrum_sdk-0.2.4.dist-info/licenses/LICENSE,sha256=A7B9zAs2uCAzJoZPPyJW0G86yM-BTyum90vD4nSsOe0,1084
|
|
9
|
+
simulacrum_sdk-0.2.4.dist-info/METADATA,sha256=hPAAYiBNWUrnvozwCrCGsir1c8M1sS9Nxagbi0iQVjk,6326
|
|
10
|
+
simulacrum_sdk-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
simulacrum_sdk-0.2.4.dist-info/top_level.txt,sha256=CLUzfwEa7vZDf6zBAH8eXC2lZLMtj92MlXHOc3-V16o,11
|
|
12
|
+
simulacrum_sdk-0.2.4.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
simulacrum/__init__.py,sha256=yzQa-yO8unFv9uKF0gj-o3VoYT_t51-SAatr-KTKyzk,437
|
|
2
|
-
simulacrum/api.py,sha256=IZ8qNikv81Wd2l3VuaiGcLTjLOIzoxMfrbkQLwK0usg,1889
|
|
3
|
-
simulacrum/client.py,sha256=kjPnb0nAuf9djDCR8DNJ3VtPASrr7aFGDx6upN48ygk,5183
|
|
4
|
-
simulacrum/config.py,sha256=n3iN-cVUF3ucINS3iydDgMnFoMtVxDeVBIQwGiMKNk8,157
|
|
5
|
-
simulacrum/exceptions.py,sha256=6VjdyJaFx9c8nE8iIezJH6pWkdRBHRs5ORdEZUrbJmg,1289
|
|
6
|
-
simulacrum/models.py,sha256=V14jf_dcRGaBbYyKdy8c9w0yDLD5yqqE5qYvgG0ocTk,3157
|
|
7
|
-
simulacrum_sdk-0.2.0.dist-info/licenses/LICENSE,sha256=A7B9zAs2uCAzJoZPPyJW0G86yM-BTyum90vD4nSsOe0,1084
|
|
8
|
-
simulacrum_sdk-0.2.0.dist-info/METADATA,sha256=gFjEmlvV5UALjRP2M5NZpzm6sdIJQdKBlka8cd5NPmY,6329
|
|
9
|
-
simulacrum_sdk-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
simulacrum_sdk-0.2.0.dist-info/top_level.txt,sha256=CLUzfwEa7vZDf6zBAH8eXC2lZLMtj92MlXHOc3-V16o,11
|
|
11
|
-
simulacrum_sdk-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|