glaip-sdk 0.0.20__py3-none-any.whl → 0.6.5b6__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 (157) hide show
  1. glaip_sdk/__init__.py +5 -2
  2. glaip_sdk/_version.py +10 -3
  3. glaip_sdk/agents/__init__.py +27 -0
  4. glaip_sdk/agents/base.py +1126 -0
  5. glaip_sdk/branding.py +15 -6
  6. glaip_sdk/cli/account_store.py +540 -0
  7. glaip_sdk/cli/agent_config.py +2 -6
  8. glaip_sdk/cli/auth.py +265 -45
  9. glaip_sdk/cli/commands/__init__.py +2 -2
  10. glaip_sdk/cli/commands/accounts.py +746 -0
  11. glaip_sdk/cli/commands/agents.py +270 -173
  12. glaip_sdk/cli/commands/common_config.py +101 -0
  13. glaip_sdk/cli/commands/configure.py +735 -143
  14. glaip_sdk/cli/commands/mcps.py +265 -134
  15. glaip_sdk/cli/commands/models.py +13 -9
  16. glaip_sdk/cli/commands/tools.py +67 -88
  17. glaip_sdk/cli/commands/transcripts.py +755 -0
  18. glaip_sdk/cli/commands/update.py +3 -8
  19. glaip_sdk/cli/config.py +49 -7
  20. glaip_sdk/cli/constants.py +38 -0
  21. glaip_sdk/cli/context.py +8 -0
  22. glaip_sdk/cli/core/__init__.py +79 -0
  23. glaip_sdk/cli/core/context.py +124 -0
  24. glaip_sdk/cli/core/output.py +846 -0
  25. glaip_sdk/cli/core/prompting.py +649 -0
  26. glaip_sdk/cli/core/rendering.py +187 -0
  27. glaip_sdk/cli/display.py +45 -32
  28. glaip_sdk/cli/hints.py +57 -0
  29. glaip_sdk/cli/io.py +14 -17
  30. glaip_sdk/cli/main.py +232 -143
  31. glaip_sdk/cli/masking.py +21 -33
  32. glaip_sdk/cli/mcp_validators.py +5 -15
  33. glaip_sdk/cli/pager.py +12 -19
  34. glaip_sdk/cli/parsers/__init__.py +1 -3
  35. glaip_sdk/cli/parsers/json_input.py +11 -22
  36. glaip_sdk/cli/resolution.py +3 -9
  37. glaip_sdk/cli/rich_helpers.py +1 -3
  38. glaip_sdk/cli/slash/__init__.py +0 -9
  39. glaip_sdk/cli/slash/accounts_controller.py +500 -0
  40. glaip_sdk/cli/slash/accounts_shared.py +75 -0
  41. glaip_sdk/cli/slash/agent_session.py +61 -28
  42. glaip_sdk/cli/slash/prompt.py +13 -10
  43. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  44. glaip_sdk/cli/slash/session.py +772 -222
  45. glaip_sdk/cli/slash/tui/__init__.py +9 -0
  46. glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
  47. glaip_sdk/cli/slash/tui/accounts_app.py +872 -0
  48. glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
  49. glaip_sdk/cli/slash/tui/loading.py +58 -0
  50. glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
  51. glaip_sdk/cli/transcript/__init__.py +12 -52
  52. glaip_sdk/cli/transcript/cache.py +258 -60
  53. glaip_sdk/cli/transcript/capture.py +72 -21
  54. glaip_sdk/cli/transcript/history.py +815 -0
  55. glaip_sdk/cli/transcript/launcher.py +1 -3
  56. glaip_sdk/cli/transcript/viewer.py +77 -329
  57. glaip_sdk/cli/update_notifier.py +177 -24
  58. glaip_sdk/cli/utils.py +242 -1309
  59. glaip_sdk/cli/validators.py +16 -18
  60. glaip_sdk/client/__init__.py +2 -1
  61. glaip_sdk/client/_agent_payloads.py +53 -37
  62. glaip_sdk/client/agent_runs.py +147 -0
  63. glaip_sdk/client/agents.py +320 -92
  64. glaip_sdk/client/base.py +78 -35
  65. glaip_sdk/client/main.py +19 -10
  66. glaip_sdk/client/mcps.py +123 -15
  67. glaip_sdk/client/run_rendering.py +218 -78
  68. glaip_sdk/client/shared.py +21 -0
  69. glaip_sdk/client/tools.py +161 -34
  70. glaip_sdk/client/validators.py +20 -48
  71. glaip_sdk/config/constants.py +11 -0
  72. glaip_sdk/exceptions.py +1 -3
  73. glaip_sdk/icons.py +9 -3
  74. glaip_sdk/mcps/__init__.py +21 -0
  75. glaip_sdk/mcps/base.py +345 -0
  76. glaip_sdk/models/__init__.py +90 -0
  77. glaip_sdk/models/agent.py +47 -0
  78. glaip_sdk/models/agent_runs.py +116 -0
  79. glaip_sdk/models/common.py +42 -0
  80. glaip_sdk/models/mcp.py +33 -0
  81. glaip_sdk/models/tool.py +33 -0
  82. glaip_sdk/payload_schemas/__init__.py +1 -13
  83. glaip_sdk/payload_schemas/agent.py +1 -3
  84. glaip_sdk/registry/__init__.py +55 -0
  85. glaip_sdk/registry/agent.py +164 -0
  86. glaip_sdk/registry/base.py +139 -0
  87. glaip_sdk/registry/mcp.py +253 -0
  88. glaip_sdk/registry/tool.py +231 -0
  89. glaip_sdk/rich_components.py +58 -2
  90. glaip_sdk/runner/__init__.py +59 -0
  91. glaip_sdk/runner/base.py +84 -0
  92. glaip_sdk/runner/deps.py +115 -0
  93. glaip_sdk/runner/langgraph.py +597 -0
  94. glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
  95. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
  96. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +158 -0
  97. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
  98. glaip_sdk/runner/tool_adapter/__init__.py +18 -0
  99. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
  100. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +177 -0
  101. glaip_sdk/tools/__init__.py +22 -0
  102. glaip_sdk/tools/base.py +435 -0
  103. glaip_sdk/utils/__init__.py +58 -12
  104. glaip_sdk/utils/a2a/__init__.py +34 -0
  105. glaip_sdk/utils/a2a/event_processor.py +188 -0
  106. glaip_sdk/utils/agent_config.py +4 -14
  107. glaip_sdk/utils/bundler.py +267 -0
  108. glaip_sdk/utils/client.py +111 -0
  109. glaip_sdk/utils/client_utils.py +46 -28
  110. glaip_sdk/utils/datetime_helpers.py +58 -0
  111. glaip_sdk/utils/discovery.py +78 -0
  112. glaip_sdk/utils/display.py +25 -21
  113. glaip_sdk/utils/export.py +143 -0
  114. glaip_sdk/utils/general.py +1 -36
  115. glaip_sdk/utils/import_export.py +15 -16
  116. glaip_sdk/utils/import_resolver.py +492 -0
  117. glaip_sdk/utils/instructions.py +101 -0
  118. glaip_sdk/utils/rendering/__init__.py +115 -1
  119. glaip_sdk/utils/rendering/formatting.py +38 -23
  120. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  121. glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
  122. glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
  123. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  124. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  125. glaip_sdk/utils/rendering/models.py +18 -8
  126. glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
  127. glaip_sdk/utils/rendering/renderer/base.py +476 -882
  128. glaip_sdk/utils/rendering/renderer/config.py +4 -10
  129. glaip_sdk/utils/rendering/renderer/debug.py +30 -34
  130. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  131. glaip_sdk/utils/rendering/renderer/stream.py +13 -54
  132. glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
  133. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  134. glaip_sdk/utils/rendering/renderer/toggle.py +182 -0
  135. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  136. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  137. glaip_sdk/utils/rendering/state.py +204 -0
  138. glaip_sdk/utils/rendering/step_tree_state.py +100 -0
  139. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  140. glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
  141. glaip_sdk/utils/rendering/steps/format.py +176 -0
  142. glaip_sdk/utils/rendering/{steps.py → steps/manager.py} +122 -26
  143. glaip_sdk/utils/rendering/timing.py +36 -0
  144. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  145. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  146. glaip_sdk/utils/resource_refs.py +29 -26
  147. glaip_sdk/utils/runtime_config.py +422 -0
  148. glaip_sdk/utils/serialization.py +32 -46
  149. glaip_sdk/utils/sync.py +142 -0
  150. glaip_sdk/utils/tool_detection.py +33 -0
  151. glaip_sdk/utils/validation.py +20 -28
  152. {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/METADATA +49 -4
  153. glaip_sdk-0.6.5b6.dist-info/RECORD +159 -0
  154. {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/WHEEL +1 -1
  155. glaip_sdk/models.py +0 -259
  156. glaip_sdk-0.0.20.dist-info/RECORD +0 -80
  157. {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.6.5b6.dist-info}/entry_points.txt +0 -0
glaip_sdk/client/tools.py CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  Authors:
5
5
  Raymond Christopher (raymond.christopher@gdplabs.id)
6
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
6
7
  """
7
8
 
8
9
  import logging
@@ -16,11 +17,14 @@ from glaip_sdk.config.constants import (
16
17
  DEFAULT_TOOL_TYPE,
17
18
  DEFAULT_TOOL_VERSION,
18
19
  )
19
- from glaip_sdk.models import Tool
20
+ from glaip_sdk.models import ToolResponse
21
+ from glaip_sdk.tools import Tool
20
22
  from glaip_sdk.utils.client_utils import (
23
+ add_kwargs_to_payload,
21
24
  create_model_instances,
22
25
  find_by_name,
23
26
  )
27
+ from glaip_sdk.utils.resource_refs import is_uuid
24
28
 
25
29
  # API endpoints
26
30
  TOOLS_ENDPOINT = "/tools/"
@@ -58,11 +62,11 @@ class ToolClient(BaseClient):
58
62
  def get_tool_by_id(self, tool_id: str) -> Tool:
59
63
  """Get tool by ID."""
60
64
  data = self._request("GET", f"{TOOLS_ENDPOINT}{tool_id}")
61
- return Tool(**data)._set_client(self)
65
+ response = ToolResponse(**data)
66
+ return Tool.from_response(response, client=self)
62
67
 
63
68
  def find_tools(self, name: str | None = None) -> list[Tool]:
64
69
  """Find tools by name."""
65
- # Backend doesn't support name query parameter, so we fetch all and filter client-side
66
70
  data = self._request("GET", TOOLS_ENDPOINT)
67
71
  tools = create_model_instances(data, Tool, self)
68
72
  return find_by_name(tools, name, case_sensitive=False)
@@ -96,9 +100,7 @@ class ToolClient(BaseClient):
96
100
  """
97
101
  return os.path.splitext(os.path.basename(file_path))[0]
98
102
 
99
- def _prepare_upload_data(
100
- self, name: str, framework: str, description: str | None = None, **kwargs
101
- ) -> dict:
103
+ def _prepare_upload_data(self, name: str, framework: str, description: str | None = None, **kwargs) -> dict:
102
104
  """Prepare upload data dictionary.
103
105
 
104
106
  Args:
@@ -113,6 +115,7 @@ class ToolClient(BaseClient):
113
115
  data = {
114
116
  "name": name,
115
117
  "framework": framework,
118
+ "type": kwargs.pop("tool_type", DEFAULT_TOOL_TYPE), # Default to custom
116
119
  }
117
120
 
118
121
  if description:
@@ -154,7 +157,8 @@ class ToolClient(BaseClient):
154
157
  data=upload_data,
155
158
  )
156
159
 
157
- return Tool(**response)._set_client(self)
160
+ tool_response = ToolResponse(**response)
161
+ return Tool.from_response(tool_response, client=self)
158
162
 
159
163
  def _build_create_payload(
160
164
  self,
@@ -202,9 +206,7 @@ class ToolClient(BaseClient):
202
206
 
203
207
  # Add any other kwargs (excluding already handled ones)
204
208
  excluded_keys = {"tags", "version"}
205
- for key, value in kwargs.items():
206
- if key not in excluded_keys:
207
- payload[key] = value
209
+ add_kwargs_to_payload(payload, kwargs, excluded_keys)
208
210
 
209
211
  return payload
210
212
 
@@ -217,29 +219,21 @@ class ToolClient(BaseClient):
217
219
  elif hasattr(current_tool, "description") and current_tool.description:
218
220
  update_data["description"] = current_tool.description
219
221
 
220
- def _handle_tags_update(
221
- self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool
222
- ) -> None:
222
+ def _handle_tags_update(self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool) -> None:
223
223
  """Handle tags field in update payload."""
224
224
  if kwargs.get("tags"):
225
225
  if isinstance(kwargs["tags"], list):
226
- update_data["tags"] = ",".join(
227
- str(tag).strip() for tag in kwargs["tags"]
228
- )
226
+ update_data["tags"] = ",".join(str(tag).strip() for tag in kwargs["tags"])
229
227
  else:
230
228
  update_data["tags"] = str(kwargs["tags"])
231
229
  elif hasattr(current_tool, "tags") and current_tool.tags:
232
230
  # Preserve existing tags if present
233
231
  if isinstance(current_tool.tags, list):
234
- update_data["tags"] = ",".join(
235
- str(tag).strip() for tag in current_tool.tags
236
- )
232
+ update_data["tags"] = ",".join(str(tag).strip() for tag in current_tool.tags)
237
233
  else:
238
234
  update_data["tags"] = str(current_tool.tags)
239
235
 
240
- def _handle_additional_kwargs(
241
- self, update_data: dict[str, Any], kwargs: dict[str, Any]
242
- ) -> None:
236
+ def _handle_additional_kwargs(self, update_data: dict[str, Any], kwargs: dict[str, Any]) -> None:
243
237
  """Handle additional kwargs in update payload."""
244
238
  excluded_keys = {
245
239
  "tags",
@@ -286,16 +280,15 @@ class ToolClient(BaseClient):
286
280
  or getattr(current_tool, "type", None)
287
281
  or DEFAULT_TOOL_TYPE
288
282
  )
283
+ # Convert enum to string value for API payload
284
+ if hasattr(current_type, "value"):
285
+ current_type = current_type.value
289
286
 
290
287
  update_data = {
291
288
  "name": name if name is not None else current_tool.name,
292
289
  "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
- ),
290
+ "framework": kwargs.get("framework", getattr(current_tool, "framework", DEFAULT_TOOL_FRAMEWORK)),
291
+ "version": kwargs.get("version", getattr(current_tool, "version", DEFAULT_TOOL_VERSION)),
299
292
  }
300
293
 
301
294
  # Handle description update
@@ -355,9 +348,7 @@ class ToolClient(BaseClient):
355
348
 
356
349
  try:
357
350
  # Prepare upload data
358
- upload_data = self._prepare_upload_data(
359
- name=name, framework=framework, description=description, **kwargs
360
- )
351
+ upload_data = self._prepare_upload_data(name=name, framework=framework, description=description, **kwargs)
361
352
 
362
353
  # Upload file
363
354
  return self._upload_tool_file(temp_file_path, upload_data)
@@ -451,12 +442,147 @@ class ToolClient(BaseClient):
451
442
  def update_tool(self, tool_id: str, **kwargs) -> Tool:
452
443
  """Update an existing tool."""
453
444
  data = self._request("PUT", f"{TOOLS_ENDPOINT}{tool_id}", json=kwargs)
454
- return Tool(**data)._set_client(self)
445
+ response = ToolResponse(**data)
446
+ return Tool.from_response(response, client=self)
455
447
 
456
448
  def delete_tool(self, tool_id: str) -> None:
457
449
  """Delete a tool."""
458
450
  self._request("DELETE", f"{TOOLS_ENDPOINT}{tool_id}")
459
451
 
452
+ def upsert_tool(
453
+ self,
454
+ identifier: str | Tool,
455
+ code: str | None = None,
456
+ description: str | None = None,
457
+ framework: str = "langchain",
458
+ **kwargs,
459
+ ) -> Tool:
460
+ """Create or update a tool by instance, ID, or name.
461
+
462
+ Args:
463
+ identifier: Tool instance, ID (UUID string), or name
464
+ code: Python code containing the tool plugin (required for create)
465
+ description: Tool description
466
+ framework: Tool framework (defaults to "langchain")
467
+ **kwargs: Additional parameters (tags, version, etc.)
468
+
469
+ Returns:
470
+ The created or updated tool.
471
+
472
+ Example:
473
+ >>> # By name with code (creates if not exists)
474
+ >>> tool = client.tools.upsert_tool(
475
+ ... "greeting",
476
+ ... code=bundled_source,
477
+ ... description="A greeting tool",
478
+ ... )
479
+ >>> # By instance
480
+ >>> tool = client.tools.upsert_tool(existing_tool, code=new_code)
481
+ >>> # By ID
482
+ >>> tool = client.tools.upsert_tool("uuid-here", code=new_code)
483
+ """
484
+ # Handle Tool instance
485
+ if isinstance(identifier, Tool):
486
+ if identifier.id:
487
+ logger.info("Updating tool by instance: %s", identifier.name)
488
+ return self._do_tool_upsert_update(
489
+ identifier.id,
490
+ identifier.name,
491
+ code,
492
+ description,
493
+ framework,
494
+ **kwargs,
495
+ )
496
+ identifier = identifier.name
497
+
498
+ # Handle string (ID or name)
499
+ if isinstance(identifier, str):
500
+ if is_uuid(identifier):
501
+ logger.info("Updating tool by ID: %s", identifier)
502
+ existing = self.get_tool_by_id(identifier)
503
+ return self._do_tool_upsert_update(identifier, existing.name, code, description, framework, **kwargs)
504
+
505
+ # It's a name - find or create
506
+ return self._upsert_tool_by_name(identifier, code, description, framework, **kwargs)
507
+
508
+ raise ValueError(f"Invalid identifier type: {type(identifier)}")
509
+
510
+ def _do_tool_upsert_update(
511
+ self,
512
+ tool_id: str,
513
+ name: str | None,
514
+ code: str | None,
515
+ description: str | None,
516
+ framework: str,
517
+ **kwargs,
518
+ ) -> Tool:
519
+ """Perform the update part of tool upsert."""
520
+ if code:
521
+ # Update via file upload
522
+ with tempfile.NamedTemporaryFile(
523
+ mode="w",
524
+ suffix=".py",
525
+ prefix=f"{name or 'tool'}_",
526
+ delete=False,
527
+ encoding="utf-8",
528
+ ) as temp_file:
529
+ temp_file.write(code)
530
+ temp_file_path = temp_file.name
531
+
532
+ try:
533
+ return self.update_tool_via_file(
534
+ tool_id,
535
+ temp_file_path,
536
+ name=name,
537
+ description=description,
538
+ framework=framework,
539
+ **kwargs,
540
+ )
541
+ finally:
542
+ try:
543
+ os.unlink(temp_file_path)
544
+ except OSError:
545
+ pass
546
+ else:
547
+ # Metadata-only update
548
+ update_kwargs = {"framework": framework, **kwargs}
549
+ if name:
550
+ update_kwargs["name"] = name
551
+ if description:
552
+ update_kwargs["description"] = description
553
+ return self.update_tool(tool_id, **update_kwargs)
554
+
555
+ def _upsert_tool_by_name(
556
+ self,
557
+ name: str,
558
+ code: str | None,
559
+ description: str | None,
560
+ framework: str,
561
+ **kwargs,
562
+ ) -> Tool:
563
+ """Find tool by name and update, or create if not found."""
564
+ existing = self.find_tools(name)
565
+
566
+ if len(existing) == 1:
567
+ logger.info("Updating existing tool: %s", name)
568
+ return self._do_tool_upsert_update(existing[0].id, name, code, description, framework, **kwargs)
569
+
570
+ if len(existing) > 1:
571
+ raise ValueError(f"Multiple tools found with name '{name}'")
572
+
573
+ # Create new tool - code is required
574
+ if not code:
575
+ raise ValueError(f"Tool '{name}' not found and no code provided for creation")
576
+
577
+ logger.info("Creating new tool: %s", name)
578
+ return self.create_tool_from_code(
579
+ name=name,
580
+ code=code,
581
+ framework=framework,
582
+ description=description,
583
+ **kwargs,
584
+ )
585
+
460
586
  def get_tool_script(self, tool_id: str) -> str:
461
587
  """Get the tool script content.
462
588
 
@@ -525,8 +651,9 @@ class ToolClient(BaseClient):
525
651
  data=update_payload,
526
652
  )
527
653
 
528
- return Tool(**response)._set_client(self)
654
+ tool_response = ToolResponse(**response)
655
+ return Tool.from_response(tool_response, client=self)
529
656
 
530
657
  except Exception as e:
531
- logger.error(f"Failed to update tool {tool_id} via file: {e}")
658
+ logger.error("Failed to update tool %s via file: %s", tool_id, e)
532
659
  raise
@@ -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
@@ -39,3 +39,14 @@ DEFAULT_MCP_TRANSPORT = "stdio"
39
39
 
40
40
  # Default error messages
41
41
  DEFAULT_ERROR_MESSAGE = "Unknown error"
42
+
43
+ # Agent configuration fields used for CLI args and payload building
44
+ AGENT_CONFIG_FIELDS = (
45
+ "name",
46
+ "instruction",
47
+ "model",
48
+ "tools",
49
+ "agents",
50
+ "mcps",
51
+ "timeout",
52
+ )
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
glaip_sdk/icons.py CHANGED
@@ -5,10 +5,13 @@ Authors:
5
5
  """
6
6
 
7
7
  ICON_AGENT = "🤖"
8
- ICON_AGENT_STEP = "🧠"
8
+ ICON_AGENT_STEP = "🤖"
9
9
  ICON_TOOL = "🔧"
10
- ICON_TOOL_STEP = "⚙️"
11
- ICON_DELEGATE = ICON_AGENT
10
+ ICON_TOOL_STEP = "🔧"
11
+ ICON_DELEGATE = ICON_AGENT_STEP
12
+ ICON_STATUS_SUCCESS = "✓"
13
+ ICON_STATUS_FAILED = "✗"
14
+ ICON_STATUS_WARNING = "⚠"
12
15
 
13
16
  __all__ = [
14
17
  "ICON_AGENT",
@@ -16,4 +19,7 @@ __all__ = [
16
19
  "ICON_TOOL",
17
20
  "ICON_TOOL_STEP",
18
21
  "ICON_DELEGATE",
22
+ "ICON_STATUS_SUCCESS",
23
+ "ICON_STATUS_FAILED",
24
+ "ICON_STATUS_WARNING",
19
25
  ]
@@ -0,0 +1,21 @@
1
+ """MCP (Model Context Protocol) package for GL AIP platform.
2
+
3
+ This package provides the MCP class and MCPRegistry for managing
4
+ Model Context Protocol configurations on the GL AIP platform.
5
+
6
+ Example:
7
+ >>> from glaip_sdk.mcps import MCP, get_mcp_registry
8
+ >>> mcp = MCP.from_native("arxiv-search")
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from glaip_sdk.mcps.base import MCP, MCPConfigValue
14
+ from glaip_sdk.registry.mcp import MCPRegistry, get_mcp_registry
15
+
16
+ __all__ = [
17
+ "MCP",
18
+ "MCPConfigValue",
19
+ "MCPRegistry",
20
+ "get_mcp_registry",
21
+ ]