glaip-sdk 0.1.0__py3-none-any.whl → 0.1.1__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.
Files changed (63) hide show
  1. glaip_sdk/_version.py +1 -3
  2. glaip_sdk/branding.py +2 -6
  3. glaip_sdk/cli/agent_config.py +2 -6
  4. glaip_sdk/cli/auth.py +11 -30
  5. glaip_sdk/cli/commands/agents.py +45 -107
  6. glaip_sdk/cli/commands/configure.py +12 -36
  7. glaip_sdk/cli/commands/mcps.py +26 -63
  8. glaip_sdk/cli/commands/models.py +2 -4
  9. glaip_sdk/cli/commands/tools.py +22 -35
  10. glaip_sdk/cli/commands/update.py +3 -8
  11. glaip_sdk/cli/config.py +1 -3
  12. glaip_sdk/cli/display.py +4 -12
  13. glaip_sdk/cli/io.py +8 -14
  14. glaip_sdk/cli/main.py +10 -30
  15. glaip_sdk/cli/mcp_validators.py +5 -15
  16. glaip_sdk/cli/pager.py +3 -9
  17. glaip_sdk/cli/parsers/json_input.py +11 -22
  18. glaip_sdk/cli/resolution.py +3 -9
  19. glaip_sdk/cli/rich_helpers.py +1 -3
  20. glaip_sdk/cli/slash/agent_session.py +5 -10
  21. glaip_sdk/cli/slash/prompt.py +3 -10
  22. glaip_sdk/cli/slash/session.py +46 -95
  23. glaip_sdk/cli/transcript/cache.py +6 -19
  24. glaip_sdk/cli/transcript/capture.py +6 -20
  25. glaip_sdk/cli/transcript/launcher.py +1 -3
  26. glaip_sdk/cli/transcript/viewer.py +11 -40
  27. glaip_sdk/cli/update_notifier.py +165 -21
  28. glaip_sdk/cli/utils.py +33 -84
  29. glaip_sdk/cli/validators.py +11 -12
  30. glaip_sdk/client/_agent_payloads.py +10 -30
  31. glaip_sdk/client/agents.py +33 -63
  32. glaip_sdk/client/base.py +6 -22
  33. glaip_sdk/client/mcps.py +1 -3
  34. glaip_sdk/client/run_rendering.py +6 -14
  35. glaip_sdk/client/tools.py +8 -24
  36. glaip_sdk/client/validators.py +20 -48
  37. glaip_sdk/exceptions.py +1 -3
  38. glaip_sdk/models.py +14 -33
  39. glaip_sdk/payload_schemas/agent.py +1 -3
  40. glaip_sdk/utils/agent_config.py +4 -14
  41. glaip_sdk/utils/client_utils.py +7 -21
  42. glaip_sdk/utils/display.py +2 -6
  43. glaip_sdk/utils/general.py +1 -3
  44. glaip_sdk/utils/import_export.py +3 -9
  45. glaip_sdk/utils/rendering/formatting.py +2 -5
  46. glaip_sdk/utils/rendering/models.py +2 -6
  47. glaip_sdk/utils/rendering/renderer/__init__.py +1 -3
  48. glaip_sdk/utils/rendering/renderer/base.py +63 -189
  49. glaip_sdk/utils/rendering/renderer/debug.py +4 -14
  50. glaip_sdk/utils/rendering/renderer/panels.py +1 -3
  51. glaip_sdk/utils/rendering/renderer/progress.py +3 -11
  52. glaip_sdk/utils/rendering/renderer/stream.py +7 -19
  53. glaip_sdk/utils/rendering/renderer/toggle.py +1 -3
  54. glaip_sdk/utils/rendering/step_tree_state.py +1 -3
  55. glaip_sdk/utils/rendering/steps.py +29 -83
  56. glaip_sdk/utils/resource_refs.py +4 -13
  57. glaip_sdk/utils/serialization.py +14 -46
  58. glaip_sdk/utils/validation.py +4 -4
  59. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.1.1.dist-info}/METADATA +1 -1
  60. glaip_sdk-0.1.1.dist-info/RECORD +82 -0
  61. glaip_sdk-0.1.0.dist-info/RECORD +0 -82
  62. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.1.1.dist-info}/WHEEL +0 -0
  63. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.1.1.dist-info}/entry_points.txt +0 -0
@@ -34,7 +34,7 @@ from glaip_sdk.config.constants import (
34
34
  DEFAULT_AGENT_VERSION,
35
35
  DEFAULT_MODEL,
36
36
  )
37
- from glaip_sdk.exceptions import NotFoundError
37
+ from glaip_sdk.exceptions import NotFoundError, ValidationError
38
38
  from glaip_sdk.models import Agent
39
39
  from glaip_sdk.payload_schemas.agent import list_server_only_fields
40
40
  from glaip_sdk.utils.agent_config import normalize_agent_config_for_import
@@ -97,9 +97,7 @@ def _merge_override_maps(
97
97
  for key, value in source.items():
98
98
  if value is None:
99
99
  continue
100
- merged[key] = (
101
- _normalise_sequence(value) if key in _MERGED_SEQUENCE_FIELDS else value
102
- )
100
+ merged[key] = _normalise_sequence(value) if key in _MERGED_SEQUENCE_FIELDS else value
103
101
  return merged
104
102
 
105
103
 
@@ -134,9 +132,7 @@ def _prepare_agent_metadata(value: Any) -> dict[str, Any]:
134
132
  return prepared
135
133
 
136
134
 
137
- def _load_agent_file_payload(
138
- file_path: Path, *, model_override: str | None
139
- ) -> dict[str, Any]:
135
+ def _load_agent_file_payload(file_path: Path, *, model_override: str | None) -> dict[str, Any]:
140
136
  """Load agent configuration from disk and normalise legacy fields."""
141
137
  if not file_path.exists():
142
138
  raise FileNotFoundError(f"Agent configuration file not found: {file_path}")
@@ -168,9 +164,7 @@ def _prepare_import_payload(
168
164
  raw_definition = load_resource_from_file(file_path)
169
165
  original_refs = _extract_original_refs(raw_definition)
170
166
 
171
- base_payload = _load_agent_file_payload(
172
- file_path, model_override=overrides_dict.get("model")
173
- )
167
+ base_payload = _load_agent_file_payload(file_path, model_override=overrides_dict.get("model"))
174
168
 
175
169
  cli_args = _build_cli_args(overrides_dict)
176
170
 
@@ -223,11 +217,7 @@ def _build_cli_args(overrides_dict: dict) -> dict[str, Any]:
223
217
 
224
218
  def _build_additional_args(overrides_dict: dict, cli_args: dict) -> dict[str, Any]:
225
219
  """Build additional args not already in CLI args."""
226
- return {
227
- key: value
228
- for key, value in overrides_dict.items()
229
- if value is not None and key not in cli_args
230
- }
220
+ return {key: value for key, value in overrides_dict.items() if value is not None and key not in cli_args}
231
221
 
232
222
 
233
223
  def _remove_model_fields_if_needed(merged: dict, overrides_dict: dict) -> None:
@@ -278,9 +268,7 @@ class AgentClient(BaseClient):
278
268
  """
279
269
  if query is not None and kwargs:
280
270
  # Both query object and individual parameters provided
281
- raise ValueError(
282
- "Provide either `query` or individual filter arguments, not both."
283
- )
271
+ raise ValueError("Provide either `query` or individual filter arguments, not both.")
284
272
 
285
273
  if query is None:
286
274
  # Create query from individual parameters for backward compatibility
@@ -339,7 +327,19 @@ class AgentClient(BaseClient):
339
327
 
340
328
  def get_agent_by_id(self, agent_id: str) -> Agent:
341
329
  """Get agent by ID."""
342
- data = self._request("GET", f"/agents/{agent_id}")
330
+ try:
331
+ data = self._request("GET", f"/agents/{agent_id}")
332
+ except ValidationError as exc:
333
+ if exc.status_code == 422:
334
+ message = f"Agent '{agent_id}' not found"
335
+ raise NotFoundError(
336
+ message,
337
+ status_code=404,
338
+ error_type=exc.error_type,
339
+ payload=exc.payload,
340
+ request_id=exc.request_id,
341
+ ) from exc
342
+ raise
343
343
 
344
344
  if isinstance(data, str):
345
345
  # Some backends may respond with plain text for missing agents.
@@ -372,9 +372,7 @@ class AgentClient(BaseClient):
372
372
  self._renderer_manager = manager
373
373
  return manager
374
374
 
375
- def _create_renderer(
376
- self, renderer: RichStreamRenderer | str | None, **kwargs: Any
377
- ) -> RichStreamRenderer:
375
+ def _create_renderer(self, renderer: RichStreamRenderer | str | None, **kwargs: Any) -> RichStreamRenderer:
378
376
  manager = self._get_renderer_manager()
379
377
  verbose = kwargs.get("verbose", False)
380
378
  if isinstance(renderer, RichStreamRenderer) or hasattr(renderer, "on_start"):
@@ -506,9 +504,7 @@ class AgentClient(BaseClient):
506
504
  return entry_id
507
505
 
508
506
  if entry_name:
509
- resolved, success = self._resolve_resource_by_name(
510
- find_by_name, entry_name, singular, plural
511
- )
507
+ resolved, success = self._resolve_resource_by_name(find_by_name, entry_name, singular, plural)
512
508
  return resolved if success else entry_name
513
509
 
514
510
  raise ValueError(f"{singular} references must include a valid ID or name.")
@@ -523,9 +519,7 @@ class AgentClient(BaseClient):
523
519
  return str(entry)
524
520
 
525
521
  @staticmethod
526
- def _validate_resource_id(
527
- fetch_by_id: Callable[[str], Any], candidate_id: str | None
528
- ) -> str | None:
522
+ def _validate_resource_id(fetch_by_id: Callable[[str], Any], candidate_id: str | None) -> str | None:
529
523
  if not candidate_id:
530
524
  return None
531
525
  try:
@@ -547,21 +541,13 @@ class AgentClient(BaseClient):
547
541
  return entry_name, False
548
542
 
549
543
  if not matches:
550
- raise ValueError(
551
- f"{singular} '{entry_name}' not found in current workspace."
552
- )
544
+ raise ValueError(f"{singular} '{entry_name}' not found in current workspace.")
553
545
  if len(matches) > 1:
554
- exact = [
555
- m
556
- for m in matches
557
- if getattr(m, "name", "").lower() == entry_name.lower()
558
- ]
546
+ exact = [m for m in matches if getattr(m, "name", "").lower() == entry_name.lower()]
559
547
  if len(exact) == 1:
560
548
  matches = exact
561
549
  else:
562
- raise ValueError(
563
- f"Multiple {plural} named '{entry_name}'. Please disambiguate."
564
- )
550
+ raise ValueError(f"Multiple {plural} named '{entry_name}'. Please disambiguate.")
565
551
  return str(matches[0].id), True
566
552
 
567
553
  def _resolve_tool_ids(
@@ -610,9 +596,7 @@ class AgentClient(BaseClient):
610
596
 
611
597
  def _create_agent_from_payload(self, payload: Mapping[str, Any]) -> "Agent":
612
598
  """Create an agent using a fully prepared payload mapping."""
613
- known, extras = _split_known_and_extra(
614
- payload, AgentCreateRequest.__dataclass_fields__
615
- )
599
+ known, extras = _split_known_and_extra(payload, AgentCreateRequest.__dataclass_fields__)
616
600
 
617
601
  name = known.pop("name", None)
618
602
  instruction = known.pop("instruction", None)
@@ -721,9 +705,7 @@ class AgentClient(BaseClient):
721
705
  overrides = _merge_override_maps(base_overrides, kwargs)
722
706
 
723
707
  if file is not None:
724
- payload = _prepare_import_payload(
725
- Path(file).expanduser(), overrides, drop_model_fields=True
726
- )
708
+ payload = _prepare_import_payload(Path(file).expanduser(), overrides, drop_model_fields=True)
727
709
  if overrides.get("model") is None:
728
710
  payload.pop("model", None)
729
711
  else:
@@ -746,9 +728,7 @@ class AgentClient(BaseClient):
746
728
  payload: Mapping[str, Any],
747
729
  ) -> "Agent":
748
730
  """Update an agent using a prepared payload mapping."""
749
- known, extras = _split_known_and_extra(
750
- payload, AgentUpdateRequest.__dataclass_fields__
751
- )
731
+ known, extras = _split_known_and_extra(payload, AgentUpdateRequest.__dataclass_fields__)
752
732
  _normalise_sequence_fields(known)
753
733
 
754
734
  tool_refs = extras.pop("_tool_refs", None)
@@ -818,9 +798,7 @@ class AgentClient(BaseClient):
818
798
  overrides = _merge_override_maps(base_overrides, kwargs)
819
799
 
820
800
  if file is not None:
821
- payload = _prepare_import_payload(
822
- Path(file).expanduser(), overrides, drop_model_fields=True
823
- )
801
+ payload = _prepare_import_payload(Path(file).expanduser(), overrides, drop_model_fields=True)
824
802
  else:
825
803
  payload = overrides
826
804
 
@@ -882,9 +860,7 @@ class AgentClient(BaseClient):
882
860
  payload["tty"] = True
883
861
  return payload, None, None, headers, None
884
862
 
885
- def _get_timeout_values(
886
- self, timeout: float | None, **kwargs: Any
887
- ) -> tuple[float, float]:
863
+ def _get_timeout_values(self, timeout: float | None, **kwargs: Any) -> tuple[float, float]:
888
864
  """Get request timeout and execution timeout values.
889
865
 
890
866
  Args:
@@ -1009,9 +985,7 @@ class AgentClient(BaseClient):
1009
985
  headers = {"Accept": SSE_CONTENT_TYPE}
1010
986
  return payload, None, None, headers
1011
987
 
1012
- def _create_async_client_config(
1013
- self, timeout: float | None, headers: dict | None
1014
- ) -> dict:
988
+ def _create_async_client_config(self, timeout: float | None, headers: dict | None) -> dict:
1015
989
  """Create async client configuration with proper headers and timeout."""
1016
990
  config = self._build_async_client(timeout or self.timeout)
1017
991
  if headers:
@@ -1040,9 +1014,7 @@ class AgentClient(BaseClient):
1040
1014
  ) as stream_response:
1041
1015
  stream_response.raise_for_status()
1042
1016
 
1043
- async for event in aiter_sse_events(
1044
- stream_response, timeout_seconds, agent_name
1045
- ):
1017
+ async for event in aiter_sse_events(stream_response, timeout_seconds, agent_name):
1046
1018
  try:
1047
1019
  chunk = json.loads(event["data"])
1048
1020
  yield chunk
@@ -1077,9 +1049,7 @@ class AgentClient(BaseClient):
1077
1049
  Exception: For other unexpected errors
1078
1050
  """
1079
1051
  # Prepare request data
1080
- payload, data_payload, files_payload, headers = self._prepare_request_data(
1081
- message, files, **kwargs
1082
- )
1052
+ payload, data_payload, files_payload, headers = self._prepare_request_data(message, files, **kwargs)
1083
1053
 
1084
1054
  # Create async client configuration
1085
1055
  async_client_config = self._create_async_client_config(timeout, headers)
glaip_sdk/client/base.py CHANGED
@@ -151,12 +151,7 @@ class BaseClient:
151
151
  def timeout(self, value: float) -> None:
152
152
  """Set timeout and rebuild client."""
153
153
  self._timeout = value
154
- if (
155
- hasattr(self, "http_client")
156
- and self.http_client
157
- and not self._session_scoped
158
- and not self._parent_client
159
- ):
154
+ if hasattr(self, "http_client") and self.http_client and not self._session_scoped and not self._parent_client:
160
155
  self.http_client.close()
161
156
  self.http_client = self._build_client(value)
162
157
 
@@ -246,18 +241,14 @@ class BaseClient:
246
241
  client_log.debug(f"Response status: {response.status_code}")
247
242
  return response
248
243
  except httpx.ConnectError as e:
249
- client_log.warning(
250
- f"Connection error on {method} {endpoint}, retrying once: {e}"
251
- )
244
+ client_log.warning(f"Connection error on {method} {endpoint}, retrying once: {e}")
252
245
  try:
253
246
  response = self.http_client.request(method, endpoint, **kwargs)
254
- client_log.debug(
255
- f"Retry successful, response status: {response.status_code}"
256
- )
247
+ client_log.debug(f"Retry successful, response status: {response.status_code}")
257
248
  return response
258
249
  except httpx.ConnectError:
259
250
  client_log.error(f"Retry failed for {method} {endpoint}: {e}")
260
- raise e
251
+ raise
261
252
 
262
253
  def _request(self, method: str, endpoint: str, **kwargs) -> Any:
263
254
  """Make HTTP request with error handling and unwrap success envelopes."""
@@ -381,9 +372,7 @@ class BaseClient:
381
372
  error_message = self._get_error_message(response)
382
373
  # Try to parse response content for payload
383
374
  parsed_content = self._parse_response_content(response)
384
- self._raise_api_error(
385
- response.status_code, error_message, payload=parsed_content
386
- )
375
+ self._raise_api_error(response.status_code, error_message, payload=parsed_content)
387
376
  return None # Won't be reached but helps with type checking
388
377
 
389
378
  parsed = self._parse_response_content(response)
@@ -435,12 +424,7 @@ class BaseClient:
435
424
 
436
425
  def close(self) -> None:
437
426
  """Close the HTTP client."""
438
- if (
439
- hasattr(self, "http_client")
440
- and self.http_client
441
- and not self._session_scoped
442
- and not self._parent_client
443
- ):
427
+ if hasattr(self, "http_client") and self.http_client and not self._session_scoped and not self._parent_client:
444
428
  self.http_client.close()
445
429
 
446
430
  def __enter__(self) -> "BaseClient":
glaip_sdk/client/mcps.py CHANGED
@@ -179,9 +179,7 @@ class MCPClient(BaseClient):
179
179
  update_data = {
180
180
  "name": name if name is not None else current_mcp.name,
181
181
  "type": DEFAULT_MCP_TYPE, # Required by backend, MCPs are always server type
182
- "transport": kwargs.get(
183
- "transport", getattr(current_mcp, "transport", DEFAULT_MCP_TRANSPORT)
184
- ),
182
+ "transport": kwargs.get("transport", getattr(current_mcp, "transport", DEFAULT_MCP_TRANSPORT)),
185
183
  }
186
184
 
187
185
  # Handle description with proper None handling
@@ -43,9 +43,7 @@ def _update_state_transcript(state: Any, text_value: str) -> bool:
43
43
 
44
44
  updated = False
45
45
 
46
- if hasattr(state, "final_text") and not _has_visible_text(
47
- getattr(state, "final_text", "")
48
- ):
46
+ if hasattr(state, "final_text") and not _has_visible_text(getattr(state, "final_text", "")):
49
47
  try:
50
48
  state.final_text = text_value
51
49
  updated = True
@@ -66,11 +64,9 @@ def _update_renderer_transcript(renderer: Any, text_value: str) -> None:
66
64
  if _update_state_transcript(state, text_value):
67
65
  return
68
66
 
69
- if hasattr(renderer, "final_text") and not _has_visible_text(
70
- getattr(renderer, "final_text", "")
71
- ):
67
+ if hasattr(renderer, "final_text") and not _has_visible_text(getattr(renderer, "final_text", "")):
72
68
  try:
73
- setattr(renderer, "final_text", text_value)
69
+ renderer.final_text = text_value
74
70
  except Exception:
75
71
  pass
76
72
 
@@ -220,9 +216,7 @@ class AgentRunRenderingManager:
220
216
  meta: dict[str, Any],
221
217
  renderer: RichStreamRenderer,
222
218
  ) -> None:
223
- req_id = stream_response.headers.get(
224
- "x-request-id"
225
- ) or stream_response.headers.get("x-run-id")
219
+ req_id = stream_response.headers.get("x-request-id") or stream_response.headers.get("x-run-id")
226
220
  if req_id:
227
221
  meta["run_id"] = req_id
228
222
  renderer.on_start(meta)
@@ -318,9 +312,7 @@ class AgentRunRenderingManager:
318
312
  meta["run_id"] = ev["run_id"]
319
313
  renderer.on_start(meta)
320
314
 
321
- def _ensure_renderer_final_content(
322
- self, renderer: RichStreamRenderer, text: str
323
- ) -> None:
315
+ def _ensure_renderer_final_content(self, renderer: RichStreamRenderer, text: str) -> None:
324
316
  """Populate renderer state with final output when the stream omits it."""
325
317
  if not text:
326
318
  return
@@ -351,7 +343,7 @@ class AgentRunRenderingManager:
351
343
  if hasattr(renderer, "state") and hasattr(renderer.state, "buffer"):
352
344
  buffer_values = renderer.state.buffer
353
345
  elif hasattr(renderer, "buffer"):
354
- buffer_values = getattr(renderer, "buffer")
346
+ buffer_values = renderer.buffer
355
347
 
356
348
  if buffer_values is not None:
357
349
  try:
glaip_sdk/client/tools.py CHANGED
@@ -96,9 +96,7 @@ class ToolClient(BaseClient):
96
96
  """
97
97
  return os.path.splitext(os.path.basename(file_path))[0]
98
98
 
99
- def _prepare_upload_data(
100
- self, name: str, framework: str, description: str | None = None, **kwargs
101
- ) -> dict:
99
+ def _prepare_upload_data(self, name: str, framework: str, description: str | None = None, **kwargs) -> dict:
102
100
  """Prepare upload data dictionary.
103
101
 
104
102
  Args:
@@ -217,29 +215,21 @@ class ToolClient(BaseClient):
217
215
  elif hasattr(current_tool, "description") and current_tool.description:
218
216
  update_data["description"] = current_tool.description
219
217
 
220
- def _handle_tags_update(
221
- self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool
222
- ) -> None:
218
+ def _handle_tags_update(self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool) -> None:
223
219
  """Handle tags field in update payload."""
224
220
  if kwargs.get("tags"):
225
221
  if isinstance(kwargs["tags"], list):
226
- update_data["tags"] = ",".join(
227
- str(tag).strip() for tag in kwargs["tags"]
228
- )
222
+ update_data["tags"] = ",".join(str(tag).strip() for tag in kwargs["tags"])
229
223
  else:
230
224
  update_data["tags"] = str(kwargs["tags"])
231
225
  elif hasattr(current_tool, "tags") and current_tool.tags:
232
226
  # Preserve existing tags if present
233
227
  if isinstance(current_tool.tags, list):
234
- update_data["tags"] = ",".join(
235
- str(tag).strip() for tag in current_tool.tags
236
- )
228
+ update_data["tags"] = ",".join(str(tag).strip() for tag in current_tool.tags)
237
229
  else:
238
230
  update_data["tags"] = str(current_tool.tags)
239
231
 
240
- def _handle_additional_kwargs(
241
- self, update_data: dict[str, Any], kwargs: dict[str, Any]
242
- ) -> None:
232
+ def _handle_additional_kwargs(self, update_data: dict[str, Any], kwargs: dict[str, Any]) -> None:
243
233
  """Handle additional kwargs in update payload."""
244
234
  excluded_keys = {
245
235
  "tags",
@@ -290,12 +280,8 @@ class ToolClient(BaseClient):
290
280
  update_data = {
291
281
  "name": name if name is not None else current_tool.name,
292
282
  "type": current_type,
293
- "framework": kwargs.get(
294
- "framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)
295
- ),
296
- "version": kwargs.get(
297
- "version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)
298
- ),
283
+ "framework": kwargs.get("framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)),
284
+ "version": kwargs.get("version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)),
299
285
  }
300
286
 
301
287
  # Handle description update
@@ -355,9 +341,7 @@ class ToolClient(BaseClient):
355
341
 
356
342
  try:
357
343
  # Prepare upload data
358
- upload_data = self._prepare_upload_data(
359
- name=name, framework=framework, description=description, **kwargs
360
- )
344
+ upload_data = self._prepare_upload_data(name=name, framework=framework, description=description, **kwargs)
361
345
 
362
346
  # Upload file
363
347
  return self._upload_tool_file(temp_file_path, upload_data)
@@ -39,9 +39,7 @@ class ResourceValidator:
39
39
  if len(found_tools) == 1:
40
40
  return str(found_tools[0].id)
41
41
  elif len(found_tools) > 1:
42
- raise AmbiguousResourceError(
43
- f"Multiple tools found with name '{tool_name}': {[t.id for t in found_tools]}"
44
- )
42
+ raise AmbiguousResourceError(f"Multiple tools found with name '{tool_name}': {[t.id for t in found_tools]}")
45
43
  else:
46
44
  raise NotFoundError(f"Tool not found: {tool_name}")
47
45
 
@@ -51,9 +49,7 @@ class ResourceValidator:
51
49
  if len(found_tools) == 1:
52
50
  return str(found_tools[0].id)
53
51
  elif len(found_tools) > 1:
54
- raise AmbiguousResourceError(
55
- f"Multiple tools found with name '{tool.name}': {[t.id for t in found_tools]}"
56
- )
52
+ raise AmbiguousResourceError(f"Multiple tools found with name '{tool.name}': {[t.id for t in found_tools]}")
57
53
  else:
58
54
  raise NotFoundError(f"Tool not found: {tool.name}")
59
55
 
@@ -73,9 +69,7 @@ class ResourceValidator:
73
69
  elif hasattr(tool, "name") and tool.name is not None:
74
70
  return self._resolve_tool_by_name_attribute(tool, client)
75
71
  else:
76
- raise ValidationError(
77
- f"Invalid tool reference: {tool} - must have 'id' or 'name' attribute"
78
- )
72
+ raise ValidationError(f"Invalid tool reference: {tool} - must have 'id' or 'name' attribute")
79
73
 
80
74
  def _process_single_tool(self, tool: str | Tool, client: Any) -> str:
81
75
  """Process a single tool reference and return its ID."""
@@ -99,22 +93,14 @@ class ResourceValidator:
99
93
  try:
100
94
  tool_id = cls()._process_single_tool(tool, client)
101
95
  tool_ids.append(tool_id)
102
- except (AmbiguousResourceError, NotFoundError) as e:
96
+ except (AmbiguousResourceError, NotFoundError) as err:
103
97
  # Determine the tool name for the error message
104
- tool_name = (
105
- tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
106
- )
107
- raise ValidationError(
108
- f"Failed to resolve tool name '{tool_name}' to ID: {e}"
109
- )
110
- except Exception as e:
98
+ tool_name = tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
99
+ raise ValidationError(f"Failed to resolve tool name '{tool_name}' to ID: {err}") from err
100
+ except Exception as err:
111
101
  # For other exceptions, wrap them appropriately
112
- tool_name = (
113
- tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
114
- )
115
- raise ValidationError(
116
- f"Failed to resolve tool name '{tool_name}' to ID: {e}"
117
- )
102
+ tool_name = tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
103
+ raise ValidationError(f"Failed to resolve tool name '{tool_name}' to ID: {err}") from err
118
104
 
119
105
  return tool_ids
120
106
 
@@ -158,9 +144,7 @@ class ResourceValidator:
158
144
  elif hasattr(agent, "name") and agent.name is not None:
159
145
  return self._resolve_agent_by_name_attribute(agent, client)
160
146
  else:
161
- raise ValidationError(
162
- f"Invalid agent reference: {agent} - must have 'id' or 'name' attribute"
163
- )
147
+ raise ValidationError(f"Invalid agent reference: {agent} - must have 'id' or 'name' attribute")
164
148
 
165
149
  def _process_single_agent(self, agent: str | Any, client: Any) -> str:
166
150
  """Process a single agent reference and return its ID."""
@@ -184,26 +168,14 @@ class ResourceValidator:
184
168
  try:
185
169
  agent_id = cls()._process_single_agent(agent, client)
186
170
  agent_ids.append(agent_id)
187
- except (AmbiguousResourceError, NotFoundError) as e:
171
+ except (AmbiguousResourceError, NotFoundError) as err:
188
172
  # Determine the agent name for the error message
189
- agent_name = (
190
- agent
191
- if isinstance(agent, str)
192
- else getattr(agent, "name", str(agent))
193
- )
194
- raise ValidationError(
195
- f"Failed to resolve agent name '{agent_name}' to ID: {e}"
196
- )
197
- except Exception as e:
173
+ agent_name = agent if isinstance(agent, str) else getattr(agent, "name", str(agent))
174
+ raise ValidationError(f"Failed to resolve agent name '{agent_name}' to ID: {err}") from err
175
+ except Exception as err:
198
176
  # For other exceptions, wrap them appropriately
199
- agent_name = (
200
- agent
201
- if isinstance(agent, str)
202
- else getattr(agent, "name", str(agent))
203
- )
204
- raise ValidationError(
205
- f"Failed to resolve agent name '{agent_name}' to ID: {e}"
206
- )
177
+ agent_name = agent if isinstance(agent, str) else getattr(agent, "name", str(agent))
178
+ raise ValidationError(f"Failed to resolve agent name '{agent_name}' to ID: {err}") from err
207
179
 
208
180
  return agent_ids
209
181
 
@@ -213,8 +185,8 @@ class ResourceValidator:
213
185
  for tool_id in tool_ids:
214
186
  try:
215
187
  client.get_tool_by_id(tool_id)
216
- except NotFoundError:
217
- raise ValidationError(f"Tool not found: {tool_id}")
188
+ except NotFoundError as err:
189
+ raise ValidationError(f"Tool not found: {tool_id}") from err
218
190
 
219
191
  @classmethod
220
192
  def validate_agents_exist(cls, agent_ids: list[str], client: Any) -> None:
@@ -222,5 +194,5 @@ class ResourceValidator:
222
194
  for agent_id in agent_ids:
223
195
  try:
224
196
  client.get_agent_by_id(agent_id)
225
- except NotFoundError:
226
- raise ValidationError(f"Agent not found: {agent_id}")
197
+ except NotFoundError as err:
198
+ raise ValidationError(f"Agent not found: {agent_id}") from err
glaip_sdk/exceptions.py CHANGED
@@ -107,9 +107,7 @@ class AgentTimeoutError(TimeoutError):
107
107
  agent_name: Optional name of the agent that timed out
108
108
  """
109
109
  agent_info = f" for agent '{agent_name}'" if agent_name else ""
110
- message = (
111
- f"Agent execution timed out after {timeout_seconds} seconds{agent_info}"
112
- )
110
+ message = f"Agent execution timed out after {timeout_seconds} seconds{agent_info}"
113
111
  super().__init__(message)
114
112
  self.timeout_seconds = timeout_seconds
115
113
  self.agent_name = agent_name