syvain-metrics-api-client 0.0.57__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.
- syvain_metrics_api_client-0.0.57/PKG-INFO +12 -0
- syvain_metrics_api_client-0.0.57/pyproject.toml +16 -0
- syvain_metrics_api_client-0.0.57/src/syvain_metrics_api_client/__init__.py +99 -0
- syvain_metrics_api_client-0.0.57/src/syvain_metrics_api_client/auth.py +140 -0
- syvain_metrics_api_client-0.0.57/src/syvain_metrics_api_client/client.py +869 -0
- syvain_metrics_api_client-0.0.57/src/syvain_metrics_api_client/py.typed +1 -0
- syvain_metrics_api_client-0.0.57/src/syvain_metrics_api_client/types.py +386 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: syvain-metrics-api-client
|
|
3
|
+
Version: 0.0.57
|
|
4
|
+
Summary: Typed Python client for the Syvain Metrics REST API
|
|
5
|
+
Requires-Dist: niquests>=3.16.0
|
|
6
|
+
Requires-Dist: pydantic>=2.13.3
|
|
7
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
8
|
+
Requires-Dist: pytest>=8.0.0 ; extra == 'dev'
|
|
9
|
+
Requires-Dist: ruff>=0.15.12 ; extra == 'dev'
|
|
10
|
+
Requires-Dist: ty>=0.0.34 ; extra == 'dev'
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Provides-Extra: dev
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "syvain-metrics-api-client"
|
|
3
|
+
version = "0.0.57"
|
|
4
|
+
description = "Typed Python client for the Syvain Metrics REST API"
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = ["niquests>=3.16.0", "pydantic>=2.13.3", "typing-extensions>=4.15.0"]
|
|
7
|
+
|
|
8
|
+
[project.optional-dependencies]
|
|
9
|
+
dev = ["pytest>=8.0.0", "ruff>=0.15.12", "ty>=0.0.34"]
|
|
10
|
+
|
|
11
|
+
[tool.pytest.ini_options]
|
|
12
|
+
testpaths = ["tests"]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["uv_build>=0.11.9,<0.12"]
|
|
16
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from syvain_metrics_api_client.auth import (
|
|
2
|
+
ClientCredentials,
|
|
3
|
+
MetricsApiAuthError,
|
|
4
|
+
cli_auth_path,
|
|
5
|
+
load_cli_auth,
|
|
6
|
+
normalize_metrics_host,
|
|
7
|
+
)
|
|
8
|
+
from syvain_metrics_api_client.client import (
|
|
9
|
+
MetricsApiError,
|
|
10
|
+
MetricsApiRequestError,
|
|
11
|
+
MetricsApiResponseError,
|
|
12
|
+
SyvainMetricsApiClient,
|
|
13
|
+
)
|
|
14
|
+
from syvain_metrics_api_client.types import (
|
|
15
|
+
Annotation,
|
|
16
|
+
AnnotationCreateInput,
|
|
17
|
+
AnnotationsResponse,
|
|
18
|
+
AuthStatusResponse,
|
|
19
|
+
ComparisonMetricSeries,
|
|
20
|
+
Experiment,
|
|
21
|
+
ExperimentAnnotationResponse,
|
|
22
|
+
ExperimentCreateInput,
|
|
23
|
+
ExperimentResponse,
|
|
24
|
+
ExperimentsResponse,
|
|
25
|
+
ExperimentStatusError,
|
|
26
|
+
ExperimentStatusUpdateInput,
|
|
27
|
+
Folder,
|
|
28
|
+
FolderContentsResponse,
|
|
29
|
+
FolderCreateInput,
|
|
30
|
+
FolderPatchInput,
|
|
31
|
+
FolderResponse,
|
|
32
|
+
FoldersResponse,
|
|
33
|
+
IngestCreateInput,
|
|
34
|
+
IngestResponse,
|
|
35
|
+
IngestionKey,
|
|
36
|
+
IngestionKeyCreateInput,
|
|
37
|
+
IngestionKeyCreateResponse,
|
|
38
|
+
IngestionKeyResponse,
|
|
39
|
+
IngestionKeysResponse,
|
|
40
|
+
JsonObject,
|
|
41
|
+
JsonPayload,
|
|
42
|
+
JsonValue,
|
|
43
|
+
MetricCatalogItem,
|
|
44
|
+
MetricCatalogMetadataValue,
|
|
45
|
+
MetricCatalogResponse,
|
|
46
|
+
MetricCatalogValues,
|
|
47
|
+
MetricCatalogValuesResponse,
|
|
48
|
+
MetricCreateInput,
|
|
49
|
+
MetricEventRow,
|
|
50
|
+
RowsResponse,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"Annotation",
|
|
55
|
+
"AnnotationCreateInput",
|
|
56
|
+
"AnnotationsResponse",
|
|
57
|
+
"AuthStatusResponse",
|
|
58
|
+
"ClientCredentials",
|
|
59
|
+
"ComparisonMetricSeries",
|
|
60
|
+
"Experiment",
|
|
61
|
+
"ExperimentAnnotationResponse",
|
|
62
|
+
"ExperimentCreateInput",
|
|
63
|
+
"ExperimentResponse",
|
|
64
|
+
"ExperimentsResponse",
|
|
65
|
+
"ExperimentStatusError",
|
|
66
|
+
"ExperimentStatusUpdateInput",
|
|
67
|
+
"Folder",
|
|
68
|
+
"FolderContentsResponse",
|
|
69
|
+
"FolderCreateInput",
|
|
70
|
+
"FolderPatchInput",
|
|
71
|
+
"FolderResponse",
|
|
72
|
+
"FoldersResponse",
|
|
73
|
+
"IngestCreateInput",
|
|
74
|
+
"IngestResponse",
|
|
75
|
+
"IngestionKey",
|
|
76
|
+
"IngestionKeyCreateInput",
|
|
77
|
+
"IngestionKeyCreateResponse",
|
|
78
|
+
"IngestionKeyResponse",
|
|
79
|
+
"IngestionKeysResponse",
|
|
80
|
+
"JsonObject",
|
|
81
|
+
"JsonPayload",
|
|
82
|
+
"JsonValue",
|
|
83
|
+
"MetricCatalogItem",
|
|
84
|
+
"MetricCatalogMetadataValue",
|
|
85
|
+
"MetricCatalogResponse",
|
|
86
|
+
"MetricCatalogValues",
|
|
87
|
+
"MetricCatalogValuesResponse",
|
|
88
|
+
"MetricCreateInput",
|
|
89
|
+
"MetricEventRow",
|
|
90
|
+
"MetricsApiAuthError",
|
|
91
|
+
"MetricsApiError",
|
|
92
|
+
"MetricsApiRequestError",
|
|
93
|
+
"MetricsApiResponseError",
|
|
94
|
+
"RowsResponse",
|
|
95
|
+
"SyvainMetricsApiClient",
|
|
96
|
+
"cli_auth_path",
|
|
97
|
+
"load_cli_auth",
|
|
98
|
+
"normalize_metrics_host",
|
|
99
|
+
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Literal, cast
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
|
|
10
|
+
AuthMode = Literal["local", "local-dev"]
|
|
11
|
+
|
|
12
|
+
DEFAULT_DEV_HOST = "https://syvain-metrics-app-dev.syvaintech.workers.dev"
|
|
13
|
+
DEFAULT_PROD_HOST = "https://metrics.syvain.com"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MetricsApiAuthError(RuntimeError):
|
|
17
|
+
"""Raised when client authentication cannot be resolved."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class ClientCredentials:
|
|
22
|
+
api_key: str
|
|
23
|
+
host: str
|
|
24
|
+
org_id: str | None = None
|
|
25
|
+
login_at: int | None = None
|
|
26
|
+
source: str = "api_key"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def default_host_for_mode(mode: AuthMode | None = None) -> str:
|
|
30
|
+
return DEFAULT_DEV_HOST if mode == "local-dev" else DEFAULT_PROD_HOST
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def normalize_metrics_host(value: str | None, *, default_host: str) -> str:
|
|
34
|
+
raw_host = (value or "").strip() or default_host
|
|
35
|
+
parsed = urlparse(raw_host)
|
|
36
|
+
if parsed.scheme not in {"http", "https"}:
|
|
37
|
+
raise MetricsApiAuthError("Metrics API host must use http or https.")
|
|
38
|
+
if (
|
|
39
|
+
parsed.netloc == ""
|
|
40
|
+
or parsed.username is not None
|
|
41
|
+
or parsed.password is not None
|
|
42
|
+
):
|
|
43
|
+
raise MetricsApiAuthError("Metrics API host must be a valid HTTP(S) origin.")
|
|
44
|
+
if parsed.path not in {"", "/"} or parsed.params or parsed.query or parsed.fragment:
|
|
45
|
+
raise MetricsApiAuthError(
|
|
46
|
+
"Metrics API host must not include a path, query, or fragment."
|
|
47
|
+
)
|
|
48
|
+
return f"{parsed.scheme}://{parsed.netloc}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def cli_auth_path(mode: AuthMode) -> Path:
|
|
52
|
+
config_home = os.environ.get("XDG_CONFIG_HOME")
|
|
53
|
+
root = Path(config_home).expanduser() if config_home else Path.home() / ".config"
|
|
54
|
+
app_name = "syvain-metrics-dev" if mode == "local-dev" else "syvain-metrics"
|
|
55
|
+
return root / app_name / "auth.json"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def load_cli_auth(mode: AuthMode) -> ClientCredentials:
|
|
59
|
+
path = cli_auth_path(mode)
|
|
60
|
+
try:
|
|
61
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
62
|
+
except FileNotFoundError as error:
|
|
63
|
+
raise MetricsApiAuthError(
|
|
64
|
+
f"Metrics CLI auth file was not found at {path}."
|
|
65
|
+
) from error
|
|
66
|
+
except json.JSONDecodeError as error:
|
|
67
|
+
raise MetricsApiAuthError(
|
|
68
|
+
f"Metrics CLI auth file is not valid JSON: {path}."
|
|
69
|
+
) from error
|
|
70
|
+
|
|
71
|
+
if not isinstance(raw, dict):
|
|
72
|
+
raise MetricsApiAuthError(
|
|
73
|
+
f"Metrics CLI auth file must contain a JSON object: {path}."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
api_key = raw.get("apiKey")
|
|
77
|
+
if not isinstance(api_key, str) or len(api_key) == 0:
|
|
78
|
+
raise MetricsApiAuthError(
|
|
79
|
+
f"Metrics CLI auth file does not contain apiKey: {path}."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
raw_host = raw.get("host")
|
|
83
|
+
host = normalize_metrics_host(
|
|
84
|
+
raw_host if isinstance(raw_host, str) else None,
|
|
85
|
+
default_host=default_host_for_mode(mode),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
raw_org_id = raw.get("orgId")
|
|
89
|
+
org_id = raw_org_id if isinstance(raw_org_id, str) and raw_org_id else None
|
|
90
|
+
|
|
91
|
+
raw_login_at = raw.get("loginAt")
|
|
92
|
+
login_at = raw_login_at if isinstance(raw_login_at, int) else None
|
|
93
|
+
|
|
94
|
+
return ClientCredentials(
|
|
95
|
+
api_key=api_key,
|
|
96
|
+
host=host,
|
|
97
|
+
org_id=org_id,
|
|
98
|
+
login_at=login_at,
|
|
99
|
+
source=mode,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def resolve_client_credentials(
|
|
104
|
+
auth: str | ClientCredentials,
|
|
105
|
+
*,
|
|
106
|
+
host: str | None = None,
|
|
107
|
+
) -> ClientCredentials:
|
|
108
|
+
if isinstance(auth, ClientCredentials):
|
|
109
|
+
resolved_host = normalize_metrics_host(
|
|
110
|
+
host or auth.host, default_host=auth.host
|
|
111
|
+
)
|
|
112
|
+
return ClientCredentials(
|
|
113
|
+
api_key=auth.api_key,
|
|
114
|
+
host=resolved_host,
|
|
115
|
+
org_id=auth.org_id,
|
|
116
|
+
login_at=auth.login_at,
|
|
117
|
+
source=auth.source,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if auth in {"local", "local-dev"}:
|
|
121
|
+
mode = cast(AuthMode, auth)
|
|
122
|
+
credentials = load_cli_auth(mode)
|
|
123
|
+
if host is None:
|
|
124
|
+
return credentials
|
|
125
|
+
return ClientCredentials(
|
|
126
|
+
api_key=credentials.api_key,
|
|
127
|
+
host=normalize_metrics_host(host, default_host=credentials.host),
|
|
128
|
+
org_id=credentials.org_id,
|
|
129
|
+
login_at=credentials.login_at,
|
|
130
|
+
source=credentials.source,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
api_key = auth.strip()
|
|
134
|
+
if len(api_key) == 0:
|
|
135
|
+
raise MetricsApiAuthError("Metrics API key must not be empty.")
|
|
136
|
+
|
|
137
|
+
return ClientCredentials(
|
|
138
|
+
api_key=api_key,
|
|
139
|
+
host=normalize_metrics_host(host, default_host=DEFAULT_PROD_HOST),
|
|
140
|
+
)
|