nuclia 4.8.7__py3-none-any.whl → 4.8.9__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.
nuclia/cli/run.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import sys
3
- from pathlib import Path
3
+ from importlib.metadata import version
4
4
 
5
5
  import fire # type: ignore
6
6
  from nucliadb_sdk import exceptions
@@ -9,12 +9,13 @@ from nuclia.data import get_auth
9
9
  from nuclia.exceptions import NeedUserToken, UserTokenExpired
10
10
  from nuclia.lib.utils import serialize
11
11
  from nuclia.sdk.accounts import NucliaAccounts
12
+ from nuclia.sdk.backup import NucliaBackup
12
13
  from nuclia.sdk.kb import NucliaKB
13
14
  from nuclia.sdk.kbs import NucliaKBS
14
- from nuclia.sdk.backup import NucliaBackup
15
15
  from nuclia.sdk.logger import logger
16
16
  from nuclia.sdk.nua import NucliaNUA
17
17
  from nuclia.sdk.zones import NucliaZones
18
+
18
19
  from .utils import CustomFormatter
19
20
 
20
21
 
@@ -30,9 +31,7 @@ class NucliaCLI(object):
30
31
 
31
32
  def version(self):
32
33
  """Print the version of the CLI"""
33
- _dir = Path(__file__).resolve().parent.parent.parent
34
- VERSION = _dir.joinpath("VERSION").open().read().strip()
35
- return VERSION
34
+ return version("nuclia")
36
35
 
37
36
 
38
37
  def run():
nuclia/lib/kb.py CHANGED
@@ -25,7 +25,7 @@ from nuclia_models.events.activity_logs import ( # type: ignore
25
25
  from nuclia_models.events.remi import RemiQuery
26
26
  from nuclia_models.worker.tasks import TaskStartKB
27
27
  from nuclia.exceptions import RateLimitError
28
- from nuclia.lib.utils import handle_http_errors
28
+ from nuclia.lib.utils import handle_http_sync_errors, handle_http_async_errors
29
29
  from datetime import datetime
30
30
  from nuclia.lib.utils import build_httpx_client, build_httpx_async_client, USER_AGENT
31
31
 
@@ -220,7 +220,7 @@ class NucliaDBClient(BaseNucliaDBClient):
220
220
  raise Exception("KB not configured")
221
221
  url = f"{self.url}{NOTIFICATIONS}"
222
222
  response = self.stream_session.get(url, stream=True, timeout=3660)
223
- handle_http_errors(response)
223
+ handle_http_sync_errors(response)
224
224
  return response
225
225
 
226
226
  def ask(self, request: AskRequest, timeout: int = 1000):
@@ -233,7 +233,7 @@ class NucliaDBClient(BaseNucliaDBClient):
233
233
  stream=True,
234
234
  timeout=timeout,
235
235
  )
236
- handle_http_errors(response)
236
+ handle_http_sync_errors(response)
237
237
  return response
238
238
 
239
239
  def download_export(self, export_id: str, path: str, chunk_size: int):
@@ -276,7 +276,7 @@ class NucliaDBClient(BaseNucliaDBClient):
276
276
  new_uri = "/".join(uri_parts[3:])
277
277
  url = DOWNLOAD_URL.format(uri=new_uri)
278
278
  response: httpx.Response = self.reader_session.get(url)
279
- handle_http_errors(response)
279
+ handle_http_sync_errors(response)
280
280
  return response.content
281
281
 
282
282
  @backoff.on_exception(
@@ -320,7 +320,7 @@ class NucliaDBClient(BaseNucliaDBClient):
320
320
  headers["x-extract-strategy"] = extract_strategy
321
321
 
322
322
  response: httpx.Response = self.writer_session.post(url, headers=headers)
323
- handle_http_errors(response)
323
+ handle_http_sync_errors(response)
324
324
  return response.headers.get("Location")
325
325
 
326
326
  def patch_tus_upload(self, upload_url: str, data: bytes, offset: int) -> int:
@@ -338,7 +338,7 @@ class NucliaDBClient(BaseNucliaDBClient):
338
338
  response: httpx.Response = self.writer_session.patch(
339
339
  url, headers=headers, content=data
340
340
  )
341
- handle_http_errors(response)
341
+ handle_http_sync_errors(response)
342
342
  return int(response.headers.get("Upload-Offset"))
343
343
 
344
344
  def summarize(self, request: SummarizeRequest, timeout: int = 1000):
@@ -349,7 +349,7 @@ class NucliaDBClient(BaseNucliaDBClient):
349
349
  response = self.reader_session.post(
350
350
  url, json=request.model_dump(), timeout=timeout
351
351
  )
352
- handle_http_errors(response)
352
+ handle_http_sync_errors(response)
353
353
  return response
354
354
 
355
355
  def logs(self, type: LogType, month: str) -> list[list[str]]:
@@ -359,12 +359,12 @@ class NucliaDBClient(BaseNucliaDBClient):
359
359
  if type != "feedback":
360
360
  url = LEGACY_ACTIVITY_LOG_URL.format(type=type.value, month=month)
361
361
  response: httpx.Response = self.reader_session.get(url)
362
- handle_http_errors(response)
362
+ handle_http_sync_errors(response)
363
363
  return [row for row in csv.reader(response.iter_lines())]
364
364
  else:
365
365
  feedback_url = f"{self.url}{FEEDBACK_LOG_URL.format(month=month)}"
366
366
  feedback_response: httpx.Response = self.reader_session.get(feedback_url)
367
- handle_http_errors(feedback_response)
367
+ handle_http_sync_errors(feedback_response)
368
368
  feedbacks = [row for row in csv.reader(feedback_response.iter_lines())]
369
369
  answers = self.logs(type=LogType.CHAT, month=month)
370
370
  # first row with the columns headers
@@ -396,7 +396,7 @@ class NucliaDBClient(BaseNucliaDBClient):
396
396
  json=query.model_dump(mode="json", exclude_unset=True),
397
397
  stream=True,
398
398
  )
399
- handle_http_errors(response)
399
+ handle_http_sync_errors(response)
400
400
  return response
401
401
 
402
402
  def logs_download(
@@ -420,7 +420,7 @@ class NucliaDBClient(BaseNucliaDBClient):
420
420
  json=query.model_dump(mode="json", exclude_unset=True),
421
421
  headers={"accept": format_header_value},
422
422
  )
423
- handle_http_errors(response)
423
+ handle_http_sync_errors(response)
424
424
  return response
425
425
 
426
426
  def get_download_request(
@@ -431,7 +431,7 @@ class NucliaDBClient(BaseNucliaDBClient):
431
431
  raise Exception("KB not configured")
432
432
  download_request_url = f"{self.url}{ACTIVITY_LOG_DOWNLOAD_REQUEST_URL.format(request_id=request_id)}"
433
433
  response: httpx.Response = self.reader_session.get(download_request_url)
434
- handle_http_errors(response)
434
+ handle_http_sync_errors(response)
435
435
  return response
436
436
 
437
437
  def remi_query(
@@ -446,7 +446,7 @@ class NucliaDBClient(BaseNucliaDBClient):
446
446
  json=query.model_dump(mode="json", exclude_unset=True),
447
447
  timeout=10,
448
448
  )
449
- handle_http_errors(response)
449
+ handle_http_sync_errors(response)
450
450
  return response
451
451
 
452
452
  def get_remi_event(
@@ -459,7 +459,7 @@ class NucliaDBClient(BaseNucliaDBClient):
459
459
  response: httpx.Response = self.reader_session.get(
460
460
  f"{self.url}{REMI_EVENT_URL.format(event_id=event_id)}"
461
461
  )
462
- handle_http_errors(response)
462
+ handle_http_sync_errors(response)
463
463
  return response
464
464
 
465
465
  def get_remi_scores(
@@ -476,7 +476,7 @@ class NucliaDBClient(BaseNucliaDBClient):
476
476
  response: httpx.Response = self.reader_session.get(
477
477
  f"{self.url}{REMI_SCORES_URL}", params=params, timeout=10
478
478
  )
479
- handle_http_errors(response)
479
+ handle_http_sync_errors(response)
480
480
  return response
481
481
 
482
482
  def list_tasks(self) -> httpx.Response:
@@ -484,7 +484,7 @@ class NucliaDBClient(BaseNucliaDBClient):
484
484
  raise Exception("KB not configured")
485
485
 
486
486
  response: httpx.Response = self.reader_session.get(f"{self.url}{LIST_TASKS}")
487
- handle_http_errors(response)
487
+ handle_http_sync_errors(response)
488
488
  return response
489
489
 
490
490
  def start_task(self, body: TaskStartKB) -> httpx.Response:
@@ -495,7 +495,7 @@ class NucliaDBClient(BaseNucliaDBClient):
495
495
  f"{self.url}{START_TASK}",
496
496
  json=body.model_dump(mode="json", exclude_unset=True),
497
497
  )
498
- handle_http_errors(response)
498
+ handle_http_sync_errors(response)
499
499
  return response
500
500
 
501
501
  def delete_task(self, task_id: str) -> httpx.Response:
@@ -505,7 +505,7 @@ class NucliaDBClient(BaseNucliaDBClient):
505
505
  response: httpx.Response = self.writer_session.delete(
506
506
  f"{self.url}{DELETE_TASK.format(task_id=task_id)}",
507
507
  )
508
- handle_http_errors(response)
508
+ handle_http_sync_errors(response)
509
509
  return response
510
510
 
511
511
  def stop_task(self, task_id: str) -> httpx.Response:
@@ -515,7 +515,7 @@ class NucliaDBClient(BaseNucliaDBClient):
515
515
  response: httpx.Response = self.writer_session.post(
516
516
  f"{self.url}{STOP_TASK.format(task_id=task_id)}",
517
517
  )
518
- handle_http_errors(response)
518
+ handle_http_sync_errors(response)
519
519
  return response
520
520
 
521
521
  def get_task(self, task_id: str) -> httpx.Response:
@@ -525,7 +525,7 @@ class NucliaDBClient(BaseNucliaDBClient):
525
525
  response: httpx.Response = self.reader_session.get(
526
526
  f"{self.url}{GET_TASK.format(task_id=task_id)}",
527
527
  )
528
- handle_http_errors(response)
528
+ handle_http_sync_errors(response)
529
529
  return response
530
530
 
531
531
  def restart_task(self, task_id: str) -> httpx.Response:
@@ -535,7 +535,7 @@ class NucliaDBClient(BaseNucliaDBClient):
535
535
  response: httpx.Response = self.writer_session.post(
536
536
  f"{self.url}{RESTART_TASK.format(task_id=task_id)}",
537
537
  )
538
- handle_http_errors(response)
538
+ handle_http_sync_errors(response)
539
539
  return response
540
540
 
541
541
 
@@ -583,7 +583,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
583
583
  url = f"{self.url}{NOTIFICATIONS}"
584
584
  req = self.reader_session.build_request("GET", url, timeout=3660)
585
585
  response = await self.reader_session.send(req, stream=True)
586
- handle_http_errors(response)
586
+ await handle_http_async_errors(response)
587
587
  return response
588
588
 
589
589
  async def ask(self, request: AskRequest, timeout: int = 1000):
@@ -594,7 +594,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
594
594
  "POST", url, json=request.model_dump(), timeout=timeout
595
595
  )
596
596
  response = await self.reader_session.send(req, stream=True)
597
- handle_http_errors(response)
597
+ await handle_http_async_errors(response)
598
598
  return response
599
599
 
600
600
  async def download_export(self, export_id: str, path: str, chunk_size: int):
@@ -635,7 +635,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
635
635
  new_uri = "/".join(uri_parts[3:])
636
636
  url = DOWNLOAD_URL.format(uri=new_uri)
637
637
  response = await self.reader_session.get(url)
638
- handle_http_errors(response)
638
+ await handle_http_async_errors(response)
639
639
  return response.content
640
640
 
641
641
  @backoff.on_exception(
@@ -679,7 +679,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
679
679
  headers["x-extract-strategy"] = extract_strategy
680
680
 
681
681
  response = await self.writer_session.post(url, headers=headers)
682
- handle_http_errors(response)
682
+ await handle_http_async_errors(response)
683
683
  return response.headers.get("Location")
684
684
 
685
685
  async def patch_tus_upload(self, upload_url: str, data: bytes, offset: int) -> int:
@@ -695,7 +695,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
695
695
  url = httpx.URL("/".join(url.path.split("/")[3:]))
696
696
 
697
697
  response = await self.writer_session.patch(url, headers=headers, content=data)
698
- handle_http_errors(response)
698
+ await handle_http_async_errors(response)
699
699
  return int(response.headers.get("Upload-Offset"))
700
700
 
701
701
  async def summarize(self, request: SummarizeRequest, timeout: int = 1000):
@@ -706,7 +706,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
706
706
  response = await self.reader_session.post(
707
707
  url, json=request.model_dump(), timeout=timeout
708
708
  )
709
- handle_http_errors(response)
709
+ await handle_http_async_errors(response)
710
710
  return response
711
711
 
712
712
  async def logs(self, type: LogType, month: str) -> list[list[str]]:
@@ -716,14 +716,14 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
716
716
  if type != "feedback":
717
717
  url = LEGACY_ACTIVITY_LOG_URL.format(type=type.value, month=month)
718
718
  response: httpx.Response = await self.reader_session.get(url)
719
- handle_http_errors(response)
719
+ await handle_http_async_errors(response)
720
720
  return [row for row in csv.reader(response.iter_lines())]
721
721
  else:
722
722
  feedback_url = f"{self.url}{FEEDBACK_LOG_URL.format(month=month)}"
723
723
  feedback_response: httpx.Response = await self.reader_session.get(
724
724
  feedback_url
725
725
  )
726
- handle_http_errors(feedback_response)
726
+ await handle_http_async_errors(feedback_response)
727
727
  feedbacks = [row for row in csv.reader(feedback_response.iter_lines())]
728
728
  answers = await self.logs(type=LogType.CHAT, month=month)
729
729
  # first row with the columns headers
@@ -754,7 +754,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
754
754
  f"{self.url}{ACTIVITY_LOG_QUERY_URL.format(type=type.value)}",
755
755
  json=query.model_dump(mode="json", exclude_unset=True),
756
756
  )
757
- handle_http_errors(response)
757
+ await handle_http_async_errors(response)
758
758
  return response
759
759
 
760
760
  async def logs_download(
@@ -778,7 +778,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
778
778
  json=query.model_dump(mode="json", exclude_unset=True),
779
779
  headers={"accept": format_header_value},
780
780
  )
781
- handle_http_errors(response)
781
+ await handle_http_async_errors(response)
782
782
  return response
783
783
 
784
784
  async def get_download_request(
@@ -789,7 +789,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
789
789
  raise Exception("KB not configured")
790
790
  download_request_url = f"{self.url}{ACTIVITY_LOG_DOWNLOAD_REQUEST_URL.format(request_id=request_id)}"
791
791
  response: httpx.Response = await self.reader_session.get(download_request_url)
792
- handle_http_errors(response)
792
+ await handle_http_async_errors(response)
793
793
  return response
794
794
 
795
795
  async def remi_query(
@@ -804,7 +804,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
804
804
  json=query.model_dump(mode="json", exclude_unset=True),
805
805
  timeout=10,
806
806
  )
807
- handle_http_errors(response)
807
+ await handle_http_async_errors(response)
808
808
  return response
809
809
 
810
810
  async def get_remi_event(
@@ -817,7 +817,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
817
817
  response: httpx.Response = await self.reader_session.get(
818
818
  f"{self.url}{REMI_EVENT_URL.format(event_id=event_id)}"
819
819
  )
820
- handle_http_errors(response)
820
+ await handle_http_async_errors(response)
821
821
  return response
822
822
 
823
823
  async def get_remi_scores(
@@ -834,7 +834,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
834
834
  response: httpx.Response = await self.reader_session.get(
835
835
  f"{self.url}{REMI_SCORES_URL}", params=params, timeout=10
836
836
  )
837
- handle_http_errors(response)
837
+ await handle_http_async_errors(response)
838
838
  return response
839
839
 
840
840
  async def list_tasks(self) -> httpx.Response:
@@ -844,7 +844,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
844
844
  response: httpx.Response = await self.reader_session.get(
845
845
  f"{self.url}{LIST_TASKS}"
846
846
  )
847
- handle_http_errors(response)
847
+ await handle_http_async_errors(response)
848
848
  return response
849
849
 
850
850
  async def start_task(self, body: TaskStartKB) -> httpx.Response:
@@ -855,7 +855,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
855
855
  f"{self.url}{START_TASK}",
856
856
  json=body.model_dump(mode="json", exclude_unset=True),
857
857
  )
858
- handle_http_errors(response)
858
+ await handle_http_async_errors(response)
859
859
  return response
860
860
 
861
861
  async def delete_task(self, task_id: str) -> httpx.Response:
@@ -865,7 +865,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
865
865
  response: httpx.Response = await self.writer_session.delete(
866
866
  f"{self.url}{DELETE_TASK.format(task_id=task_id)}",
867
867
  )
868
- handle_http_errors(response)
868
+ await handle_http_async_errors(response)
869
869
  return response
870
870
 
871
871
  async def stop_task(self, task_id: str) -> httpx.Response:
@@ -875,7 +875,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
875
875
  response: httpx.Response = await self.writer_session.post(
876
876
  f"{self.url}{STOP_TASK.format(task_id=task_id)}",
877
877
  )
878
- handle_http_errors(response)
878
+ await handle_http_async_errors(response)
879
879
  return response
880
880
 
881
881
  async def get_task(self, task_id: str) -> httpx.Response:
@@ -885,7 +885,7 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
885
885
  response: httpx.Response = await self.reader_session.get(
886
886
  f"{self.url}{GET_TASK.format(task_id=task_id)}",
887
887
  )
888
- handle_http_errors(response)
888
+ await handle_http_async_errors(response)
889
889
  return response
890
890
 
891
891
  async def restart_task(self, task_id: str) -> httpx.Response:
@@ -895,5 +895,5 @@ class AsyncNucliaDBClient(BaseNucliaDBClient):
895
895
  response: httpx.Response = await self.writer_session.post(
896
896
  f"{self.url}{RESTART_TASK.format(task_id=task_id)}",
897
897
  )
898
- handle_http_errors(response)
898
+ await handle_http_async_errors(response)
899
899
  return response
nuclia/lib/nua.py CHANGED
@@ -56,6 +56,8 @@ import os
56
56
  from tqdm import tqdm
57
57
  import asyncio
58
58
  from nuclia.lib.utils import build_httpx_client, build_httpx_async_client
59
+ from pydantic import ValidationError
60
+
59
61
 
60
62
  if TYPE_CHECKING:
61
63
  from nucliadb_protos.writer_pb2 import BrokerMessage
@@ -147,8 +149,19 @@ class NuaClient:
147
149
  json=payload,
148
150
  timeout=timeout,
149
151
  ) as response:
150
- for json_body in response.iter_lines():
151
- yield GenerativeChunk.model_validate_json(json_body) # type: ignore
152
+ if response.headers.get("content-type") == "application/x-ndjson":
153
+ for json_body in response.iter_lines():
154
+ try:
155
+ yield GenerativeChunk.model_validate_json(json_body) # type: ignore
156
+ except ValidationError as e:
157
+ raise RuntimeError(f"Invalid stream chunk: {json_body}") from e
158
+
159
+ else:
160
+ # Read the full error body and raise an appropriate exception
161
+ error_content = response.content
162
+ raise RuntimeError(
163
+ f"Stream request failed with status {response.status_code}: {error_content.decode('utf-8')}"
164
+ )
152
165
 
153
166
  def add_config_predict(self, kbid: str, config: LearningConfigurationCreation):
154
167
  endpoint = f"{self.url}{CONFIG}/{kbid}"
@@ -456,8 +469,19 @@ class AsyncNuaClient:
456
469
  json=payload,
457
470
  timeout=timeout,
458
471
  ) as response:
459
- async for json_body in response.aiter_lines():
460
- yield GenerativeChunk.model_validate_json(json_body) # type: ignore
472
+ if response.headers.get("content-type") == "application/x-ndjson":
473
+ async for json_body in response.aiter_lines():
474
+ try:
475
+ yield GenerativeChunk.model_validate_json(json_body) # type: ignore
476
+ except ValidationError as e:
477
+ raise RuntimeError(f"Invalid stream chunk: {json_body}") from e
478
+
479
+ else:
480
+ # Read the full error body and raise an appropriate exception
481
+ error_content = await response.aread()
482
+ raise RuntimeError(
483
+ f"Stream request failed with status {response.status_code}: {error_content.decode('utf-8')}"
484
+ )
461
485
 
462
486
  async def add_config_predict(
463
487
  self, kbid: str, config: LearningConfigurationCreation
@@ -120,10 +120,10 @@ class ChatModel(BaseModel):
120
120
  raise ValueError("Can not setup markdown and JSON Schema at the same time")
121
121
  if self.citations is True and self.json_schema is not None:
122
122
  raise ValueError("Can not setup citations and JSON Schema at the same time")
123
- if self.citations is True and self.tools is not None:
123
+ if self.citations is True and len(self.tools) > 0:
124
124
  raise ValueError("Can not setup citations and Tools at the same time")
125
- if self.tools is True and self.json_schema is not None:
126
- raise ValueError("Can not setup tools and JSON Schema at the same time")
125
+ if len(self.tools) > 0 and self.json_schema is not None:
126
+ raise ValueError("Can not setup Tools and JSON Schema at the same time")
127
127
  return self
128
128
 
129
129
 
nuclia/lib/utils.py CHANGED
@@ -1,9 +1,9 @@
1
1
  import json
2
- from typing import Union
3
2
 
4
3
  import httpx
5
4
  import importlib.metadata
6
5
  import requests
6
+ from httpx import Response as HttpxResponse, HTTPStatusError
7
7
  from tabulate import tabulate
8
8
  from typing import Optional
9
9
 
@@ -18,24 +18,67 @@ from nucliadb_models.search import SyncAskResponse
18
18
  from nuclia.lib.models import ActivityLogsOutput
19
19
  from nuclia_models.worker.tasks import TaskDefinition, TaskList
20
20
  from nucliadb_models.resource import KnowledgeBoxList
21
+ from requests import Response as RequestsResponse, HTTPError as RequestsHTTPError
22
+
21
23
 
22
24
  USER_AGENT = f"nuclia.py/{importlib.metadata.version('nuclia')}"
23
25
 
24
26
 
25
- def handle_http_errors(response: Union[httpx.Response, requests.models.Response]):
26
- if (
27
- response.status_code == 403
28
- and "Hydra token is either unexistent or revoked" in response.text
29
- ):
27
+ def handle_http_sync_errors(response):
28
+ try:
29
+ content = response.text
30
+ except (httpx.ResponseNotRead, requests.exceptions.RequestException):
31
+ content = "<streaming content not read>"
32
+ except Exception as e:
33
+ content = f"<error decoding content: {e}>"
34
+
35
+ _raise_for_status(response.status_code, content, response=response)
36
+
37
+
38
+ async def handle_http_async_errors(response: httpx.Response):
39
+ try:
40
+ if not response.is_closed and not response.is_stream_consumed:
41
+ await response.aread()
42
+ except Exception as e:
43
+ content = f"<failed to read stream: {e}>"
44
+ _raise_for_status(
45
+ response.status_code, content, response=response, request=response.request
46
+ )
47
+ return # Defensive
48
+ try:
49
+ content = response.text
50
+ except httpx.ResponseNotRead:
51
+ content = "<streaming content not read>"
52
+ except Exception as e:
53
+ content = f"<error decoding content: {e}>"
54
+
55
+ _raise_for_status(
56
+ response.status_code, content, response=response, request=response.request
57
+ )
58
+
59
+
60
+ def _raise_for_status(status_code: int, content: str, response=None, request=None):
61
+ if status_code == 403 and "Hydra token is either unexistent or revoked" in content:
30
62
  raise UserTokenExpired()
31
- elif response.status_code == 429:
32
- raise RateLimitError(f"Rate limited: {response.text}")
33
- elif response.status_code == 409:
34
- raise DuplicateError(f"Duplicate resource: {response.text}")
35
- elif response.status_code == 422:
36
- raise InvalidPayload(f"Invalid payload: {response.text}")
37
- elif response.status_code >= 400:
38
- raise httpx.HTTPError(f"Status code {response.status_code}: {response.text}")
63
+ elif status_code == 429:
64
+ raise RateLimitError(f"Rate limited: {content}")
65
+ elif status_code == 409:
66
+ raise DuplicateError(f"Duplicate resource: {content}")
67
+ elif status_code == 422:
68
+ raise InvalidPayload(f"Invalid payload: {content}")
69
+ elif status_code >= 400:
70
+ if isinstance(response, HttpxResponse):
71
+ raise HTTPStatusError(
72
+ f"Status code {status_code}: {content}",
73
+ request=request,
74
+ response=response,
75
+ )
76
+ elif isinstance(response, RequestsResponse):
77
+ raise RequestsHTTPError(
78
+ f"Status code {status_code}: {content}", response=response
79
+ )
80
+ else:
81
+ raise Exception(f"Status code {status_code}: {content}")
39
82
 
40
83
 
41
84
  def serialize(obj):
@@ -69,7 +69,7 @@ def test_search(testing_config):
69
69
  def test_catalog(testing_config):
70
70
  search = NucliaSearch()
71
71
  results = search.catalog()
72
- assert len(results.resources.keys()) == 2
72
+ assert len(results.resources.keys()) >= 2
73
73
 
74
74
 
75
75
  def test_search_object(testing_config):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nuclia
3
- Version: 4.8.7
3
+ Version: 4.8.9
4
4
  Summary: Nuclia Python SDK
5
5
  Author-email: Nuclia <info@nuclia.com>
6
6
  License-Expression: MIT
@@ -26,7 +26,7 @@ Requires-Dist: httpcore>=1.0.0
26
26
  Requires-Dist: prompt_toolkit
27
27
  Requires-Dist: nucliadb_sdk<7,>=6.4
28
28
  Requires-Dist: nucliadb_models<7,>=6.4
29
- Requires-Dist: nuclia-models>=0.38.1
29
+ Requires-Dist: nuclia-models>=0.39.0
30
30
  Requires-Dist: tqdm
31
31
  Requires-Dist: aiofiles
32
32
  Requires-Dist: backoff
@@ -5,16 +5,16 @@ nuclia/decorators.py,sha256=GUhGDSrNtQ3DHpgsFArVJXpjykbDpi-Y8XRV5Vm4r6I,9879
5
5
  nuclia/exceptions.py,sha256=5nuWelyxjV7-oDJUnYRgIw_gnDnRUzJxvtXrAknV7So,748
6
6
  nuclia/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  nuclia/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- nuclia/cli/run.py,sha256=po6XRcWhtf-OcV2BkD4YJ0PmNTm6Hll4bs_rpotBEWo,1661
8
+ nuclia/cli/run.py,sha256=B1hP0upSbSCqqT89WAwsd93ZxkAoF6ajVyLOdYmo8fU,1560
9
9
  nuclia/cli/utils.py,sha256=iZ3P8juBdAGvaRUd2BGz7bpUXNDHdPrC5p876yyZ2Cs,1223
10
10
  nuclia/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  nuclia/lib/conversations.py,sha256=M6qhL9NPEKroYF767S-Q2XWokRrjX02kpYTzRvZKwUE,149
12
- nuclia/lib/kb.py,sha256=98zpJ8Dp4rQYMNKm4nhpOms3PY-PFOd8acNgDmBud-o,31819
12
+ nuclia/lib/kb.py,sha256=uUBts_s_St4DBeUTn3wSUdRSqtX2GbeLC5jAUVQAy-c,32190
13
13
  nuclia/lib/models.py,sha256=lO3TZa4jTQ6C8ptGO1oX-5pxDgv8ocO13z7j5jXOC0Y,1554
14
- nuclia/lib/nua.py,sha256=Y8W0sJWnJqNLQ8y8fPbcJAV-6UtbE1FearHs68qwZQA,25888
14
+ nuclia/lib/nua.py,sha256=sUVFdCjvLigTqUUhILywdHpiC0qKCtKPABn5kUXfuxQ,27064
15
15
  nuclia/lib/nua_chat.py,sha256=ApL1Y1FWvAVUt-Y9a_8TUSJIhg8-UmBSy8TlDPn6tD8,3874
16
- nuclia/lib/nua_responses.py,sha256=zji-T-ipYRPo-e1U163Nn_saIiYJYNW3PfjTSL2fQXg,12726
17
- nuclia/lib/utils.py,sha256=XAVRRICPem-qOIEcssqDuv-vqD-9PICk6Iuj6DK5GKk,4808
16
+ nuclia/lib/nua_responses.py,sha256=9WRGpvvhA2ZOyOv9kzX1zI7C3ypM8ySqciVYCSibUjo,12724
17
+ nuclia/lib/utils.py,sha256=9l6DxBk-11WqUhXRn99cqeuVTUOJXj-1S6zckal7wOk,6312
18
18
  nuclia/sdk/__init__.py,sha256=-nAw8i53XBdmbfTa1FJZ0FNRMNakimDVpD6W4OdES-c,1374
19
19
  nuclia/sdk/accounts.py,sha256=7XQ3K9_jlSuk2Cez868FtazZ05xSGab6h3Mt1qMMwIE,647
20
20
  nuclia/sdk/agent.py,sha256=ot_oA4yi7TkXahPnhcRIxztq9Dtskc85-A57WN1BNGg,1961
@@ -47,7 +47,7 @@ nuclia/tests/test_kb/test_labels.py,sha256=IUdTq4mzv0OrOkwBWWy4UwKGKyJybtoHrgvXr
47
47
  nuclia/tests/test_kb/test_logs.py,sha256=Iw0v-R5QLlfHKxL8roPeOQKjTylgpYgot0wG4vsXQTE,2609
48
48
  nuclia/tests/test_kb/test_remi.py,sha256=OX5N-MHbgcwpLg6fBjrAK_KhqkMspJo_VKQHCBCayZ8,2080
49
49
  nuclia/tests/test_kb/test_resource.py,sha256=05Xgmg5fwcPW2PZKnUSSjr6MPXp5w8XDgx8plfNcR68,1102
50
- nuclia/tests/test_kb/test_search.py,sha256=GWI1_TqCma9PSdn2yvHaFm-IHBldmEnVGQflQ556EJE,4660
50
+ nuclia/tests/test_kb/test_search.py,sha256=bsQhfB6-NYFwY3gqkrVJJnru153UgZqEhV22ho4VeWM,4660
51
51
  nuclia/tests/test_kb/test_tasks.py,sha256=tfJl1js2o0_dKyXdOxiwdj929kbzMkfl5XjO8HVMInQ,3456
52
52
  nuclia/tests/test_manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
53
  nuclia/tests/test_manage/test_account.py,sha256=u9NhRK8gJLS7BEY618aGoYoV2rgDLHZUeSsWWYkDhF8,289
@@ -61,9 +61,9 @@ nuclia/tests/test_nucliadb/test_crud.py,sha256=GuY76HRvt2DFaNgioKm5n0Aco1HnG7zzV
61
61
  nuclia/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  nuclia/tests/unit/test_export_import.py,sha256=xo_wVbjUnNlVV65ZGH7LtZ38qy39EkJp2hjOuTHC1nU,980
63
63
  nuclia/tests/unit/test_nua_responses.py,sha256=t_hIdVztTi27RWvpfTJUYcCL0lpKdZFegZIwLdaPNh8,319
64
- nuclia-4.8.7.dist-info/licenses/LICENSE,sha256=Ops2LTti_HJtpmWcanuUTdTY3vKDR1myJ0gmGBKC0FA,1063
65
- nuclia-4.8.7.dist-info/METADATA,sha256=L_ftUZ5c8tQ5NA4gpDuNzKTkn8Id_XfSxjEk1pleolU,2337
66
- nuclia-4.8.7.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
67
- nuclia-4.8.7.dist-info/entry_points.txt,sha256=iZHOyXPNS54r3eQmdi5So20xO1gudI9K2oP4sQsCJRw,46
68
- nuclia-4.8.7.dist-info/top_level.txt,sha256=cqn_EitXOoXOSUvZnd4q6QGrhm04pg8tLAZtem-Zfdo,7
69
- nuclia-4.8.7.dist-info/RECORD,,
64
+ nuclia-4.8.9.dist-info/licenses/LICENSE,sha256=Ops2LTti_HJtpmWcanuUTdTY3vKDR1myJ0gmGBKC0FA,1063
65
+ nuclia-4.8.9.dist-info/METADATA,sha256=1Jn5Qc4x7iXS9dfqyY__tEBakgz8xpPxbnl-rCqTWLM,2337
66
+ nuclia-4.8.9.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
67
+ nuclia-4.8.9.dist-info/entry_points.txt,sha256=iZHOyXPNS54r3eQmdi5So20xO1gudI9K2oP4sQsCJRw,46
68
+ nuclia-4.8.9.dist-info/top_level.txt,sha256=cqn_EitXOoXOSUvZnd4q6QGrhm04pg8tLAZtem-Zfdo,7
69
+ nuclia-4.8.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.4.0)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5