astrocyte-ingestion-github 0.8.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.
@@ -0,0 +1,53 @@
1
+ # Node (Starlight in docs/)
2
+ node_modules/
3
+ .pnpm-store/
4
+ docs/.astro/
5
+
6
+ # Generated by docs/scripts/fetch-pypi-pins.mjs (recreated on every pnpm dev/build)
7
+ docs/src/data/pypi-install-pins.json
8
+
9
+ # Generated by docs/scripts/sync-docs.mjs (recreated on every pnpm dev/build)
10
+ docs/src/content/docs/introduction.md
11
+ docs/src/content/docs/design/
12
+ docs/src/content/docs/plugins/
13
+ docs/src/content/docs/end-user/
14
+ docs/src/content/docs/tutorials/
15
+
16
+ # Claude Code
17
+ .claude/
18
+
19
+ # OS
20
+ .DS_Store
21
+
22
+ # Local reference material (not tracked)
23
+ references/
24
+
25
+ # nWave feature artifacts (per-feature discover/deliver, roadmaps, execution logs).
26
+ # Intended to be a separate private worktree or nested git repo; not part of public Astrocyte.
27
+ docs/feature/
28
+
29
+ # Python
30
+ __pycache__/
31
+ *.py[cod]
32
+ *$py.class
33
+ .venv/
34
+ venv/
35
+ .env
36
+ .env.*
37
+ !.env.example
38
+ *.egg-info/
39
+ .eggs/
40
+ dist/
41
+ build/
42
+ .pytest_cache/
43
+ .mypy_cache/
44
+ .ruff_cache/
45
+ .coverage
46
+ htmlcov/
47
+ benchmark-results/
48
+
49
+ # Rust
50
+ target/
51
+
52
+ # IDE (optional)
53
+ .idea/
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: astrocyte-ingestion-github
3
+ Version: 0.8.0
4
+ Summary: GitHub Issues API poll IngestSource adapter for Astrocyte
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: astrocyte<0.9,>=0.7.0
8
+ Requires-Dist: httpx>=0.27
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
11
+ Requires-Dist: pytest>=8.0; extra == 'dev'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # astrocyte-ingestion-github
15
+
16
+ **Poll** driver for Astrocyte `sources:` — ingests **GitHub repository issues** (not pull requests) via the [REST API](https://docs.github.com/en/rest/issues/issues#list-repository-issues).
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pip install astrocyte-ingestion-github
22
+ # or
23
+ pip install 'astrocyte[poll]'
24
+ ```
25
+
26
+ ## Config (`astrocyte.yaml`)
27
+
28
+ ```yaml
29
+ sources:
30
+ gh_issues:
31
+ type: poll
32
+ driver: github
33
+ path: octocat/Hello-World # owner/repo
34
+ interval_seconds: 120 # >= 60 (GitHub API rate limits)
35
+ target_bank: engineering
36
+ auth:
37
+ token: ${GITHUB_TOKEN} # classic PAT or fine-grained token (issues read)
38
+ extraction_profile: builtin_text # optional
39
+ # Optional: GitHub Enterprise Server API root
40
+ # url: https://github.example.com/api/v3
41
+ ```
42
+
43
+ The adapter sets `Authorization: Bearer …` and uses `since` (max `updated_at` from the last response) to limit traffic. Each issue is retained as text `[GitHub #N] title` plus body; metadata includes `github.issue_id`, `number`, `html_url`, `updated_at`, `author`.
44
+
45
+ Principal for bank resolution: `sources.*.principal` if set; otherwise `github:<author_login>` from the issue.
46
+
47
+ ## Entry point
48
+
49
+ Registers as **`github`** under **`astrocyte.ingest_poll_drivers`** (same discovery pattern as stream drivers).
@@ -0,0 +1,36 @@
1
+ # astrocyte-ingestion-github
2
+
3
+ **Poll** driver for Astrocyte `sources:` — ingests **GitHub repository issues** (not pull requests) via the [REST API](https://docs.github.com/en/rest/issues/issues#list-repository-issues).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install astrocyte-ingestion-github
9
+ # or
10
+ pip install 'astrocyte[poll]'
11
+ ```
12
+
13
+ ## Config (`astrocyte.yaml`)
14
+
15
+ ```yaml
16
+ sources:
17
+ gh_issues:
18
+ type: poll
19
+ driver: github
20
+ path: octocat/Hello-World # owner/repo
21
+ interval_seconds: 120 # >= 60 (GitHub API rate limits)
22
+ target_bank: engineering
23
+ auth:
24
+ token: ${GITHUB_TOKEN} # classic PAT or fine-grained token (issues read)
25
+ extraction_profile: builtin_text # optional
26
+ # Optional: GitHub Enterprise Server API root
27
+ # url: https://github.example.com/api/v3
28
+ ```
29
+
30
+ The adapter sets `Authorization: Bearer …` and uses `since` (max `updated_at` from the last response) to limit traffic. Each issue is retained as text `[GitHub #N] title` plus body; metadata includes `github.issue_id`, `number`, `html_url`, `updated_at`, `author`.
31
+
32
+ Principal for bank resolution: `sources.*.principal` if set; otherwise `github:<author_login>` from the issue.
33
+
34
+ ## Entry point
35
+
36
+ Registers as **`github`** under **`astrocyte.ingest_poll_drivers`** (same discovery pattern as stream drivers).
@@ -0,0 +1,5 @@
1
+ """GitHub REST API poll :class:`~astrocyte.ingest.source.IngestSource` for Astrocyte."""
2
+
3
+ from astrocyte_ingestion_github.source import GithubPollIngestSource
4
+
5
+ __all__ = ["GithubPollIngestSource"]
@@ -0,0 +1,278 @@
1
+ """GitHub REST API poll :class:`~astrocyte.ingest.source.IngestSource` — repository issues."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import contextlib
7
+ import logging
8
+ from typing import Any
9
+ from urllib.parse import urlsplit
10
+
11
+ import httpx
12
+ from astrocyte.config import SourceConfig
13
+ from astrocyte.errors import IngestError
14
+ from astrocyte.ingest.bank_resolve import resolve_ingest_bank_id
15
+ from astrocyte.ingest.logutil import log_ingest_event
16
+ from astrocyte.ingest.webhook import RetainCallable
17
+ from astrocyte.types import AstrocyteContext, HealthStatus
18
+
19
+ logger = logging.getLogger("astrocyte_ingestion_github")
20
+
21
+
22
+ def _token(cfg: SourceConfig) -> str:
23
+ auth = cfg.auth or {}
24
+ raw = auth.get("token")
25
+ if raw is None or not str(raw).strip():
26
+ raise IngestError("GitHub poll requires auth.token")
27
+ return str(raw).strip()
28
+
29
+
30
+ def _api_base(cfg: SourceConfig) -> str:
31
+ u = (cfg.url or "").strip()
32
+ if u:
33
+ return u.rstrip("/")
34
+ return "https://api.github.com"
35
+
36
+
37
+ def _owner_repo(cfg: SourceConfig) -> tuple[str, str]:
38
+ p = (cfg.path or "").strip()
39
+ if not p or p.count("/") != 1:
40
+ raise IngestError("GitHub poll requires path: owner/repo")
41
+ owner, repo = p.split("/", 1)
42
+ if not owner.strip() or not repo.strip():
43
+ raise IngestError("GitHub poll path owner/repo must be non-empty")
44
+ return owner.strip(), repo.strip()
45
+
46
+
47
+ def _int_header(raw: object) -> int | None:
48
+ """Parse GitHub numeric response headers; invalid values become ``None``."""
49
+ try:
50
+ return int(str(raw).strip())
51
+ except ValueError:
52
+ return None
53
+
54
+
55
+ class GithubPollIngestSource:
56
+ """Poll ``GET /repos/{owner}/{repo}/issues`` and retain new/updated issues (not pull requests)."""
57
+
58
+ def __init__(
59
+ self,
60
+ source_id: str,
61
+ config: SourceConfig,
62
+ *,
63
+ retain: RetainCallable,
64
+ ) -> None:
65
+ self._source_id = source_id
66
+ self._config = config
67
+ self._retain = retain
68
+ self._task: asyncio.Task[None] | None = None
69
+ self._stop = asyncio.Event()
70
+ self._client: httpx.AsyncClient | None = None
71
+ self._running = False
72
+ self._last_error: str | None = None
73
+ # GitHub issue id -> last ingested updated_at (ISO) to avoid duplicate retains
74
+ self._seen_updated: dict[int, str] = {}
75
+ self._since: str | None = None
76
+
77
+ @property
78
+ def source_id(self) -> str:
79
+ return self._source_id
80
+
81
+ @property
82
+ def source_type(self) -> str:
83
+ return "poll"
84
+
85
+ @property
86
+ def config(self) -> SourceConfig:
87
+ return self._config
88
+
89
+ def _interval_s(self) -> float:
90
+ n = self._config.interval_seconds
91
+ if n is None or int(n) < 60:
92
+ raise IngestError("poll requires interval_seconds >= 60")
93
+ return float(int(n))
94
+
95
+ async def start(self) -> None:
96
+ if self._task is not None:
97
+ return
98
+ self._stop.clear()
99
+ self._last_error = None
100
+ self._running = True
101
+ base = _api_base(self._config)
102
+ host = urlsplit(base).hostname or ""
103
+ self._client = httpx.AsyncClient(
104
+ base_url=base,
105
+ headers={
106
+ "Accept": "application/vnd.github+json",
107
+ "X-GitHub-Api-Version": "2022-11-28",
108
+ "Authorization": f"Bearer {_token(self._config)}",
109
+ "User-Agent": "astrocyte-ingestion-github",
110
+ },
111
+ timeout=60.0,
112
+ )
113
+ # Enterprise often uses self-signed or custom CA; keep verify=True by default
114
+ if "github.com" not in host and host:
115
+ logger.info("github poll using API base %s", base)
116
+ self._task = asyncio.create_task(self._run_loop(), name=f"astrocyte-github-poll-{self._source_id}")
117
+
118
+ async def stop(self) -> None:
119
+ self._running = False
120
+ self._stop.set()
121
+ if self._task:
122
+ self._task.cancel()
123
+ with contextlib.suppress(asyncio.CancelledError):
124
+ await self._task
125
+ self._task = None
126
+ if self._client is not None:
127
+ await self._client.aclose()
128
+ self._client = None
129
+
130
+ async def health_check(self) -> HealthStatus:
131
+ if not self._running or self._task is None:
132
+ return HealthStatus(healthy=False, message="github poll source stopped")
133
+ if self._last_error:
134
+ return HealthStatus(healthy=False, message=self._last_error)
135
+ return HealthStatus(healthy=True, message="github poll loop running")
136
+
137
+ async def _run_loop(self) -> None:
138
+ assert self._client is not None
139
+ interval = self._interval_s()
140
+ while not self._stop.is_set():
141
+ try:
142
+ await self._poll_once()
143
+ except asyncio.CancelledError:
144
+ raise
145
+ except Exception as e:
146
+ self._last_error = str(e)
147
+ log_ingest_event(
148
+ logger,
149
+ "github_poll_cycle_failed",
150
+ source_id=self._source_id,
151
+ error=str(e),
152
+ )
153
+ logger.exception("github poll failed for %s", self._source_id)
154
+ # interruptible sleep
155
+ try:
156
+ await asyncio.wait_for(self._stop.wait(), timeout=interval)
157
+ break
158
+ except asyncio.TimeoutError:
159
+ continue
160
+
161
+ async def _poll_once(self) -> None:
162
+ assert self._client is not None
163
+ owner, repo = _owner_repo(self._config)
164
+ params: dict[str, Any] = {
165
+ "state": "all",
166
+ "per_page": 50,
167
+ "sort": "updated",
168
+ "direction": "desc",
169
+ }
170
+ if self._since:
171
+ params["since"] = self._since
172
+
173
+ r = await self._client.get(f"/repos/{owner}/{repo}/issues", params=params)
174
+ rem_raw = r.headers.get("x-ratelimit-remaining") or r.headers.get("X-RateLimit-Remaining")
175
+ if rem_raw is not None:
176
+ rem = _int_header(rem_raw)
177
+ if rem is not None and rem < 20:
178
+ log_ingest_event(
179
+ logger,
180
+ "github_poll_rate_limit_low",
181
+ source_id=self._source_id,
182
+ remaining=rem,
183
+ reset=r.headers.get("x-ratelimit-reset") or r.headers.get("X-RateLimit-Reset"),
184
+ )
185
+ try:
186
+ r.raise_for_status()
187
+ except httpx.HTTPStatusError as e:
188
+ log_ingest_event(
189
+ logger,
190
+ "github_poll_http_error",
191
+ source_id=self._source_id,
192
+ status_code=e.response.status_code,
193
+ url=str(e.request.url),
194
+ )
195
+ raise
196
+ issues = r.json()
197
+ if not isinstance(issues, list):
198
+ raise IngestError("GitHub issues response must be a JSON array")
199
+
200
+ cursor_max: str | None = None
201
+ for issue in issues:
202
+ if not isinstance(issue, dict):
203
+ continue
204
+ uat = issue.get("updated_at")
205
+ if isinstance(uat, str):
206
+ if cursor_max is None or uat > cursor_max:
207
+ cursor_max = uat
208
+
209
+ for issue in issues:
210
+ if not isinstance(issue, dict):
211
+ continue
212
+ if issue.get("pull_request"):
213
+ continue
214
+ iid = issue.get("id")
215
+ upd = issue.get("updated_at")
216
+ if not isinstance(iid, int) or not isinstance(upd, str):
217
+ continue
218
+ if self._seen_updated.get(iid) == upd:
219
+ continue
220
+ title = issue.get("title")
221
+ body = issue.get("body")
222
+ num = issue.get("number")
223
+ html_url = issue.get("html_url")
224
+ t = title if isinstance(title, str) else ""
225
+ b = body if isinstance(body, str) else ""
226
+ n = num if isinstance(num, int) else 0
227
+ u = html_url if isinstance(html_url, str) else ""
228
+ text = f"[GitHub #{n}] {t}\n\n{b}".strip()
229
+ if not text:
230
+ text = f"[GitHub #{n}] (empty)"
231
+
232
+ user = issue.get("user")
233
+ login = None
234
+ if isinstance(user, dict):
235
+ lg = user.get("login")
236
+ if isinstance(lg, str):
237
+ login = lg
238
+
239
+ eff_principal = self._config.principal
240
+ if isinstance(eff_principal, str) and eff_principal.strip():
241
+ p = eff_principal.strip()
242
+ elif login:
243
+ p = f"github:{login}"
244
+ else:
245
+ p = None
246
+
247
+ try:
248
+ bank_id = resolve_ingest_bank_id(self._config, principal=p)
249
+ except IngestError as e:
250
+ logger.warning("github poll %s skip issue %s: %s", self._source_id, iid, e)
251
+ continue
252
+
253
+ profile = self._config.extraction_profile
254
+ ctx = AstrocyteContext(principal=p) if p else None
255
+ metadata = {
256
+ "github": {
257
+ "issue_id": iid,
258
+ "number": n,
259
+ "html_url": u,
260
+ "updated_at": upd,
261
+ "author": login,
262
+ }
263
+ }
264
+
265
+ await self._retain(
266
+ text,
267
+ bank_id,
268
+ metadata=metadata,
269
+ content_type="text",
270
+ extraction_profile=profile,
271
+ source=self._source_id,
272
+ context=ctx,
273
+ )
274
+ self._seen_updated[iid] = upd
275
+
276
+ if cursor_max:
277
+ self._since = cursor_max
278
+ self._last_error = None
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "astrocyte-ingestion-github"
7
+ version = "0.8.0"
8
+ description = "GitHub Issues API poll IngestSource adapter for Astrocyte"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "Apache-2.0"
12
+ dependencies = [
13
+ "astrocyte>=0.7.0,<0.9",
14
+ "httpx>=0.27",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest>=8.0", "pytest-asyncio>=0.23"]
19
+
20
+ [project.entry-points."astrocyte.ingest_poll_drivers"]
21
+ github = "astrocyte_ingestion_github.source:GithubPollIngestSource"
22
+
23
+ [tool.hatch.build.targets.wheel]
24
+ packages = ["astrocyte_ingestion_github"]
25
+
26
+ [tool.uv.sources]
27
+ astrocyte = { path = "../../astrocyte-py", editable = true }
28
+
29
+ [tool.pytest.ini_options]
30
+ asyncio_mode = "auto"
31
+ testpaths = ["tests"]
32
+
33
+ [tool.ruff]
34
+ line-length = 120
35
+ target-version = "py311"
36
+
37
+ [tool.ruff.lint]
38
+ select = ["E", "W", "F", "I"]
@@ -0,0 +1,75 @@
1
+ """Unit tests for GithubPollIngestSource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import httpx
6
+ import pytest
7
+ from astrocyte.config import SourceConfig
8
+
9
+ from astrocyte_ingestion_github import GithubPollIngestSource
10
+
11
+
12
+ @pytest.mark.asyncio
13
+ async def test_poll_once_skips_pull_requests_and_recalls_retain() -> None:
14
+ retained: list[tuple[str, str, dict[str, object]]] = []
15
+
16
+ async def retain(text: str, bank_id: str, **kwargs: object) -> object:
17
+ from astrocyte.types import RetainResult
18
+
19
+ meta = kwargs.get("metadata")
20
+ retained.append((text, bank_id, meta if isinstance(meta, dict) else {}))
21
+ return RetainResult(stored=True, memory_id="m1")
22
+
23
+ def handler(request: httpx.Request) -> httpx.Response:
24
+ assert request.method == "GET"
25
+ assert "/repos/o/r/issues" in str(request.url)
26
+ payload = [
27
+ {
28
+ "id": 1001,
29
+ "updated_at": "2024-01-02T00:00:00Z",
30
+ "title": "Hello",
31
+ "body": "World",
32
+ "number": 10,
33
+ "html_url": "https://github.com/o/r/issues/10",
34
+ "user": {"login": "bob"},
35
+ },
36
+ {
37
+ "id": 1002,
38
+ "updated_at": "2024-01-01T00:00:00Z",
39
+ "title": "PR",
40
+ "pull_request": {
41
+ "url": "https://api.github.com/repos/o/r/pulls/1",
42
+ "html_url": "https://github.com/o/r/pull/1",
43
+ },
44
+ },
45
+ ]
46
+ return httpx.Response(200, json=payload)
47
+
48
+ cfg = SourceConfig(
49
+ type="poll",
50
+ driver="github",
51
+ path="o/r",
52
+ interval_seconds=60,
53
+ target_bank="bank-a",
54
+ auth={"token": "ghp_test_token"},
55
+ )
56
+ src = GithubPollIngestSource("gh", cfg, retain=retain)
57
+ src._client = httpx.AsyncClient(
58
+ transport=httpx.MockTransport(handler),
59
+ base_url="https://api.github.com",
60
+ headers={
61
+ "Accept": "application/vnd.github+json",
62
+ "Authorization": "Bearer ghp_test_token",
63
+ },
64
+ )
65
+ try:
66
+ await src._poll_once()
67
+ finally:
68
+ await src._client.aclose()
69
+ src._client = None
70
+
71
+ assert len(retained) == 1
72
+ assert retained[0][1] == "bank-a"
73
+ assert "[GitHub #10]" in retained[0][0]
74
+ assert "Hello" in retained[0][0]
75
+ assert retained[0][2].get("github", {}).get("number") == 10
@@ -0,0 +1,284 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.11"
4
+
5
+ [[package]]
6
+ name = "anyio"
7
+ version = "4.13.0"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ dependencies = [
10
+ { name = "idna" },
11
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
12
+ ]
13
+ sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
14
+ wheels = [
15
+ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
16
+ ]
17
+
18
+ [[package]]
19
+ name = "astrocyte"
20
+ source = { editable = "../../astrocyte-py" }
21
+ dependencies = [
22
+ { name = "httpx" },
23
+ { name = "pyyaml" },
24
+ ]
25
+
26
+ [package.metadata]
27
+ requires-dist = [
28
+ { name = "astrocyte-ingestion-github", marker = "extra == 'all'", editable = "." },
29
+ { name = "astrocyte-ingestion-github", marker = "extra == 'dev'", editable = "." },
30
+ { name = "astrocyte-ingestion-github", marker = "extra == 'poll'", editable = "." },
31
+ { name = "astrocyte-ingestion-kafka", marker = "extra == 'all'", editable = "../astrocyte-ingestion-kafka" },
32
+ { name = "astrocyte-ingestion-kafka", marker = "extra == 'dev'", editable = "../astrocyte-ingestion-kafka" },
33
+ { name = "astrocyte-ingestion-kafka", marker = "extra == 'stream'", editable = "../astrocyte-ingestion-kafka" },
34
+ { name = "astrocyte-ingestion-redis", marker = "extra == 'all'", editable = "../astrocyte-ingestion-redis" },
35
+ { name = "astrocyte-ingestion-redis", marker = "extra == 'dev'", editable = "../astrocyte-ingestion-redis" },
36
+ { name = "astrocyte-ingestion-redis", marker = "extra == 'stream'", editable = "../astrocyte-ingestion-redis" },
37
+ { name = "deepeval", marker = "extra == 'all'", specifier = ">=2.0" },
38
+ { name = "deepeval", marker = "extra == 'dev'", specifier = ">=2.0" },
39
+ { name = "deepeval", marker = "extra == 'eval'", specifier = ">=2.0" },
40
+ { name = "fastapi", marker = "extra == 'all'", specifier = ">=0.115" },
41
+ { name = "fastapi", marker = "extra == 'dev'", specifier = ">=0.115" },
42
+ { name = "fastapi", marker = "extra == 'gateway'", specifier = ">=0.115" },
43
+ { name = "fastmcp", marker = "extra == 'all'", specifier = ">=3.0" },
44
+ { name = "fastmcp", marker = "extra == 'dev'", specifier = ">=3.0" },
45
+ { name = "fastmcp", marker = "extra == 'mcp'", specifier = ">=3.0" },
46
+ { name = "httpx", specifier = ">=0.27" },
47
+ { name = "openai", marker = "extra == 'all'", specifier = ">=1.0" },
48
+ { name = "openai", marker = "extra == 'openai'", specifier = ">=1.0" },
49
+ { name = "opentelemetry-api", marker = "extra == 'all'", specifier = ">=1.20" },
50
+ { name = "opentelemetry-api", marker = "extra == 'otel'", specifier = ">=1.20" },
51
+ { name = "opentelemetry-sdk", marker = "extra == 'all'", specifier = ">=1.20" },
52
+ { name = "opentelemetry-sdk", marker = "extra == 'otel'", specifier = ">=1.20" },
53
+ { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0" },
54
+ { name = "prometheus-client", marker = "extra == 'all'", specifier = ">=0.20" },
55
+ { name = "prometheus-client", marker = "extra == 'prometheus'", specifier = ">=0.20" },
56
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
57
+ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
58
+ { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0" },
59
+ { name = "pytest-timeout", marker = "extra == 'dev'", specifier = ">=2.2" },
60
+ { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.5" },
61
+ { name = "pyyaml", specifier = ">=6.0" },
62
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4" },
63
+ { name = "uvicorn", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.30" },
64
+ { name = "uvicorn", extras = ["standard"], marker = "extra == 'dev'", specifier = ">=0.30" },
65
+ { name = "uvicorn", extras = ["standard"], marker = "extra == 'gateway'", specifier = ">=0.30" },
66
+ ]
67
+ provides-extras = ["all", "dev", "eval", "gateway", "mcp", "openai", "otel", "poll", "prometheus", "stream"]
68
+
69
+ [[package]]
70
+ name = "astrocyte-ingestion-github"
71
+ version = "0.1.0"
72
+ source = { editable = "." }
73
+ dependencies = [
74
+ { name = "astrocyte" },
75
+ { name = "httpx" },
76
+ ]
77
+
78
+ [package.optional-dependencies]
79
+ dev = [
80
+ { name = "pytest" },
81
+ { name = "pytest-asyncio" },
82
+ ]
83
+
84
+ [package.metadata]
85
+ requires-dist = [
86
+ { name = "astrocyte", editable = "../../astrocyte-py" },
87
+ { name = "httpx", specifier = ">=0.27" },
88
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
89
+ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
90
+ ]
91
+ provides-extras = ["dev"]
92
+
93
+ [[package]]
94
+ name = "certifi"
95
+ version = "2026.2.25"
96
+ source = { registry = "https://pypi.org/simple" }
97
+ sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" }
98
+ wheels = [
99
+ { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
100
+ ]
101
+
102
+ [[package]]
103
+ name = "colorama"
104
+ version = "0.4.6"
105
+ source = { registry = "https://pypi.org/simple" }
106
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
107
+ wheels = [
108
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
109
+ ]
110
+
111
+ [[package]]
112
+ name = "h11"
113
+ version = "0.16.0"
114
+ source = { registry = "https://pypi.org/simple" }
115
+ sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
116
+ wheels = [
117
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
118
+ ]
119
+
120
+ [[package]]
121
+ name = "httpcore"
122
+ version = "1.0.9"
123
+ source = { registry = "https://pypi.org/simple" }
124
+ dependencies = [
125
+ { name = "certifi" },
126
+ { name = "h11" },
127
+ ]
128
+ sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
129
+ wheels = [
130
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
131
+ ]
132
+
133
+ [[package]]
134
+ name = "httpx"
135
+ version = "0.28.1"
136
+ source = { registry = "https://pypi.org/simple" }
137
+ dependencies = [
138
+ { name = "anyio" },
139
+ { name = "certifi" },
140
+ { name = "httpcore" },
141
+ { name = "idna" },
142
+ ]
143
+ sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
144
+ wheels = [
145
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
146
+ ]
147
+
148
+ [[package]]
149
+ name = "idna"
150
+ version = "3.11"
151
+ source = { registry = "https://pypi.org/simple" }
152
+ sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
153
+ wheels = [
154
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
155
+ ]
156
+
157
+ [[package]]
158
+ name = "iniconfig"
159
+ version = "2.3.0"
160
+ source = { registry = "https://pypi.org/simple" }
161
+ sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
162
+ wheels = [
163
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
164
+ ]
165
+
166
+ [[package]]
167
+ name = "packaging"
168
+ version = "26.0"
169
+ source = { registry = "https://pypi.org/simple" }
170
+ sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
171
+ wheels = [
172
+ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
173
+ ]
174
+
175
+ [[package]]
176
+ name = "pluggy"
177
+ version = "1.6.0"
178
+ source = { registry = "https://pypi.org/simple" }
179
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
180
+ wheels = [
181
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
182
+ ]
183
+
184
+ [[package]]
185
+ name = "pygments"
186
+ version = "2.20.0"
187
+ source = { registry = "https://pypi.org/simple" }
188
+ sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
189
+ wheels = [
190
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
191
+ ]
192
+
193
+ [[package]]
194
+ name = "pytest"
195
+ version = "9.0.3"
196
+ source = { registry = "https://pypi.org/simple" }
197
+ dependencies = [
198
+ { name = "colorama", marker = "sys_platform == 'win32'" },
199
+ { name = "iniconfig" },
200
+ { name = "packaging" },
201
+ { name = "pluggy" },
202
+ { name = "pygments" },
203
+ ]
204
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
205
+ wheels = [
206
+ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
207
+ ]
208
+
209
+ [[package]]
210
+ name = "pytest-asyncio"
211
+ version = "1.3.0"
212
+ source = { registry = "https://pypi.org/simple" }
213
+ dependencies = [
214
+ { name = "pytest" },
215
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
216
+ ]
217
+ sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
218
+ wheels = [
219
+ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
220
+ ]
221
+
222
+ [[package]]
223
+ name = "pyyaml"
224
+ version = "6.0.3"
225
+ source = { registry = "https://pypi.org/simple" }
226
+ sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
227
+ wheels = [
228
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
229
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
230
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
231
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
232
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
233
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
234
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
235
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
236
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
237
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
238
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
239
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
240
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
241
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
242
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
243
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
244
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
245
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
246
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
247
+ { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
248
+ { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
249
+ { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
250
+ { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
251
+ { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
252
+ { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
253
+ { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
254
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
255
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
256
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
257
+ { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
258
+ { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
259
+ { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
260
+ { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
261
+ { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
262
+ { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
263
+ { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
264
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
265
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
266
+ { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
267
+ { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
268
+ { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
269
+ { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
270
+ { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
271
+ { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
272
+ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
273
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
274
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
275
+ ]
276
+
277
+ [[package]]
278
+ name = "typing-extensions"
279
+ version = "4.15.0"
280
+ source = { registry = "https://pypi.org/simple" }
281
+ sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
282
+ wheels = [
283
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
284
+ ]