glaip-sdk 0.6.5b6__py3-none-any.whl → 0.7.12__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 (127) hide show
  1. glaip_sdk/__init__.py +42 -5
  2. glaip_sdk/agents/base.py +217 -42
  3. glaip_sdk/branding.py +113 -2
  4. glaip_sdk/cli/account_store.py +15 -0
  5. glaip_sdk/cli/auth.py +14 -8
  6. glaip_sdk/cli/commands/accounts.py +1 -1
  7. glaip_sdk/cli/commands/agents/__init__.py +119 -0
  8. glaip_sdk/cli/commands/agents/_common.py +561 -0
  9. glaip_sdk/cli/commands/agents/create.py +151 -0
  10. glaip_sdk/cli/commands/agents/delete.py +64 -0
  11. glaip_sdk/cli/commands/agents/get.py +89 -0
  12. glaip_sdk/cli/commands/agents/list.py +129 -0
  13. glaip_sdk/cli/commands/agents/run.py +264 -0
  14. glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
  15. glaip_sdk/cli/commands/agents/update.py +112 -0
  16. glaip_sdk/cli/commands/common_config.py +15 -12
  17. glaip_sdk/cli/commands/configure.py +2 -3
  18. glaip_sdk/cli/commands/mcps/__init__.py +94 -0
  19. glaip_sdk/cli/commands/mcps/_common.py +459 -0
  20. glaip_sdk/cli/commands/mcps/connect.py +82 -0
  21. glaip_sdk/cli/commands/mcps/create.py +152 -0
  22. glaip_sdk/cli/commands/mcps/delete.py +73 -0
  23. glaip_sdk/cli/commands/mcps/get.py +212 -0
  24. glaip_sdk/cli/commands/mcps/list.py +69 -0
  25. glaip_sdk/cli/commands/mcps/tools.py +235 -0
  26. glaip_sdk/cli/commands/mcps/update.py +190 -0
  27. glaip_sdk/cli/commands/models.py +2 -4
  28. glaip_sdk/cli/commands/shared/__init__.py +21 -0
  29. glaip_sdk/cli/commands/shared/formatters.py +91 -0
  30. glaip_sdk/cli/commands/tools/__init__.py +69 -0
  31. glaip_sdk/cli/commands/tools/_common.py +80 -0
  32. glaip_sdk/cli/commands/tools/create.py +228 -0
  33. glaip_sdk/cli/commands/tools/delete.py +61 -0
  34. glaip_sdk/cli/commands/tools/get.py +103 -0
  35. glaip_sdk/cli/commands/tools/list.py +69 -0
  36. glaip_sdk/cli/commands/tools/script.py +49 -0
  37. glaip_sdk/cli/commands/tools/update.py +102 -0
  38. glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
  39. glaip_sdk/cli/commands/transcripts/_common.py +9 -0
  40. glaip_sdk/cli/commands/transcripts/clear.py +5 -0
  41. glaip_sdk/cli/commands/transcripts/detail.py +5 -0
  42. glaip_sdk/cli/commands/{transcripts.py → transcripts_original.py} +2 -1
  43. glaip_sdk/cli/commands/update.py +163 -17
  44. glaip_sdk/cli/config.py +1 -0
  45. glaip_sdk/cli/core/output.py +12 -7
  46. glaip_sdk/cli/entrypoint.py +20 -0
  47. glaip_sdk/cli/main.py +127 -39
  48. glaip_sdk/cli/pager.py +3 -3
  49. glaip_sdk/cli/resolution.py +2 -1
  50. glaip_sdk/cli/slash/accounts_controller.py +112 -32
  51. glaip_sdk/cli/slash/agent_session.py +5 -2
  52. glaip_sdk/cli/slash/prompt.py +11 -0
  53. glaip_sdk/cli/slash/remote_runs_controller.py +3 -1
  54. glaip_sdk/cli/slash/session.py +369 -23
  55. glaip_sdk/cli/slash/tui/__init__.py +26 -1
  56. glaip_sdk/cli/slash/tui/accounts.tcss +79 -5
  57. glaip_sdk/cli/slash/tui/accounts_app.py +1027 -88
  58. glaip_sdk/cli/slash/tui/clipboard.py +195 -0
  59. glaip_sdk/cli/slash/tui/context.py +87 -0
  60. glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
  61. glaip_sdk/cli/slash/tui/layouts/__init__.py +14 -0
  62. glaip_sdk/cli/slash/tui/layouts/harlequin.py +160 -0
  63. glaip_sdk/cli/slash/tui/remote_runs_app.py +119 -12
  64. glaip_sdk/cli/slash/tui/terminal.py +407 -0
  65. glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
  66. glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
  67. glaip_sdk/cli/slash/tui/theme/manager.py +112 -0
  68. glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
  69. glaip_sdk/cli/slash/tui/toast.py +374 -0
  70. glaip_sdk/cli/transcript/history.py +1 -1
  71. glaip_sdk/cli/transcript/viewer.py +5 -3
  72. glaip_sdk/cli/tui_settings.py +125 -0
  73. glaip_sdk/cli/update_notifier.py +215 -7
  74. glaip_sdk/cli/validators.py +1 -1
  75. glaip_sdk/client/__init__.py +2 -1
  76. glaip_sdk/client/_schedule_payloads.py +89 -0
  77. glaip_sdk/client/agents.py +50 -8
  78. glaip_sdk/client/hitl.py +136 -0
  79. glaip_sdk/client/main.py +7 -1
  80. glaip_sdk/client/mcps.py +44 -13
  81. glaip_sdk/client/payloads/agent/__init__.py +23 -0
  82. glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +22 -47
  83. glaip_sdk/client/payloads/agent/responses.py +43 -0
  84. glaip_sdk/client/run_rendering.py +414 -3
  85. glaip_sdk/client/schedules.py +439 -0
  86. glaip_sdk/client/tools.py +57 -26
  87. glaip_sdk/guardrails/__init__.py +80 -0
  88. glaip_sdk/guardrails/serializer.py +89 -0
  89. glaip_sdk/hitl/__init__.py +48 -0
  90. glaip_sdk/hitl/base.py +64 -0
  91. glaip_sdk/hitl/callback.py +43 -0
  92. glaip_sdk/hitl/local.py +121 -0
  93. glaip_sdk/hitl/remote.py +523 -0
  94. glaip_sdk/models/__init__.py +17 -0
  95. glaip_sdk/models/agent_runs.py +2 -1
  96. glaip_sdk/models/schedule.py +224 -0
  97. glaip_sdk/payload_schemas/agent.py +1 -0
  98. glaip_sdk/payload_schemas/guardrails.py +34 -0
  99. glaip_sdk/registry/tool.py +273 -59
  100. glaip_sdk/runner/__init__.py +20 -3
  101. glaip_sdk/runner/deps.py +5 -8
  102. glaip_sdk/runner/langgraph.py +318 -42
  103. glaip_sdk/runner/logging_config.py +77 -0
  104. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +104 -5
  105. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +72 -7
  106. glaip_sdk/schedules/__init__.py +22 -0
  107. glaip_sdk/schedules/base.py +291 -0
  108. glaip_sdk/tools/base.py +67 -14
  109. glaip_sdk/utils/__init__.py +1 -0
  110. glaip_sdk/utils/bundler.py +138 -2
  111. glaip_sdk/utils/import_resolver.py +43 -11
  112. glaip_sdk/utils/rendering/renderer/base.py +58 -0
  113. glaip_sdk/utils/runtime_config.py +15 -12
  114. glaip_sdk/utils/sync.py +31 -11
  115. glaip_sdk/utils/tool_detection.py +274 -6
  116. glaip_sdk/utils/tool_storage_provider.py +140 -0
  117. {glaip_sdk-0.6.5b6.dist-info → glaip_sdk-0.7.12.dist-info}/METADATA +49 -37
  118. glaip_sdk-0.7.12.dist-info/RECORD +219 -0
  119. {glaip_sdk-0.6.5b6.dist-info → glaip_sdk-0.7.12.dist-info}/WHEEL +2 -1
  120. glaip_sdk-0.7.12.dist-info/entry_points.txt +2 -0
  121. glaip_sdk-0.7.12.dist-info/top_level.txt +1 -0
  122. glaip_sdk/cli/commands/agents.py +0 -1509
  123. glaip_sdk/cli/commands/mcps.py +0 -1356
  124. glaip_sdk/cli/commands/tools.py +0 -576
  125. glaip_sdk/cli/utils.py +0 -263
  126. glaip_sdk-0.6.5b6.dist-info/RECORD +0 -159
  127. glaip_sdk-0.6.5b6.dist-info/entry_points.txt +0 -3
@@ -0,0 +1,89 @@
1
+ """Guardrail serialization logic.
2
+
3
+ This module provides functionality to serialize GuardrailManager and its engines
4
+ into the JSON format expected by the GL AIP backend. This keeps the serialization
5
+ logic within the SDK rather than polluting the core aip-agents logic.
6
+
7
+ Authors:
8
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import TYPE_CHECKING, Any
14
+
15
+ if TYPE_CHECKING:
16
+ from glaip_sdk.guardrails import (
17
+ GuardrailManager,
18
+ NemoGuardrailEngine,
19
+ PhraseMatcherEngine,
20
+ )
21
+
22
+
23
+ def _serialize_phrase_matcher(engine: PhraseMatcherEngine) -> dict[str, Any]:
24
+ """Serialize a PhraseMatcherEngine configuration."""
25
+ config: dict[str, Any] = {}
26
+
27
+ # Extract config from BaseGuardrailEngineConfig
28
+ if hasattr(engine, "config") and engine.config:
29
+ config.update(engine.config.model_dump())
30
+
31
+ # Extract specific fields
32
+ if hasattr(engine, "banned_phrases"):
33
+ config["banned_phrases"] = engine.banned_phrases
34
+
35
+ return config
36
+
37
+
38
+ def _serialize_nemo(engine: NemoGuardrailEngine) -> dict[str, Any]:
39
+ """Serialize a NemoGuardrailEngine configuration."""
40
+ config: dict[str, Any] = {}
41
+
42
+ # Extract config from BaseGuardrailEngineConfig
43
+ if hasattr(engine, "config") and engine.config:
44
+ config.update(engine.config.model_dump())
45
+
46
+ # Extract specific fields
47
+ nemo_fields = [
48
+ "topic_safety_mode",
49
+ "allowed_topics",
50
+ "denied_topics",
51
+ "include_core_restrictions",
52
+ "core_restriction_categories",
53
+ "config_dict",
54
+ "denial_phrases",
55
+ ]
56
+ for field in nemo_fields:
57
+ if hasattr(engine, field):
58
+ val = getattr(engine, field)
59
+ if val is not None:
60
+ config[field] = val
61
+
62
+ return config
63
+
64
+
65
+ def serialize_guardrail_manager(manager: GuardrailManager) -> dict[str, Any]:
66
+ """Serialize a GuardrailManager into the backend JSON format.
67
+
68
+ Args:
69
+ manager: The GuardrailManager instance to serialize.
70
+
71
+ Returns:
72
+ A dictionary matching the agent_config.guardrails schema.
73
+ """
74
+ from glaip_sdk.guardrails import NemoGuardrailEngine, PhraseMatcherEngine # noqa: PLC0415
75
+
76
+ engines_config = []
77
+
78
+ if hasattr(manager, "engines"):
79
+ for engine in manager.engines:
80
+ if isinstance(engine, PhraseMatcherEngine):
81
+ engines_config.append({"type": "phrase_matcher", "config": _serialize_phrase_matcher(engine)})
82
+ elif isinstance(engine, NemoGuardrailEngine):
83
+ engines_config.append({"type": "nemo", "config": _serialize_nemo(engine)})
84
+ else:
85
+ # Fallback for unknown engines
86
+ continue
87
+
88
+ enabled = getattr(manager, "enabled", True)
89
+ return {"enabled": enabled, "engines": engines_config}
@@ -0,0 +1,48 @@
1
+ """Human-in-the-Loop (HITL) utilities for glaip-sdk.
2
+
3
+ This package provides utilities for HITL approval workflows in both local
4
+ and remote agent execution modes.
5
+
6
+ For local development, LocalPromptHandler is automatically injected when
7
+ agent_config.hitl_enabled is True. No manual setup required.
8
+
9
+ For remote execution, use RemoteHITLHandler to handle HITL events programmatically.
10
+
11
+ Authors:
12
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
13
+ GLAIP SDK Team
14
+ """
15
+
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ # These don't require aip_agents, so import them directly
19
+ from glaip_sdk.hitl.base import HITLCallback, HITLDecision, HITLRequest, HITLResponse
20
+ from glaip_sdk.hitl.callback import PauseResumeCallback
21
+ from glaip_sdk.hitl.remote import RemoteHITLHandler
22
+
23
+ if TYPE_CHECKING:
24
+ from glaip_sdk.hitl.local import LocalPromptHandler
25
+
26
+ __all__ = [
27
+ "LocalPromptHandler",
28
+ "PauseResumeCallback",
29
+ "HITLCallback",
30
+ "HITLDecision",
31
+ "HITLRequest",
32
+ "HITLResponse",
33
+ "RemoteHITLHandler",
34
+ ]
35
+
36
+
37
+ def __getattr__(name: str) -> Any: # noqa: ANN401
38
+ """Lazy import for LocalPromptHandler.
39
+
40
+ This defers the import of aip_agents until LocalPromptHandler is actually accessed,
41
+ preventing ImportError when aip-agents is not installed but HITL is not being used.
42
+ """
43
+ if name == "LocalPromptHandler":
44
+ from glaip_sdk.hitl.local import LocalPromptHandler # noqa: PLC0415
45
+
46
+ globals()["LocalPromptHandler"] = LocalPromptHandler
47
+ return LocalPromptHandler
48
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
glaip_sdk/hitl/base.py ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env python3
2
+ """Base types for HITL approval handling.
3
+
4
+ Authors:
5
+ GLAIP SDK Team
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from typing import Any, Protocol, runtime_checkable
11
+
12
+
13
+ class HITLDecision(str, Enum):
14
+ """HITL decision types."""
15
+
16
+ APPROVED = "approved"
17
+ REJECTED = "rejected"
18
+ SKIPPED = "skipped"
19
+
20
+
21
+ @dataclass
22
+ class HITLRequest:
23
+ """HITL approval request from SSE stream."""
24
+
25
+ request_id: str
26
+ tool_name: str
27
+ tool_args: dict[str, Any]
28
+ timeout_at: str # ISO 8601, authoritative deadline
29
+ timeout_seconds: int # Informational, fallback only
30
+
31
+ # Raw metadata for advanced use cases
32
+ hitl_metadata: dict[str, Any]
33
+ tool_metadata: dict[str, Any]
34
+
35
+
36
+ @dataclass
37
+ class HITLResponse:
38
+ """HITL decision response."""
39
+
40
+ decision: HITLDecision
41
+ operator_input: str | None = None
42
+
43
+
44
+ @runtime_checkable
45
+ class HITLCallback(Protocol):
46
+ """Protocol for HITL approval callbacks.
47
+
48
+ Callbacks should complete within the computed callback timeout.
49
+ Callbacks should handle exceptions internally or let them propagate.
50
+ """
51
+
52
+ def __call__(self, request: HITLRequest) -> HITLResponse:
53
+ """Handle HITL approval request.
54
+
55
+ Args:
56
+ request: HITL request with tool info and metadata
57
+
58
+ Returns:
59
+ HITLResponse with decision and optional operator input
60
+
61
+ Raises:
62
+ Any exception will be caught, logged, and treated as REJECTED.
63
+ """
64
+ ...
@@ -0,0 +1,43 @@
1
+ """Pause/resume callback for HITL renderer control.
2
+
3
+ This module provides PauseResumeCallback which allows HITL prompt handlers
4
+ to control the live renderer without directly coupling to the renderer implementation.
5
+
6
+ Author:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ from typing import Any
11
+
12
+
13
+ class PauseResumeCallback:
14
+ """Simple callback object for pausing/resuming the live renderer.
15
+
16
+ This allows the LocalPromptHandler to control the renderer without
17
+ directly coupling to the renderer implementation.
18
+ """
19
+
20
+ def __init__(self) -> None:
21
+ """Initialize the callback."""
22
+ self._renderer: Any | None = None
23
+
24
+ def set_renderer(self, renderer: Any) -> None:
25
+ """Set the renderer instance.
26
+
27
+ Args:
28
+ renderer: RichStreamRenderer instance with pause_live() and resume_live() methods.
29
+ """
30
+ self._renderer = renderer
31
+
32
+ def pause(self) -> None:
33
+ """Pause the live renderer before prompting."""
34
+ if self._renderer and hasattr(self._renderer, "_shutdown_live"):
35
+ self._renderer._shutdown_live()
36
+
37
+ def resume(self) -> None:
38
+ """Resume the live renderer after prompting."""
39
+ if self._renderer and hasattr(self._renderer, "_ensure_live"):
40
+ self._renderer._ensure_live()
41
+
42
+
43
+ __all__ = ["PauseResumeCallback"]
@@ -0,0 +1,121 @@
1
+ """Local HITL prompt handler with interactive console support.
2
+
3
+ Author:
4
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
5
+ """
6
+
7
+ import os
8
+ from typing import Any
9
+
10
+ try:
11
+ from aip_agents.agent.hitl.prompt.base import BasePromptHandler
12
+ from aip_agents.schema.hitl import ApprovalDecision, ApprovalDecisionType, ApprovalRequest
13
+ except ImportError as e:
14
+ raise ImportError("aip_agents is required for local HITL. Install with: pip install 'glaip-sdk[local]'") from e
15
+
16
+ from rich.console import Console
17
+ from rich.prompt import Prompt
18
+
19
+
20
+ class LocalPromptHandler(BasePromptHandler):
21
+ """Local HITL prompt handler with interactive console prompts.
22
+
23
+ Experimental local HITL implementation with known limitations:
24
+ - Timeouts are not enforced (interactive prompts wait indefinitely)
25
+ - Relies on private renderer methods for pause/resume
26
+ - Only supports interactive terminal environments
27
+
28
+ The key insight from Rich documentation is that Live must be stopped before
29
+ using Prompt/input(), otherwise the input won't render properly.
30
+
31
+ Environment variables:
32
+ GLAIP_HITL_AUTO_APPROVE: Set to "true" (case-insensitive) to auto-approve
33
+ all requests without user interaction. Useful for integration tests and CI.
34
+ """
35
+
36
+ def __init__(self, *, pause_resume_callback: Any | None = None) -> None:
37
+ """Initialize the prompt handler.
38
+
39
+ Args:
40
+ pause_resume_callback: Optional callable with pause() and resume() methods
41
+ to control the live renderer during prompts. This is needed because
42
+ Rich Live interferes with Prompt/input().
43
+ """
44
+ super().__init__()
45
+ self._pause_resume = pause_resume_callback
46
+ self._console = Console()
47
+
48
+ async def prompt_for_decision(
49
+ self,
50
+ request: ApprovalRequest,
51
+ timeout_seconds: int,
52
+ context_keys: list[str] | None = None,
53
+ ) -> ApprovalDecision:
54
+ """Prompt for approval decision with live renderer pause/resume.
55
+
56
+ Supports auto-approval via GLAIP_HITL_AUTO_APPROVE environment variable
57
+ for integration testing and CI environments. Set to "true" (case-insensitive) to enable.
58
+ """
59
+ _ = (timeout_seconds, context_keys) # Suppress unused parameter warnings.
60
+
61
+ # Check for auto-approve mode (for integration tests/CI)
62
+ auto_approve = os.getenv("GLAIP_HITL_AUTO_APPROVE", "").lower() == "true"
63
+
64
+ if auto_approve:
65
+ # Auto-approve without user interaction
66
+ return ApprovalDecision(
67
+ request_id=request.request_id,
68
+ decision=ApprovalDecisionType.APPROVED,
69
+ operator_input="auto-approved",
70
+ )
71
+
72
+ # Pause the live renderer if callback is available
73
+ if self._pause_resume:
74
+ self._pause_resume.pause()
75
+
76
+ try:
77
+ # POC/MVP: Show what we're approving (still auto-approve for now)
78
+ self._print_request_info(request)
79
+
80
+ # POC/MVP: For testing, we can do actual input here
81
+ # Uncomment to enable real prompting:
82
+ response = Prompt.ask(
83
+ "\n[yellow]Approve this tool call?[/yellow] [dim](y/n/s)[/dim]",
84
+ console=self._console,
85
+ default="y",
86
+ )
87
+ response = response.lower().strip()
88
+
89
+ if response in ("y", "yes"):
90
+ decision = ApprovalDecisionType.APPROVED
91
+ elif response in ("n", "no"):
92
+ decision = ApprovalDecisionType.REJECTED
93
+ else:
94
+ decision = ApprovalDecisionType.SKIPPED
95
+
96
+ return ApprovalDecision(
97
+ request_id=request.request_id,
98
+ decision=decision,
99
+ operator_input=response if decision != ApprovalDecisionType.SKIPPED else None,
100
+ )
101
+ finally:
102
+ # Always resume the live renderer
103
+ if self._pause_resume:
104
+ self._pause_resume.resume()
105
+
106
+ def _print_request_info(self, request: ApprovalRequest) -> None:
107
+ """Print the approval request information."""
108
+ self._console.print()
109
+ self._console.rule("[yellow]HITL Approval Request[/yellow]", style="yellow")
110
+
111
+ tool_name = request.tool_name or "unknown"
112
+ self._console.print(f"[cyan]Tool:[/cyan] {tool_name}")
113
+
114
+ if hasattr(request, "arguments_preview") and request.arguments_preview:
115
+ self._console.print(f"[cyan]Arguments:[/cyan] {request.arguments_preview}")
116
+
117
+ if request.context:
118
+ self._console.print(f"[dim]Context: {request.context}[/dim]")
119
+
120
+
121
+ __all__ = ["LocalPromptHandler"]