spice-mcp 0.1.2__py3-none-any.whl → 0.1.3__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.
@@ -45,7 +45,7 @@ if TYPE_CHECKING:
45
45
 
46
46
 
47
47
  # ---------------------------------------------------------------------------
48
- # Back-compat helpers expected by tests and adapter code
48
+ # Internal helpers used by adapter code and tests
49
49
 
50
50
  def _is_sql(query: int | str) -> bool:
51
51
  if isinstance(query, int):
@@ -305,11 +305,24 @@ def query(
305
305
  df = get_results(**execute_kwargs, **result_kwargs)
306
306
  if df is not None:
307
307
  return process_result(df, execution, **output_kwargs)
308
- execution = execute_query(**execute_kwargs, verbose=verbose)
308
+ try:
309
+ execution = execute_query(**execute_kwargs, verbose=verbose)
310
+ except Exception as e:
311
+ # Re-raise with more context about the failure
312
+ if verbose:
313
+ print(f'execute_query failed for query_id={query_id}, parameters={parameters}')
314
+ raise Exception(f'failed to execute query {query_id}: {e}') from e
315
+ else:
316
+ # query_id is None or falsy - this shouldn't happen for valid inputs
317
+ if verbose:
318
+ print(f'query_id is falsy: {query_id}, query_or_execution={query_or_execution}')
309
319
 
310
- # await execution completion
320
+ # check execution status
311
321
  if execution is None:
312
- raise Exception('could not determine execution')
322
+ error_detail = f'query_id={query_id}, query_type={type(query_or_execution).__name__}'
323
+ if isinstance(query_or_execution, str):
324
+ error_detail += f', query_preview={query_or_execution[:100]}'
325
+ raise Exception(f'could not determine execution ({error_detail})')
313
326
  if poll:
314
327
  poll_execution(execution, **poll_kwargs)
315
328
  df = get_results(execution, api_key, **result_kwargs)
@@ -322,206 +335,8 @@ def query(
322
335
 
323
336
 
324
337
  @overload
325
- async def async_query(
326
- query_or_execution: Query | Execution,
327
- *,
328
- verbose: bool = True,
329
- refresh: bool = False,
330
- max_age: float | None = None,
331
- parameters: Mapping[str, Any] | None = None,
332
- api_key: str | None = None,
333
- performance: Performance = 'medium',
334
- poll: Literal[False],
335
- poll_interval: float = 1.0,
336
- timeout_seconds: float | None = None,
337
- limit: int | None = None,
338
- offset: int | None = None,
339
- sample_count: int | None = None,
340
- sort_by: str | None = None,
341
- columns: Sequence[str] | None = None,
342
- extras: Mapping[str, Any] | None = None,
343
- types: Sequence[type[pl.DataType]]
344
- | Mapping[str, type[pl.DataType]]
345
- | None = None,
346
- all_types: Sequence[type[pl.DataType]]
347
- | Mapping[str, type[pl.DataType]]
348
- | None = None,
349
- cache: bool = True,
350
- cache_dir: str | None = None,
351
- save_to_cache: bool = True,
352
- load_from_cache: bool = True,
353
- include_execution: bool = False,
354
- ) -> Execution: ...
355
-
356
-
357
338
  @overload
358
- async def async_query(
359
- query_or_execution: Query | Execution,
360
- *,
361
- verbose: bool = True,
362
- refresh: bool = False,
363
- max_age: float | None = None,
364
- parameters: Mapping[str, Any] | None = None,
365
- api_key: str | None = None,
366
- performance: Performance = 'medium',
367
- poll: Literal[True] = True,
368
- poll_interval: float = 1.0,
369
- limit: int | None = None,
370
- offset: int | None = None,
371
- sample_count: int | None = None,
372
- sort_by: str | None = None,
373
- columns: Sequence[str] | None = None,
374
- extras: Mapping[str, Any] | None = None,
375
- types: Sequence[type[pl.DataType]]
376
- | Mapping[str, type[pl.DataType]]
377
- | None = None,
378
- all_types: Sequence[type[pl.DataType]]
379
- | Mapping[str, type[pl.DataType]]
380
- | None = None,
381
- cache: bool = True,
382
- cache_dir: str | None = None,
383
- save_to_cache: bool = True,
384
- load_from_cache: bool = True,
385
- include_execution: Literal[False] = False,
386
- ) -> pl.DataFrame: ...
387
-
388
-
389
339
  @overload
390
- async def async_query(
391
- query_or_execution: Query | Execution,
392
- *,
393
- verbose: bool = True,
394
- refresh: bool = False,
395
- max_age: float | None = None,
396
- parameters: Mapping[str, Any] | None = None,
397
- api_key: str | None = None,
398
- performance: Performance = 'medium',
399
- poll: Literal[True] = True,
400
- poll_interval: float = 1.0,
401
- limit: int | None = None,
402
- offset: int | None = None,
403
- sample_count: int | None = None,
404
- sort_by: str | None = None,
405
- columns: Sequence[str] | None = None,
406
- extras: Mapping[str, Any] | None = None,
407
- types: Sequence[type[pl.DataType]]
408
- | Mapping[str, type[pl.DataType]]
409
- | None = None,
410
- all_types: Sequence[type[pl.DataType]]
411
- | Mapping[str, type[pl.DataType]]
412
- | None = None,
413
- cache: bool = True,
414
- cache_dir: str | None = None,
415
- save_to_cache: bool = True,
416
- load_from_cache: bool = True,
417
- include_execution: Literal[True],
418
- ) -> tuple[pl.DataFrame, Execution]: ...
419
-
420
-
421
- async def async_query(
422
- query_or_execution: Query | Execution,
423
- *,
424
- verbose: bool = True,
425
- refresh: bool = False,
426
- max_age: float | None = None,
427
- parameters: Mapping[str, Any] | None = None,
428
- api_key: str | None = None,
429
- performance: Performance = 'medium',
430
- poll: bool = True,
431
- poll_interval: float = 1.0,
432
- timeout_seconds: float | None = None,
433
- limit: int | None = None,
434
- offset: int | None = None,
435
- sample_count: int | None = None,
436
- sort_by: str | None = None,
437
- columns: Sequence[str] | None = None,
438
- extras: Mapping[str, Any] | None = None,
439
- types: Sequence[type[pl.DataType]]
440
- | Mapping[str, type[pl.DataType]]
441
- | None = None,
442
- all_types: Sequence[type[pl.DataType]]
443
- | Mapping[str, type[pl.DataType]]
444
- | None = None,
445
- cache: bool = True,
446
- cache_dir: str | None = None,
447
- save_to_cache: bool = True,
448
- load_from_cache: bool = True,
449
- include_execution: bool = False,
450
- ):
451
-
452
- # determine whether target is a query or an execution
453
- query_id, execution, parameters = _determine_input_type(
454
- query_or_execution,
455
- parameters,
456
- )
457
-
458
- # gather arguments
459
- execute_kwargs: ExecuteKwargs = {
460
- 'query_id': query_id,
461
- 'api_key': api_key,
462
- 'parameters': parameters,
463
- 'performance': performance,
464
- }
465
- poll_kwargs: PollKwargs = {
466
- 'poll_interval': poll_interval,
467
- 'api_key': api_key,
468
- 'verbose': verbose,
469
- 'timeout_seconds': timeout_seconds,
470
- }
471
- result_kwargs: RetrievalKwargs = {
472
- 'limit': limit,
473
- 'offset': offset,
474
- 'sample_count': sample_count,
475
- 'sort_by': sort_by,
476
- 'columns': columns,
477
- 'extras': extras,
478
- 'types': types,
479
- 'all_types': all_types,
480
- 'verbose': verbose,
481
- }
482
- output_kwargs: OutputKwargs = {
483
- 'execute_kwargs': execute_kwargs,
484
- 'result_kwargs': result_kwargs,
485
- 'cache': cache,
486
- 'save_to_cache': save_to_cache,
487
- 'cache_dir': cache_dir,
488
- 'include_execution': include_execution,
489
- }
490
-
491
- # execute or retrieve query
492
- if query_id:
493
- if cache and cache_dir is not None and not refresh:
494
- cache_result, cache_execution = await _cache.async_load_from_cache(
495
- execute_kwargs, result_kwargs, output_kwargs
496
- )
497
- if cache_result is not None:
498
- return cache_result
499
- if execution is None and cache_execution is not None:
500
- execution = cache_execution
501
- if max_age is not None and not refresh:
502
- age = await async_get_query_latest_age(**execute_kwargs, verbose=verbose) # type: ignore
503
- if age is None or age > max_age:
504
- refresh = True
505
- if not refresh:
506
- df = await async_get_results(**execute_kwargs, **result_kwargs)
507
- if df is not None:
508
- return await async_process_result(df, execution, **output_kwargs)
509
- execution = await async_execute_query(**execute_kwargs, verbose=verbose)
510
-
511
- # await execution completion
512
- if execution is None:
513
- raise Exception('could not determine execution')
514
- if poll:
515
- await async_poll_execution(execution, **poll_kwargs)
516
- df = await async_get_results(execution, api_key, **result_kwargs)
517
- if df is not None:
518
- return await async_process_result(df, execution, **output_kwargs)
519
- else:
520
- raise Exception('no successful execution for query')
521
- else:
522
- return execution
523
-
524
-
525
340
  def _process_result(
526
341
  df: pl.DataFrame,
527
342
  execution: Execution | None,
@@ -551,35 +366,6 @@ def _process_result(
551
366
  return df
552
367
 
553
368
 
554
- async def _async_process_result(
555
- df: pl.DataFrame,
556
- execution: Execution | None,
557
- execute_kwargs: ExecuteKwargs,
558
- result_kwargs: RetrievalKwargs,
559
- cache: bool,
560
- save_to_cache: bool,
561
- cache_dir: str | None,
562
- include_execution: bool,
563
- ) -> pl.DataFrame | tuple[pl.DataFrame, Execution]:
564
- if cache and save_to_cache and execute_kwargs['query_id'] is not None:
565
- if execution is None:
566
- execution = await async_get_latest_execution(execute_kwargs)
567
- if execution is None:
568
- raise Exception('could not get execution')
569
- _cache.save_to_cache(
570
- df, execution, execute_kwargs, result_kwargs, cache_dir
571
- )
572
-
573
- if include_execution:
574
- if execution is None:
575
- execution = await async_get_latest_execution(execute_kwargs)
576
- if execution is None:
577
- raise Exception('could not get execution')
578
- return df, execution
579
- else:
580
- return df
581
-
582
-
583
369
  def _get_query_latest_age(
584
370
  query_id: int,
585
371
  *,
@@ -654,83 +440,6 @@ def _parse_timestamp(timestamp: str) -> int:
654
440
  return int(timestamp_float)
655
441
 
656
442
 
657
- async def _async_get_query_latest_age(
658
- query_id: int,
659
- *,
660
- verbose: bool = True,
661
- parameters: Mapping[str, Any] | None = None,
662
- performance: Performance = 'medium',
663
- api_key: str | None = None,
664
- ) -> float | None:
665
- import datetime
666
- import json
667
-
668
- import aiohttp
669
-
670
- # process inputs
671
- if api_key is None:
672
- api_key = _urls.get_api_key()
673
- headers = {'X-Dune-API-Key': api_key, 'User-Agent': get_user_agent()}
674
- data = {}
675
- if parameters is not None:
676
- data['query_parameters'] = parameters
677
- url = _urls.get_query_results_url(query_id, parameters=data, csv=False)
678
-
679
- # print summary
680
- if verbose:
681
- print('checking age of last execution, query_id = ' + str(query_id))
682
-
683
- # perform request with retry/backoff for 429/502
684
- timeout = aiohttp.ClientTimeout(total=30)
685
- async with aiohttp.ClientSession(timeout=timeout) as session:
686
- attempts = 0
687
- backoff = 0.5
688
- while True:
689
- async with session.get(url, headers=headers) as response:
690
- if response.status in (429, 502):
691
- attempts += 1
692
- if attempts >= 3:
693
- break
694
- import asyncio
695
- import random
696
- await asyncio.sleep(backoff * random.uniform(1.5, 2.5))
697
- backoff = min(5.0, backoff * 2)
698
- continue
699
- result: Mapping[str, Any] = await response.json()
700
- break
701
-
702
- # check if result is error
703
- try:
704
- if 'error' in result:
705
- if (
706
- result['error']
707
- == 'not found: No execution found for the latest version of the given query'
708
- ):
709
- if verbose:
710
- print(
711
- 'no age for query, because no previous executions exist'
712
- )
713
- return None
714
- raise Exception(result['error'])
715
- except json.JSONDecodeError:
716
- pass
717
-
718
- # process result
719
- if 'execution_started_at' in result:
720
- now = datetime.datetime.now(datetime.UTC).timestamp()
721
- started = _parse_timestamp(result['execution_started_at'])
722
- age = now - started
723
-
724
- if verbose:
725
- print('latest result age:', age)
726
-
727
- return age
728
- else:
729
- if verbose:
730
- print('no age for query, because no previous executions exist')
731
- return None
732
-
733
-
734
443
  def _execute(
735
444
  query_id: int | str,
736
445
  *,
@@ -755,50 +464,25 @@ def _execute(
755
464
 
756
465
  # perform request
757
466
  response = _transport_post(url, headers=headers, json=data, timeout=_POST_TIMEOUT)
758
- result: Mapping[str, Any] = response.json()
759
-
760
- # check for errors
761
- if 'execution_id' not in result:
762
- raise Exception(result['error'])
763
-
764
- # process result
765
- execution_id = result['execution_id']
766
- return {'execution_id': execution_id, 'timestamp': None}
767
-
768
-
769
- async def _async_execute(
770
- query_id: int | str,
771
- *,
772
- parameters: Mapping[str, Any] | None = None,
773
- performance: Performance = 'medium',
774
- api_key: str | None = None,
775
- verbose: bool = True,
776
- ) -> Execution:
777
- import aiohttp
778
-
779
- # process inputs
780
- url = _urls.get_query_execute_url(query_id)
781
- if api_key is None:
782
- api_key = _urls.get_api_key()
783
- headers = {'X-Dune-API-Key': api_key, 'User-Agent': get_user_agent()}
784
- data = {}
785
- if parameters is not None:
786
- data['query_parameters'] = parameters
787
- data['performance'] = performance
788
-
789
- # print summary
790
- if verbose:
791
- print('executing query, query_id = ' + str(query_id))
792
-
793
- # perform request
794
- timeout = aiohttp.ClientTimeout(total=_POST_TIMEOUT)
795
- async with aiohttp.ClientSession(timeout=timeout) as session:
796
- async with session.post(url, headers=headers, json=data) as response:
797
- result: Mapping[str, Any] = await response.json()
467
+
468
+ # Parse response with better error handling
469
+ try:
470
+ result: Mapping[str, Any] = response.json()
471
+ except Exception as e:
472
+ if verbose:
473
+ print(f'failed to parse response JSON: {e}')
474
+ print(f'response status: {response.status_code}')
475
+ print(f'response text: {response.text[:500]}')
476
+ raise Exception(f'failed to parse response: {e}') from e
798
477
 
799
478
  # check for errors
800
479
  if 'execution_id' not in result:
801
- raise Exception(result['error'])
480
+ error_msg = result.get('error', f'response missing execution_id: {result}')
481
+ if verbose:
482
+ print(f'execution failed: {error_msg}')
483
+ print(f'response status: {response.status_code}')
484
+ print(f'full response: {result}')
485
+ raise Exception(error_msg)
802
486
 
803
487
  # process result
804
488
  execution_id = result['execution_id']
@@ -912,124 +596,6 @@ def _get_results(
912
596
  return df
913
597
 
914
598
 
915
- async def _async_get_results(
916
- execution: Execution | None = None,
917
- api_key: str | None = None,
918
- *,
919
- query_id: int | None = None,
920
- parameters: Mapping[str, Any] | None = None,
921
- performance: Performance = 'medium',
922
- limit: int | None = None,
923
- offset: int | None = None,
924
- sample_count: int | None = None,
925
- sort_by: str | None = None,
926
- columns: Sequence[str] | None = None,
927
- extras: Mapping[str, Any] | None = None,
928
- types: Sequence[type[pl.DataType]]
929
- | Mapping[str, type[pl.DataType]]
930
- | None = None,
931
- all_types: Sequence[type[pl.DataType]]
932
- | Mapping[str, type[pl.DataType]]
933
- | None = None,
934
- verbose: bool = True,
935
- ) -> pl.DataFrame | None:
936
- import asyncio
937
- import random
938
-
939
- import aiohttp
940
- import polars as pl
941
-
942
- if api_key is None:
943
- api_key = _urls.get_api_key()
944
- headers = {'X-Dune-API-Key': api_key, 'User-Agent': get_user_agent()}
945
- params: dict[str, Any] = {
946
- 'limit': limit,
947
- 'offset': offset,
948
- 'sample_count': sample_count,
949
- 'sort_by': sort_by,
950
- 'columns': columns,
951
- }
952
- if extras is not None:
953
- params.update(extras)
954
- if parameters is not None:
955
- params['query_parameters'] = parameters
956
- if query_id is not None:
957
- url = _urls.get_query_results_url(query_id, parameters=params)
958
- elif execution is not None:
959
- url = _urls.get_execution_results_url(execution['execution_id'], params)
960
- else:
961
- raise Exception('must specify query_id or execution')
962
-
963
- # print summary
964
- if verbose:
965
- if query_id is not None:
966
- print('getting results, query_id = ' + str(query_id))
967
- elif execution is not None:
968
- print('getting results, execution_id = ' + str(execution['execution_id']))
969
-
970
- # perform request
971
- timeout = aiohttp.ClientTimeout(total=_GET_TIMEOUT)
972
- async with aiohttp.ClientSession(timeout=timeout) as session:
973
- # GET with simple retry/backoff for 429/502
974
- attempts = 0
975
- backoff = 0.5
976
- while True:
977
- async with session.get(url, headers=headers) as response:
978
- if response.status in (429, 502):
979
- attempts += 1
980
- if attempts >= 3:
981
- break
982
- await asyncio.sleep(backoff * random.uniform(1.5, 2.5))
983
- backoff = min(5.0, backoff * 2)
984
- continue
985
- if response.status == 404:
986
- return None
987
- result = await response.text()
988
- response_headers = response.headers
989
- break
990
-
991
- # process result
992
- df = _process_raw_table(result, types=types, all_types=all_types)
993
-
994
- # support pagination when using limit
995
- if limit is not None:
996
- import polars as pl
997
-
998
- n_rows = len(df)
999
- pages = []
1000
- timeout = aiohttp.ClientTimeout(total=30)
1001
- async with aiohttp.ClientSession(timeout=timeout) as session:
1002
- while 'x-dune-next-uri' in response_headers and n_rows < limit:
1003
- if verbose:
1004
- off = response.headers.get('x-dune-next-offset', 'unknown')
1005
- print('gathering additional page, offset = ' + str(off))
1006
- next_url = response_headers['x-dune-next-uri']
1007
- # Pager GET with retry/backoff
1008
- attempts = 0
1009
- backoff = 0.5
1010
- while True:
1011
- async with session.get(next_url, headers=headers) as response:
1012
- if response.status in (429, 502):
1013
- attempts += 1
1014
- if attempts >= 3:
1015
- break
1016
- await asyncio.sleep(backoff * random.uniform(1.5, 2.5))
1017
- backoff = min(5.0, backoff * 2)
1018
- continue
1019
- result = await response.text()
1020
- response_headers = response.headers
1021
- break
1022
- page = _process_raw_table(
1023
- result, types=types, all_types=all_types
1024
- )
1025
- n_rows += len(page)
1026
- pages.append(page)
1027
-
1028
- df = pl.concat([df, *pages]).limit(limit)
1029
-
1030
- return df
1031
-
1032
-
1033
599
  def _process_raw_table(
1034
600
  raw_csv: str,
1035
601
  types: Sequence[type[pl.DataType] | None]
@@ -1224,97 +790,6 @@ def _poll_execution(
1224
790
  )
1225
791
 
1226
792
 
1227
- async def _async_poll_execution(
1228
- execution: Execution,
1229
- *,
1230
- api_key: str | None,
1231
- poll_interval: float,
1232
- verbose: bool,
1233
- timeout_seconds: float | None,
1234
- ) -> None:
1235
- import asyncio
1236
- import random
1237
-
1238
- import aiohttp
1239
-
1240
- # process inputs
1241
- url = _urls.get_execution_status_url(execution['execution_id'])
1242
- execution_id = execution['execution_id']
1243
- if api_key is None:
1244
- api_key = _urls.get_api_key()
1245
- headers = {'X-Dune-API-Key': api_key, 'User-Agent': get_user_agent()}
1246
-
1247
- # print summary
1248
- t_start = time.time()
1249
-
1250
- # poll until completion
1251
- timeout = aiohttp.ClientTimeout(total=_GET_TIMEOUT)
1252
- async with aiohttp.ClientSession(timeout=timeout) as session:
1253
- sleep_amount = poll_interval
1254
- while True:
1255
- t_poll = time.time()
1256
-
1257
- # print summary
1258
- if verbose:
1259
- print(
1260
- 'waiting for results, execution_id = '
1261
- + str(execution['execution_id'])
1262
- + ', t = '
1263
- + str(t_poll - t_start)
1264
- )
1265
-
1266
- # poll
1267
- async with session.get(url, headers=headers) as response:
1268
- result = await response.json()
1269
- if (
1270
- 'is_execution_finished' not in result
1271
- and response.status == 429
1272
- ):
1273
- sleep_amount = sleep_amount * random.uniform(1, 2)
1274
- await asyncio.sleep(sleep_amount)
1275
- continue
1276
- if result['is_execution_finished']:
1277
- if result['state'] == 'QUERY_STATE_FAILED':
1278
- err_detail = ''
1279
- try:
1280
- if 'error' in result and result['error']:
1281
- err_detail = f", error={result['error']}"
1282
- except Exception:
1283
- pass
1284
- raise Exception(
1285
- f"QUERY FAILED execution_id={execution_id} state={result.get('state')}{err_detail}"
1286
- )
1287
- execution['timestamp'] = _parse_timestamp(
1288
- result['execution_started_at']
1289
- )
1290
- break
1291
-
1292
- # timeout check
1293
- if timeout_seconds is not None and (t_poll - t_start) > timeout_seconds:
1294
- raise TimeoutError(
1295
- f'query polling timed out after {timeout_seconds} seconds'
1296
- )
1297
-
1298
- # wait until polling interval
1299
- t_wait = time.time() - t_poll
1300
- if t_wait < poll_interval:
1301
- import asyncio
1302
-
1303
- await asyncio.sleep(poll_interval - t_wait)
1304
-
1305
- # check for errors
1306
- if result['state'] == 'QUERY_STATE_FAILED':
1307
- err_detail = ''
1308
- try:
1309
- if 'error' in result and result['error']:
1310
- err_detail = f", error={result['error']}"
1311
- except Exception:
1312
- pass
1313
- raise Exception(
1314
- f"QUERY FAILED execution_id={execution_id} state={result.get('state')}{err_detail}"
1315
- )
1316
-
1317
-
1318
793
  def get_latest_execution(
1319
794
  execute_kwargs: ExecuteKwargs,
1320
795
  *,
@@ -1378,74 +853,6 @@ def get_latest_execution(
1378
853
  return execution
1379
854
 
1380
855
 
1381
- async def async_get_latest_execution(
1382
- execute_kwargs: ExecuteKwargs,
1383
- *,
1384
- allow_unfinished: bool = False,
1385
- ) -> Execution | None:
1386
- import json
1387
- import random
1388
-
1389
- import aiohttp
1390
-
1391
- query_id = execute_kwargs['query_id']
1392
- api_key = execute_kwargs['api_key']
1393
- parameters = execute_kwargs['parameters']
1394
- if query_id is None:
1395
- raise Exception('query_id required for async_get_latest_execution')
1396
-
1397
- # process inputs
1398
- if api_key is None:
1399
- api_key = _urls.get_api_key()
1400
- headers = {'X-Dune-API-Key': api_key, 'User-Agent': get_user_agent()}
1401
- data: dict[str, Any] = {}
1402
- if parameters is not None:
1403
- data['query_parameters'] = parameters
1404
- data['limit'] = 0
1405
- url = _urls.get_query_results_url(query_id, parameters=data, csv=False)
1406
-
1407
- # perform request
1408
- timeout = aiohttp.ClientTimeout(total=30)
1409
- async with aiohttp.ClientSession(timeout=timeout) as session:
1410
- sleep_amount = 1.0
1411
- while True:
1412
- async with session.get(url, headers=headers) as response:
1413
- if response.status in (429, 502):
1414
- sleep_amount = sleep_amount * random.uniform(1, 2)
1415
- import asyncio
1416
- await asyncio.sleep(sleep_amount)
1417
- continue
1418
- result: Mapping[str, Any] = await response.json()
1419
-
1420
- # check if result is error
1421
- try:
1422
- if 'error' in result:
1423
- if (
1424
- result['error']
1425
- == 'not found: No execution found for the latest version of the given query'
1426
- ):
1427
- return None
1428
- if response.status == 429:
1429
- import asyncio
1430
-
1431
- sleep_amount = sleep_amount * random.uniform(1, 2)
1432
- await asyncio.sleep(sleep_amount)
1433
- raise Exception(result['error'])
1434
- except json.JSONDecodeError:
1435
- pass
1436
- break
1437
-
1438
- # process result
1439
- if not result['is_execution_finished'] and not allow_unfinished:
1440
- return None
1441
- execution: Execution = {'execution_id': result['execution_id']}
1442
- if 'execution_started_at' in result:
1443
- execution['timestamp'] = int(
1444
- _parse_timestamp(result['execution_started_at'])
1445
- )
1446
- return execution
1447
-
1448
-
1449
856
  def get_user_agent() -> str:
1450
857
  # Identify as spice-mcp vendored spice client
1451
858
  return 'spice-mcp/' + ADAPTER_VERSION
@@ -1455,12 +862,7 @@ def get_user_agent() -> str:
1455
862
 
1456
863
  determine_input_type = _determine_input_type
1457
864
  get_query_latest_age = _get_query_latest_age
1458
- async_get_query_latest_age = _async_get_query_latest_age
1459
865
  execute_query = _execute
1460
- async_execute_query = _async_execute
1461
866
  get_results = _get_results
1462
- async_get_results = _async_get_results
1463
867
  process_result = _process_result
1464
- async_process_result = _async_process_result
1465
868
  poll_execution = _poll_execution
1466
- async_poll_execution = _async_poll_execution