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 +4 -5
- nuclia/lib/kb.py +41 -41
- nuclia/lib/nua.py +28 -4
- nuclia/lib/nua_responses.py +3 -3
- nuclia/lib/utils.py +57 -14
- nuclia/tests/test_kb/test_search.py +1 -1
- {nuclia-4.8.7.dist-info → nuclia-4.8.9.dist-info}/METADATA +2 -2
- {nuclia-4.8.7.dist-info → nuclia-4.8.9.dist-info}/RECORD +12 -12
- {nuclia-4.8.7.dist-info → nuclia-4.8.9.dist-info}/WHEEL +1 -1
- {nuclia-4.8.7.dist-info → nuclia-4.8.9.dist-info}/entry_points.txt +0 -0
- {nuclia-4.8.7.dist-info → nuclia-4.8.9.dist-info}/licenses/LICENSE +0 -0
- {nuclia-4.8.7.dist-info → nuclia-4.8.9.dist-info}/top_level.txt +0 -0
nuclia/cli/run.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
|
-
from
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
151
|
-
|
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
|
-
|
460
|
-
|
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
|
nuclia/lib/nua_responses.py
CHANGED
@@ -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
|
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
|
126
|
-
raise ValueError("Can not setup
|
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
|
26
|
-
|
27
|
-
response.
|
28
|
-
|
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
|
32
|
-
raise RateLimitError(f"Rate limited: {
|
33
|
-
elif
|
34
|
-
raise DuplicateError(f"Duplicate resource: {
|
35
|
-
elif
|
36
|
-
raise InvalidPayload(f"Invalid payload: {
|
37
|
-
elif
|
38
|
-
|
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())
|
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.
|
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.
|
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=
|
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=
|
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=
|
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=
|
17
|
-
nuclia/lib/utils.py,sha256=
|
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=
|
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.
|
65
|
-
nuclia-4.8.
|
66
|
-
nuclia-4.8.
|
67
|
-
nuclia-4.8.
|
68
|
-
nuclia-4.8.
|
69
|
-
nuclia-4.8.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|