kurrent-agent-schema 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.
@@ -0,0 +1,47 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ .pytest_cache/
7
+ .mypy_cache/
8
+ .ruff_cache/
9
+ .tox/
10
+ .coverage
11
+ coverage.xml
12
+ htmlcov/
13
+ .venv/
14
+ venv/
15
+ env/
16
+ dist/
17
+ build/
18
+
19
+ # .NET
20
+ bin/
21
+ obj/
22
+ TestResults/
23
+ *.user
24
+ *.suo
25
+ *.ncrunchproject
26
+ *.ncrunchsolution
27
+ .vs/
28
+
29
+ # Editors / tools
30
+ .idea/
31
+ .vscode/
32
+ .qodo/
33
+ .capacitor/
34
+ .claude/
35
+
36
+ # Git worktrees
37
+ .worktrees/
38
+
39
+ # OS
40
+ .DS_Store
41
+ Thumbs.db
42
+
43
+ # Env files (be strict — only commit templates, not real credentials)
44
+ *.env
45
+ !*.env.example
46
+ appsettings.Development.json
47
+ appsettings.Local.json
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: kurrent-agent-schema
3
+ Version: 0.1.0
4
+ Summary: Canonical event schema for Kurrent agent integrations (schema v2)
5
+ Author: Kurrent, Inc.
6
+ License: Apache-2.0
7
+ Keywords: agents,eventsourcing,kurrentdb,llm,schema
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: pydantic>=2.5
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8; extra == 'dev'
12
+ Requires-Dist: ruff>=0.6; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # kurrent-agent-schema
16
+
17
+ Canonical event schema types for Kurrent agent integrations — schema version **2**.
18
+
19
+ This package is the Python mirror of the canonical agent event schema shared across Kurrent's agent-framework integrations (Google ADK, Microsoft Agent Framework, Strands, OpenAI Agents, Claude Agent SDK) and Capacitor. The prose specification lives in [`schema/SCHEMA_v2.md`](../SCHEMA_v2.md); the .NET mirror is [`Kurrent.Agent.Schema`](../dotnet/Kurrent.Agent.Schema/).
20
+
21
+ ## What's here
22
+
23
+ - **Canonical event models** (Pydantic v2): `SessionStarted`, `SessionEnded`, `SessionContinuedAs`, `UserMessageReceived`, `AssistantTextGenerated`, `AssistantToolCallsGenerated`, `AssistantThinkingGenerated`, `ToolResultReceived`, `InterruptIssued`, `InterruptResolved`, `SubagentStarted`, `SubagentCompleted`, `FactRetained`, `ArtifactVersionCreated`, `EvalRunStarted`, `TurnScored`, `EvalRunCompleted`.
24
+ - **Value types**: `AgentConfig`, `ToolSpec`, `ToolCallInfo`.
25
+ - **Usage metadata**: `TokenUsage` (carried on KurrentDB event metadata under the `$usage` key, not as a standalone event).
26
+ - **Stream-name builders**: `agent_session_stream`, `agent_subsession_stream`, `agent_memory_stream`, `agent_artifact_stream`, `eval_run_stream`.
27
+ - **Type-name registry**: `EVENT_TYPE_NAMES` / `EVENT_TYPE_BY_NAME` for wiring up KurrentDB event-type to/from CLR-type conversion.
28
+
29
+ ## What's not here
30
+
31
+ - **Framework-specific events and extension shapes.** ADK's `AgentTransferred`/`Rewind`, AFW's workflow checkpoints, Capacitor's `AgentRunStarted`/visibility events, coding-agent fields under `extensions.claude_code.*`, etc. Those live in their owning integration packages — this package deliberately carries only the portable vocabulary.
32
+
33
+ ## Usage
34
+
35
+ ```python
36
+ from datetime import datetime, timezone
37
+ from kurrent_agent_schema import (
38
+ SessionStarted, UserMessageReceived, AssistantTextGenerated,
39
+ TokenUsage, USAGE_METADATA_KEY, agent_session_stream,
40
+ )
41
+
42
+ stream = agent_session_stream("sess-0001") # "AgentSession-sess-0001"
43
+
44
+ event = UserMessageReceived(
45
+ content="hello",
46
+ message_index=0,
47
+ timestamp=datetime.now(tz=timezone.utc),
48
+ extensions={"adk": {"invocation_id": "inv_abc"}},
49
+ )
50
+
51
+ # $usage goes on KurrentDB metadata, not the payload
52
+ usage = TokenUsage(input_tokens=1507, output_tokens=203, model="claude-sonnet-4-6")
53
+ ```
54
+
55
+ ## Drift guard
56
+
57
+ Every canonical event has a JSON fixture under [`schema/fixtures/events/`](../fixtures/events/). The test suite here runs a round-trip assertion per fixture; an equivalent suite in the .NET package runs against the same fixtures. Adding or modifying a canonical field requires a coordinated PR: update the model **and** the fixture, in both Python and .NET packages. CI fails otherwise.
58
+
59
+ ```bash
60
+ cd schema/python
61
+ uv pip install -e ".[dev]"
62
+ pytest
63
+ ```
64
+
65
+ ## Version
66
+
67
+ - Package: `0.1.0` (initial pre-1.0 release carrying schema v2).
68
+ - Schema: `SCHEMA_VERSION = 2`, stamped on KurrentDB metadata under `$schema_version` by integration writers.
@@ -0,0 +1,54 @@
1
+ # kurrent-agent-schema
2
+
3
+ Canonical event schema types for Kurrent agent integrations — schema version **2**.
4
+
5
+ This package is the Python mirror of the canonical agent event schema shared across Kurrent's agent-framework integrations (Google ADK, Microsoft Agent Framework, Strands, OpenAI Agents, Claude Agent SDK) and Capacitor. The prose specification lives in [`schema/SCHEMA_v2.md`](../SCHEMA_v2.md); the .NET mirror is [`Kurrent.Agent.Schema`](../dotnet/Kurrent.Agent.Schema/).
6
+
7
+ ## What's here
8
+
9
+ - **Canonical event models** (Pydantic v2): `SessionStarted`, `SessionEnded`, `SessionContinuedAs`, `UserMessageReceived`, `AssistantTextGenerated`, `AssistantToolCallsGenerated`, `AssistantThinkingGenerated`, `ToolResultReceived`, `InterruptIssued`, `InterruptResolved`, `SubagentStarted`, `SubagentCompleted`, `FactRetained`, `ArtifactVersionCreated`, `EvalRunStarted`, `TurnScored`, `EvalRunCompleted`.
10
+ - **Value types**: `AgentConfig`, `ToolSpec`, `ToolCallInfo`.
11
+ - **Usage metadata**: `TokenUsage` (carried on KurrentDB event metadata under the `$usage` key, not as a standalone event).
12
+ - **Stream-name builders**: `agent_session_stream`, `agent_subsession_stream`, `agent_memory_stream`, `agent_artifact_stream`, `eval_run_stream`.
13
+ - **Type-name registry**: `EVENT_TYPE_NAMES` / `EVENT_TYPE_BY_NAME` for wiring up KurrentDB event-type to/from CLR-type conversion.
14
+
15
+ ## What's not here
16
+
17
+ - **Framework-specific events and extension shapes.** ADK's `AgentTransferred`/`Rewind`, AFW's workflow checkpoints, Capacitor's `AgentRunStarted`/visibility events, coding-agent fields under `extensions.claude_code.*`, etc. Those live in their owning integration packages — this package deliberately carries only the portable vocabulary.
18
+
19
+ ## Usage
20
+
21
+ ```python
22
+ from datetime import datetime, timezone
23
+ from kurrent_agent_schema import (
24
+ SessionStarted, UserMessageReceived, AssistantTextGenerated,
25
+ TokenUsage, USAGE_METADATA_KEY, agent_session_stream,
26
+ )
27
+
28
+ stream = agent_session_stream("sess-0001") # "AgentSession-sess-0001"
29
+
30
+ event = UserMessageReceived(
31
+ content="hello",
32
+ message_index=0,
33
+ timestamp=datetime.now(tz=timezone.utc),
34
+ extensions={"adk": {"invocation_id": "inv_abc"}},
35
+ )
36
+
37
+ # $usage goes on KurrentDB metadata, not the payload
38
+ usage = TokenUsage(input_tokens=1507, output_tokens=203, model="claude-sonnet-4-6")
39
+ ```
40
+
41
+ ## Drift guard
42
+
43
+ Every canonical event has a JSON fixture under [`schema/fixtures/events/`](../fixtures/events/). The test suite here runs a round-trip assertion per fixture; an equivalent suite in the .NET package runs against the same fixtures. Adding or modifying a canonical field requires a coordinated PR: update the model **and** the fixture, in both Python and .NET packages. CI fails otherwise.
44
+
45
+ ```bash
46
+ cd schema/python
47
+ uv pip install -e ".[dev]"
48
+ pytest
49
+ ```
50
+
51
+ ## Version
52
+
53
+ - Package: `0.1.0` (initial pre-1.0 release carrying schema v2).
54
+ - Schema: `SCHEMA_VERSION = 2`, stamped on KurrentDB metadata under `$schema_version` by integration writers.
@@ -0,0 +1,78 @@
1
+ """Canonical event schema for Kurrent agent integrations.
2
+
3
+ See ``schema/SCHEMA_v2.md`` at the repo root for the prose specification.
4
+ This package is the Python mirror; ``Kurrent.Agent.Schema`` is the .NET mirror.
5
+ """
6
+
7
+ from kurrent_agent_schema.events import (
8
+ AgentConfig,
9
+ ArtifactVersionCreated,
10
+ AssistantTextGenerated,
11
+ AssistantThinkingGenerated,
12
+ AssistantToolCallsGenerated,
13
+ EvalRunCompleted,
14
+ EvalRunStarted,
15
+ FactRetained,
16
+ InterruptIssued,
17
+ InterruptResolved,
18
+ SessionContinuedAs,
19
+ SessionEnded,
20
+ SessionStarted,
21
+ SubagentCompleted,
22
+ SubagentStarted,
23
+ ToolCallInfo,
24
+ ToolResultReceived,
25
+ ToolSpec,
26
+ TurnScored,
27
+ UserMessageReceived,
28
+ )
29
+ from kurrent_agent_schema.streams import (
30
+ agent_artifact_stream,
31
+ agent_memory_stream,
32
+ agent_session_stream,
33
+ agent_subsession_stream,
34
+ eval_run_stream,
35
+ )
36
+ from kurrent_agent_schema.usage import TokenUsage, USAGE_METADATA_KEY
37
+ from kurrent_agent_schema.version import SCHEMA_VERSION
38
+
39
+ __all__ = [
40
+ # Value types
41
+ "AgentConfig",
42
+ "ToolSpec",
43
+ "ToolCallInfo",
44
+ "TokenUsage",
45
+ # Session lifecycle
46
+ "SessionStarted",
47
+ "SessionEnded",
48
+ "SessionContinuedAs",
49
+ # Conversation
50
+ "UserMessageReceived",
51
+ "AssistantTextGenerated",
52
+ "AssistantToolCallsGenerated",
53
+ "AssistantThinkingGenerated",
54
+ "ToolResultReceived",
55
+ # Interrupts
56
+ "InterruptIssued",
57
+ "InterruptResolved",
58
+ # Subagents
59
+ "SubagentStarted",
60
+ "SubagentCompleted",
61
+ # Memory
62
+ "FactRetained",
63
+ # Artifacts
64
+ "ArtifactVersionCreated",
65
+ # Evaluation
66
+ "EvalRunStarted",
67
+ "TurnScored",
68
+ "EvalRunCompleted",
69
+ # Stream builders
70
+ "agent_session_stream",
71
+ "agent_subsession_stream",
72
+ "agent_memory_stream",
73
+ "agent_artifact_stream",
74
+ "eval_run_stream",
75
+ # Constants
76
+ "USAGE_METADATA_KEY",
77
+ "SCHEMA_VERSION",
78
+ ]
@@ -0,0 +1,304 @@
1
+ """Canonical event models (schema v2).
2
+
3
+ See ``schema/SCHEMA_v2.md`` at the repo root for the prose specification.
4
+
5
+ All events inherit from ``_EventBase``, which carries the ``extensions``
6
+ envelope and the shared Pydantic configuration (``extra="ignore"`` for
7
+ forward-compatibility, frozen for hashability, snake_case JSON).
8
+
9
+ Stream placement and semantic conventions live in ``streams.py`` and the
10
+ schema doc — this module is strictly the type shapes.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from datetime import datetime
16
+ from typing import Any
17
+
18
+ from pydantic import BaseModel, ConfigDict
19
+
20
+
21
+ class _EventBase(BaseModel):
22
+ """Shared configuration for every canonical event.
23
+
24
+ ``extra="ignore"`` preserves forward-compatibility: unknown fields added
25
+ in a later minor version are dropped on read, not raised.
26
+
27
+ ``ser_json_bytes="base64"`` / ``val_json_bytes="base64"`` round-trip
28
+ ``bytes`` fields through JSON for ``ArtifactVersionCreated.inline_bytes``.
29
+ """
30
+
31
+ model_config = ConfigDict(
32
+ populate_by_name=True,
33
+ extra="ignore",
34
+ frozen=True,
35
+ ser_json_bytes="base64",
36
+ val_json_bytes="base64",
37
+ )
38
+
39
+ extensions: dict[str, dict[str, Any]] | None = None
40
+ """Framework-specific extension envelope keyed by slug (``adk``, ``afw``,
41
+ ``strands``, ``openai``, ``claude_sdk``, ``claude_code``, …).
42
+ See ``schema/SCHEMA_v2.md §5``."""
43
+
44
+
45
+ # --- Value types -------------------------------------------------------------
46
+
47
+
48
+ class ToolSpec(BaseModel):
49
+ """Tool description captured in ``AgentConfig.tools``."""
50
+
51
+ model_config = ConfigDict(populate_by_name=True, extra="ignore", frozen=True)
52
+
53
+ name: str
54
+ description: str | None = None
55
+ input_schema: dict[str, Any] | None = None
56
+ source: str | None = None
57
+
58
+
59
+ class AgentConfig(BaseModel):
60
+ """Informational snapshot of the agent configuration at session start.
61
+
62
+ All fields optional. Not a contract — readers use this for context, not
63
+ to reproduce the writer's agent.
64
+ """
65
+
66
+ model_config = ConfigDict(populate_by_name=True, extra="ignore", frozen=True)
67
+
68
+ tools: list[ToolSpec] | None = None
69
+ plugins: list[str] | None = None
70
+ conversation_manager: dict[str, Any] | None = None
71
+ model_parameters: dict[str, Any] | None = None
72
+
73
+
74
+ class ToolCallInfo(BaseModel):
75
+ """One tool call within ``AssistantToolCallsGenerated.tool_calls``."""
76
+
77
+ model_config = ConfigDict(populate_by_name=True, extra="ignore", frozen=True)
78
+
79
+ call_id: str
80
+ tool_name: str
81
+ arguments: dict[str, Any] | None = None
82
+
83
+
84
+ # --- Session lifecycle (SCHEMA_v2.md §3.1) -----------------------------------
85
+
86
+
87
+ class SessionStarted(_EventBase):
88
+ app_name: str | None = None
89
+ agent_name: str | None = None
90
+ model: str | None = None
91
+ tenant_id: str | None = None
92
+ user_id: str | None = None
93
+ agent_config: AgentConfig | None = None
94
+ previous_session_id: str | None = None
95
+ """Prior session this one resumes or forks from. New in v2."""
96
+ timestamp: datetime
97
+
98
+
99
+ class SessionEnded(_EventBase):
100
+ reason: str | None = None
101
+ timestamp: datetime
102
+
103
+
104
+ class SessionContinuedAs(_EventBase):
105
+ """Written to the predecessor session pointing forward to its successor.
106
+
107
+ Paired with ``SessionStarted.previous_session_id`` on the successor, this
108
+ forms a bidirectional chain. New in v2.
109
+ """
110
+
111
+ next_session_id: str
112
+ reason: str | None = None
113
+ timestamp: datetime
114
+
115
+
116
+ # --- Conversation events (SCHEMA_v2.md §3.4) ---------------------------------
117
+
118
+
119
+ class UserMessageReceived(_EventBase):
120
+ content: str | None = None
121
+ message_id: str | None = None
122
+ author_name: str | None = None
123
+ created_at: datetime | None = None
124
+ message_index: int
125
+ timestamp: datetime
126
+
127
+
128
+ class AssistantTextGenerated(_EventBase):
129
+ content: str | None = None
130
+ message_id: str | None = None
131
+ author_name: str | None = None
132
+ created_at: datetime | None = None
133
+ message_index: int
134
+ timestamp: datetime
135
+
136
+
137
+ class AssistantToolCallsGenerated(_EventBase):
138
+ tool_calls: list[ToolCallInfo]
139
+ content: str | None = None
140
+ message_id: str | None = None
141
+ author_name: str | None = None
142
+ created_at: datetime | None = None
143
+ message_index: int
144
+ timestamp: datetime
145
+
146
+
147
+ class AssistantThinkingGenerated(_EventBase):
148
+ """Assistant reasoning output (extended thinking / o-series / Gemini thinking).
149
+
150
+ Plaintext reasoning in ``content`` for Claude and Gemini; encrypted blobs
151
+ from OpenAI o-series have ``encrypted=True`` with the opaque value in
152
+ ``extensions.openai.thinking.raw`` and the provider signature on
153
+ ``signature``. New in v2 (SCHEMA_v2.md §3.2).
154
+ """
155
+
156
+ content: str | None = None
157
+ encrypted: bool = False
158
+ signature: str | None = None
159
+ message_id: str | None = None
160
+ author_name: str | None = None
161
+ created_at: datetime | None = None
162
+ message_index: int
163
+ timestamp: datetime
164
+
165
+
166
+ class ToolResultReceived(_EventBase):
167
+ call_id: str
168
+ tool_name: str | None = None
169
+ result: str | None = None
170
+ message_id: str | None = None
171
+ author_name: str | None = None
172
+ created_at: datetime | None = None
173
+ message_index: int
174
+ timestamp: datetime
175
+
176
+
177
+ # --- Interrupts (SCHEMA_v2.md §3.3) ------------------------------------------
178
+
179
+
180
+ class InterruptIssued(_EventBase):
181
+ """Mid-turn human-in-the-loop pause (permission prompt, approval, input, auth).
182
+
183
+ ``kind`` is an open string; the documented set is
184
+ ``permission | approval | input | auth``, readers must tolerate unknowns.
185
+ Framework-specific details (tool_input, auth challenge) go in
186
+ ``extensions.{framework}.interrupt``. New in v2.
187
+ """
188
+
189
+ request_id: str
190
+ kind: str
191
+ tool_name: str | None = None
192
+ prompt: str | None = None
193
+ timestamp: datetime
194
+
195
+
196
+ class InterruptResolved(_EventBase):
197
+ """Resolution of an ``InterruptIssued``, keyed by the same ``request_id``.
198
+
199
+ ``outcome`` documented set: ``allow | allow_once | allow_always | deny |
200
+ cancel | answered | timeout``. Open string; readers tolerate unknowns.
201
+ New in v2.
202
+ """
203
+
204
+ request_id: str
205
+ outcome: str
206
+ response: str | None = None
207
+ timestamp: datetime
208
+
209
+
210
+ # --- Subagents (SCHEMA_v2.md §3.5) -------------------------------------------
211
+ # Written to the PARENT session stream to record subagent lifecycle; the
212
+ # subagent's own conversation lives in AgentSubsession-{parent}-{agent_id}.
213
+
214
+
215
+ class SubagentStarted(_EventBase):
216
+ agent_id: str
217
+ agent_type: str | None = None
218
+ prompt: str | None = None
219
+ subsession_stream: str | None = None
220
+ timestamp: datetime
221
+
222
+
223
+ class SubagentCompleted(_EventBase):
224
+ agent_id: str
225
+ outcome: str | None = None
226
+ summary: str | None = None
227
+ timestamp: datetime
228
+
229
+
230
+ # --- Memory (SCHEMA_v2.md §3.7) ----------------------------------------------
231
+
232
+
233
+ class FactRetained(_EventBase):
234
+ fact: str
235
+ retained_at: datetime
236
+
237
+
238
+ # --- Artifacts (SCHEMA_v2.md §3.7) -------------------------------------------
239
+
240
+
241
+ class ArtifactVersionCreated(_EventBase):
242
+ version: int
243
+ mime_type: str | None = None
244
+ inline_bytes: bytes | None = None
245
+ canonical_uri: str | None = None
246
+ custom_metadata: dict[str, Any] | None = None
247
+ created_at: datetime
248
+
249
+
250
+ # --- Evaluation (SCHEMA_v2.md §3.7) ------------------------------------------
251
+
252
+
253
+ class EvalRunStarted(_EventBase):
254
+ session_id: str
255
+ scorer: str
256
+ criteria: str
257
+ timestamp: datetime
258
+
259
+
260
+ class TurnScored(_EventBase):
261
+ session_id: str
262
+ turn_index: int
263
+ input: str | None = None
264
+ output: str | None = None
265
+ score: float
266
+ score_label: str | None = None
267
+ reason: str | None = None
268
+ timestamp: datetime
269
+
270
+
271
+ class EvalRunCompleted(_EventBase):
272
+ session_id: str
273
+ turns_scored: int
274
+ average_score: float
275
+ total_cost: float | None = None
276
+ timestamp: datetime
277
+
278
+
279
+ # --- Event type name registry -------------------------------------------------
280
+
281
+ EVENT_TYPE_NAMES: dict[type[_EventBase], str] = {
282
+ SessionStarted: "SessionStarted",
283
+ SessionEnded: "SessionEnded",
284
+ SessionContinuedAs: "SessionContinuedAs",
285
+ UserMessageReceived: "UserMessageReceived",
286
+ AssistantTextGenerated: "AssistantTextGenerated",
287
+ AssistantToolCallsGenerated: "AssistantToolCallsGenerated",
288
+ AssistantThinkingGenerated: "AssistantThinkingGenerated",
289
+ ToolResultReceived: "ToolResultReceived",
290
+ InterruptIssued: "InterruptIssued",
291
+ InterruptResolved: "InterruptResolved",
292
+ SubagentStarted: "SubagentStarted",
293
+ SubagentCompleted: "SubagentCompleted",
294
+ FactRetained: "FactRetained",
295
+ ArtifactVersionCreated: "ArtifactVersionCreated",
296
+ EvalRunStarted: "EvalRunStarted",
297
+ TurnScored: "TurnScored",
298
+ EvalRunCompleted: "EvalRunCompleted",
299
+ }
300
+ """Canonical event type name on the wire (KurrentDB ``event_type``)
301
+ for each model class. Integration writers use this to stamp events;
302
+ readers use the inverse lookup."""
303
+
304
+ EVENT_TYPE_BY_NAME: dict[str, type[_EventBase]] = {v: k for k, v in EVENT_TYPE_NAMES.items()}
@@ -0,0 +1,43 @@
1
+ """Canonical stream-name builders.
2
+
3
+ See ``schema/SCHEMA_v2.md §2`` for the full stream taxonomy.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ AGENT_SESSION_PREFIX: str = "AgentSession-"
9
+ AGENT_SUBSESSION_PREFIX: str = "AgentSubsession-"
10
+ AGENT_MEMORY_PREFIX: str = "AgentMemory-"
11
+ AGENT_ARTIFACT_PREFIX: str = "AgentArtifact-"
12
+ EVAL_RUN_PREFIX: str = "EvalRun-"
13
+
14
+
15
+ def agent_session_stream(session_id: str) -> str:
16
+ """Primary conversation stream for a session."""
17
+ return f"{AGENT_SESSION_PREFIX}{session_id}"
18
+
19
+
20
+ def agent_subsession_stream(parent_session_id: str, agent_id: str) -> str:
21
+ """Subagent conversation stream, scoped under a parent session.
22
+
23
+ See ``schema/SCHEMA_v2.md §3.5``.
24
+ """
25
+ return f"{AGENT_SUBSESSION_PREFIX}{parent_session_id}-{agent_id}"
26
+
27
+
28
+ def agent_memory_stream(app_name: str, user_id: str) -> str:
29
+ """Per-app, per-user retained facts. v1 default scope.
30
+
31
+ See ``schema/SCHEMA_v2.md §3.7`` (inherited from v1 §3.6).
32
+ """
33
+ return f"{AGENT_MEMORY_PREFIX}{app_name}-{user_id}"
34
+
35
+
36
+ def agent_artifact_stream(scope: str, filename: str) -> str:
37
+ """Binary artifact versions. ``scope`` is integration-defined."""
38
+ return f"{AGENT_ARTIFACT_PREFIX}{scope}-{filename}"
39
+
40
+
41
+ def eval_run_stream(run_id: str) -> str:
42
+ """Eval run events."""
43
+ return f"{EVAL_RUN_PREFIX}{run_id}"
@@ -0,0 +1,32 @@
1
+ """Token usage metadata model.
2
+
3
+ Usage rides on KurrentDB event metadata under the ``$usage`` key, on every
4
+ assistant event (``AssistantTextGenerated``, ``AssistantToolCallsGenerated``,
5
+ ``AssistantThinkingGenerated``). It is *not* a canonical event payload.
6
+
7
+ See ``schema/SCHEMA_v2.md §3.6``.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pydantic import BaseModel, ConfigDict
13
+
14
+ USAGE_METADATA_KEY: str = "$usage"
15
+ """KurrentDB metadata key under which ``TokenUsage`` is written."""
16
+
17
+
18
+ class TokenUsage(BaseModel):
19
+ """Token counts reported by the model provider for one assistant event.
20
+
21
+ All fields optional — providers differ in which counts they return.
22
+ For ``AssistantThinkingGenerated``, populate ``reasoning_tokens``.
23
+ """
24
+
25
+ model_config = ConfigDict(populate_by_name=True, extra="ignore", frozen=True)
26
+
27
+ input_tokens: int | None = None
28
+ output_tokens: int | None = None
29
+ total_tokens: int | None = None
30
+ cached_input_tokens: int | None = None
31
+ reasoning_tokens: int | None = None
32
+ model: str | None = None
@@ -0,0 +1,9 @@
1
+ """Schema version constant.
2
+
3
+ Stamped on event metadata under ``$schema_version``. Writers set this to
4
+ ``SCHEMA_VERSION``; readers that encounter a higher version should either
5
+ reject the event or degrade gracefully via ``extra="ignore"`` semantics.
6
+ """
7
+
8
+ SCHEMA_VERSION: int = 2
9
+ """Kurrent agent event schema version. See ``schema/SCHEMA_v2.md``."""
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kurrent-agent-schema"
7
+ version = "0.1.0"
8
+ description = "Canonical event schema for Kurrent agent integrations (schema v2)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Kurrent, Inc." }]
13
+ keywords = ["kurrentdb", "eventsourcing", "agents", "llm", "schema"]
14
+ dependencies = [
15
+ "pydantic >= 2.5",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ dev = [
20
+ "pytest >= 8",
21
+ "ruff >= 0.6",
22
+ ]
23
+
24
+ [tool.hatch.build.targets.wheel]
25
+ packages = ["kurrent_agent_schema"]
26
+
27
+ [tool.pytest.ini_options]
28
+ testpaths = ["tests"]
29
+
30
+ [tool.ruff]
31
+ line-length = 120
32
+ target-version = "py311"
33
+
34
+ [tool.ruff.lint]
35
+ select = ["E", "F", "I", "B", "UP", "N", "RUF"]
@@ -0,0 +1,76 @@
1
+ """Drift-detection tests for the Python canonical-types package.
2
+
3
+ For every fixture under ``schema/fixtures/events/``, load the JSON, deserialise
4
+ into the matching Pydantic model, reserialise, and assert byte-equality with
5
+ the original. The matching .NET package runs an equivalent test against the
6
+ same fixtures — together they guarantee the two implementations cannot drift
7
+ without failing CI.
8
+
9
+ See ``schema/fixtures/README.md`` for the fixture contract.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+
19
+ from kurrent_agent_schema import TokenUsage
20
+ from kurrent_agent_schema.events import EVENT_TYPE_BY_NAME
21
+
22
+ FIXTURES_ROOT = Path(__file__).resolve().parents[2] / "fixtures"
23
+ EVENTS_DIR = FIXTURES_ROOT / "events"
24
+ METADATA_DIR = FIXTURES_ROOT / "metadata"
25
+
26
+
27
+ def _load(path: Path) -> dict:
28
+ return json.loads(path.read_text())
29
+
30
+
31
+ def _normalise(obj):
32
+ """Sort dict keys recursively so key ordering differences don't fail equality."""
33
+ if isinstance(obj, dict):
34
+ return {k: _normalise(obj[k]) for k in sorted(obj)}
35
+ if isinstance(obj, list):
36
+ return [_normalise(x) for x in obj]
37
+ return obj
38
+
39
+
40
+ def _event_fixture_cases():
41
+ if not EVENTS_DIR.exists():
42
+ return []
43
+ return sorted(EVENTS_DIR.glob("*.json"))
44
+
45
+
46
+ @pytest.mark.parametrize("fixture_path", _event_fixture_cases(), ids=lambda p: p.stem)
47
+ def test_event_fixture_round_trip(fixture_path: Path) -> None:
48
+ event_name = fixture_path.stem
49
+ model = EVENT_TYPE_BY_NAME.get(event_name)
50
+ assert model is not None, f"No canonical model registered for '{event_name}'"
51
+
52
+ original = _load(fixture_path)
53
+ parsed = model.model_validate(original)
54
+ serialised = json.loads(
55
+ parsed.model_dump_json(exclude_none=True, by_alias=True)
56
+ )
57
+
58
+ assert _normalise(serialised) == _normalise(original), (
59
+ f"Round-trip drift for {event_name}: serialised form does not match fixture"
60
+ )
61
+
62
+
63
+ def test_every_canonical_event_has_a_fixture() -> None:
64
+ """Every registered canonical event type must have a fixture file."""
65
+ missing = [
66
+ name for name in EVENT_TYPE_BY_NAME
67
+ if not (EVENTS_DIR / f"{name}.json").exists()
68
+ ]
69
+ assert not missing, f"Missing fixtures for canonical events: {missing}"
70
+
71
+
72
+ def test_usage_metadata_round_trip() -> None:
73
+ original = _load(METADATA_DIR / "usage.json")
74
+ parsed = TokenUsage.model_validate(original)
75
+ serialised = json.loads(parsed.model_dump_json(exclude_none=True, by_alias=True))
76
+ assert _normalise(serialised) == _normalise(original)
@@ -0,0 +1,258 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.11"
4
+
5
+ [[package]]
6
+ name = "annotated-types"
7
+ version = "0.7.0"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
12
+ ]
13
+
14
+ [[package]]
15
+ name = "colorama"
16
+ version = "0.4.6"
17
+ source = { registry = "https://pypi.org/simple" }
18
+ 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" }
19
+ wheels = [
20
+ { 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" },
21
+ ]
22
+
23
+ [[package]]
24
+ name = "iniconfig"
25
+ version = "2.3.0"
26
+ source = { registry = "https://pypi.org/simple" }
27
+ 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" }
28
+ wheels = [
29
+ { 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" },
30
+ ]
31
+
32
+ [[package]]
33
+ name = "kurrent-agent-schema"
34
+ version = "0.1.0"
35
+ source = { editable = "." }
36
+ dependencies = [
37
+ { name = "pydantic" },
38
+ ]
39
+
40
+ [package.optional-dependencies]
41
+ dev = [
42
+ { name = "pytest" },
43
+ { name = "ruff" },
44
+ ]
45
+
46
+ [package.metadata]
47
+ requires-dist = [
48
+ { name = "pydantic", specifier = ">=2.5" },
49
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8" },
50
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.6" },
51
+ ]
52
+ provides-extras = ["dev"]
53
+
54
+ [[package]]
55
+ name = "packaging"
56
+ version = "26.1"
57
+ source = { registry = "https://pypi.org/simple" }
58
+ sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" }
59
+ wheels = [
60
+ { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" },
61
+ ]
62
+
63
+ [[package]]
64
+ name = "pluggy"
65
+ version = "1.6.0"
66
+ source = { registry = "https://pypi.org/simple" }
67
+ 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" }
68
+ wheels = [
69
+ { 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" },
70
+ ]
71
+
72
+ [[package]]
73
+ name = "pydantic"
74
+ version = "2.13.3"
75
+ source = { registry = "https://pypi.org/simple" }
76
+ dependencies = [
77
+ { name = "annotated-types" },
78
+ { name = "pydantic-core" },
79
+ { name = "typing-extensions" },
80
+ { name = "typing-inspection" },
81
+ ]
82
+ sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" }
83
+ wheels = [
84
+ { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" },
85
+ ]
86
+
87
+ [[package]]
88
+ name = "pydantic-core"
89
+ version = "2.46.3"
90
+ source = { registry = "https://pypi.org/simple" }
91
+ dependencies = [
92
+ { name = "typing-extensions" },
93
+ ]
94
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" }
95
+ wheels = [
96
+ { url = "https://files.pythonhosted.org/packages/22/a2/1ba90a83e85a3f94c796b184f3efde9c72f2830dcda493eea8d59ba78e6d/pydantic_core-2.46.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5", size = 2106740, upload-time = "2026-04-20T14:41:20.932Z" },
97
+ { url = "https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c", size = 1948293, upload-time = "2026-04-20T14:43:42.115Z" },
98
+ { url = "https://files.pythonhosted.org/packages/3e/b8/2e8e636dc9e3f16c2e16bf0849e24be82c5ee82c603c65fc0326666328fc/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e", size = 1973222, upload-time = "2026-04-20T14:41:57.841Z" },
99
+ { url = "https://files.pythonhosted.org/packages/34/36/0e730beec4d83c5306f417afbd82ff237d9a21e83c5edf675f31ed84c1fe/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287", size = 2053852, upload-time = "2026-04-20T14:40:43.077Z" },
100
+ { url = "https://files.pythonhosted.org/packages/4b/f0/3071131f47e39136a17814576e0fada9168569f7f8c0e6ac4d1ede6a4958/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe", size = 2221134, upload-time = "2026-04-20T14:43:03.349Z" },
101
+ { url = "https://files.pythonhosted.org/packages/2f/a9/a2dc023eec5aa4b02a467874bad32e2446957d2adcab14e107eab502e978/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050", size = 2279785, upload-time = "2026-04-20T14:41:19.285Z" },
102
+ { url = "https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2", size = 2089404, upload-time = "2026-04-20T14:43:10.108Z" },
103
+ { url = "https://files.pythonhosted.org/packages/2a/78/8692e3aa72b2d004f7a5d937f1dfdc8552ba26caf0bec75f342c40f00dec/pydantic_core-2.46.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa", size = 2114898, upload-time = "2026-04-20T14:44:51.475Z" },
104
+ { url = "https://files.pythonhosted.org/packages/6a/62/e83133f2e7832532060175cebf1f13748f4c7e7e7165cdd1f611f174494b/pydantic_core-2.46.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c", size = 2157856, upload-time = "2026-04-20T14:43:46.64Z" },
105
+ { url = "https://files.pythonhosted.org/packages/6d/ec/6a500e3ad7718ee50583fae79c8651f5d37e3abce1fa9ae177ae65842c53/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf", size = 2180168, upload-time = "2026-04-20T14:42:00.302Z" },
106
+ { url = "https://files.pythonhosted.org/packages/d8/53/8267811054b1aa7fc1dc7ded93812372ef79a839f5e23558136a6afbfde1/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b", size = 2322885, upload-time = "2026-04-20T14:41:05.253Z" },
107
+ { url = "https://files.pythonhosted.org/packages/c8/c1/1c0acdb3aa0856ddc4ecc55214578f896f2de16f400cf51627eb3c26c1c4/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e", size = 2360328, upload-time = "2026-04-20T14:41:43.991Z" },
108
+ { url = "https://files.pythonhosted.org/packages/f0/d0/ef39cd0f4a926814f360e71c1adeab48ad214d9727e4deb48eedfb5bce1a/pydantic_core-2.46.3-cp311-cp311-win32.whl", hash = "sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb", size = 1979464, upload-time = "2026-04-20T14:43:12.215Z" },
109
+ { url = "https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl", hash = "sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346", size = 2070837, upload-time = "2026-04-20T14:41:47.707Z" },
110
+ { url = "https://files.pythonhosted.org/packages/9f/1e/264a17cd582f6ed50950d4d03dd5fefd84e570e238afe1cb3e25cf238769/pydantic_core-2.46.3-cp311-cp311-win_arm64.whl", hash = "sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6", size = 2053647, upload-time = "2026-04-20T14:42:27.535Z" },
111
+ { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" },
112
+ { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" },
113
+ { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" },
114
+ { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" },
115
+ { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" },
116
+ { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" },
117
+ { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" },
118
+ { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" },
119
+ { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" },
120
+ { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" },
121
+ { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" },
122
+ { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" },
123
+ { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" },
124
+ { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" },
125
+ { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" },
126
+ { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" },
127
+ { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" },
128
+ { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" },
129
+ { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" },
130
+ { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" },
131
+ { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" },
132
+ { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" },
133
+ { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" },
134
+ { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" },
135
+ { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" },
136
+ { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" },
137
+ { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" },
138
+ { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" },
139
+ { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" },
140
+ { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" },
141
+ { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" },
142
+ { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" },
143
+ { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" },
144
+ { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" },
145
+ { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" },
146
+ { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" },
147
+ { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" },
148
+ { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" },
149
+ { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" },
150
+ { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" },
151
+ { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" },
152
+ { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" },
153
+ { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" },
154
+ { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" },
155
+ { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" },
156
+ { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" },
157
+ { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" },
158
+ { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" },
159
+ { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" },
160
+ { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" },
161
+ { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" },
162
+ { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" },
163
+ { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" },
164
+ { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" },
165
+ { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" },
166
+ { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" },
167
+ { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" },
168
+ { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" },
169
+ { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" },
170
+ { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" },
171
+ { url = "https://files.pythonhosted.org/packages/66/7f/03dbad45cd3aa9083fbc93c210ae8b005af67e4136a14186950a747c6874/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46", size = 2105683, upload-time = "2026-04-20T14:42:19.779Z" },
172
+ { url = "https://files.pythonhosted.org/packages/26/22/4dc186ac8ea6b257e9855031f51b62a9637beac4d68ac06bee02f046f836/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874", size = 1940052, upload-time = "2026-04-20T14:43:59.274Z" },
173
+ { url = "https://files.pythonhosted.org/packages/0d/ca/d376391a5aff1f2e8188960d7873543608130a870961c2b6b5236627c116/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76", size = 1988172, upload-time = "2026-04-20T14:41:17.469Z" },
174
+ { url = "https://files.pythonhosted.org/packages/0e/6b/523b9f85c23788755d6ab949329de692a2e3a584bc6beb67fef5e035aa9d/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531", size = 2128596, upload-time = "2026-04-20T14:40:41.707Z" },
175
+ { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" },
176
+ { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" },
177
+ { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" },
178
+ { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" },
179
+ { url = "https://files.pythonhosted.org/packages/1f/da/99d40830684f81dec901cac521b5b91c095394cc1084b9433393cde1c2df/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25", size = 2107973, upload-time = "2026-04-20T14:42:06.175Z" },
180
+ { url = "https://files.pythonhosted.org/packages/99/a5/87024121818d75bbb2a98ddbaf638e40e7a18b5e0f5492c9ca4b1b316107/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3", size = 1947191, upload-time = "2026-04-20T14:43:14.319Z" },
181
+ { url = "https://files.pythonhosted.org/packages/60/62/0c1acfe10945b83a6a59d19fbaa92f48825381509e5701b855c08f13db76/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536", size = 2123791, upload-time = "2026-04-20T14:43:22.766Z" },
182
+ { url = "https://files.pythonhosted.org/packages/75/3e/3b2393b4c8f44285561dc30b00cf307a56a2eff7c483a824db3b8221ca51/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1", size = 2153197, upload-time = "2026-04-20T14:44:27.932Z" },
183
+ { url = "https://files.pythonhosted.org/packages/ba/75/5af02fb35505051eee727c061f2881c555ab4f8ddb2d42da715a42c9731b/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c", size = 2181073, upload-time = "2026-04-20T14:43:20.729Z" },
184
+ { url = "https://files.pythonhosted.org/packages/10/92/7e0e1bd9ca3c68305db037560ca2876f89b2647deb2f8b6319005de37505/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85", size = 2315886, upload-time = "2026-04-20T14:44:04.826Z" },
185
+ { url = "https://files.pythonhosted.org/packages/b8/d8/101655f27eaf3e44558ead736b2795d12500598beed4683f279396fa186e/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8", size = 2360528, upload-time = "2026-04-20T14:40:47.431Z" },
186
+ { url = "https://files.pythonhosted.org/packages/07/0f/1c34a74c8d07136f0d729ffe5e1fdab04fbdaa7684f61a92f92511a84a15/pydantic_core-2.46.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff", size = 2184144, upload-time = "2026-04-20T14:42:57Z" },
187
+ ]
188
+
189
+ [[package]]
190
+ name = "pygments"
191
+ version = "2.20.0"
192
+ source = { registry = "https://pypi.org/simple" }
193
+ 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" }
194
+ wheels = [
195
+ { 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" },
196
+ ]
197
+
198
+ [[package]]
199
+ name = "pytest"
200
+ version = "9.0.3"
201
+ source = { registry = "https://pypi.org/simple" }
202
+ dependencies = [
203
+ { name = "colorama", marker = "sys_platform == 'win32'" },
204
+ { name = "iniconfig" },
205
+ { name = "packaging" },
206
+ { name = "pluggy" },
207
+ { name = "pygments" },
208
+ ]
209
+ 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" }
210
+ wheels = [
211
+ { 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" },
212
+ ]
213
+
214
+ [[package]]
215
+ name = "ruff"
216
+ version = "0.15.11"
217
+ source = { registry = "https://pypi.org/simple" }
218
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/8d/192f3d7103816158dfd5ea50d098ef2aec19194e6cbccd4b3485bdb2eb2d/ruff-0.15.11.tar.gz", hash = "sha256:f092b21708bf0e7437ce9ada249dfe688ff9a0954fc94abab05dcea7dcd29c33", size = 4637264, upload-time = "2026-04-16T18:46:26.58Z" }
219
+ wheels = [
220
+ { url = "https://files.pythonhosted.org/packages/02/1e/6aca3427f751295ab011828e15e9bf452200ac74484f1db4be0197b8170b/ruff-0.15.11-py3-none-linux_armv6l.whl", hash = "sha256:e927cfff503135c558eb581a0c9792264aae9507904eb27809cdcff2f2c847b7", size = 10607943, upload-time = "2026-04-16T18:46:05.967Z" },
221
+ { url = "https://files.pythonhosted.org/packages/e7/26/1341c262e74f36d4e84f3d6f4df0ac68cd53331a66bfc5080daa17c84c0b/ruff-0.15.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7a1b5b2938d8f890b76084d4fa843604d787a912541eae85fd7e233398bbb73e", size = 10988592, upload-time = "2026-04-16T18:46:00.742Z" },
222
+ { url = "https://files.pythonhosted.org/packages/03/71/850b1d6ffa9564fbb6740429bad53df1094082fe515c8c1e74b6d8d05f18/ruff-0.15.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d4176f3d194afbdaee6e41b9ccb1a2c287dba8700047df474abfbe773825d1cb", size = 10338501, upload-time = "2026-04-16T18:46:03.723Z" },
223
+ { url = "https://files.pythonhosted.org/packages/f2/11/cc1284d3e298c45a817a6aadb6c3e1d70b45c9b36d8d9cce3387b495a03a/ruff-0.15.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b17c886fb88203ced3afe7f14e8d5ae96e9d2f4ccc0ee66aa19f2c2675a27e4", size = 10670693, upload-time = "2026-04-16T18:46:41.941Z" },
224
+ { url = "https://files.pythonhosted.org/packages/ce/9e/f8288b034ab72b371513c13f9a41d9ba3effac54e24bfb467b007daee2ca/ruff-0.15.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49fafa220220afe7758a487b048de4c8f9f767f37dfefad46b9dd06759d003eb", size = 10416177, upload-time = "2026-04-16T18:46:21.717Z" },
225
+ { url = "https://files.pythonhosted.org/packages/85/71/504d79abfd3d92532ba6bbe3d1c19fada03e494332a59e37c7c2dabae427/ruff-0.15.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2ab8427e74a00d93b8bda1307b1e60970d40f304af38bccb218e056c220120d", size = 11221886, upload-time = "2026-04-16T18:46:15.086Z" },
226
+ { url = "https://files.pythonhosted.org/packages/43/5a/947e6ab7a5ad603d65b474be15a4cbc6d29832db5d762cd142e4e3a74164/ruff-0.15.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:195072c0c8e1fc8f940652073df082e37a5d9cb43b4ab1e4d0566ab8977a13b7", size = 12075183, upload-time = "2026-04-16T18:46:07.944Z" },
227
+ { url = "https://files.pythonhosted.org/packages/9f/a1/0b7bb6268775fdd3a0818aee8efd8f5b4e231d24dd4d528ced2534023182/ruff-0.15.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a0996d486af3920dec930a2e7daed4847dfc12649b537a9335585ada163e9e", size = 11516575, upload-time = "2026-04-16T18:46:31.687Z" },
228
+ { url = "https://files.pythonhosted.org/packages/30/c3/bb5168fc4d233cc06e95f482770d0f3c87945a0cd9f614b90ea8dc2f2833/ruff-0.15.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bef2cb556d509259f1fe440bb9cd33c756222cf0a7afe90d15edf0866702431", size = 11306537, upload-time = "2026-04-16T18:46:36.988Z" },
229
+ { url = "https://files.pythonhosted.org/packages/e4/92/4cfae6441f3967317946f3b788136eecf093729b94d6561f963ed810c82e/ruff-0.15.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:030d921a836d7d4a12cf6e8d984a88b66094ccb0e0f17ddd55067c331191bf19", size = 11296813, upload-time = "2026-04-16T18:46:24.182Z" },
230
+ { url = "https://files.pythonhosted.org/packages/43/26/972784c5dde8313acde8ac71ba8ac65475b85db4a2352a76c9934361f9bc/ruff-0.15.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0e783b599b4577788dbbb66b9addcef87e9a8832f4ce0c19e34bf55543a2f890", size = 10633136, upload-time = "2026-04-16T18:46:39.802Z" },
231
+ { url = "https://files.pythonhosted.org/packages/5b/53/3985a4f185020c2f367f2e08a103032e12564829742a1b417980ce1514a0/ruff-0.15.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ae90592246625ba4a34349d68ec28d4400d75182b71baa196ddb9f82db025ef5", size = 10424701, upload-time = "2026-04-16T18:46:10.381Z" },
232
+ { url = "https://files.pythonhosted.org/packages/d3/57/bf0dfb32241b56c83bb663a826133da4bf17f682ba8c096973065f6e6a68/ruff-0.15.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1f111d62e3c983ed20e0ca2e800f8d77433a5b1161947df99a5c2a3fb60514f0", size = 10873887, upload-time = "2026-04-16T18:46:29.157Z" },
233
+ { url = "https://files.pythonhosted.org/packages/02/05/e48076b2a57dc33ee8c7a957296f97c744ca891a8ffb4ffb1aaa3b3f517d/ruff-0.15.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:06f483d6646f59eaffba9ae30956370d3a886625f511a3108994000480621d1c", size = 11404316, upload-time = "2026-04-16T18:46:19.462Z" },
234
+ { url = "https://files.pythonhosted.org/packages/88/27/0195d15fe7a897cbcba0904792c4b7c9fdd958456c3a17d2ea6093716a9a/ruff-0.15.11-py3-none-win32.whl", hash = "sha256:476a2aa56b7da0b73a3ee80b6b2f0e19cce544245479adde7baa65466664d5f3", size = 10655535, upload-time = "2026-04-16T18:46:12.47Z" },
235
+ { url = "https://files.pythonhosted.org/packages/3a/5e/c927b325bd4c1d3620211a4b96f47864633199feed60fa936025ab27e090/ruff-0.15.11-py3-none-win_amd64.whl", hash = "sha256:8b6756d88d7e234fb0c98c91511aae3cd519d5e3ed271cae31b20f39cb2a12a3", size = 11779692, upload-time = "2026-04-16T18:46:17.268Z" },
236
+ { url = "https://files.pythonhosted.org/packages/63/b6/aeadee5443e49baa2facd51131159fd6301cc4ccfc1541e4df7b021c37dd/ruff-0.15.11-py3-none-win_arm64.whl", hash = "sha256:063fed18cc1bbe0ee7393957284a6fe8b588c6a406a285af3ee3f46da2391ee4", size = 11032614, upload-time = "2026-04-16T18:46:34.487Z" },
237
+ ]
238
+
239
+ [[package]]
240
+ name = "typing-extensions"
241
+ version = "4.15.0"
242
+ source = { registry = "https://pypi.org/simple" }
243
+ 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" }
244
+ wheels = [
245
+ { 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" },
246
+ ]
247
+
248
+ [[package]]
249
+ name = "typing-inspection"
250
+ version = "0.4.2"
251
+ source = { registry = "https://pypi.org/simple" }
252
+ dependencies = [
253
+ { name = "typing-extensions" },
254
+ ]
255
+ sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
256
+ wheels = [
257
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
258
+ ]