robosystems-client 0.2.22__py3-none-any.whl → 0.2.24__py3-none-any.whl

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.

Potentially problematic release.


This version of robosystems-client might be problematic. Click here for more details.

@@ -134,7 +134,7 @@ def sync_detailed(
134
134
  **Execution Strategies (automatic):**
135
135
  - Fast operations (<5s): Immediate synchronous response
136
136
  - Medium operations (5-30s): SSE streaming with progress updates
137
- - Long operations (>30s): Async Celery worker with operation tracking
137
+ - Long operations (>30s): Background queue with operation tracking
138
138
 
139
139
  **Response Mode Override:**
140
140
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -228,7 +228,7 @@ def sync(
228
228
  **Execution Strategies (automatic):**
229
229
  - Fast operations (<5s): Immediate synchronous response
230
230
  - Medium operations (5-30s): SSE streaming with progress updates
231
- - Long operations (>30s): Async Celery worker with operation tracking
231
+ - Long operations (>30s): Background queue with operation tracking
232
232
 
233
233
  **Response Mode Override:**
234
234
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -317,7 +317,7 @@ async def asyncio_detailed(
317
317
  **Execution Strategies (automatic):**
318
318
  - Fast operations (<5s): Immediate synchronous response
319
319
  - Medium operations (5-30s): SSE streaming with progress updates
320
- - Long operations (>30s): Async Celery worker with operation tracking
320
+ - Long operations (>30s): Background queue with operation tracking
321
321
 
322
322
  **Response Mode Override:**
323
323
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -409,7 +409,7 @@ async def asyncio(
409
409
  **Execution Strategies (automatic):**
410
410
  - Fast operations (<5s): Immediate synchronous response
411
411
  - Medium operations (5-30s): SSE streaming with progress updates
412
- - Long operations (>30s): Async Celery worker with operation tracking
412
+ - Long operations (>30s): Background queue with operation tracking
413
413
 
414
414
  **Response Mode Override:**
415
415
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -124,7 +124,7 @@ def sync_detailed(
124
124
  **Execution Strategies (automatic):**
125
125
  - Fast operations (<5s): Immediate synchronous response
126
126
  - Medium operations (5-30s): SSE streaming with progress updates
127
- - Long operations (>30s): Async Celery worker with operation tracking
127
+ - Long operations (>30s): Background queue with operation tracking
128
128
 
129
129
  **Response Mode Override:**
130
130
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -179,7 +179,7 @@ def sync(
179
179
  **Execution Strategies (automatic):**
180
180
  - Fast operations (<5s): Immediate synchronous response
181
181
  - Medium operations (5-30s): SSE streaming with progress updates
182
- - Long operations (>30s): Async Celery worker with operation tracking
182
+ - Long operations (>30s): Background queue with operation tracking
183
183
 
184
184
  **Response Mode Override:**
185
185
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -229,7 +229,7 @@ async def asyncio_detailed(
229
229
  **Execution Strategies (automatic):**
230
230
  - Fast operations (<5s): Immediate synchronous response
231
231
  - Medium operations (5-30s): SSE streaming with progress updates
232
- - Long operations (>30s): Async Celery worker with operation tracking
232
+ - Long operations (>30s): Background queue with operation tracking
233
233
 
234
234
  **Response Mode Override:**
235
235
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -282,7 +282,7 @@ async def asyncio(
282
282
  **Execution Strategies (automatic):**
283
283
  - Fast operations (<5s): Immediate synchronous response
284
284
  - Medium operations (5-30s): SSE streaming with progress updates
285
- - Long operations (>30s): Async Celery worker with operation tracking
285
+ - Long operations (>30s): Background queue with operation tracking
286
286
 
287
287
  **Response Mode Override:**
288
288
  Use query parameter `?mode=sync|async` to override automatic strategy selection.
@@ -103,7 +103,7 @@ def sync_detailed(
103
103
  **What Happens (status='uploaded'):**
104
104
  1. File validated in S3
105
105
  2. Row count calculated
106
- 3. DuckDB staging triggered immediately (Celery task)
106
+ 3. DuckDB staging triggered immediately (background task)
107
107
  4. If ingest_to_graph=true, graph ingestion queued
108
108
  5. File queryable in DuckDB within seconds
109
109
 
@@ -165,7 +165,7 @@ def sync(
165
165
  **What Happens (status='uploaded'):**
166
166
  1. File validated in S3
167
167
  2. Row count calculated
168
- 3. DuckDB staging triggered immediately (Celery task)
168
+ 3. DuckDB staging triggered immediately (background task)
169
169
  4. If ingest_to_graph=true, graph ingestion queued
170
170
  5. File queryable in DuckDB within seconds
171
171
 
@@ -222,7 +222,7 @@ async def asyncio_detailed(
222
222
  **What Happens (status='uploaded'):**
223
223
  1. File validated in S3
224
224
  2. Row count calculated
225
- 3. DuckDB staging triggered immediately (Celery task)
225
+ 3. DuckDB staging triggered immediately (background task)
226
226
  4. If ingest_to_graph=true, graph ingestion queued
227
227
  5. File queryable in DuckDB within seconds
228
228
 
@@ -282,7 +282,7 @@ async def asyncio(
282
282
  **What Happens (status='uploaded'):**
283
283
  1. File validated in S3
284
284
  2. Row count calculated
285
- 3. DuckDB staging triggered immediately (Celery task)
285
+ 3. DuckDB staging triggered immediately (background task)
286
286
  4. If ingest_to_graph=true, graph ingestion queued
287
287
  5. File queryable in DuckDB within seconds
288
288
 
@@ -1,6 +1,6 @@
1
1
  # RoboSystems Python Client Extensions
2
2
 
3
- 🚀 **Production-Ready Extensions** for the RoboSystems Financial Knowledge Graph API
3
+ **Production-Ready Extensions** for the RoboSystems Financial Knowledge Graph API
4
4
 
5
5
  [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -17,7 +17,7 @@ The RoboSystems Python Client Extensions provide enhanced functionality for the
17
17
  - **Caching** with TTL and LRU eviction
18
18
  - **Full Async/Await Support** throughout
19
19
 
20
- ## 🚀 Quick Start
20
+ ## Quick Start
21
21
 
22
22
  ### Installation
23
23
 
@@ -84,7 +84,7 @@ async def main():
84
84
  asyncio.run(main())
85
85
  ```
86
86
 
87
- ## 🔐 Authentication
87
+ ## Authentication
88
88
 
89
89
  ### API Key Authentication (Recommended)
90
90
 
@@ -139,7 +139,7 @@ dev_ext = create_extensions(
139
139
  )
140
140
  ```
141
141
 
142
- ## 🛠 Advanced Features
142
+ ## Advanced Features
143
143
 
144
144
  ### Query Builder
145
145
 
@@ -182,7 +182,7 @@ print(f"Complexity: {cost['complexity_category']} (score: {cost['complexity_scor
182
182
 
183
183
  # Get optimization recommendations
184
184
  for rec in cost['recommendations']:
185
- print(f"💡 {rec}")
185
+ print(f"Tip: {rec}")
186
186
  ```
187
187
 
188
188
  ### Result Processing
@@ -271,7 +271,7 @@ client.connect("operation_id")
271
271
  client.close()
272
272
  ```
273
273
 
274
- ## 📊 Examples
274
+ ## Examples
275
275
 
276
276
  ### Financial Data Analysis
277
277
 
@@ -332,7 +332,7 @@ if final_batch:
332
332
  process_transaction_batch(final_batch)
333
333
  total_processed += len(final_batch)
334
334
 
335
- print(f"Processed {total_processed:,} transactions total")
335
+ print(f"Processed {total_processed:,} transactions total")
336
336
  ```
337
337
 
338
338
  ### Error Handling
@@ -361,7 +361,7 @@ except Exception as e:
361
361
  print(f"Query failed: {e}")
362
362
  ```
363
363
 
364
- ## Performance Optimization
364
+ ## Performance Optimization
365
365
 
366
366
  ### Connection Pooling
367
367
 
@@ -391,21 +391,21 @@ query = "MATCH (c:Company) WHERE c.revenue > 1000000 RETURN c"
391
391
  # Check syntax
392
392
  validation = validate_cypher_query(query)
393
393
  if not validation['valid']:
394
- print("Query has syntax errors:", validation['issues'])
394
+ print("Query has syntax errors:", validation['issues'])
395
395
 
396
396
  # Estimate cost
397
397
  cost = estimate_query_cost(query)
398
- print(f"📊 Query complexity: {cost['complexity_category']}")
398
+ print(f"Query complexity: {cost['complexity_category']}")
399
399
 
400
400
  # Follow recommendations
401
401
  for rec in cost['recommendations']:
402
- print(f"💡 Optimization tip: {rec}")
402
+ print(f"Optimization tip: {rec}")
403
403
 
404
404
  # Execute only if reasonable complexity
405
405
  if cost['complexity_category'] in ['low', 'medium']:
406
406
  result = extensions.execute_query("graph_id", query)
407
407
  else:
408
- print("⚠️ Query may be too expensive - consider optimization")
408
+ print("Query may be too expensive - consider optimization")
409
409
  ```
410
410
 
411
411
  ### Caching Strategy
@@ -425,7 +425,7 @@ else:
425
425
  cache = results_cache
426
426
  ```
427
427
 
428
- ## 🧪 Testing
428
+ ## Testing
429
429
 
430
430
  Run the test suite:
431
431
 
@@ -457,7 +457,7 @@ def test_query_execution():
457
457
  assert result["data"] == [{"count": 100}]
458
458
  ```
459
459
 
460
- ## 🔧 Configuration
460
+ ## Configuration
461
461
 
462
462
  ### Environment Variables
463
463
 
@@ -492,7 +492,7 @@ config = RoboSystemsExtensionConfig(
492
492
  extensions = RoboSystemsExtensions(config)
493
493
  ```
494
494
 
495
- ## 📚 API Reference
495
+ ## API Reference
496
496
 
497
497
  ### Core Classes
498
498
 
@@ -528,7 +528,7 @@ extensions = RoboSystemsExtensions(config)
528
528
  - **`format_duration(milliseconds)`** - Human-readable time formatting
529
529
  - **`create_extensions(method, **kwargs)`** - Extensions factory
530
530
 
531
- ## 🐛 Troubleshooting
531
+ ## Troubleshooting
532
532
 
533
533
  ### Common Issues
534
534
 
@@ -545,9 +545,9 @@ pip install pandas # For DataFrame conversion (optional)
545
545
  extensions = AuthenticatedExtensions("your-api-key")
546
546
  try:
547
547
  result = extensions.execute_query("graph_id", "MATCH (n) RETURN count(n) LIMIT 1")
548
- print("Authentication successful")
548
+ print("Authentication successful")
549
549
  except Exception as e:
550
- print(f"Auth failed: {e}")
550
+ print(f"Auth failed: {e}")
551
551
  ```
552
552
 
553
553
  **Connection Issues**
@@ -585,11 +585,7 @@ logging.basicConfig(level=logging.DEBUG)
585
585
  extensions = AuthenticatedExtensions("your-key")
586
586
  ```
587
587
 
588
- ## 📄 License
589
-
590
- MIT License - see [LICENSE](LICENSE) file for details.
591
-
592
- ## 🤝 Contributing
588
+ ## Contributing
593
589
 
594
590
  1. Fork the repository
595
591
  2. Create a feature branch
@@ -597,7 +593,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
597
593
  4. Run the test suite: `python run_tests.py`
598
594
  5. Submit a pull request
599
595
 
600
- ## 📞 Support
596
+ ## Support
601
597
 
602
598
  - **API Reference**: [api.robosystems.ai](https://api.robosystems.ai)
603
599
  - **Issues**: [GitHub Issues](https://github.com/RoboFinSystems/robosystems-python-client/issues)
@@ -177,7 +177,7 @@ class AgentClient:
177
177
  else datetime.now().isoformat(),
178
178
  )
179
179
 
180
- # Check if this is a queued response (async Celery execution)
180
+ # Check if this is a queued response (async background task execution)
181
181
  is_queued = False
182
182
  queued_response = None
183
183
 
@@ -1,13 +1,17 @@
1
1
  """Graph Management Client
2
2
 
3
3
  Provides high-level graph management operations with automatic operation monitoring.
4
+ Supports both SSE (Server-Sent Events) for real-time updates and polling fallback.
4
5
  """
5
6
 
6
7
  from dataclasses import dataclass
7
8
  from typing import Dict, Any, Optional, Callable
8
9
  import time
10
+ import json
9
11
  import logging
10
12
 
13
+ import httpx
14
+
11
15
  logger = logging.getLogger(__name__)
12
16
 
13
17
 
@@ -62,10 +66,14 @@ class GraphClient:
62
66
  timeout: int = 60,
63
67
  poll_interval: int = 2,
64
68
  on_progress: Optional[Callable[[str], None]] = None,
69
+ use_sse: bool = True,
65
70
  ) -> str:
66
71
  """
67
72
  Create a graph and wait for completion.
68
73
 
74
+ Uses SSE (Server-Sent Events) for real-time progress updates with
75
+ automatic fallback to polling if SSE connection fails.
76
+
69
77
  Args:
70
78
  metadata: Graph metadata
71
79
  initial_entity: Optional initial entity data
@@ -73,8 +81,9 @@ class GraphClient:
73
81
  Only applies when initial_entity is provided. Set to False to create
74
82
  graph without populating entity data (useful for file-based ingestion).
75
83
  timeout: Maximum time to wait in seconds
76
- poll_interval: Time between status checks in seconds
84
+ poll_interval: Time between status checks in seconds (for polling fallback)
77
85
  on_progress: Callback for progress updates
86
+ use_sse: Whether to try SSE first (default True). Falls back to polling on failure.
78
87
 
79
88
  Returns:
80
89
  graph_id when creation completes
@@ -84,7 +93,6 @@ class GraphClient:
84
93
  """
85
94
  from ..client import AuthenticatedClient
86
95
  from ..api.graphs.create_graph import sync_detailed as create_graph
87
- from ..api.operations.get_operation_status import sync_detailed as get_status
88
96
  from ..models.create_graph_request import CreateGraphRequest
89
97
  from ..models.graph_metadata import GraphMetadata as APIGraphMetadata
90
98
 
@@ -151,13 +159,165 @@ class GraphClient:
151
159
  on_progress(f"Graph created: {graph_id}")
152
160
  return graph_id
153
161
 
154
- # Otherwise, poll operation until complete
162
+ # Otherwise, wait for operation to complete
155
163
  if not operation_id:
156
164
  raise RuntimeError("No graph_id or operation_id in response")
157
165
 
158
166
  if on_progress:
159
167
  on_progress(f"Graph creation queued (operation: {operation_id})")
160
168
 
169
+ # Try SSE first, fall back to polling
170
+ if use_sse:
171
+ try:
172
+ return self._wait_with_sse(operation_id, timeout, on_progress)
173
+ except Exception as e:
174
+ logger.debug(f"SSE connection failed, falling back to polling: {e}")
175
+ if on_progress:
176
+ on_progress("SSE unavailable, using polling...")
177
+
178
+ # Fallback to polling
179
+ return self._wait_with_polling(
180
+ operation_id, timeout, poll_interval, on_progress, client
181
+ )
182
+
183
+ def _wait_with_sse(
184
+ self,
185
+ operation_id: str,
186
+ timeout: int,
187
+ on_progress: Optional[Callable[[str], None]],
188
+ ) -> str:
189
+ """
190
+ Wait for operation completion using SSE stream.
191
+
192
+ Args:
193
+ operation_id: Operation ID to monitor
194
+ timeout: Maximum time to wait in seconds
195
+ on_progress: Callback for progress updates
196
+
197
+ Returns:
198
+ graph_id when operation completes
199
+
200
+ Raises:
201
+ RuntimeError: If operation fails
202
+ TimeoutError: If operation times out
203
+ """
204
+ stream_url = f"{self.base_url}/v1/operations/{operation_id}/stream"
205
+ headers = {"X-API-Key": self.token, "Accept": "text/event-stream"}
206
+
207
+ with httpx.Client(timeout=httpx.Timeout(timeout + 5.0)) as http_client:
208
+ with http_client.stream("GET", stream_url, headers=headers) as response:
209
+ if response.status_code != 200:
210
+ raise RuntimeError(f"SSE connection failed: {response.status_code}")
211
+
212
+ start_time = time.time()
213
+ event_type = None
214
+ event_data = ""
215
+
216
+ for line in response.iter_lines():
217
+ # Check timeout
218
+ if time.time() - start_time > timeout:
219
+ raise TimeoutError(f"Graph creation timed out after {timeout}s")
220
+
221
+ line = line.strip()
222
+
223
+ if not line:
224
+ # Empty line = end of event, process it
225
+ if event_type and event_data:
226
+ result = self._process_sse_event(event_type, event_data, on_progress)
227
+ if result is not None:
228
+ return result
229
+ event_type = None
230
+ event_data = ""
231
+ continue
232
+
233
+ if line.startswith("event:"):
234
+ event_type = line[6:].strip()
235
+ elif line.startswith("data:"):
236
+ event_data = line[5:].strip()
237
+ # Ignore other lines (comments, id, retry, etc.)
238
+
239
+ raise TimeoutError(f"SSE stream ended without completion after {timeout}s")
240
+
241
+ def _process_sse_event(
242
+ self,
243
+ event_type: str,
244
+ event_data: str,
245
+ on_progress: Optional[Callable[[str], None]],
246
+ ) -> Optional[str]:
247
+ """
248
+ Process a single SSE event.
249
+
250
+ Returns:
251
+ graph_id if operation completed, None to continue waiting
252
+
253
+ Raises:
254
+ RuntimeError: If operation failed
255
+ """
256
+ try:
257
+ data = json.loads(event_data)
258
+ except json.JSONDecodeError:
259
+ logger.debug(f"Failed to parse SSE event data: {event_data}")
260
+ return None
261
+
262
+ if event_type == "operation_progress":
263
+ if on_progress:
264
+ message = data.get("message", "Processing...")
265
+ percent = data.get("progress_percent")
266
+ if percent is not None:
267
+ on_progress(f"{message} ({percent:.0f}%)")
268
+ else:
269
+ on_progress(message)
270
+ return None
271
+
272
+ elif event_type == "operation_completed":
273
+ result = data.get("result", {})
274
+ graph_id = result.get("graph_id") if isinstance(result, dict) else None
275
+
276
+ if graph_id:
277
+ if on_progress:
278
+ on_progress(f"Graph created: {graph_id}")
279
+ return graph_id
280
+ else:
281
+ raise RuntimeError("Operation completed but no graph_id in result")
282
+
283
+ elif event_type == "operation_error":
284
+ error = data.get("error", "Unknown error")
285
+ raise RuntimeError(f"Graph creation failed: {error}")
286
+
287
+ elif event_type == "operation_cancelled":
288
+ reason = data.get("reason", "Operation was cancelled")
289
+ raise RuntimeError(f"Graph creation cancelled: {reason}")
290
+
291
+ # Ignore other event types (keepalive, etc.)
292
+ return None
293
+
294
+ def _wait_with_polling(
295
+ self,
296
+ operation_id: str,
297
+ timeout: int,
298
+ poll_interval: int,
299
+ on_progress: Optional[Callable[[str], None]],
300
+ client: Any,
301
+ ) -> str:
302
+ """
303
+ Wait for operation completion using polling.
304
+
305
+ Args:
306
+ operation_id: Operation ID to monitor
307
+ timeout: Maximum time to wait in seconds
308
+ poll_interval: Time between status checks
309
+ on_progress: Callback for progress updates
310
+ client: Authenticated HTTP client
311
+
312
+ Returns:
313
+ graph_id when operation completes
314
+
315
+ Raises:
316
+ RuntimeError: If operation fails
317
+ TimeoutError: If operation times out
318
+ """
319
+ from ..api.operations.get_operation_status import sync_detailed as get_status
320
+
161
321
  max_attempts = timeout // poll_interval
162
322
  for attempt in range(max_attempts):
163
323
  time.sleep(poll_interval)
@@ -15,6 +15,7 @@ from ..api.materialize.get_materialization_status import (
15
15
  sync_detailed as get_materialization_status,
16
16
  )
17
17
  from ..models.materialize_request import MaterializeRequest
18
+ from .operation_client import OperationClient, OperationProgress, MonitorOptions
18
19
 
19
20
  logger = logging.getLogger(__name__)
20
21
 
@@ -27,6 +28,7 @@ class MaterializationOptions:
27
28
  rebuild: bool = False
28
29
  force: bool = False
29
30
  on_progress: Optional[Callable[[str], None]] = None
31
+ timeout: Optional[int] = 600 # 10 minute default timeout
30
32
 
31
33
 
32
34
  @dataclass
@@ -66,6 +68,14 @@ class MaterializationClient:
66
68
  self.base_url = config["base_url"]
67
69
  self.headers = config.get("headers", {})
68
70
  self.token = config.get("token")
71
+ self._operation_client = None
72
+
73
+ @property
74
+ def operation_client(self) -> OperationClient:
75
+ """Get or create the operation client for SSE monitoring."""
76
+ if self._operation_client is None:
77
+ self._operation_client = OperationClient(self.config)
78
+ return self._operation_client
69
79
 
70
80
  def materialize(
71
81
  self,
@@ -75,13 +85,13 @@ class MaterializationClient:
75
85
  """
76
86
  Materialize graph from DuckDB staging tables.
77
87
 
78
- Rebuilds the complete graph database from the current state of DuckDB
79
- staging tables. Automatically discovers all tables, materializes them in
80
- the correct order (nodes before relationships), and clears the staleness flag.
88
+ Submits a materialization job to Dagster and monitors progress via SSE.
89
+ The operation runs asynchronously on the server but this method waits
90
+ for completion and returns the final result.
81
91
 
82
92
  Args:
83
93
  graph_id: Graph database identifier
84
- options: Materialization options (ignore_errors, rebuild, force)
94
+ options: Materialization options (ignore_errors, rebuild, force, timeout)
85
95
 
86
96
  Returns:
87
97
  MaterializationResult with detailed execution information
@@ -96,7 +106,7 @@ class MaterializationClient:
96
106
 
97
107
  try:
98
108
  if options.on_progress:
99
- options.on_progress("Starting graph materialization...")
109
+ options.on_progress("Submitting materialization job...")
100
110
 
101
111
  request = MaterializeRequest(
102
112
  ignore_errors=options.ignore_errors,
@@ -125,6 +135,7 @@ class MaterializationClient:
125
135
 
126
136
  response = materialize_graph(**kwargs)
127
137
 
138
+ # Handle non-200 status codes
128
139
  if response.status_code != 200 or not response.parsed:
129
140
  error_msg = f"Materialization failed: {response.status_code}"
130
141
  if hasattr(response, "content"):
@@ -148,25 +159,68 @@ class MaterializationClient:
148
159
  error=error_msg,
149
160
  )
150
161
 
162
+ # Get the operation_id from the queued response
151
163
  result_data = response.parsed
164
+ operation_id = result_data.operation_id
152
165
 
153
166
  if options.on_progress:
154
- options.on_progress(
155
- f"✅ Materialization complete: {len(result_data.tables_materialized)} tables, "
156
- f"{result_data.total_rows:,} rows in {result_data.execution_time_ms:.2f}ms"
157
- )
158
-
159
- return MaterializationResult(
160
- status=result_data.status,
161
- was_stale=result_data.was_stale,
162
- stale_reason=result_data.stale_reason,
163
- tables_materialized=result_data.tables_materialized,
164
- total_rows=result_data.total_rows,
165
- execution_time_ms=result_data.execution_time_ms,
166
- message=result_data.message,
167
- success=True,
167
+ options.on_progress(f"Materialization queued (operation: {operation_id})")
168
+
169
+ # Monitor the operation via SSE until completion
170
+ def on_sse_progress(progress: OperationProgress):
171
+ if options.on_progress:
172
+ msg = progress.message
173
+ if progress.percentage is not None:
174
+ msg += f" ({progress.percentage:.0f}%)"
175
+ options.on_progress(msg)
176
+
177
+ monitor_options = MonitorOptions(
178
+ on_progress=on_sse_progress,
179
+ timeout=options.timeout,
168
180
  )
169
181
 
182
+ op_result = self.operation_client.monitor_operation(operation_id, monitor_options)
183
+
184
+ # Convert operation result to materialization result
185
+ if op_result.status.value == "completed":
186
+ # Extract details from SSE completion event result
187
+ sse_result = op_result.result or {}
188
+
189
+ if options.on_progress:
190
+ tables = sse_result.get("tables_materialized", [])
191
+ rows = sse_result.get("total_rows", 0)
192
+ time_ms = sse_result.get("execution_time_ms", 0)
193
+ options.on_progress(
194
+ f"✅ Materialization complete: {len(tables)} tables, "
195
+ f"{rows:,} rows in {time_ms:.2f}ms"
196
+ )
197
+
198
+ return MaterializationResult(
199
+ status="success",
200
+ was_stale=sse_result.get("was_stale", False),
201
+ stale_reason=sse_result.get("stale_reason"),
202
+ tables_materialized=sse_result.get("tables_materialized", []),
203
+ total_rows=sse_result.get("total_rows", 0),
204
+ execution_time_ms=sse_result.get(
205
+ "execution_time_ms", op_result.execution_time_ms or 0
206
+ ),
207
+ message=sse_result.get("message", "Graph materialized successfully"),
208
+ success=True,
209
+ )
210
+ else:
211
+ # Operation failed or was cancelled
212
+ return MaterializationResult(
213
+ status=op_result.status.value,
214
+ was_stale=False,
215
+ stale_reason=None,
216
+ tables_materialized=[],
217
+ total_rows=0,
218
+ execution_time_ms=op_result.execution_time_ms or 0,
219
+ message=op_result.error or f"Operation {op_result.status.value}",
220
+ success=False,
221
+ error=op_result.error,
222
+ )
223
+
170
224
  except Exception as e:
171
225
  logger.error(f"Materialization failed: {e}")
172
226
  return MaterializationResult(
@@ -104,8 +104,10 @@ class SSEClient:
104
104
 
105
105
  try:
106
106
  self.client = httpx.Client(timeout=self.config.timeout)
107
- self._response = self.client.stream("GET", url, params=params, headers=headers)
108
- self._response.__enter__()
107
+ self._context_manager = self.client.stream(
108
+ "GET", url, params=params, headers=headers
109
+ )
110
+ self._response = self._context_manager.__enter__()
109
111
 
110
112
  self.reconnect_attempts = 0
111
113
  self.emit("connected", None)
@@ -124,11 +126,9 @@ class SSEClient:
124
126
 
125
127
  try:
126
128
  event_buffer = {"event": None, "data": [], "id": None, "retry": None}
127
- print("[SSE DEBUG] Starting to process events...")
128
129
 
129
130
  for line in self._response.iter_lines():
130
131
  if self.closed:
131
- print("[SSE DEBUG] Stream closed, breaking out of loop")
132
132
  break
133
133
 
134
134
  line = line.strip()
@@ -136,7 +136,6 @@ class SSEClient:
136
136
  # Empty line indicates end of event
137
137
  if not line:
138
138
  if event_buffer["data"] or event_buffer["event"]:
139
- print(f"[SSE DEBUG] Dispatching event: {event_buffer.get('event')}")
140
139
  self._dispatch_event(event_buffer)
141
140
  event_buffer = {"event": None, "data": [], "id": None, "retry": None}
142
141
  continue
@@ -172,13 +171,9 @@ class SSEClient:
172
171
 
173
172
  # Handle final event if stream ends without empty line
174
173
  if event_buffer["data"] or event_buffer["event"]:
175
- print("[SSE DEBUG] Dispatching final event after stream end")
176
174
  self._dispatch_event(event_buffer)
177
175
 
178
- print("[SSE DEBUG] Event processing loop ended")
179
-
180
176
  except Exception as error:
181
- print(f"[SSE DEBUG] Exception in event processing: {error}")
182
177
  if not self.closed:
183
178
  self.emit("error", error)
184
179
 
@@ -214,13 +209,14 @@ class SSEClient:
214
209
  # Emit typed event
215
210
  self.emit(event_type, parsed_data)
216
211
 
217
- # Check for completion events
212
+ # Check for completion events - just set flag, don't close from within loop
213
+ # The loop will break on next iteration and close() will be called in finally
218
214
  if event_type in [
219
215
  EventType.OPERATION_COMPLETED.value,
220
216
  EventType.OPERATION_ERROR.value,
221
217
  EventType.OPERATION_CANCELLED.value,
222
218
  ]:
223
- self.close()
219
+ self.closed = True
224
220
 
225
221
  def _handle_error(
226
222
  self, error: Exception, operation_id: str, from_sequence: int
@@ -285,12 +281,13 @@ class SSEClient:
285
281
  """Close the SSE connection"""
286
282
  self.closed = True
287
283
 
288
- if self._response:
284
+ if hasattr(self, "_context_manager") and self._context_manager:
289
285
  try:
290
- self._response.__exit__(None, None, None)
286
+ self._context_manager.__exit__(None, None, None)
291
287
  except Exception:
292
288
  pass
293
- self._response = None
289
+ self._context_manager = None
290
+ self._response = None
294
291
 
295
292
  if self.client:
296
293
  self.client.close()
@@ -334,10 +331,10 @@ class AsyncSSEClient:
334
331
 
335
332
  try:
336
333
  self.client = httpx.AsyncClient(timeout=self.config.timeout)
337
- self._response = await self.client.stream(
334
+ self._context_manager = self.client.stream(
338
335
  "GET", url, params=params, headers=headers
339
336
  )
340
- await self._response.__aenter__()
337
+ self._response = await self._context_manager.__aenter__()
341
338
 
342
339
  self.reconnect_attempts = 0
343
340
  self.emit("connected", None)
@@ -401,13 +398,9 @@ class AsyncSSEClient:
401
398
 
402
399
  # Handle final event if stream ends without empty line
403
400
  if event_buffer["data"] or event_buffer["event"]:
404
- print("[SSE DEBUG] Dispatching final event after stream end")
405
401
  self._dispatch_event(event_buffer)
406
402
 
407
- print("[SSE DEBUG] Event processing loop ended")
408
-
409
403
  except Exception as error:
410
- print(f"[SSE DEBUG] Exception in event processing: {error}")
411
404
  if not self.closed:
412
405
  self.emit("error", error)
413
406
 
@@ -443,13 +436,14 @@ class AsyncSSEClient:
443
436
  # Emit typed event
444
437
  self.emit(event_type, parsed_data)
445
438
 
446
- # Check for completion events
439
+ # Check for completion events - just set flag, don't close from within loop
440
+ # The loop will break on next iteration and close() will be called in finally
447
441
  if event_type in [
448
442
  EventType.OPERATION_COMPLETED.value,
449
443
  EventType.OPERATION_ERROR.value,
450
444
  EventType.OPERATION_CANCELLED.value,
451
445
  ]:
452
- asyncio.create_task(self.close())
446
+ self.closed = True
453
447
 
454
448
  async def _handle_error(
455
449
  self, error: Exception, operation_id: str, from_sequence: int
@@ -512,12 +506,13 @@ class AsyncSSEClient:
512
506
  """Close the SSE connection (async)"""
513
507
  self.closed = True
514
508
 
515
- if self._response:
509
+ if hasattr(self, "_context_manager") and self._context_manager:
516
510
  try:
517
- await self._response.__aexit__(None, None, None)
511
+ await self._context_manager.__aexit__(None, None, None)
518
512
  except Exception:
519
513
  pass
520
- self._response = None
514
+ self._context_manager = None
515
+ self._response = None
521
516
 
522
517
  if self.client:
523
518
  await self.client.aclose()
@@ -197,7 +197,9 @@ class SubgraphWorkspaceClient:
197
197
  sse_url = f"{self.api._base_url}/v1/operations/{operation_id}/stream"
198
198
  headers = {"X-API-Key": self.api.token}
199
199
 
200
- async with httpx.AsyncClient() as client:
200
+ # Use longer timeout for SSE streaming (Dagster jobs can take time)
201
+ timeout = httpx.Timeout(connect=30.0, read=120.0, write=30.0, pool=30.0)
202
+ async with httpx.AsyncClient(timeout=timeout) as client:
201
203
  async with client.stream("GET", sse_url, headers=headers) as sse_response:
202
204
  async for line in sse_response.aiter_lines():
203
205
  if line.startswith("data: "):
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Mapping
4
- from typing import Any, TypeVar, cast
4
+ from typing import Any, TypeVar
5
5
 
6
6
  from attrs import define as _attrs_define
7
7
  from attrs import field as _attrs_field
@@ -13,102 +13,64 @@ T = TypeVar("T", bound="MaterializeResponse")
13
13
 
14
14
  @_attrs_define
15
15
  class MaterializeResponse:
16
- """
16
+ """Response for queued materialization operation.
17
+
18
+ Example:
19
+ {'graph_id': 'kg_abc123', 'message': 'Materialization queued. Monitor via SSE stream.', 'operation_id':
20
+ '550e8400-e29b-41d4-a716-446655440000', 'status': 'queued'}
21
+
17
22
  Attributes:
18
- status (str): Materialization status
19
23
  graph_id (str): Graph database identifier
20
- was_stale (bool): Whether graph was stale before materialization
21
- tables_materialized (list[str]): List of tables successfully materialized
22
- total_rows (int): Total rows materialized across all tables
23
- execution_time_ms (float): Total materialization time
24
+ operation_id (str): SSE operation ID for progress tracking
24
25
  message (str): Human-readable status message
25
- stale_reason (None | str | Unset): Reason graph was stale
26
+ status (str | Unset): Operation status Default: 'queued'.
26
27
  """
27
28
 
28
- status: str
29
29
  graph_id: str
30
- was_stale: bool
31
- tables_materialized: list[str]
32
- total_rows: int
33
- execution_time_ms: float
30
+ operation_id: str
34
31
  message: str
35
- stale_reason: None | str | Unset = UNSET
32
+ status: str | Unset = "queued"
36
33
  additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
37
34
 
38
35
  def to_dict(self) -> dict[str, Any]:
39
- status = self.status
40
-
41
36
  graph_id = self.graph_id
42
37
 
43
- was_stale = self.was_stale
44
-
45
- tables_materialized = self.tables_materialized
46
-
47
- total_rows = self.total_rows
48
-
49
- execution_time_ms = self.execution_time_ms
38
+ operation_id = self.operation_id
50
39
 
51
40
  message = self.message
52
41
 
53
- stale_reason: None | str | Unset
54
- if isinstance(self.stale_reason, Unset):
55
- stale_reason = UNSET
56
- else:
57
- stale_reason = self.stale_reason
42
+ status = self.status
58
43
 
59
44
  field_dict: dict[str, Any] = {}
60
45
  field_dict.update(self.additional_properties)
61
46
  field_dict.update(
62
47
  {
63
- "status": status,
64
48
  "graph_id": graph_id,
65
- "was_stale": was_stale,
66
- "tables_materialized": tables_materialized,
67
- "total_rows": total_rows,
68
- "execution_time_ms": execution_time_ms,
49
+ "operation_id": operation_id,
69
50
  "message": message,
70
51
  }
71
52
  )
72
- if stale_reason is not UNSET:
73
- field_dict["stale_reason"] = stale_reason
53
+ if status is not UNSET:
54
+ field_dict["status"] = status
74
55
 
75
56
  return field_dict
76
57
 
77
58
  @classmethod
78
59
  def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
79
60
  d = dict(src_dict)
80
- status = d.pop("status")
81
-
82
61
  graph_id = d.pop("graph_id")
83
62
 
84
- was_stale = d.pop("was_stale")
85
-
86
- tables_materialized = cast(list[str], d.pop("tables_materialized"))
87
-
88
- total_rows = d.pop("total_rows")
89
-
90
- execution_time_ms = d.pop("execution_time_ms")
63
+ operation_id = d.pop("operation_id")
91
64
 
92
65
  message = d.pop("message")
93
66
 
94
- def _parse_stale_reason(data: object) -> None | str | Unset:
95
- if data is None:
96
- return data
97
- if isinstance(data, Unset):
98
- return data
99
- return cast(None | str | Unset, data)
100
-
101
- stale_reason = _parse_stale_reason(d.pop("stale_reason", UNSET))
67
+ status = d.pop("status", UNSET)
102
68
 
103
69
  materialize_response = cls(
104
- status=status,
105
70
  graph_id=graph_id,
106
- was_stale=was_stale,
107
- tables_materialized=tables_materialized,
108
- total_rows=total_rows,
109
- execution_time_ms=execution_time_ms,
71
+ operation_id=operation_id,
110
72
  message=message,
111
- stale_reason=stale_reason,
73
+ status=status,
112
74
  )
113
75
 
114
76
  materialize_response.additional_properties = d
@@ -14,12 +14,12 @@ class TokenPricing:
14
14
  """AI token pricing for a specific model.
15
15
 
16
16
  Attributes:
17
- input_per_1k_tokens (int): Credits per 1K input tokens
18
- output_per_1k_tokens (int): Credits per 1K output tokens
17
+ input_per_1k_tokens (float): Credits per 1K input tokens
18
+ output_per_1k_tokens (float): Credits per 1K output tokens
19
19
  """
20
20
 
21
- input_per_1k_tokens: int
22
- output_per_1k_tokens: int
21
+ input_per_1k_tokens: float
22
+ output_per_1k_tokens: float
23
23
  additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
24
24
 
25
25
  def to_dict(self) -> dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robosystems-client
3
- Version: 0.2.22
3
+ Version: 0.2.24
4
4
  Summary: Python Client for RoboSystems financial graph database API
5
5
  Author: RFS LLC
6
6
  License: MIT
@@ -33,8 +33,8 @@ Requires-Dist: pydantic>=2.11.7
33
33
  Requires-Dist: python-dateutil>=2.8.0
34
34
  Requires-Dist: typing-extensions>=4.0.0
35
35
  Provides-Extra: all
36
- Requires-Dist: pandas>=1.5.0; extra == 'all'
37
- Requires-Dist: pyarrow>=10.0.0; extra == 'all'
36
+ Requires-Dist: pandas>=2.1.0; extra == 'all'
37
+ Requires-Dist: pyarrow>=17.0.0; extra == 'all'
38
38
  Provides-Extra: dev
39
39
  Requires-Dist: basedpyright>=1.21.0; extra == 'dev'
40
40
  Requires-Dist: build>=1.0.0; extra == 'dev'
@@ -45,11 +45,11 @@ Requires-Dist: pytest>=8.3.5; extra == 'dev'
45
45
  Requires-Dist: ruff>=0.12; extra == 'dev'
46
46
  Requires-Dist: twine>=5.0.0; extra == 'dev'
47
47
  Provides-Extra: extensions
48
- Requires-Dist: pandas>=1.5.0; extra == 'extensions'
49
- Requires-Dist: pyarrow>=10.0.0; extra == 'extensions'
48
+ Requires-Dist: pandas>=2.1.0; extra == 'extensions'
49
+ Requires-Dist: pyarrow>=17.0.0; extra == 'extensions'
50
50
  Provides-Extra: tables
51
- Requires-Dist: pandas>=1.5.0; extra == 'tables'
52
- Requires-Dist: pyarrow>=10.0.0; extra == 'tables'
51
+ Requires-Dist: pandas>=2.1.0; extra == 'tables'
52
+ Requires-Dist: pyarrow>=17.0.0; extra == 'tables'
53
53
  Description-Content-Type: text/markdown
54
54
 
55
55
  # RoboSystems Python Client
@@ -6,9 +6,9 @@ robosystems_client/sdk-config.yaml,sha256=Y_A8qSC2zHLYy6d443Rlgdkw2GleOSFjYvq_Qm
6
6
  robosystems_client/types.py,sha256=p37iLHBX-cpNh7jZmbG4Xl1UI6hexvRS-HkY_QBlZWE,1301
7
7
  robosystems_client/api/__init__.py,sha256=zTSiG_ujSjAqWPyc435YXaX9XTlpMjiJWBbV-f-YtdA,45
8
8
  robosystems_client/api/agent/__init__.py,sha256=5vd9uJWAjRqa9xzxzYkLD1yoZ12Ld_bAaNB5WX4fbE8,56
9
- robosystems_client/api/agent/auto_select_agent.py,sha256=yeav6I1-DAL7g-E0f3AhClva_BEsKRXL0HQzKvnDroU,16408
9
+ robosystems_client/api/agent/auto_select_agent.py,sha256=uHoIjrKoA6e7GyQu1DMAIPGZ1JAx-xA8sYodyLTFUfU,16396
10
10
  robosystems_client/api/agent/batch_process_queries.py,sha256=d0htQmRZoTig8XnSQHINhPDf10w_kgPfTSBNGDUrg1c,6697
11
- robosystems_client/api/agent/execute_specific_agent.py,sha256=70Y9dVy84z-xuQlwDFUUULi-mb-tBCmDO67Avmny6eo,9440
11
+ robosystems_client/api/agent/execute_specific_agent.py,sha256=pZWf6dsWtMjJ2qkV0tOLEhWB8s9HlsEXUtu2DZKv_RE,9428
12
12
  robosystems_client/api/agent/get_agent_metadata.py,sha256=MS4MCA5fQE2F654iVTTnWGxckDx3YlPvYScMtsyKkAk,5730
13
13
  robosystems_client/api/agent/list_agents.py,sha256=BZ0H9ZfpwyXY57TPvkMVSsm72d6JcOY9qDUU4LxTvyE,6183
14
14
  robosystems_client/api/agent/recommend_agent.py,sha256=fy4cm-DA_FHiDsX9gtrHfITOmHJFGNekOkxAhPWz6I8,6601
@@ -67,7 +67,7 @@ robosystems_client/api/files/create_file_upload.py,sha256=OVMbGuJ004lqYrHmUFzr2g
67
67
  robosystems_client/api/files/delete_file.py,sha256=wAt0wHIYgX1D57HwQvUUb1GMr2GQzXNq3wz0MuCSI3I,9815
68
68
  robosystems_client/api/files/get_file.py,sha256=cZv2YurkjF8bhBzC_pprblPUguunV0ciOjuB4ygQS1Q,10558
69
69
  robosystems_client/api/files/list_files.py,sha256=3JnJ4gwZS7k4KdjnHp7fv0jq8JuTGzV0xRhZcMEfBcA,10541
70
- robosystems_client/api/files/update_file.py,sha256=PTFUwT8qsI8tUFo7Cno4iG7ouicIBGYWme0BiLy6Ogc,9027
70
+ robosystems_client/api/files/update_file.py,sha256=X80_TbYmQvL7mLdQejCy-UzUiyVx96SpCE2EHNJJSr8,9043
71
71
  robosystems_client/api/graph_health/__init__.py,sha256=5vd9uJWAjRqa9xzxzYkLD1yoZ12Ld_bAaNB5WX4fbE8,56
72
72
  robosystems_client/api/graph_health/get_database_health.py,sha256=rGMjLUjD5DfIGv1Qu8BARWRafZhTqyrsb65YCIFe1k8,8738
73
73
  robosystems_client/api/graph_info/__init__.py,sha256=5vd9uJWAjRqa9xzxzYkLD1yoZ12Ld_bAaNB5WX4fbE8,56
@@ -141,20 +141,20 @@ robosystems_client/api/user/update_user_password.py,sha256=0couyI63dlspqvtO62cI4
141
141
  robosystems_client/api/views/__init__.py,sha256=5vd9uJWAjRqa9xzxzYkLD1yoZ12Ld_bAaNB5WX4fbE8,56
142
142
  robosystems_client/api/views/create_view.py,sha256=aBjk0FsAj6A3K2iqjE7DcTLKjDRsxegFbs5RC0hmbqA,6316
143
143
  robosystems_client/api/views/save_view.py,sha256=T7PUUNvp0JIeOYWG2n8Nm4Y9hhEWV7i-Gl2Vl8Oz4Ls,9224
144
- robosystems_client/extensions/README.md,sha256=qfHFjdgA_J-zNXziNZE6M1MKJiwVkocBi01w_HhvzEk,16136
144
+ robosystems_client/extensions/README.md,sha256=NUkPte5GM8rJkT-tVrVXbujbKVxP-sw-QocYaLZfAL0,15971
145
145
  robosystems_client/extensions/__init__.py,sha256=eTuJQGygQTOWC51YVhJOWUWFUMLcPo7MpZ0H3GaxoR0,7076
146
- robosystems_client/extensions/agent_client.py,sha256=Db2C4hrakVsf6ScnBcNk6rte3Kwn4cQBEHsR_joWMTs,17750
146
+ robosystems_client/extensions/agent_client.py,sha256=kvC5UO5JjTpsVB_1ARFN2H7MSDZYjw016zJrnNTMZUM,17759
147
147
  robosystems_client/extensions/auth_integration.py,sha256=ABOJ8aVjfHehNGNzim1iR9-Cdh7Mr22ce-WgWWeqJt0,6535
148
148
  robosystems_client/extensions/dataframe_utils.py,sha256=gK1bgkVqBF0TvWVdGQvqWrt-ur_Rw11j8uNtMoulLWE,12312
149
149
  robosystems_client/extensions/element_mapping_client.py,sha256=yuh0QPQBPM33E7r6QWWDiKm3T4TfCdbn2kvO3Jlw4Cs,18516
150
150
  robosystems_client/extensions/extensions.py,sha256=IqTuVJNt6vvO5LhnH-9SexCF5k9Abqig1sBdY4SdD1I,6858
151
151
  robosystems_client/extensions/file_client.py,sha256=bknqmbYrIqxc2PnWskeDvcWYP2bemBQaxY_4A6zr5LY,12326
152
- robosystems_client/extensions/graph_client.py,sha256=OBi0xj0SLIRKLeSu_DiGt2ZakCmhggvNrMP3jdRfEgQ,10326
153
- robosystems_client/extensions/materialization_client.py,sha256=Pvq4Gz5fS4B2eTxFrpCGTj7-diOZwOtWnNo7wKT9mVE,6811
152
+ robosystems_client/extensions/graph_client.py,sha256=2Y9DaIH3pkzofS7ZujrDxzgBGSov0_uBUsM2B0jWrLw,15294
153
+ robosystems_client/extensions/materialization_client.py,sha256=3PzzEYn5NqENbb4PggGb1Z31SWmcW9D2mJ83uem22_c,8923
154
154
  robosystems_client/extensions/operation_client.py,sha256=B1qju-wWQrnrnVJixKGgsA_KEInviwJwdlJxzm_i7P0,13359
155
155
  robosystems_client/extensions/query_client.py,sha256=cX3e8EBoTeg4Lwm6edJYRULM2UmGpfqNX3f48S8TQbE,19430
156
- robosystems_client/extensions/sse_client.py,sha256=XvQIq3JQ0Yiax11E7cwclhupShYOpEMURM2cYQodiz8,15058
157
- robosystems_client/extensions/subgraph_workspace_client.py,sha256=Ioc7FNJEKaD_kAJBeymwtFlVI-U9t47RouD5ibUHv4g,24036
156
+ robosystems_client/extensions/sse_client.py,sha256=rqYs3mfnLhE3kwdhEWcbeiAFfT_6AFZRY5rpXpV5KHg,14956
157
+ robosystems_client/extensions/subgraph_workspace_client.py,sha256=ISMo7xkOB1Py35njIV_qjw94RJJKEZk8Ag4RGkII3-o,24208
158
158
  robosystems_client/extensions/table_client.py,sha256=hegYiPlR-HMacyinDDACZaMeNn0bamkYtbkzDkVawVI,4521
159
159
  robosystems_client/extensions/token_utils.py,sha256=qCK_s1vBzRnSYwtgncPZRLJVIw3WXmzqNTWjdEEpdgs,10899
160
160
  robosystems_client/extensions/utils.py,sha256=vhmUnEsq-UEAMgNhmkqlbJg4oJj096QPiHALEHJ-y4A,16207
@@ -300,7 +300,7 @@ robosystems_client/models/list_table_files_response.py,sha256=D4SuwlQy11oOhEq9k2
300
300
  robosystems_client/models/login_request.py,sha256=8LbzTIfoUWAKzkEYs7E6AXr4TEPAeejoX8kIMBRm7XQ,1579
301
301
  robosystems_client/models/logout_user_response_logoutuser.py,sha256=dpvqyIqCDQdXVyx-gDyl3qex2KuyjJUE0OuldQcL4k0,1257
302
302
  robosystems_client/models/materialize_request.py,sha256=t3TXLcPJabN_lSx6BwEeku2v2zGceHFRVSS6TpK59Iw,1483
303
- robosystems_client/models/materialize_response.py,sha256=RQSN9CqXKL1HvRlZP7DGj_unMWMp4vTcsFv_BBjsIMg,3540
303
+ robosystems_client/models/materialize_response.py,sha256=Luuf3FjUj1cZQ0QmcONR5ulMbKKvjhL5SkkMx7lBp-Q,2386
304
304
  robosystems_client/models/materialize_status_response.py,sha256=eXNmPgudtCneoVb2rbSW9DGGTG10DFoGN-oaiHuYjLo,5402
305
305
  robosystems_client/models/mcp_tool_call.py,sha256=dz--5OucQNcbFT7GaUR1uw-ZgSxHI0-zVYmz0J9fixE,2215
306
306
  robosystems_client/models/mcp_tool_call_arguments.py,sha256=FX9fVbI_8tzM1XF4oKaH7Tf2WFChsSuIHn03yZY0Q5w,1245
@@ -395,7 +395,7 @@ robosystems_client/models/table_info.py,sha256=vjOwl4srG9ktgg_ExG58kruAOh727Ithc
395
395
  robosystems_client/models/table_list_response.py,sha256=Yq6CMAEpWFGsLVGSXGOH4Q5ylqycqbFb-NfIWk8HPKU,2035
396
396
  robosystems_client/models/table_query_request.py,sha256=jcrXMey2DEebEgalsvVxJLtTNMMQYkBKIYPMDshd0i8,1918
397
397
  robosystems_client/models/table_query_response.py,sha256=HZcsK0XjPN7XukPStVyH3DDAsYVKuY1so3ft4dDU7-c,2304
398
- robosystems_client/models/token_pricing.py,sha256=r2ai68qdKuquv0cAE-1uB1P3g2kX66tayy7AkeFKswY,1877
398
+ robosystems_client/models/token_pricing.py,sha256=VR5CI6czu1b0amw4W0B1FxwTtIiztVvZqHmfho9Ac-Q,1885
399
399
  robosystems_client/models/transaction_summary_response.py,sha256=zP6d-iypgggzkRTrdK35MTIKugWl_UVt4bKFmQx6tB0,3693
400
400
  robosystems_client/models/upcoming_invoice.py,sha256=n2RyBnypJpkWp1jWEt73BB80YIlO9tBQCZXHNpGKOnw,3518
401
401
  robosystems_client/models/update_api_key_request.py,sha256=JEGm8NzYAh_5AzxBMZDCpYak9QbDbb85lPQ6qZlzwRE,2506
@@ -414,7 +414,7 @@ robosystems_client/models/view_axis_config_member_labels_type_0.py,sha256=kkzpHx
414
414
  robosystems_client/models/view_config.py,sha256=HQnqYjLMXRhjZLOc5ypwILriMFKuvPzu0hPQi2vyNoM,3795
415
415
  robosystems_client/models/view_source.py,sha256=h66cASj-P_-qOptKv26uAIe9PtIewU2nTs42Ls-lFFk,4098
416
416
  robosystems_client/models/view_source_type.py,sha256=KpgczHUeOinV01jdLvytZ2URKwcsRcp1doPx2D3USyw,169
417
- robosystems_client-0.2.22.dist-info/METADATA,sha256=yEG5FHqaUwnCVaVxGPrcXOAMY3DMYelvRAvJK3p5ikE,3950
418
- robosystems_client-0.2.22.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
419
- robosystems_client-0.2.22.dist-info/licenses/LICENSE,sha256=LjFqQPU4eQh7jAQ04SmE9eC0j74HCdXvzbo0hjW4mWo,1063
420
- robosystems_client-0.2.22.dist-info/RECORD,,
417
+ robosystems_client-0.2.24.dist-info/METADATA,sha256=H_jYC_RHMKkZjsZfrf7Tq3ohH0OaOK3xVQDQV7ieX4A,3950
418
+ robosystems_client-0.2.24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
419
+ robosystems_client-0.2.24.dist-info/licenses/LICENSE,sha256=LjFqQPU4eQh7jAQ04SmE9eC0j74HCdXvzbo0hjW4mWo,1063
420
+ robosystems_client-0.2.24.dist-info/RECORD,,