simulacrum-sdk 0.1.0__tar.gz → 0.2.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulacrum-sdk
3
- Version: 0.1.0
3
+ Version: 0.2.0
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulacrum-sdk"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "Official Python SDK for accessing the Simulacrum API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -12,4 +12,4 @@ Example:
12
12
  from .client import Simulacrum
13
13
 
14
14
  __all__ = ["Simulacrum", "__version__"]
15
- __version__: str = "0.1.0"
15
+ __version__: str = "0.2.0"
@@ -88,7 +88,7 @@ class Simulacrum:
88
88
  request_body: Dict[str, Any] = payload.model_dump()
89
89
  response_data: Dict[str, Any] = send_request(
90
90
  method="POST",
91
- url=f"{self.base_url}/v1/forecast",
91
+ url=f"{self.base_url}/{model}/v1/forecast",
92
92
  headers=self.headers,
93
93
  json=request_body,
94
94
  )
@@ -97,7 +97,7 @@ class Simulacrum:
97
97
  )
98
98
  return validated_response.get_forecast()
99
99
 
100
- def validate(self) -> ValidateAPIKeyResponse:
100
+ def validate(self, model: str = "tempo") -> ValidateAPIKeyResponse:
101
101
  """Validate the configured API key and return its metadata.
102
102
 
103
103
  Returns:
@@ -115,8 +115,19 @@ class Simulacrum:
115
115
  """
116
116
  response_data: Dict[str, Any] = send_request(
117
117
  method="GET",
118
- url=f"{self.base_url}/v1/validate",
118
+ url=f"{self.base_url}/{model}/v1/validate",
119
119
  headers=self.headers,
120
120
  json=None,
121
121
  )
122
- return ValidateAPIKeyResponse.model_validate(response_data)
122
+
123
+ validation: ValidateAPIKeyResponse = ValidateAPIKeyResponse.model_validate(
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
@@ -2,9 +2,10 @@
2
2
 
3
3
  from datetime import datetime
4
4
  from typing import List, Optional, Sequence, Union
5
+ from enum import Enum
5
6
 
6
7
  import numpy as np
7
- from pydantic import BaseModel, field_validator
8
+ from pydantic import BaseModel, field_validator, Field, constr
8
9
 
9
10
 
10
11
  class ForecastRequest(BaseModel):
@@ -57,7 +58,6 @@ class ForecastResponse(BaseModel):
57
58
  """
58
59
 
59
60
  forecast: List[float]
60
- model_used: str
61
61
 
62
62
  def get_forecast(self) -> np.ndarray:
63
63
  """Return forecast values as a numpy array for downstream processing.
@@ -68,15 +68,39 @@ class ForecastResponse(BaseModel):
68
68
  return np.array(self.forecast, dtype=float)
69
69
 
70
70
 
71
- class ValidateAPIKeyResponse(BaseModel):
72
- """Metadata describing the validity of an API key.
71
+ class ApiKeyStatus(str, Enum):
72
+ """Possible statuses for an API key."""
73
73
 
74
- Attributes:
75
- valid (bool): Indicates whether the API key is currently valid.
76
- client (str): Identifier of the owning client account.
77
- expires_at (datetime | None): Expiration timestamp if provided by the API.
78
- """
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
+ class ValidateAPIKeyResponse(BaseModel):
90
+ """Structured representation of an API key validation response."""
79
91
 
80
92
  valid: bool
81
- client: str
82
- expires_at: Optional[datetime]
93
+ key_id: constr(pattern=r"^[A-Za-z0-9]+$")
94
+ status: ApiKeyStatus
95
+ env: constr(pattern=r"^(live|test)$")
96
+ project_id: str
97
+ organization_id: str
98
+ user_id: str
99
+ product_id: str
100
+ name: constr(min_length=1, max_length=255)
101
+ expires_at: Optional[datetime] = None
102
+ rate_limit_per_minute: int = Field(ge=1)
103
+ rate_limit_per_hour: int = Field(ge=1)
104
+
105
+ class Config:
106
+ extra = "forbid"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulacrum-sdk
3
- Version: 0.1.0
3
+ Version: 0.2.0
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
@@ -1,7 +1,12 @@
1
+ from pathlib import Path
2
+ import sys
3
+
1
4
  import numpy as np
2
5
  import pytest
3
6
  from pydantic import ValidationError
4
7
 
8
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
9
+
5
10
  import simulacrum.client as simulacrum_client
6
11
 
7
12
 
@@ -25,8 +30,17 @@ def test_validate_invokes_send_request_and_parses_response(client, monkeypatch):
25
30
  captured["json"] = json
26
31
  return {
27
32
  "valid": True,
28
- "client": "client-123",
33
+ "key_id": "key123",
34
+ "status": "active",
35
+ "env": "test",
36
+ "project_id": "project-123",
37
+ "organization_id": "org-456",
38
+ "user_id": "user-789",
39
+ "product_id": "product-abc",
40
+ "name": "Test Key",
29
41
  "expires_at": "2024-01-01T00:00:00Z",
42
+ "rate_limit_per_minute": 60,
43
+ "rate_limit_per_hour": 1000,
30
44
  }
31
45
 
32
46
  monkeypatch.setattr(simulacrum_client, "send_request", fake_send_request)
@@ -35,12 +49,13 @@ def test_validate_invokes_send_request_and_parses_response(client, monkeypatch):
35
49
 
36
50
  assert captured == {
37
51
  "method": "GET",
38
- "url": "https://api.test/v1/validate",
52
+ "url": "https://api.test/tempo/v1/validate",
39
53
  "headers": client.headers,
40
54
  "json": None,
41
55
  }
42
56
  assert response.valid is True
43
- assert response.client == "client-123"
57
+ assert response.key_id == "key123"
58
+ assert response.status.value == "active"
44
59
 
45
60
 
46
61
  def test_forecast_builds_payload_and_returns_numpy_array(client, monkeypatch):
@@ -82,7 +97,7 @@ def test_forecast_builds_payload_and_returns_numpy_array(client, monkeypatch):
82
97
  assert captured_payload["horizon"] == 2
83
98
  assert captured_payload["model"] == "prophet"
84
99
  assert captured_payload["method"] == "POST"
85
- assert captured_payload["url"] == "https://api.test/v1/forecast"
100
+ assert captured_payload["url"] == "https://api.test/prophet/v1/forecast"
86
101
  assert captured_payload["headers"] == client.headers
87
102
  assert captured_payload["json"] == {
88
103
  "series": [1.0, 2.0, 3.0],
File without changes
File without changes
File without changes