ascendkit 0.2.2__tar.gz → 0.2.5__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.
Files changed (24) hide show
  1. ascendkit-0.2.5/.github/workflows/auto-release.yml +13 -0
  2. ascendkit-0.2.5/.github/workflows/publish.yml +58 -0
  3. {ascendkit-0.2.2 → ascendkit-0.2.5}/CLAUDE.md +5 -11
  4. ascendkit-0.2.5/COMMANDS.md +6 -0
  5. {ascendkit-0.2.2 → ascendkit-0.2.5}/PKG-INFO +1 -1
  6. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_client.py +27 -4
  7. {ascendkit-0.2.2 → ascendkit-0.2.5}/pyproject.toml +1 -1
  8. ascendkit-0.2.2/.github/workflows/publish.yml +0 -29
  9. {ascendkit-0.2.2 → ascendkit-0.2.5}/.github/workflows/enforce-source-branch.yml +0 -0
  10. {ascendkit-0.2.2 → ascendkit-0.2.5}/.gitignore +0 -0
  11. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/__init__.py +0 -0
  12. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_access_token.py +0 -0
  13. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_analytics.py +0 -0
  14. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_errors.py +0 -0
  15. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_version.py +0 -0
  16. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_webhooks.py +0 -0
  17. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/auth.py +0 -0
  18. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/fastapi.py +0 -0
  19. {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/py.typed +0 -0
  20. {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/__init__.py +0 -0
  21. {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/test_client.py +0 -0
  22. {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/test_version_header.py +0 -0
  23. {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/test_webhooks.py +0 -0
  24. {ascendkit-0.2.2 → ascendkit-0.2.5}/uv.lock +0 -0
@@ -0,0 +1,13 @@
1
+ name: Auto-release on version bump
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ release:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - run: echo "Use publish.yml for versioned releases."
@@ -0,0 +1,58 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - pyproject.toml
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write
12
+
13
+ jobs:
14
+ publish-and-release:
15
+ runs-on: ubuntu-latest
16
+ environment: pypi
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Read version
21
+ id: version
22
+ run: echo "value=$(python3 -c 'import pathlib, re; m = re.search(r\"^version\\s*=\\s*\\\"([^\\\"]+)\\\"\", pathlib.Path(\"pyproject.toml\").read_text(), re.M); print(m.group(1))')" >> "$GITHUB_OUTPUT"
23
+
24
+ - name: Check if tag exists
25
+ id: tag
26
+ run: |
27
+ if git ls-remote --tags origin "refs/tags/v${{ steps.version.outputs.value }}" | grep -q .; then
28
+ echo "exists=true" >> "$GITHUB_OUTPUT"
29
+ else
30
+ echo "exists=false" >> "$GITHUB_OUTPUT"
31
+ fi
32
+
33
+ - uses: actions/setup-python@v5
34
+ if: steps.tag.outputs.exists == 'false'
35
+ with:
36
+ python-version: "3.12"
37
+
38
+ - name: Install build tools
39
+ if: steps.tag.outputs.exists == 'false'
40
+ run: pip install build
41
+
42
+ - name: Build package
43
+ if: steps.tag.outputs.exists == 'false'
44
+ run: python -m build
45
+
46
+ - name: Publish to PyPI (trusted publishing)
47
+ if: steps.tag.outputs.exists == 'false'
48
+ uses: pypa/gh-action-pypi-publish@release/v1
49
+
50
+ - name: Create release
51
+ if: steps.tag.outputs.exists == 'false'
52
+ env:
53
+ GH_TOKEN: ${{ github.token }}
54
+ run: |
55
+ gh release create "v${{ steps.version.outputs.value }}" \
56
+ --target main \
57
+ --title "v${{ steps.version.outputs.value }}" \
58
+ --generate-notes
@@ -2,24 +2,18 @@
2
2
 
3
3
  Python 3.10+ SDK. httpx (async), published to PyPI.
4
4
 
5
- ## Commands
6
- ```bash
7
- pip install -e ".[dev]" # Install editable
8
- pytest # Tests
9
- ruff check . && ruff format . # Lint + format
10
- mypy ascendkit/ # Type checking
11
- ```
12
-
13
5
  ## Rules
14
6
  - MUST use `httpx` — not `requests`. Support both sync and async patterns.
15
7
  - Logger: drop-in for `logging` module. Keyword args for context: `logger.info("msg", port=3000)`
16
8
  - Batch logs (5s / 100 events) via `POST /api/logs/ingest` — NEVER send one-at-a-time
17
9
  - Flush on interpreter shutdown via `atexit`
18
- - Public API through `ascendkit/__init__.py` — internal modules prefixed `_` (e.g., `_client.py`)
19
- - Typed exceptions (`AscendKitError`, `AuthenticationError`) — NEVER swallow errors
20
10
  - All public functions MUST have type annotations
21
11
  - Gracefully handle network failures on flush — buffer and retry, NEVER crash host app
22
12
  - In async frameworks, use `httpx.AsyncClient` — sync client blocks the event loop
23
13
 
14
+ ## Release & Publish
15
+ Trigger: GitHub release (`release: published`) → `python -m build` → PyPI trusted publishing (`pypa/gh-action-pypi-publish`)
16
+ To ship: bump `pyproject.toml` version on `dev` → merge to `main` → create GitHub release tagged `vX.Y.Z`
17
+
24
18
  ## GUIDANCE
25
- You are a thin client, rely on backend api for business logic validation
19
+ You are a thin client, rely on backend api for business logic validation
@@ -0,0 +1,6 @@
1
+ ```bash
2
+ pip install -e ".[dev]" # Install editable
3
+ pytest # Tests
4
+ ruff check . && ruff format . # Lint + format
5
+ mypy ascendkit/ # Type checking
6
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ascendkit
3
- Version: 0.2.2
3
+ Version: 0.2.5
4
4
  Summary: AscendKit Python SDK
5
5
  Project-URL: Homepage, https://ascendkit.dev
6
6
  Project-URL: Documentation, https://ascendkit.dev/docs
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  import os
7
+ import uuid
7
8
  from typing import Any
8
9
 
9
10
  import httpx
@@ -60,23 +61,34 @@ class AscendKit:
60
61
  reads ``ASCENDKIT_ENV_KEY`` from the environment.
61
62
  """
62
63
 
63
- def __init__(self, public_key: str | None = None, api_url: str = _DEFAULT_API_URL) -> None:
64
+ def __init__(
65
+ self,
66
+ public_key: str | None = None,
67
+ api_url: str = _DEFAULT_API_URL,
68
+ invocation_id: str | None = None,
69
+ ) -> None:
64
70
  resolved_key = public_key or os.environ.get("ASCENDKIT_ENV_KEY")
65
71
  if not resolved_key:
66
72
  raise ValueError(
67
73
  "AscendKit: missing public key. Pass public_key or set ASCENDKIT_ENV_KEY."
68
74
  )
69
75
  self.public_key = resolved_key
76
+ self._invocation_id = invocation_id or str(uuid.uuid4())
70
77
  self._client = httpx.Client(
71
78
  base_url=api_url,
72
79
  headers={
73
80
  "X-AscendKit-Public-Key": resolved_key,
81
+ "X-AscendKit-Invocation-Id": self._invocation_id,
74
82
  **_VERSION_HEADER,
75
83
  },
76
84
  )
77
85
 
78
86
  def _request(self, method: str, path: str, **kwargs: Any) -> Any:
79
- response = self._client.request(method, path, **kwargs)
87
+ per_request_headers = {"X-AscendKit-Client-Request-Id": str(uuid.uuid4())}
88
+ existing = kwargs.pop("headers", None)
89
+ if existing:
90
+ per_request_headers.update(existing)
91
+ response = self._client.request(method, path, headers=per_request_headers, **kwargs)
80
92
  _check_upgrade(response)
81
93
  _handle_error(response)
82
94
  body = response.json()
@@ -99,23 +111,34 @@ class AsyncAscendKit:
99
111
  reads ``ASCENDKIT_ENV_KEY`` from the environment.
100
112
  """
101
113
 
102
- def __init__(self, public_key: str | None = None, api_url: str = _DEFAULT_API_URL) -> None:
114
+ def __init__(
115
+ self,
116
+ public_key: str | None = None,
117
+ api_url: str = _DEFAULT_API_URL,
118
+ invocation_id: str | None = None,
119
+ ) -> None:
103
120
  resolved_key = public_key or os.environ.get("ASCENDKIT_ENV_KEY")
104
121
  if not resolved_key:
105
122
  raise ValueError(
106
123
  "AscendKit: missing public key. Pass public_key or set ASCENDKIT_ENV_KEY."
107
124
  )
108
125
  self.public_key = resolved_key
126
+ self._invocation_id = invocation_id or str(uuid.uuid4())
109
127
  self._client = httpx.AsyncClient(
110
128
  base_url=api_url,
111
129
  headers={
112
130
  "X-AscendKit-Public-Key": resolved_key,
131
+ "X-AscendKit-Invocation-Id": self._invocation_id,
113
132
  **_VERSION_HEADER,
114
133
  },
115
134
  )
116
135
 
117
136
  async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
118
- response = await self._client.request(method, path, **kwargs)
137
+ per_request_headers = {"X-AscendKit-Client-Request-Id": str(uuid.uuid4())}
138
+ existing = kwargs.pop("headers", None)
139
+ if existing:
140
+ per_request_headers.update(existing)
141
+ response = await self._client.request(method, path, headers=per_request_headers, **kwargs)
119
142
  _check_upgrade(response)
120
143
  _handle_error(response)
121
144
  body = response.json()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ascendkit"
3
- version = "0.2.2"
3
+ version = "0.2.5"
4
4
  description = "AscendKit Python SDK"
5
5
  license = { text = "MIT" }
6
6
  authors = [{ name = "ascendkit.dev" }]
@@ -1,29 +0,0 @@
1
- name: Publish to PyPI
2
-
3
- on:
4
- release:
5
- types: [published]
6
-
7
- permissions:
8
- contents: read
9
- id-token: write
10
-
11
- jobs:
12
- publish:
13
- runs-on: ubuntu-latest
14
- environment: pypi
15
- steps:
16
- - uses: actions/checkout@v4
17
-
18
- - uses: actions/setup-python@v5
19
- with:
20
- python-version: "3.12"
21
-
22
- - name: Install build tools
23
- run: pip install build
24
-
25
- - name: Build package
26
- run: python -m build
27
-
28
- - name: Publish to PyPI (trusted publishing)
29
- uses: pypa/gh-action-pypi-publish@release/v1
File without changes
File without changes
File without changes
File without changes
File without changes