o11y-one 0.1.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.
- o11y_one-0.1.0/.gitignore +26 -0
- o11y_one-0.1.0/PKG-INFO +99 -0
- o11y_one-0.1.0/README.md +81 -0
- o11y_one-0.1.0/pyproject.toml +53 -0
- o11y_one-0.1.0/src/o11y_one/sdk/__init__.py +82 -0
- o11y_one-0.1.0/src/o11y_one/sdk/agentic.py +1624 -0
- o11y_one-0.1.0/src/o11y_one/sdk/auth.py +89 -0
- o11y_one-0.1.0/src/o11y_one/sdk/client.py +166 -0
- o11y_one-0.1.0/src/o11y_one/sdk/errors.py +99 -0
- o11y_one-0.1.0/src/o11y_one/sdk/recovery.py +116 -0
- o11y_one-0.1.0/src/o11y_one/sdk/results.py +60 -0
- o11y_one-0.1.0/src/o11y_one/sdk/validation.py +114 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Build artifacts. Note what is NOT here: packages/*/src/o11y_one and
|
|
2
|
+
# gen/go/o11y_one are GENERATED but COMMITTED on purpose — see README, "The
|
|
3
|
+
# regen ritual". Only compiler output is ignored.
|
|
4
|
+
node_modules/
|
|
5
|
+
dist/
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
|
|
8
|
+
# Python
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
.venv/
|
|
12
|
+
build/
|
|
13
|
+
*.egg-info/
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
|
|
17
|
+
# Go
|
|
18
|
+
/tools/ci-runner/bin/
|
|
19
|
+
/tools/pr-diff/bin/
|
|
20
|
+
/dist-bin/
|
|
21
|
+
|
|
22
|
+
# mise/local
|
|
23
|
+
.mise.local.toml
|
|
24
|
+
|
|
25
|
+
# OS
|
|
26
|
+
.DS_Store
|
o11y_one-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: o11y-one
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Hand-written Python client for the O11y One API: transport construction, credential injection, and the O11y One error taxonomy.
|
|
5
|
+
Project-URL: Homepage, https://o11y.one
|
|
6
|
+
Project-URL: Source, https://github.com/o11y-one/o11y-one-sdk
|
|
7
|
+
Author: O11y One
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: connect-python<1,>=0.9.0
|
|
15
|
+
Requires-Dist: httpx>=0.28
|
|
16
|
+
Requires-Dist: o11y-one-api-agentic
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# `o11y-one`
|
|
20
|
+
|
|
21
|
+
The Python client for the O11y One API. A thin, hand-written layer over the
|
|
22
|
+
generated [`o11y-one-api-agentic`](../gen-py-agentic) package.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
uv add o11y-one # or: pip install o11y-one
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Use
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import os
|
|
34
|
+
|
|
35
|
+
from o11y_one.sdk import O11yClient, Disposition, classify
|
|
36
|
+
from o11y_one.agentic.v1.evaluation_pb2 import ListEvaluationDefinitionsRequest
|
|
37
|
+
from o11y_one.agentic.v1.evaluation_connect import AgenticEvaluationServiceClientSync
|
|
38
|
+
|
|
39
|
+
o11y = O11yClient(
|
|
40
|
+
base_url="https://api.o11y.one",
|
|
41
|
+
credential=os.environ["O11Y_API_KEY"], # o11y_mach.<selector>.<secret>
|
|
42
|
+
org_id=os.environ["O11Y_ORG_ID"],
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
evals = o11y.service_sync(AgenticEvaluationServiceClientSync)
|
|
46
|
+
try:
|
|
47
|
+
defs = evals.list_evaluation_definitions(ListEvaluationDefinitionsRequest())
|
|
48
|
+
except Exception as err: # noqa: BLE001 - classify sorts it out
|
|
49
|
+
failure = classify(err)
|
|
50
|
+
if failure.disposition is Disposition.INSUFFICIENT_SCOPE:
|
|
51
|
+
raise SystemExit(f"credential is missing scope {failure.missing_scope}") from err
|
|
52
|
+
raise
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## What is here, and what deliberately is not
|
|
56
|
+
|
|
57
|
+
Three modules and nothing else:
|
|
58
|
+
|
|
59
|
+
| module | responsibility |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `o11y_one.sdk.client` | transport construction, credential and scoping header injection |
|
|
62
|
+
| `o11y_one.sdk.auth` | the credential wire format and local structural validation |
|
|
63
|
+
| `o11y_one.sdk.errors` | the failure taxonomy: reauthenticate / insufficient-scope / version-skew / retry / unclassified |
|
|
64
|
+
|
|
65
|
+
Service methods are **not** wrapped. `o11y-one-api-agentic` already ships a client class
|
|
66
|
+
per service; a hand-written facade over all of them would be a second API surface
|
|
67
|
+
to keep in sync with the proto, and it would rot the first time a field is added
|
|
68
|
+
upstream. Import the generated client class and hand it to
|
|
69
|
+
`O11yClient.service_sync()` / `O11yClient.service()`.
|
|
70
|
+
|
|
71
|
+
## The distinction this package exists to preserve
|
|
72
|
+
|
|
73
|
+
`UNAUTHENTICATED` and `PERMISSION_DENIED` are different problems:
|
|
74
|
+
|
|
75
|
+
* **`UNAUTHENTICATED`** — the credential is absent, malformed, unknown, expired,
|
|
76
|
+
or revoked. Re-auth. Retrying cannot change the answer.
|
|
77
|
+
* **`PERMISSION_DENIED`** — the credential is fine; it lacks a scope (the server
|
|
78
|
+
names which one) or it was presented to a non-machine surface.
|
|
79
|
+
|
|
80
|
+
`classify()` keeps them apart, along with `UNAVAILABLE` (auth backend down —
|
|
81
|
+
retry with backoff) and `INVALID_ARGUMENT` (SDK/server version skew). Collapsing
|
|
82
|
+
these into a single "auth error" is the failure mode this module exists to
|
|
83
|
+
prevent.
|
|
84
|
+
|
|
85
|
+
## Credentials
|
|
86
|
+
|
|
87
|
+
A machine credential is `o11y_mach.<selector>.<secret>`, presented in the
|
|
88
|
+
`x-o11y-key` header. It is:
|
|
89
|
+
|
|
90
|
+
* **returned exactly once**, by `CreateMachineCredential`. There is no read-back
|
|
91
|
+
RPC and no recovery path — lose it and you rotate.
|
|
92
|
+
* **rotated create-then-revoke**, not atomically. A principal may hold several
|
|
93
|
+
active credentials at once, which is what makes zero-downtime rotation work:
|
|
94
|
+
create the new one, deploy it, then revoke the old.
|
|
95
|
+
* **revoked effective on the next request**, not on a TTL boundary.
|
|
96
|
+
* **expiring** at +365 days by default, capped at three years.
|
|
97
|
+
|
|
98
|
+
Never send it as `authorization: Bearer` — that path carries the browser session
|
|
99
|
+
JWT and a machine credential presented there is rejected.
|
o11y_one-0.1.0/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# `o11y-one`
|
|
2
|
+
|
|
3
|
+
The Python client for the O11y One API. A thin, hand-written layer over the
|
|
4
|
+
generated [`o11y-one-api-agentic`](../gen-py-agentic) package.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
uv add o11y-one # or: pip install o11y-one
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Use
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from o11y_one.sdk import O11yClient, Disposition, classify
|
|
18
|
+
from o11y_one.agentic.v1.evaluation_pb2 import ListEvaluationDefinitionsRequest
|
|
19
|
+
from o11y_one.agentic.v1.evaluation_connect import AgenticEvaluationServiceClientSync
|
|
20
|
+
|
|
21
|
+
o11y = O11yClient(
|
|
22
|
+
base_url="https://api.o11y.one",
|
|
23
|
+
credential=os.environ["O11Y_API_KEY"], # o11y_mach.<selector>.<secret>
|
|
24
|
+
org_id=os.environ["O11Y_ORG_ID"],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
evals = o11y.service_sync(AgenticEvaluationServiceClientSync)
|
|
28
|
+
try:
|
|
29
|
+
defs = evals.list_evaluation_definitions(ListEvaluationDefinitionsRequest())
|
|
30
|
+
except Exception as err: # noqa: BLE001 - classify sorts it out
|
|
31
|
+
failure = classify(err)
|
|
32
|
+
if failure.disposition is Disposition.INSUFFICIENT_SCOPE:
|
|
33
|
+
raise SystemExit(f"credential is missing scope {failure.missing_scope}") from err
|
|
34
|
+
raise
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## What is here, and what deliberately is not
|
|
38
|
+
|
|
39
|
+
Three modules and nothing else:
|
|
40
|
+
|
|
41
|
+
| module | responsibility |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `o11y_one.sdk.client` | transport construction, credential and scoping header injection |
|
|
44
|
+
| `o11y_one.sdk.auth` | the credential wire format and local structural validation |
|
|
45
|
+
| `o11y_one.sdk.errors` | the failure taxonomy: reauthenticate / insufficient-scope / version-skew / retry / unclassified |
|
|
46
|
+
|
|
47
|
+
Service methods are **not** wrapped. `o11y-one-api-agentic` already ships a client class
|
|
48
|
+
per service; a hand-written facade over all of them would be a second API surface
|
|
49
|
+
to keep in sync with the proto, and it would rot the first time a field is added
|
|
50
|
+
upstream. Import the generated client class and hand it to
|
|
51
|
+
`O11yClient.service_sync()` / `O11yClient.service()`.
|
|
52
|
+
|
|
53
|
+
## The distinction this package exists to preserve
|
|
54
|
+
|
|
55
|
+
`UNAUTHENTICATED` and `PERMISSION_DENIED` are different problems:
|
|
56
|
+
|
|
57
|
+
* **`UNAUTHENTICATED`** — the credential is absent, malformed, unknown, expired,
|
|
58
|
+
or revoked. Re-auth. Retrying cannot change the answer.
|
|
59
|
+
* **`PERMISSION_DENIED`** — the credential is fine; it lacks a scope (the server
|
|
60
|
+
names which one) or it was presented to a non-machine surface.
|
|
61
|
+
|
|
62
|
+
`classify()` keeps them apart, along with `UNAVAILABLE` (auth backend down —
|
|
63
|
+
retry with backoff) and `INVALID_ARGUMENT` (SDK/server version skew). Collapsing
|
|
64
|
+
these into a single "auth error" is the failure mode this module exists to
|
|
65
|
+
prevent.
|
|
66
|
+
|
|
67
|
+
## Credentials
|
|
68
|
+
|
|
69
|
+
A machine credential is `o11y_mach.<selector>.<secret>`, presented in the
|
|
70
|
+
`x-o11y-key` header. It is:
|
|
71
|
+
|
|
72
|
+
* **returned exactly once**, by `CreateMachineCredential`. There is no read-back
|
|
73
|
+
RPC and no recovery path — lose it and you rotate.
|
|
74
|
+
* **rotated create-then-revoke**, not atomically. A principal may hold several
|
|
75
|
+
active credentials at once, which is what makes zero-downtime rotation work:
|
|
76
|
+
create the new one, deploy it, then revoke the old.
|
|
77
|
+
* **revoked effective on the next request**, not on a TTL boundary.
|
|
78
|
+
* **expiring** at +365 days by default, capped at three years.
|
|
79
|
+
|
|
80
|
+
Never send it as `authorization: Bearer` — that path carries the browser session
|
|
81
|
+
JWT and a machine credential presented there is rejected.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "o11y-one"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Hand-written Python client for the O11y One API: transport construction, credential injection, and the O11y One error taxonomy."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [{ name = "O11y One" }]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 3 - Alpha",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Typing :: Typed",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
"o11y-one-api-agentic",
|
|
18
|
+
"connect-python>=0.9.0,<1",
|
|
19
|
+
"httpx>=0.28",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://o11y.one"
|
|
24
|
+
Source = "https://github.com/o11y-one/o11y-one-sdk"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["hatchling>=1.32.0"]
|
|
28
|
+
build-backend = "hatchling.build"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
# o11y_one is a PEP 420 namespace shared with o11y-one-api-agentic; this
|
|
32
|
+
# distribution owns exactly o11y_one/sdk. No src/o11y_one/__init__.py, ever —
|
|
33
|
+
# adding one would shadow the generated o11y_one.agentic, o11y_one.common
|
|
34
|
+
# packages.
|
|
35
|
+
packages = ["src/o11y_one"]
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.sdist]
|
|
38
|
+
include = ["src", "README.md", "pyproject.toml"]
|
|
39
|
+
|
|
40
|
+
[tool.uv.sources]
|
|
41
|
+
# Inside the workspace, resolve the generated package from the sibling member
|
|
42
|
+
# rather than PyPI, so the SDK always builds against the generated code from
|
|
43
|
+
# THIS commit. Published wheels carry the plain `o11y-one-api-agentic`
|
|
44
|
+
# requirement. The agentic SDK depends on the per-domain subset distribution
|
|
45
|
+
# (agentic + common only), not the whole o11y-one-api — see
|
|
46
|
+
# docs/proto-subsetting.md.
|
|
47
|
+
o11y-one-api-agentic = { workspace = true }
|
|
48
|
+
|
|
49
|
+
[dependency-groups]
|
|
50
|
+
dev = ["pytest>=8.4"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""``o11y_one.sdk`` — hand-written client layer over the generated ``o11y_one.*``
|
|
2
|
+
protobuf packages shipped by the ``o11y-one-api`` distribution.
|
|
3
|
+
|
|
4
|
+
Four things live here:
|
|
5
|
+
|
|
6
|
+
* transport construction (:mod:`o11y_one.sdk.client`)
|
|
7
|
+
* credential injection and its local validation (:mod:`o11y_one.sdk.auth`)
|
|
8
|
+
* the failure taxonomy an SDK exists to render (:mod:`o11y_one.sdk.errors`)
|
|
9
|
+
* an ergonomic facade over exactly ONE generated service,
|
|
10
|
+
``AgenticEvaluationService`` (:mod:`o11y_one.sdk.agentic`)
|
|
11
|
+
|
|
12
|
+
For every other generated service, methods are still NOT wrapped or
|
|
13
|
+
re-exported: import the generated ``*ServiceClient`` you need from
|
|
14
|
+
``o11y_one.<domain>.v1.<file>_connect`` and hand the class to
|
|
15
|
+
:meth:`O11yClient.service` / :meth:`O11yClient.service_sync`. The rationale for
|
|
16
|
+
staying thin there is unchanged — a hand-written facade over every one of the
|
|
17
|
+
29 generated services would be a second API surface to keep in sync with the
|
|
18
|
+
proto, and it would rot the first time a field is added upstream.
|
|
19
|
+
|
|
20
|
+
``AgenticEvaluationService`` is the deliberate exception, not a reversal of
|
|
21
|
+
that rule: it is the run-submission / lease-heartbeat-submit-release /
|
|
22
|
+
dataset-draft / annotation surface an eval-authoring or externally-executed
|
|
23
|
+
runtime integration calls minute to minute, its messages repeat the same
|
|
24
|
+
refusal-on-a-200, idempotency and capability-envelope conventions across
|
|
25
|
+
dozens of RPCs, and getting any one of those conventions wrong locally
|
|
26
|
+
degrades silently to an opaque network error rather than a loud local one. See
|
|
27
|
+
:mod:`o11y_one.sdk.agentic` for what the facade adds on top of the generated
|
|
28
|
+
client, and ``l2-notes.md`` for why this scope was drawn where it was.
|
|
29
|
+
|
|
30
|
+
Note that ``o11y_one`` itself is a PEP 420 namespace package contributed to by
|
|
31
|
+
two distributions: ``o11y-one-api`` (generated) and ``o11y-one`` (this one).
|
|
32
|
+
There is deliberately no ``o11y_one/__init__.py`` in either.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
from .agentic import (
|
|
38
|
+
MACHINE_SCOPE_NAME_TO_ENUM,
|
|
39
|
+
AgenticClient,
|
|
40
|
+
AgenticClientSync,
|
|
41
|
+
CaseOutput,
|
|
42
|
+
IssuedMachineCredential,
|
|
43
|
+
)
|
|
44
|
+
from .auth import (
|
|
45
|
+
CREDENTIAL_HEADER,
|
|
46
|
+
CREDENTIAL_MAX_LEN,
|
|
47
|
+
MACHINE_CREDENTIAL_PREFIX,
|
|
48
|
+
ORG_ID_HEADER,
|
|
49
|
+
TENANT_ID_HEADER,
|
|
50
|
+
assert_looks_like_machine_credential,
|
|
51
|
+
)
|
|
52
|
+
from .client import CredentialInterceptor, O11yClient
|
|
53
|
+
from .errors import MACHINE_SCOPES, ClassifiedFailure, Disposition, classify
|
|
54
|
+
from .recovery import bare_enum_name, find_error_detail
|
|
55
|
+
from .results import Refusal
|
|
56
|
+
from .validation import ValidationError
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"CREDENTIAL_HEADER",
|
|
60
|
+
"CREDENTIAL_MAX_LEN",
|
|
61
|
+
"MACHINE_CREDENTIAL_PREFIX",
|
|
62
|
+
"MACHINE_SCOPES",
|
|
63
|
+
"MACHINE_SCOPE_NAME_TO_ENUM",
|
|
64
|
+
"ORG_ID_HEADER",
|
|
65
|
+
"TENANT_ID_HEADER",
|
|
66
|
+
"AgenticClient",
|
|
67
|
+
"AgenticClientSync",
|
|
68
|
+
"CaseOutput",
|
|
69
|
+
"ClassifiedFailure",
|
|
70
|
+
"CredentialInterceptor",
|
|
71
|
+
"Disposition",
|
|
72
|
+
"IssuedMachineCredential",
|
|
73
|
+
"O11yClient",
|
|
74
|
+
"Refusal",
|
|
75
|
+
"ValidationError",
|
|
76
|
+
"assert_looks_like_machine_credential",
|
|
77
|
+
"bare_enum_name",
|
|
78
|
+
"classify",
|
|
79
|
+
"find_error_detail",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
__version__ = "0.0.0"
|