glaip-sdk 0.0.5__py3-none-any.whl → 0.0.6a0__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.
- glaip_sdk/__init__.py +1 -1
- glaip_sdk/branding.py +3 -2
- glaip_sdk/cli/commands/__init__.py +1 -1
- glaip_sdk/cli/commands/agents.py +444 -268
- glaip_sdk/cli/commands/configure.py +12 -11
- glaip_sdk/cli/commands/mcps.py +28 -16
- glaip_sdk/cli/commands/models.py +5 -3
- glaip_sdk/cli/commands/tools.py +109 -102
- glaip_sdk/cli/display.py +38 -16
- glaip_sdk/cli/io.py +1 -1
- glaip_sdk/cli/main.py +26 -5
- glaip_sdk/cli/resolution.py +5 -4
- glaip_sdk/cli/utils.py +376 -157
- glaip_sdk/cli/validators.py +7 -2
- glaip_sdk/client/agents.py +184 -89
- glaip_sdk/client/base.py +24 -13
- glaip_sdk/client/validators.py +154 -94
- glaip_sdk/config/constants.py +0 -2
- glaip_sdk/models.py +4 -4
- glaip_sdk/utils/__init__.py +7 -7
- glaip_sdk/utils/client_utils.py +144 -78
- glaip_sdk/utils/display.py +4 -2
- glaip_sdk/utils/general.py +8 -6
- glaip_sdk/utils/import_export.py +55 -24
- glaip_sdk/utils/rendering/formatting.py +12 -6
- glaip_sdk/utils/rendering/models.py +1 -1
- glaip_sdk/utils/rendering/renderer/base.py +412 -248
- glaip_sdk/utils/rendering/renderer/console.py +6 -5
- glaip_sdk/utils/rendering/renderer/debug.py +94 -52
- glaip_sdk/utils/rendering/renderer/stream.py +93 -48
- glaip_sdk/utils/rendering/steps.py +103 -39
- glaip_sdk/utils/rich_utils.py +1 -1
- glaip_sdk/utils/run_renderer.py +1 -1
- glaip_sdk/utils/serialization.py +3 -1
- glaip_sdk/utils/validation.py +2 -2
- glaip_sdk-0.0.6a0.dist-info/METADATA +183 -0
- glaip_sdk-0.0.6a0.dist-info/RECORD +55 -0
- {glaip_sdk-0.0.5.dist-info → glaip_sdk-0.0.6a0.dist-info}/WHEEL +1 -1
- glaip_sdk-0.0.6a0.dist-info/entry_points.txt +3 -0
- glaip_sdk-0.0.5.dist-info/METADATA +0 -645
- glaip_sdk-0.0.5.dist-info/RECORD +0 -55
- glaip_sdk-0.0.5.dist-info/entry_points.txt +0 -2
glaip_sdk/cli/validators.py
CHANGED
|
@@ -7,7 +7,9 @@ Authors:
|
|
|
7
7
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from collections.abc import Callable
|
|
10
11
|
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
11
13
|
|
|
12
14
|
import click
|
|
13
15
|
|
|
@@ -189,7 +191,7 @@ def validate_api_key_cli(api_key: str) -> str:
|
|
|
189
191
|
raise click.ClickException(str(e))
|
|
190
192
|
|
|
191
193
|
|
|
192
|
-
def coerce_timeout_cli(value) -> int:
|
|
194
|
+
def coerce_timeout_cli(value: int | float | str) -> int:
|
|
193
195
|
"""Coerce timeout value to integer with CLI-friendly error handling.
|
|
194
196
|
|
|
195
197
|
Args:
|
|
@@ -208,7 +210,10 @@ def coerce_timeout_cli(value) -> int:
|
|
|
208
210
|
|
|
209
211
|
|
|
210
212
|
def validate_name_uniqueness_cli(
|
|
211
|
-
_client
|
|
213
|
+
_client: Any,
|
|
214
|
+
name: str,
|
|
215
|
+
resource_type: str,
|
|
216
|
+
finder_func: Callable[..., list[Any]],
|
|
212
217
|
) -> None:
|
|
213
218
|
"""Validate that a resource name is unique.
|
|
214
219
|
|
glaip_sdk/client/agents.py
CHANGED
|
@@ -49,7 +49,12 @@ logger = logging.getLogger("glaip_sdk.agents")
|
|
|
49
49
|
class AgentClient(BaseClient):
|
|
50
50
|
"""Client for agent operations."""
|
|
51
51
|
|
|
52
|
-
def __init__(
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
*,
|
|
55
|
+
parent_client: BaseClient | None = None,
|
|
56
|
+
**kwargs: Any,
|
|
57
|
+
) -> None:
|
|
53
58
|
"""Initialize the agent client.
|
|
54
59
|
|
|
55
60
|
Args:
|
|
@@ -161,7 +166,7 @@ class AgentClient(BaseClient):
|
|
|
161
166
|
tools: list[str | Any] | None = None,
|
|
162
167
|
agents: list[str | Any] | None = None,
|
|
163
168
|
timeout: int = DEFAULT_AGENT_RUN_TIMEOUT,
|
|
164
|
-
**kwargs,
|
|
169
|
+
**kwargs: Any,
|
|
165
170
|
) -> dict[str, Any]:
|
|
166
171
|
"""Build payload for agent creation with proper LM selection and metadata handling.
|
|
167
172
|
|
|
@@ -365,7 +370,10 @@ class AgentClient(BaseClient):
|
|
|
365
370
|
agent_config.pop(key, None)
|
|
366
371
|
|
|
367
372
|
def _finalize_update_payload(
|
|
368
|
-
self,
|
|
373
|
+
self,
|
|
374
|
+
update_data: dict[str, Any],
|
|
375
|
+
current_agent: "Agent",
|
|
376
|
+
**kwargs: Any,
|
|
369
377
|
) -> dict[str, Any]:
|
|
370
378
|
"""Finalize the update payload with metadata and additional kwargs."""
|
|
371
379
|
# Handle metadata preservation
|
|
@@ -386,7 +394,7 @@ class AgentClient(BaseClient):
|
|
|
386
394
|
name: str | None = None,
|
|
387
395
|
instruction: str | None = None,
|
|
388
396
|
model: str | None = None,
|
|
389
|
-
**kwargs,
|
|
397
|
+
**kwargs: Any,
|
|
390
398
|
) -> dict[str, Any]:
|
|
391
399
|
"""Build payload for agent update with proper LM selection and current state preservation.
|
|
392
400
|
|
|
@@ -434,7 +442,7 @@ class AgentClient(BaseClient):
|
|
|
434
442
|
tools: list[str | Any] | None = None,
|
|
435
443
|
agents: list[str | Any] | None = None,
|
|
436
444
|
timeout: int = DEFAULT_AGENT_RUN_TIMEOUT,
|
|
437
|
-
**kwargs,
|
|
445
|
+
**kwargs: Any,
|
|
438
446
|
) -> "Agent":
|
|
439
447
|
"""Create a new agent."""
|
|
440
448
|
# Client-side validation
|
|
@@ -470,7 +478,7 @@ class AgentClient(BaseClient):
|
|
|
470
478
|
name: str | None = None,
|
|
471
479
|
instruction: str | None = None,
|
|
472
480
|
model: str | None = None,
|
|
473
|
-
**kwargs,
|
|
481
|
+
**kwargs: Any,
|
|
474
482
|
) -> "Agent":
|
|
475
483
|
"""Update an existing agent."""
|
|
476
484
|
# First, get the current agent data
|
|
@@ -493,39 +501,67 @@ class AgentClient(BaseClient):
|
|
|
493
501
|
"""Delete an agent."""
|
|
494
502
|
self._request("DELETE", f"/agents/{agent_id}")
|
|
495
503
|
|
|
496
|
-
def
|
|
497
|
-
self,
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
504
|
+
def _prepare_sync_request_data(
|
|
505
|
+
self,
|
|
506
|
+
message: str,
|
|
507
|
+
files: list[str | BinaryIO] | None,
|
|
508
|
+
tty: bool,
|
|
509
|
+
**kwargs: Any,
|
|
510
|
+
) -> tuple[dict | None, dict | None, list | None, dict, Any | None]:
|
|
511
|
+
"""Prepare request data for synchronous agent runs with renderer support.
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
message: Message to send
|
|
515
|
+
files: Optional files to include
|
|
516
|
+
tty: Whether to enable TTY mode
|
|
517
|
+
**kwargs: Additional request parameters
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
Tuple of (payload, data_payload, files_payload, headers, multipart_data)
|
|
521
|
+
"""
|
|
522
|
+
headers = {"Accept": "text/event-stream"}
|
|
502
523
|
|
|
503
524
|
if files:
|
|
525
|
+
# Handle multipart data for file uploads
|
|
504
526
|
multipart_data = prepare_multipart_data(message, files)
|
|
505
|
-
# Inject optional multipart extras expected by backend
|
|
506
527
|
if "chat_history" in kwargs and kwargs["chat_history"] is not None:
|
|
507
528
|
multipart_data.data["chat_history"] = kwargs["chat_history"]
|
|
508
529
|
if "pii_mapping" in kwargs and kwargs["pii_mapping"] is not None:
|
|
509
530
|
multipart_data.data["pii_mapping"] = kwargs["pii_mapping"]
|
|
510
531
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
532
|
+
return (
|
|
533
|
+
None,
|
|
534
|
+
multipart_data.data,
|
|
535
|
+
multipart_data.files,
|
|
536
|
+
headers,
|
|
537
|
+
multipart_data,
|
|
538
|
+
)
|
|
518
539
|
else:
|
|
519
|
-
payload
|
|
540
|
+
# Simple JSON payload for text-only requests
|
|
541
|
+
payload = {"input": message, "stream": True, **kwargs}
|
|
520
542
|
if tty:
|
|
521
543
|
payload["tty"] = True
|
|
522
|
-
payload
|
|
523
|
-
|
|
524
|
-
|
|
544
|
+
return payload, None, None, headers, None
|
|
545
|
+
|
|
546
|
+
def _get_timeout_values(
|
|
547
|
+
self, timeout: float | None, **kwargs: Any
|
|
548
|
+
) -> tuple[float, float]:
|
|
549
|
+
"""Get request timeout and execution timeout values.
|
|
525
550
|
|
|
526
|
-
|
|
551
|
+
Args:
|
|
552
|
+
timeout: Request timeout (overrides instance timeout)
|
|
553
|
+
**kwargs: Additional parameters including execution timeout
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
Tuple of (request_timeout, execution_timeout)
|
|
557
|
+
"""
|
|
558
|
+
request_timeout = timeout or self.timeout
|
|
559
|
+
execution_timeout = kwargs.get("timeout", DEFAULT_AGENT_RUN_TIMEOUT)
|
|
560
|
+
return request_timeout, execution_timeout
|
|
527
561
|
|
|
528
|
-
def _create_renderer(
|
|
562
|
+
def _create_renderer(
|
|
563
|
+
self, renderer: RichStreamRenderer | None, **kwargs: Any
|
|
564
|
+
) -> RichStreamRenderer:
|
|
529
565
|
"""Create appropriate renderer based on configuration."""
|
|
530
566
|
if isinstance(renderer, RichStreamRenderer):
|
|
531
567
|
return renderer
|
|
@@ -544,7 +580,7 @@ class AgentClient(BaseClient):
|
|
|
544
580
|
else:
|
|
545
581
|
return self._create_default_renderer(verbose)
|
|
546
582
|
|
|
547
|
-
def _create_silent_renderer(self):
|
|
583
|
+
def _create_silent_renderer(self) -> RichStreamRenderer:
|
|
548
584
|
"""Create a silent renderer that suppresses all output."""
|
|
549
585
|
silent_config = RendererConfig(
|
|
550
586
|
live=False,
|
|
@@ -558,7 +594,7 @@ class AgentClient(BaseClient):
|
|
|
558
594
|
verbose=False,
|
|
559
595
|
)
|
|
560
596
|
|
|
561
|
-
def _create_minimal_renderer(self):
|
|
597
|
+
def _create_minimal_renderer(self) -> RichStreamRenderer:
|
|
562
598
|
"""Create a minimal renderer with basic output."""
|
|
563
599
|
minimal_config = RendererConfig(
|
|
564
600
|
live=False,
|
|
@@ -572,7 +608,7 @@ class AgentClient(BaseClient):
|
|
|
572
608
|
verbose=False,
|
|
573
609
|
)
|
|
574
610
|
|
|
575
|
-
def _create_verbose_renderer(self):
|
|
611
|
+
def _create_verbose_renderer(self) -> RichStreamRenderer:
|
|
576
612
|
"""Create a verbose renderer for detailed output."""
|
|
577
613
|
verbose_config = RendererConfig(
|
|
578
614
|
theme="dark",
|
|
@@ -587,7 +623,7 @@ class AgentClient(BaseClient):
|
|
|
587
623
|
verbose=True,
|
|
588
624
|
)
|
|
589
625
|
|
|
590
|
-
def _create_default_renderer(self, verbose: bool):
|
|
626
|
+
def _create_default_renderer(self, verbose: bool) -> RichStreamRenderer:
|
|
591
627
|
"""Create the default renderer."""
|
|
592
628
|
if verbose:
|
|
593
629
|
return self._create_verbose_renderer()
|
|
@@ -595,22 +631,22 @@ class AgentClient(BaseClient):
|
|
|
595
631
|
default_config = RendererConfig(show_delegate_tool_panels=True)
|
|
596
632
|
return RichStreamRenderer(console=_Console(), cfg=default_config)
|
|
597
633
|
|
|
598
|
-
def
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
"""Process streaming events and accumulate response."""
|
|
602
|
-
final_text = ""
|
|
603
|
-
stats_usage = {}
|
|
604
|
-
started_monotonic = None
|
|
605
|
-
finished_monotonic = None
|
|
606
|
-
meta = {
|
|
634
|
+
def _initialize_stream_metadata(self, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
635
|
+
"""Initialize stream metadata."""
|
|
636
|
+
return {
|
|
607
637
|
"agent_name": kwargs.get("agent_name", ""),
|
|
608
638
|
"model": kwargs.get("model"),
|
|
609
639
|
"run_id": None,
|
|
610
640
|
"input_message": "", # Will be set from kwargs if available
|
|
611
641
|
}
|
|
612
642
|
|
|
613
|
-
|
|
643
|
+
def _capture_request_id(
|
|
644
|
+
self,
|
|
645
|
+
stream_response: httpx.Response,
|
|
646
|
+
meta: dict[str, Any],
|
|
647
|
+
renderer: RichStreamRenderer,
|
|
648
|
+
) -> None:
|
|
649
|
+
"""Capture request ID from response headers."""
|
|
614
650
|
req_id = stream_response.headers.get(
|
|
615
651
|
"x-request-id"
|
|
616
652
|
) or stream_response.headers.get("x-run-id")
|
|
@@ -618,50 +654,102 @@ class AgentClient(BaseClient):
|
|
|
618
654
|
meta["run_id"] = req_id
|
|
619
655
|
renderer.on_start(meta)
|
|
620
656
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
657
|
+
def _should_start_timer(self, ev: dict[str, Any]) -> bool:
|
|
658
|
+
"""Check if timer should be started for this event."""
|
|
659
|
+
return "content" in ev or "status" in ev or ev.get("metadata")
|
|
660
|
+
|
|
661
|
+
def _handle_content_event(self, ev: dict[str, Any], final_text: str) -> str:
|
|
662
|
+
"""Handle content events."""
|
|
663
|
+
content = ev.get("content", "")
|
|
664
|
+
if not content.startswith("Artifact received:"):
|
|
665
|
+
return content
|
|
666
|
+
return final_text
|
|
667
|
+
|
|
668
|
+
def _handle_final_response_event(self, ev: dict[str, Any]) -> str:
|
|
669
|
+
"""Handle final response events."""
|
|
670
|
+
return ev.get("content", "")
|
|
671
|
+
|
|
672
|
+
def _handle_usage_event(
|
|
673
|
+
self, ev: dict[str, Any], stats_usage: dict[str, Any]
|
|
674
|
+
) -> None:
|
|
675
|
+
"""Handle usage events."""
|
|
676
|
+
stats_usage.update(ev.get("usage") or {})
|
|
677
|
+
|
|
678
|
+
def _handle_run_info_event(
|
|
679
|
+
self, ev: dict[str, Any], meta: dict[str, Any], renderer: RichStreamRenderer
|
|
680
|
+
) -> None:
|
|
681
|
+
"""Handle run info events."""
|
|
682
|
+
if ev.get("model"):
|
|
683
|
+
meta["model"] = ev["model"]
|
|
684
|
+
renderer.on_start(meta)
|
|
685
|
+
if ev.get("run_id"):
|
|
686
|
+
meta["run_id"] = ev["run_id"]
|
|
687
|
+
renderer.on_start(meta)
|
|
688
|
+
|
|
689
|
+
def _process_single_event(
|
|
690
|
+
self,
|
|
691
|
+
event: dict[str, Any],
|
|
692
|
+
renderer: RichStreamRenderer,
|
|
693
|
+
final_text: str,
|
|
694
|
+
stats_usage: dict[str, Any],
|
|
695
|
+
meta: dict[str, Any],
|
|
696
|
+
) -> tuple[str, dict[str, Any]]:
|
|
697
|
+
"""Process a single streaming event."""
|
|
698
|
+
try:
|
|
699
|
+
ev = json.loads(event["data"])
|
|
700
|
+
except json.JSONDecodeError:
|
|
701
|
+
logger.debug("Non-JSON SSE fragment skipped")
|
|
702
|
+
return final_text, stats_usage
|
|
703
|
+
|
|
704
|
+
kind = (ev.get("metadata") or {}).get("kind")
|
|
705
|
+
renderer.on_event(ev)
|
|
706
|
+
|
|
707
|
+
# Skip artifacts from content accumulation
|
|
708
|
+
if kind == "artifact":
|
|
709
|
+
return final_text, stats_usage
|
|
710
|
+
|
|
711
|
+
# Accumulate assistant content
|
|
712
|
+
if ev.get("content"):
|
|
713
|
+
final_text = self._handle_content_event(ev, final_text)
|
|
714
|
+
elif kind == "final_response" and ev.get("content"):
|
|
715
|
+
final_text = self._handle_final_response_event(ev)
|
|
716
|
+
elif kind == "usage":
|
|
717
|
+
self._handle_usage_event(ev, stats_usage)
|
|
718
|
+
elif kind == "run_info":
|
|
719
|
+
self._handle_run_info_event(ev, meta, renderer)
|
|
720
|
+
|
|
721
|
+
return final_text, stats_usage
|
|
722
|
+
|
|
723
|
+
def _process_stream_events(
|
|
724
|
+
self,
|
|
725
|
+
stream_response: httpx.Response,
|
|
726
|
+
renderer: RichStreamRenderer,
|
|
727
|
+
timeout_seconds: float,
|
|
728
|
+
agent_name: str | None,
|
|
729
|
+
kwargs: dict[str, Any],
|
|
730
|
+
) -> tuple[str, dict[str, Any], float | None, float | None]:
|
|
731
|
+
"""Process streaming events and accumulate response."""
|
|
732
|
+
final_text = ""
|
|
733
|
+
stats_usage = {}
|
|
734
|
+
started_monotonic = None
|
|
735
|
+
finished_monotonic = None
|
|
627
736
|
|
|
737
|
+
meta = self._initialize_stream_metadata(kwargs)
|
|
738
|
+
self._capture_request_id(stream_response, meta, renderer)
|
|
739
|
+
|
|
740
|
+
for event in iter_sse_events(stream_response, timeout_seconds, agent_name):
|
|
628
741
|
# Start timer at first meaningful event
|
|
629
|
-
if started_monotonic is None
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
# Accumulate assistant content
|
|
642
|
-
if ev.get("content"):
|
|
643
|
-
if not ev["content"].startswith("Artifact received:"):
|
|
644
|
-
final_text = ev["content"]
|
|
645
|
-
continue
|
|
646
|
-
|
|
647
|
-
# Handle final response
|
|
648
|
-
if kind == "final_response" and ev.get("content"):
|
|
649
|
-
final_text = ev["content"]
|
|
650
|
-
continue
|
|
651
|
-
|
|
652
|
-
# Handle usage stats
|
|
653
|
-
if kind == "usage":
|
|
654
|
-
stats_usage.update(ev.get("usage") or {})
|
|
655
|
-
continue
|
|
656
|
-
|
|
657
|
-
# Handle run info updates
|
|
658
|
-
if kind == "run_info":
|
|
659
|
-
if ev.get("model"):
|
|
660
|
-
meta["model"] = ev["model"]
|
|
661
|
-
renderer.on_start(meta)
|
|
662
|
-
if ev.get("run_id"):
|
|
663
|
-
meta["run_id"] = ev["run_id"]
|
|
664
|
-
renderer.on_start(meta)
|
|
742
|
+
if started_monotonic is None:
|
|
743
|
+
try:
|
|
744
|
+
ev = json.loads(event["data"])
|
|
745
|
+
if self._should_start_timer(ev):
|
|
746
|
+
started_monotonic = monotonic()
|
|
747
|
+
except json.JSONDecodeError:
|
|
748
|
+
pass
|
|
749
|
+
|
|
750
|
+
final_text, stats_usage = self._process_single_event(
|
|
751
|
+
event, renderer, final_text, stats_usage, meta
|
|
752
|
+
)
|
|
665
753
|
|
|
666
754
|
finished_monotonic = monotonic()
|
|
667
755
|
return final_text, stats_usage, started_monotonic, finished_monotonic
|
|
@@ -678,9 +766,13 @@ class AgentClient(BaseClient):
|
|
|
678
766
|
) -> str:
|
|
679
767
|
"""Run an agent with a message, streaming via a renderer."""
|
|
680
768
|
# Prepare request payload and headers
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
769
|
+
(
|
|
770
|
+
payload,
|
|
771
|
+
data_payload,
|
|
772
|
+
files_payload,
|
|
773
|
+
headers,
|
|
774
|
+
multipart_data,
|
|
775
|
+
) = self._prepare_sync_request_data(message, files, tty, **kwargs)
|
|
684
776
|
|
|
685
777
|
# Create renderer
|
|
686
778
|
r = self._create_renderer(renderer, **kwargs)
|
|
@@ -712,10 +804,13 @@ class AgentClient(BaseClient):
|
|
|
712
804
|
timeout_seconds = kwargs.get("timeout", DEFAULT_AGENT_RUN_TIMEOUT)
|
|
713
805
|
agent_name = kwargs.get("agent_name")
|
|
714
806
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
807
|
+
(
|
|
808
|
+
final_text,
|
|
809
|
+
stats_usage,
|
|
810
|
+
started_monotonic,
|
|
811
|
+
finished_monotonic,
|
|
812
|
+
) = self._process_stream_events(
|
|
813
|
+
stream_response, r, timeout_seconds, agent_name, kwargs
|
|
719
814
|
)
|
|
720
815
|
|
|
721
816
|
except KeyboardInterrupt:
|
glaip_sdk/client/base.py
CHANGED
|
@@ -7,13 +7,14 @@ Authors:
|
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
9
|
import os
|
|
10
|
-
from typing import Any, Union
|
|
10
|
+
from typing import Any, NoReturn, Union
|
|
11
11
|
|
|
12
12
|
import httpx
|
|
13
13
|
from dotenv import load_dotenv
|
|
14
14
|
|
|
15
15
|
import glaip_sdk
|
|
16
|
-
from glaip_sdk.
|
|
16
|
+
from glaip_sdk._version import __version__ as SDK_VERSION
|
|
17
|
+
from glaip_sdk.config.constants import SDK_NAME
|
|
17
18
|
from glaip_sdk.exceptions import (
|
|
18
19
|
AuthenticationError,
|
|
19
20
|
ConflictError,
|
|
@@ -147,7 +148,7 @@ class BaseClient:
|
|
|
147
148
|
return self._timeout
|
|
148
149
|
|
|
149
150
|
@timeout.setter
|
|
150
|
-
def timeout(self, value: float):
|
|
151
|
+
def timeout(self, value: float) -> None:
|
|
151
152
|
"""Set timeout and rebuild client."""
|
|
152
153
|
self._timeout = value
|
|
153
154
|
if (
|
|
@@ -165,10 +166,10 @@ class BaseClient:
|
|
|
165
166
|
post_endpoint: str,
|
|
166
167
|
get_endpoint_fmt: str,
|
|
167
168
|
*,
|
|
168
|
-
json=None,
|
|
169
|
-
data=None,
|
|
170
|
-
files=None,
|
|
171
|
-
**kwargs,
|
|
169
|
+
json: Any | None = None,
|
|
170
|
+
data: Any | None = None,
|
|
171
|
+
files: Any | None = None,
|
|
172
|
+
**kwargs: Any,
|
|
172
173
|
) -> Any:
|
|
173
174
|
"""Helper for POST-then-GET pattern used in create methods.
|
|
174
175
|
|
|
@@ -210,7 +211,7 @@ class BaseClient:
|
|
|
210
211
|
get_endpoint = get_endpoint_fmt.format(id=resource_id)
|
|
211
212
|
return self._request("GET", get_endpoint)
|
|
212
213
|
|
|
213
|
-
def _ensure_client_alive(self):
|
|
214
|
+
def _ensure_client_alive(self) -> None:
|
|
214
215
|
"""Ensure HTTP client is alive, recreate if needed."""
|
|
215
216
|
if not hasattr(self, "http_client") or self.http_client is None:
|
|
216
217
|
if not self._parent_client:
|
|
@@ -289,8 +290,13 @@ class BaseClient:
|
|
|
289
290
|
self._raise_api_error(response.status_code, message, payload=parsed)
|
|
290
291
|
|
|
291
292
|
def _raise_api_error(
|
|
292
|
-
self,
|
|
293
|
-
|
|
293
|
+
self,
|
|
294
|
+
status: int,
|
|
295
|
+
message: str,
|
|
296
|
+
error_type: str | None = None,
|
|
297
|
+
*,
|
|
298
|
+
payload: Any | None = None,
|
|
299
|
+
) -> NoReturn:
|
|
294
300
|
"""Raise appropriate exception with rich context."""
|
|
295
301
|
request_id = None
|
|
296
302
|
try:
|
|
@@ -324,7 +330,7 @@ class BaseClient:
|
|
|
324
330
|
request_id=request_id,
|
|
325
331
|
)
|
|
326
332
|
|
|
327
|
-
def close(self):
|
|
333
|
+
def close(self) -> None:
|
|
328
334
|
"""Close the HTTP client."""
|
|
329
335
|
if (
|
|
330
336
|
hasattr(self, "http_client")
|
|
@@ -334,11 +340,16 @@ class BaseClient:
|
|
|
334
340
|
):
|
|
335
341
|
self.http_client.close()
|
|
336
342
|
|
|
337
|
-
def __enter__(self):
|
|
343
|
+
def __enter__(self) -> "BaseClient":
|
|
338
344
|
"""Context manager entry."""
|
|
339
345
|
return self
|
|
340
346
|
|
|
341
|
-
def __exit__(
|
|
347
|
+
def __exit__(
|
|
348
|
+
self,
|
|
349
|
+
_exc_type: type[BaseException] | None,
|
|
350
|
+
_exc_val: BaseException | None,
|
|
351
|
+
_exc_tb: Any,
|
|
352
|
+
) -> None:
|
|
342
353
|
"""Context manager exit."""
|
|
343
354
|
# Only close if this is not session-scoped
|
|
344
355
|
if not self._session_scoped:
|