reasoning-ledger 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,333 @@
1
+ Metadata-Version: 2.4
2
+ Name: reasoning-ledger
3
+ Version: 0.1.0
4
+ Summary: Python SDK for interacting with the Reasoning Ledger.
5
+ Author: Arslan Ablikim
6
+ Author-email: Arslan Ablikim <arslan.ablikim0@gmail.com>
7
+ License-Expression: MIT
8
+ Requires-Dist: pydantic>=2.13.3
9
+ Requires-Dist: httpx>=0.27
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+
13
+ # reasoning-ledger
14
+
15
+ Python SDK for the [Reasoning Ledger](https://github.com/StairAI/Reasoning-Ledger) — a tamper-evident audit trail for AI agent reasoning.
16
+
17
+ ## Install
18
+
19
+ ```sh
20
+ pip install reasoning-ledger
21
+ ```
22
+
23
+ Requires **Python 3.12+**. Dependencies: `pydantic>=2`, `httpx>=0.27`.
24
+
25
+ ## Quick start
26
+
27
+ ### 1. Register an agent
28
+
29
+ Agent registration is idempotent on `(owner, name)` — calling it again with the same name returns the existing agent.
30
+
31
+ ```python
32
+ import os
33
+ from reasoning_ledger import LedgerClient, RegisterAgentOpts
34
+
35
+ reg = LedgerClient.register_agent(RegisterAgentOpts(
36
+ api_key=os.environ["STAIRAI_API_KEY"],
37
+ name="my-agent",
38
+ ))
39
+
40
+ agent_id = reg["agent_id"]
41
+ # Store agent_id — you'll need it every time you construct LedgerClient.
42
+ ```
43
+
44
+ If you already have an `agent_id` (e.g. stored in config), skip registration. To look up an agent ID by name at startup:
45
+
46
+ ```python
47
+ from reasoning_ledger import LedgerClient, ResolveAgentOpts
48
+
49
+ agent_id = LedgerClient.resolve_agent_id(ResolveAgentOpts(
50
+ api_key=os.environ["STAIRAI_API_KEY"],
51
+ name="my-agent",
52
+ ))
53
+ ```
54
+
55
+ ### 2. Create a client
56
+
57
+ ```python
58
+ from reasoning_ledger import LedgerClient, LedgerClientConfig
59
+
60
+ client = LedgerClient(LedgerClientConfig(
61
+ api_key=os.environ["STAIRAI_API_KEY"],
62
+ agent_id=agent_id,
63
+ ))
64
+ ```
65
+
66
+ The constructor performs no network call. The API key and agent ID are validated lazily on the first request.
67
+
68
+ ### 3. Open a session and submit records
69
+
70
+ A `Session` pins a `session_id` so you don't have to pass it on every record. It is purely local sugar — there is no server-side session lifecycle.
71
+
72
+ ```python
73
+ import json
74
+
75
+ session = client.new_session() # auto-generates a session_id
76
+
77
+ # Observing — the trigger that woke your agent
78
+ session.submit({
79
+ "behavior": "Observing",
80
+ "trigger_source": "sportradar",
81
+ "trigger_type": "signal_trigger",
82
+ "trigger_description": "Match update: Spain vs Morocco, minute 47",
83
+ "trigger_payload_summary": "Spain xG 0.41, possession 62%, shots 8-2",
84
+ })
85
+
86
+ # ToolCalling — external data fetch
87
+ session.submit({
88
+ "behavior": "ToolCalling",
89
+ "tool_meta": {"tool_id": "polymarket_api", "category": "external_api"},
90
+ "description": "Fetch current Spain win odds",
91
+ "input_payload": json.dumps({"market": "esp_mar"}),
92
+ "output_payload": json.dumps({"spain_win": 0.73}),
93
+ "success": True,
94
+ })
95
+
96
+ # Thinking — analysis and decision
97
+ session.submit({
98
+ "behavior": "Thinking",
99
+ "prompt": "Given xG 0.41 and odds 0.73, should I adjust the position?",
100
+ "inputs": [],
101
+ "output_payload": json.dumps({"recommendation": "hold", "confidence": 0.81}),
102
+ })
103
+
104
+ # Acting — the commitment
105
+ session.submit({
106
+ "behavior": "Acting",
107
+ "action_type": "trade",
108
+ "target_system": "broker-api",
109
+ "action_summary": "Hold current Spain win position",
110
+ "parameters": {"symbol": "ESP_WIN", "action": "hold"},
111
+ "dry_run": False,
112
+ "execution_status": "confirmed",
113
+ })
114
+ ```
115
+
116
+ ### 4. Submit a batch
117
+
118
+ ```python
119
+ batch_ack = session.submit_batch([
120
+ {"behavior": "Thinking", "prompt": "...", "inputs": [], "output_payload": "..."},
121
+ {"behavior": "Acting", "action_type": "...", ...},
122
+ ])
123
+
124
+ for result in batch_ack["results"]:
125
+ if "code" in result:
126
+ print("Record failed:", result["record_id"], result["code"], result["message"])
127
+ ```
128
+
129
+ Up to 50 records per batch. Per-record validation runs locally before the network call; only locally-valid records are sent. Partial server-side failure does not raise — inspect `BatchAck["results"]`.
130
+
131
+ ---
132
+
133
+ ## Behavior types
134
+
135
+ All seven behaviors extend the base record fields. The `"behavior"` key is the discriminant.
136
+
137
+ | Behavior | Required fields (beyond base) |
138
+ |---|---|
139
+ | `"Observing"` | `trigger_source`, `trigger_type`, `trigger_description`, `trigger_payload_summary` |
140
+ | `"Planning"` | `goal`, `steps` |
141
+ | `"Thinking"` | `prompt`, `inputs`, `output_payload` |
142
+ | `"Acting"` | `action_type`, `target_system`, `action_summary`, `parameters`, `dry_run`, `execution_status` |
143
+ | `"Reflecting"` | `inputs`, `output_payload` |
144
+ | `"ToolCalling"` | `tool_meta`, `description`, `input_payload`, `output_payload`, `success` |
145
+ | `"Other"` | `label`, `data` |
146
+
147
+ ### Auto-filled fields
148
+
149
+ The SDK fills these if you omit them:
150
+
151
+ | Field | SDK default |
152
+ |---|---|
153
+ | `record_id` | Fresh UUID v4 |
154
+ | `schema_version` | `"1.0"` (bundled constant) |
155
+ | `client_ts_utc` | Current epoch milliseconds |
156
+ | `agent_id` | From `LedgerClientConfig.agent_id` |
157
+
158
+ ---
159
+
160
+ ## Error handling
161
+
162
+ All errors inherit from `LedgerError` and carry a stable `code` string:
163
+
164
+ ```python
165
+ from reasoning_ledger import (
166
+ AuthError,
167
+ IdempotencyConflictError,
168
+ LedgerError,
169
+ NetworkError,
170
+ NotFoundError,
171
+ RateLimitError,
172
+ ServerError,
173
+ ValidationError,
174
+ )
175
+
176
+ try:
177
+ session.submit({...})
178
+ except ValidationError as e:
179
+ # Local schema check failed — never reached the network
180
+ print(e.details.get("field"), e.details.get("reason"))
181
+ except RateLimitError as e:
182
+ wait_ms = e.details.get("retry_after_ms")
183
+ # back off and retry
184
+ except LedgerError as e:
185
+ print(e.code, e.message)
186
+ ```
187
+
188
+ | Class | `code` | When |
189
+ |---|---|---|
190
+ | `ValidationError` | `validation_failed` | Local schema check failed; record never sent |
191
+ | `AuthError` | `auth_invalid` | API key rejected |
192
+ | `RateLimitError` | `rate_limited` | Server rate-limited the request |
193
+ | `NetworkError` | `network_failed` | Request never reached the server after retries |
194
+ | `ServerError` | `server_5xx` | Non-retryable 5xx from server |
195
+ | `IdempotencyConflictError` | `record_id_conflict` | Same `record_id` submitted with different body |
196
+ | `NotFoundError` | `not_found` | Lookup target does not exist |
197
+
198
+ ---
199
+
200
+ ## Configuration
201
+
202
+ ```python
203
+ from reasoning_ledger import LedgerClientConfig
204
+
205
+ config = LedgerClientConfig(
206
+ api_key="sl_...",
207
+ agent_id="uuid-v4",
208
+
209
+ # Target environment — defaults to "production"
210
+ environment="production", # | "staging" | "development"
211
+
212
+ # Override base URL (takes precedence over `environment`)
213
+ endpoint="https://custom.api.example.com",
214
+
215
+ # Default model invocation stamped on every record unless overridden per-record
216
+ default_model_invocation={
217
+ "provider": "anthropic",
218
+ "model_name": "claude-opus-4-7",
219
+ "tokens_in": 0,
220
+ "tokens_out": 0,
221
+ },
222
+
223
+ # Retry: 3 total attempts with 500 ms / 1 s / 2 s backoff (these are the defaults)
224
+ retry={"attempts": 3, "backoff_ms": [500, 1000, 2000]},
225
+
226
+ # Custom HTTP transport — useful for tests
227
+ http_transport=my_mock_transport,
228
+ )
229
+ ```
230
+
231
+ ### Custom HTTP transport
232
+
233
+ Inject any object implementing the `HttpTransport` protocol to intercept or mock network calls:
234
+
235
+ ```python
236
+ from reasoning_ledger import HttpRequest, HttpResponse, HttpTransport
237
+
238
+ class LoggingTransport:
239
+ def request(self, req: HttpRequest) -> HttpResponse:
240
+ print(req["method"], req["url"])
241
+ # delegate to real httpx ...
242
+ ```
243
+
244
+ ---
245
+
246
+ ## API reference
247
+
248
+ ### Static / class methods
249
+
250
+ #### `LedgerClient.register_agent(opts)` → `AgentRegistration`
251
+
252
+ Register a new agent. Idempotent on `(owner, name)`.
253
+
254
+ ```python
255
+ opts = RegisterAgentOpts(
256
+ api_key="sl_...",
257
+ name="my-agent",
258
+ metadata=AgentMetadata(description="...", tags=["tag1"]),
259
+ wallet=AgentWalletInput(address="0x..."), # BYOW only
260
+ )
261
+ ```
262
+
263
+ #### `LedgerClient.resolve_agent_id(opts)` → `str`
264
+
265
+ Look up an `agent_id` by human-readable name.
266
+
267
+ ```python
268
+ opts = ResolveAgentOpts(api_key="sl_...", name="my-agent")
269
+ ```
270
+
271
+ ### Instance methods
272
+
273
+ #### `client.submit(record)` → `RecordAck`
274
+
275
+ Submit one record.
276
+
277
+ #### `client.submit_batch(records)` → `BatchAck`
278
+
279
+ Submit up to 50 records in one request.
280
+
281
+ #### `client.get_record(record_id)` → `dict`
282
+
283
+ Fetch a single stored record.
284
+
285
+ #### `client.get_session(session_id)` → `SessionFetch`
286
+
287
+ Fetch every record in a session, ordered by `server_ts_utc`.
288
+
289
+ #### `client.get_trace(opts?)` → `TracePage`
290
+
291
+ Paginated read of the agent's full trace.
292
+
293
+ ```python
294
+ from reasoning_ledger import GetTraceOpts
295
+
296
+ page = client.get_trace(GetTraceOpts(before=cursor, limit=100))
297
+ ```
298
+
299
+ #### `client.new_session(session_id=None)` → `Session`
300
+
301
+ Create a local session handle. Generates a `session_id` if not supplied.
302
+
303
+ ### Session methods
304
+
305
+ #### `session.submit(record)` → `RecordAck`
306
+
307
+ Same as `client.submit`; `session_id` is auto-injected.
308
+
309
+ #### `session.submit_batch(records)` → `BatchAck`
310
+
311
+ Same as `client.submit_batch`; `session_id` is auto-injected on each record.
312
+
313
+ #### `session.id` → `str`
314
+
315
+ The bound `session_id` (read-only property).
316
+
317
+ ### Utility functions
318
+
319
+ ```python
320
+ from reasoning_ledger import is_valid_record_id, new_record_id, now_epoch_ms
321
+
322
+ new_record_id() # → fresh UUID v4 string
323
+ now_epoch_ms() # → current epoch milliseconds (int)
324
+ is_valid_record_id("...") # → bool — is the string a valid UUID v4?
325
+ ```
326
+
327
+ Use `new_record_id()` when building dependency edges where a child needs to reference an as-yet-unsubmitted record via `upstream_record_id` or `parent_record_id`.
328
+
329
+ ---
330
+
331
+ ## License
332
+
333
+ MIT
@@ -0,0 +1,321 @@
1
+ # reasoning-ledger
2
+
3
+ Python SDK for the [Reasoning Ledger](https://github.com/StairAI/Reasoning-Ledger) — a tamper-evident audit trail for AI agent reasoning.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pip install reasoning-ledger
9
+ ```
10
+
11
+ Requires **Python 3.12+**. Dependencies: `pydantic>=2`, `httpx>=0.27`.
12
+
13
+ ## Quick start
14
+
15
+ ### 1. Register an agent
16
+
17
+ Agent registration is idempotent on `(owner, name)` — calling it again with the same name returns the existing agent.
18
+
19
+ ```python
20
+ import os
21
+ from reasoning_ledger import LedgerClient, RegisterAgentOpts
22
+
23
+ reg = LedgerClient.register_agent(RegisterAgentOpts(
24
+ api_key=os.environ["STAIRAI_API_KEY"],
25
+ name="my-agent",
26
+ ))
27
+
28
+ agent_id = reg["agent_id"]
29
+ # Store agent_id — you'll need it every time you construct LedgerClient.
30
+ ```
31
+
32
+ If you already have an `agent_id` (e.g. stored in config), skip registration. To look up an agent ID by name at startup:
33
+
34
+ ```python
35
+ from reasoning_ledger import LedgerClient, ResolveAgentOpts
36
+
37
+ agent_id = LedgerClient.resolve_agent_id(ResolveAgentOpts(
38
+ api_key=os.environ["STAIRAI_API_KEY"],
39
+ name="my-agent",
40
+ ))
41
+ ```
42
+
43
+ ### 2. Create a client
44
+
45
+ ```python
46
+ from reasoning_ledger import LedgerClient, LedgerClientConfig
47
+
48
+ client = LedgerClient(LedgerClientConfig(
49
+ api_key=os.environ["STAIRAI_API_KEY"],
50
+ agent_id=agent_id,
51
+ ))
52
+ ```
53
+
54
+ The constructor performs no network call. The API key and agent ID are validated lazily on the first request.
55
+
56
+ ### 3. Open a session and submit records
57
+
58
+ A `Session` pins a `session_id` so you don't have to pass it on every record. It is purely local sugar — there is no server-side session lifecycle.
59
+
60
+ ```python
61
+ import json
62
+
63
+ session = client.new_session() # auto-generates a session_id
64
+
65
+ # Observing — the trigger that woke your agent
66
+ session.submit({
67
+ "behavior": "Observing",
68
+ "trigger_source": "sportradar",
69
+ "trigger_type": "signal_trigger",
70
+ "trigger_description": "Match update: Spain vs Morocco, minute 47",
71
+ "trigger_payload_summary": "Spain xG 0.41, possession 62%, shots 8-2",
72
+ })
73
+
74
+ # ToolCalling — external data fetch
75
+ session.submit({
76
+ "behavior": "ToolCalling",
77
+ "tool_meta": {"tool_id": "polymarket_api", "category": "external_api"},
78
+ "description": "Fetch current Spain win odds",
79
+ "input_payload": json.dumps({"market": "esp_mar"}),
80
+ "output_payload": json.dumps({"spain_win": 0.73}),
81
+ "success": True,
82
+ })
83
+
84
+ # Thinking — analysis and decision
85
+ session.submit({
86
+ "behavior": "Thinking",
87
+ "prompt": "Given xG 0.41 and odds 0.73, should I adjust the position?",
88
+ "inputs": [],
89
+ "output_payload": json.dumps({"recommendation": "hold", "confidence": 0.81}),
90
+ })
91
+
92
+ # Acting — the commitment
93
+ session.submit({
94
+ "behavior": "Acting",
95
+ "action_type": "trade",
96
+ "target_system": "broker-api",
97
+ "action_summary": "Hold current Spain win position",
98
+ "parameters": {"symbol": "ESP_WIN", "action": "hold"},
99
+ "dry_run": False,
100
+ "execution_status": "confirmed",
101
+ })
102
+ ```
103
+
104
+ ### 4. Submit a batch
105
+
106
+ ```python
107
+ batch_ack = session.submit_batch([
108
+ {"behavior": "Thinking", "prompt": "...", "inputs": [], "output_payload": "..."},
109
+ {"behavior": "Acting", "action_type": "...", ...},
110
+ ])
111
+
112
+ for result in batch_ack["results"]:
113
+ if "code" in result:
114
+ print("Record failed:", result["record_id"], result["code"], result["message"])
115
+ ```
116
+
117
+ Up to 50 records per batch. Per-record validation runs locally before the network call; only locally-valid records are sent. Partial server-side failure does not raise — inspect `BatchAck["results"]`.
118
+
119
+ ---
120
+
121
+ ## Behavior types
122
+
123
+ All seven behaviors extend the base record fields. The `"behavior"` key is the discriminant.
124
+
125
+ | Behavior | Required fields (beyond base) |
126
+ |---|---|
127
+ | `"Observing"` | `trigger_source`, `trigger_type`, `trigger_description`, `trigger_payload_summary` |
128
+ | `"Planning"` | `goal`, `steps` |
129
+ | `"Thinking"` | `prompt`, `inputs`, `output_payload` |
130
+ | `"Acting"` | `action_type`, `target_system`, `action_summary`, `parameters`, `dry_run`, `execution_status` |
131
+ | `"Reflecting"` | `inputs`, `output_payload` |
132
+ | `"ToolCalling"` | `tool_meta`, `description`, `input_payload`, `output_payload`, `success` |
133
+ | `"Other"` | `label`, `data` |
134
+
135
+ ### Auto-filled fields
136
+
137
+ The SDK fills these if you omit them:
138
+
139
+ | Field | SDK default |
140
+ |---|---|
141
+ | `record_id` | Fresh UUID v4 |
142
+ | `schema_version` | `"1.0"` (bundled constant) |
143
+ | `client_ts_utc` | Current epoch milliseconds |
144
+ | `agent_id` | From `LedgerClientConfig.agent_id` |
145
+
146
+ ---
147
+
148
+ ## Error handling
149
+
150
+ All errors inherit from `LedgerError` and carry a stable `code` string:
151
+
152
+ ```python
153
+ from reasoning_ledger import (
154
+ AuthError,
155
+ IdempotencyConflictError,
156
+ LedgerError,
157
+ NetworkError,
158
+ NotFoundError,
159
+ RateLimitError,
160
+ ServerError,
161
+ ValidationError,
162
+ )
163
+
164
+ try:
165
+ session.submit({...})
166
+ except ValidationError as e:
167
+ # Local schema check failed — never reached the network
168
+ print(e.details.get("field"), e.details.get("reason"))
169
+ except RateLimitError as e:
170
+ wait_ms = e.details.get("retry_after_ms")
171
+ # back off and retry
172
+ except LedgerError as e:
173
+ print(e.code, e.message)
174
+ ```
175
+
176
+ | Class | `code` | When |
177
+ |---|---|---|
178
+ | `ValidationError` | `validation_failed` | Local schema check failed; record never sent |
179
+ | `AuthError` | `auth_invalid` | API key rejected |
180
+ | `RateLimitError` | `rate_limited` | Server rate-limited the request |
181
+ | `NetworkError` | `network_failed` | Request never reached the server after retries |
182
+ | `ServerError` | `server_5xx` | Non-retryable 5xx from server |
183
+ | `IdempotencyConflictError` | `record_id_conflict` | Same `record_id` submitted with different body |
184
+ | `NotFoundError` | `not_found` | Lookup target does not exist |
185
+
186
+ ---
187
+
188
+ ## Configuration
189
+
190
+ ```python
191
+ from reasoning_ledger import LedgerClientConfig
192
+
193
+ config = LedgerClientConfig(
194
+ api_key="sl_...",
195
+ agent_id="uuid-v4",
196
+
197
+ # Target environment — defaults to "production"
198
+ environment="production", # | "staging" | "development"
199
+
200
+ # Override base URL (takes precedence over `environment`)
201
+ endpoint="https://custom.api.example.com",
202
+
203
+ # Default model invocation stamped on every record unless overridden per-record
204
+ default_model_invocation={
205
+ "provider": "anthropic",
206
+ "model_name": "claude-opus-4-7",
207
+ "tokens_in": 0,
208
+ "tokens_out": 0,
209
+ },
210
+
211
+ # Retry: 3 total attempts with 500 ms / 1 s / 2 s backoff (these are the defaults)
212
+ retry={"attempts": 3, "backoff_ms": [500, 1000, 2000]},
213
+
214
+ # Custom HTTP transport — useful for tests
215
+ http_transport=my_mock_transport,
216
+ )
217
+ ```
218
+
219
+ ### Custom HTTP transport
220
+
221
+ Inject any object implementing the `HttpTransport` protocol to intercept or mock network calls:
222
+
223
+ ```python
224
+ from reasoning_ledger import HttpRequest, HttpResponse, HttpTransport
225
+
226
+ class LoggingTransport:
227
+ def request(self, req: HttpRequest) -> HttpResponse:
228
+ print(req["method"], req["url"])
229
+ # delegate to real httpx ...
230
+ ```
231
+
232
+ ---
233
+
234
+ ## API reference
235
+
236
+ ### Static / class methods
237
+
238
+ #### `LedgerClient.register_agent(opts)` → `AgentRegistration`
239
+
240
+ Register a new agent. Idempotent on `(owner, name)`.
241
+
242
+ ```python
243
+ opts = RegisterAgentOpts(
244
+ api_key="sl_...",
245
+ name="my-agent",
246
+ metadata=AgentMetadata(description="...", tags=["tag1"]),
247
+ wallet=AgentWalletInput(address="0x..."), # BYOW only
248
+ )
249
+ ```
250
+
251
+ #### `LedgerClient.resolve_agent_id(opts)` → `str`
252
+
253
+ Look up an `agent_id` by human-readable name.
254
+
255
+ ```python
256
+ opts = ResolveAgentOpts(api_key="sl_...", name="my-agent")
257
+ ```
258
+
259
+ ### Instance methods
260
+
261
+ #### `client.submit(record)` → `RecordAck`
262
+
263
+ Submit one record.
264
+
265
+ #### `client.submit_batch(records)` → `BatchAck`
266
+
267
+ Submit up to 50 records in one request.
268
+
269
+ #### `client.get_record(record_id)` → `dict`
270
+
271
+ Fetch a single stored record.
272
+
273
+ #### `client.get_session(session_id)` → `SessionFetch`
274
+
275
+ Fetch every record in a session, ordered by `server_ts_utc`.
276
+
277
+ #### `client.get_trace(opts?)` → `TracePage`
278
+
279
+ Paginated read of the agent's full trace.
280
+
281
+ ```python
282
+ from reasoning_ledger import GetTraceOpts
283
+
284
+ page = client.get_trace(GetTraceOpts(before=cursor, limit=100))
285
+ ```
286
+
287
+ #### `client.new_session(session_id=None)` → `Session`
288
+
289
+ Create a local session handle. Generates a `session_id` if not supplied.
290
+
291
+ ### Session methods
292
+
293
+ #### `session.submit(record)` → `RecordAck`
294
+
295
+ Same as `client.submit`; `session_id` is auto-injected.
296
+
297
+ #### `session.submit_batch(records)` → `BatchAck`
298
+
299
+ Same as `client.submit_batch`; `session_id` is auto-injected on each record.
300
+
301
+ #### `session.id` → `str`
302
+
303
+ The bound `session_id` (read-only property).
304
+
305
+ ### Utility functions
306
+
307
+ ```python
308
+ from reasoning_ledger import is_valid_record_id, new_record_id, now_epoch_ms
309
+
310
+ new_record_id() # → fresh UUID v4 string
311
+ now_epoch_ms() # → current epoch milliseconds (int)
312
+ is_valid_record_id("...") # → bool — is the string a valid UUID v4?
313
+ ```
314
+
315
+ Use `new_record_id()` when building dependency edges where a child needs to reference an as-yet-unsubmitted record via `upstream_record_id` or `parent_record_id`.
316
+
317
+ ---
318
+
319
+ ## License
320
+
321
+ MIT
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "reasoning-ledger"
3
+ version = "0.1.0"
4
+ license = "MIT"
5
+ description = "Python SDK for interacting with the Reasoning Ledger."
6
+ readme = "README.md"
7
+ authors = [
8
+ { name = "Arslan Ablikim", email = "arslan.ablikim0@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.12"
11
+ dependencies = [
12
+ "pydantic>=2.13.3",
13
+ "httpx>=0.27",
14
+ ]
15
+
16
+ [dependency-groups]
17
+ dev = [
18
+ "pytest>=8",
19
+ "ruff>=0.9",
20
+ "ty>=0.0.3",
21
+ ]
22
+
23
+ [build-system]
24
+ requires = ["uv_build>=0.11.7,<0.12.0"]
25
+ build-backend = "uv_build"
26
+
27
+ [tool.pytest.ini_options]
28
+ testpaths = ["tests"]
29
+
30
+ [tool.ruff]
31
+ line-length = 100
32
+ target-version = "py312"
33
+ extend-exclude = ["src/reasoning_ledger/generated"]
34
+
35
+ [tool.ruff.lint]
36
+ select = ["E", "F", "I", "UP", "B", "RUF", "PERF", "S"]
37
+ ignore = ["S101"]