glaip-sdk 0.0.20__py3-none-any.whl → 0.7.7__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 +44 -4
- glaip_sdk/_version.py +10 -3
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1250 -0
- glaip_sdk/branding.py +15 -6
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +271 -45
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents/__init__.py +119 -0
- glaip_sdk/cli/commands/agents/_common.py +561 -0
- glaip_sdk/cli/commands/agents/create.py +151 -0
- glaip_sdk/cli/commands/agents/delete.py +64 -0
- glaip_sdk/cli/commands/agents/get.py +89 -0
- glaip_sdk/cli/commands/agents/list.py +129 -0
- glaip_sdk/cli/commands/agents/run.py +264 -0
- glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
- glaip_sdk/cli/commands/agents/update.py +112 -0
- glaip_sdk/cli/commands/common_config.py +104 -0
- glaip_sdk/cli/commands/configure.py +734 -143
- glaip_sdk/cli/commands/mcps/__init__.py +94 -0
- glaip_sdk/cli/commands/mcps/_common.py +459 -0
- glaip_sdk/cli/commands/mcps/connect.py +82 -0
- glaip_sdk/cli/commands/mcps/create.py +152 -0
- glaip_sdk/cli/commands/mcps/delete.py +73 -0
- glaip_sdk/cli/commands/mcps/get.py +212 -0
- glaip_sdk/cli/commands/mcps/list.py +69 -0
- glaip_sdk/cli/commands/mcps/tools.py +235 -0
- glaip_sdk/cli/commands/mcps/update.py +190 -0
- glaip_sdk/cli/commands/models.py +14 -12
- glaip_sdk/cli/commands/shared/__init__.py +21 -0
- glaip_sdk/cli/commands/shared/formatters.py +91 -0
- glaip_sdk/cli/commands/tools/__init__.py +69 -0
- glaip_sdk/cli/commands/tools/_common.py +80 -0
- glaip_sdk/cli/commands/tools/create.py +228 -0
- glaip_sdk/cli/commands/tools/delete.py +61 -0
- glaip_sdk/cli/commands/tools/get.py +103 -0
- glaip_sdk/cli/commands/tools/list.py +69 -0
- glaip_sdk/cli/commands/tools/script.py +49 -0
- glaip_sdk/cli/commands/tools/update.py +102 -0
- glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
- glaip_sdk/cli/commands/transcripts/_common.py +9 -0
- glaip_sdk/cli/commands/transcripts/clear.py +5 -0
- glaip_sdk/cli/commands/transcripts/detail.py +5 -0
- glaip_sdk/cli/commands/transcripts_original.py +756 -0
- glaip_sdk/cli/commands/update.py +164 -23
- glaip_sdk/cli/config.py +49 -7
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/core/__init__.py +79 -0
- glaip_sdk/cli/core/context.py +124 -0
- glaip_sdk/cli/core/output.py +851 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +45 -32
- glaip_sdk/cli/entrypoint.py +20 -0
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +14 -17
- glaip_sdk/cli/main.py +344 -167
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/mcp_validators.py +5 -15
- glaip_sdk/cli/pager.py +15 -22
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/parsers/json_input.py +11 -22
- glaip_sdk/cli/resolution.py +5 -10
- glaip_sdk/cli/rich_helpers.py +1 -3
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +580 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +65 -29
- glaip_sdk/cli/slash/prompt.py +24 -10
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +827 -232
- glaip_sdk/cli/slash/tui/__init__.py +34 -0
- glaip_sdk/cli/slash/tui/accounts.tcss +88 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +933 -0
- glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
- glaip_sdk/cli/slash/tui/clipboard.py +147 -0
- glaip_sdk/cli/slash/tui/context.py +59 -0
- glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
- glaip_sdk/cli/slash/tui/loading.py +58 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
- glaip_sdk/cli/slash/tui/terminal.py +402 -0
- glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
- glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
- glaip_sdk/cli/slash/tui/theme/manager.py +86 -0
- glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
- glaip_sdk/cli/slash/tui/toast.py +123 -0
- glaip_sdk/cli/transcript/__init__.py +12 -52
- glaip_sdk/cli/transcript/cache.py +258 -60
- glaip_sdk/cli/transcript/capture.py +72 -21
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/launcher.py +1 -3
- glaip_sdk/cli/transcript/viewer.py +79 -329
- glaip_sdk/cli/update_notifier.py +385 -24
- glaip_sdk/cli/validators.py +16 -18
- glaip_sdk/client/__init__.py +3 -1
- glaip_sdk/client/_schedule_payloads.py +89 -0
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +370 -100
- glaip_sdk/client/base.py +78 -35
- glaip_sdk/client/hitl.py +136 -0
- glaip_sdk/client/main.py +25 -10
- glaip_sdk/client/mcps.py +166 -27
- glaip_sdk/client/payloads/agent/__init__.py +23 -0
- glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +65 -74
- glaip_sdk/client/payloads/agent/responses.py +43 -0
- glaip_sdk/client/run_rendering.py +583 -79
- glaip_sdk/client/schedules.py +439 -0
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +214 -56
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/exceptions.py +1 -3
- glaip_sdk/hitl/__init__.py +48 -0
- glaip_sdk/hitl/base.py +64 -0
- glaip_sdk/hitl/callback.py +43 -0
- glaip_sdk/hitl/local.py +121 -0
- glaip_sdk/hitl/remote.py +523 -0
- glaip_sdk/icons.py +9 -3
- glaip_sdk/mcps/__init__.py +21 -0
- glaip_sdk/mcps/base.py +345 -0
- glaip_sdk/models/__init__.py +107 -0
- glaip_sdk/models/agent.py +47 -0
- glaip_sdk/models/agent_runs.py +117 -0
- glaip_sdk/models/common.py +42 -0
- glaip_sdk/models/mcp.py +33 -0
- glaip_sdk/models/schedule.py +224 -0
- glaip_sdk/models/tool.py +33 -0
- glaip_sdk/payload_schemas/__init__.py +1 -13
- glaip_sdk/payload_schemas/agent.py +1 -3
- glaip_sdk/registry/__init__.py +55 -0
- glaip_sdk/registry/agent.py +164 -0
- glaip_sdk/registry/base.py +139 -0
- glaip_sdk/registry/mcp.py +253 -0
- glaip_sdk/registry/tool.py +445 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/runner/__init__.py +76 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +112 -0
- glaip_sdk/runner/langgraph.py +872 -0
- glaip_sdk/runner/logging_config.py +77 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +242 -0
- glaip_sdk/schedules/__init__.py +22 -0
- glaip_sdk/schedules/base.py +291 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +468 -0
- glaip_sdk/utils/__init__.py +59 -12
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +4 -14
- glaip_sdk/utils/bundler.py +403 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +46 -28
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +25 -21
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +1 -36
- glaip_sdk/utils/import_export.py +15 -16
- glaip_sdk/utils/import_resolver.py +524 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -1
- glaip_sdk/utils/rendering/formatting.py +38 -23
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +18 -8
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
- glaip_sdk/utils/rendering/renderer/base.py +534 -882
- glaip_sdk/utils/rendering/renderer/config.py +4 -10
- glaip_sdk/utils/rendering/renderer/debug.py +30 -34
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +13 -54
- glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/toggle.py +182 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/step_tree_state.py +100 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/{steps.py → steps/manager.py} +122 -26
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/resource_refs.py +29 -26
- glaip_sdk/utils/runtime_config.py +425 -0
- glaip_sdk/utils/serialization.py +32 -46
- glaip_sdk/utils/sync.py +162 -0
- glaip_sdk/utils/tool_detection.py +301 -0
- glaip_sdk/utils/tool_storage_provider.py +140 -0
- glaip_sdk/utils/validation.py +20 -28
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.7.7.dist-info}/METADATA +78 -23
- glaip_sdk-0.7.7.dist-info/RECORD +213 -0
- {glaip_sdk-0.0.20.dist-info → glaip_sdk-0.7.7.dist-info}/WHEEL +2 -1
- glaip_sdk-0.7.7.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.7.7.dist-info/top_level.txt +1 -0
- glaip_sdk/cli/commands/agents.py +0 -1412
- glaip_sdk/cli/commands/mcps.py +0 -1225
- glaip_sdk/cli/commands/tools.py +0 -597
- glaip_sdk/cli/utils.py +0 -1330
- glaip_sdk/models.py +0 -259
- glaip_sdk-0.0.20.dist-info/RECORD +0 -80
- glaip_sdk-0.0.20.dist-info/entry_points.txt +0 -3
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Logging configuration for CLI to suppress noisy dependency warnings.
|
|
2
|
+
|
|
3
|
+
This module provides centralized logging suppression for optional dependencies
|
|
4
|
+
that emit noisy warnings during CLI usage. Warnings are suppressed by default
|
|
5
|
+
but can be shown using GLAIP_LOG_LEVEL=DEBUG.
|
|
6
|
+
|
|
7
|
+
Authors:
|
|
8
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import warnings
|
|
14
|
+
|
|
15
|
+
NOISY_LOGGERS = ["transformers", "gllm_privacy", "google.cloud.aiplatform"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NameFilter(logging.Filter):
|
|
19
|
+
"""Filter logs by logger name prefix."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, prefixes: list[str]) -> None:
|
|
22
|
+
"""Initialize filter with logger name prefixes to suppress.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
prefixes: List of logger name prefixes to filter out.
|
|
26
|
+
"""
|
|
27
|
+
super().__init__()
|
|
28
|
+
self.prefixes = prefixes
|
|
29
|
+
|
|
30
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
31
|
+
"""Filter log records by name prefix.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
record: Log record to filter.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
False if record should be suppressed, True otherwise.
|
|
38
|
+
"""
|
|
39
|
+
return not any(record.name.startswith(p) for p in self.prefixes)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def setup_cli_logging() -> None:
|
|
43
|
+
"""Suppress INFO from noisy third-party libraries.
|
|
44
|
+
|
|
45
|
+
Use GLAIP_LOG_LEVEL=DEBUG to see all warnings.
|
|
46
|
+
This function is idempotent - calling it multiple times is safe.
|
|
47
|
+
"""
|
|
48
|
+
# Check env level FIRST before any suppression
|
|
49
|
+
env_level = os.getenv("GLAIP_LOG_LEVEL", "").upper()
|
|
50
|
+
is_debug = env_level == "DEBUG"
|
|
51
|
+
|
|
52
|
+
if is_debug:
|
|
53
|
+
# Debug mode: show everything, no suppression
|
|
54
|
+
if env_level and hasattr(logging, env_level):
|
|
55
|
+
logging.basicConfig(level=getattr(logging, env_level))
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
# Default mode: suppress noisy warnings
|
|
59
|
+
if env_level and hasattr(logging, env_level):
|
|
60
|
+
logging.basicConfig(level=getattr(logging, env_level))
|
|
61
|
+
|
|
62
|
+
# Add handler filter to suppress by name prefix (handles child loggers)
|
|
63
|
+
# Check if filter already exists to ensure idempotency
|
|
64
|
+
root_logger = logging.getLogger()
|
|
65
|
+
has_name_filter = any(isinstance(f, NameFilter) for h in root_logger.handlers for f in h.filters)
|
|
66
|
+
|
|
67
|
+
if not has_name_filter:
|
|
68
|
+
handler = logging.StreamHandler()
|
|
69
|
+
handler.addFilter(NameFilter(NOISY_LOGGERS))
|
|
70
|
+
root_logger.addHandler(handler)
|
|
71
|
+
|
|
72
|
+
# Suppress FutureWarning for GCS (idempotent - multiple calls are safe)
|
|
73
|
+
warnings.filterwarnings(
|
|
74
|
+
"ignore",
|
|
75
|
+
category=FutureWarning,
|
|
76
|
+
message=r".*google-cloud-storage.*",
|
|
77
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""MCP adapter module for local agent runtime.
|
|
2
|
+
|
|
3
|
+
This module provides MCP adapters for converting glaip-sdk MCP references
|
|
4
|
+
to backend-specific MCP configuration formats.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from glaip_sdk.runner.mcp_adapter.base_mcp_adapter import BaseMCPAdapter
|
|
11
|
+
from glaip_sdk.runner.mcp_adapter.langchain_mcp_adapter import LangChainMCPAdapter
|
|
12
|
+
|
|
13
|
+
__all__ = ["BaseMCPAdapter", "LangChainMCPAdapter"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Base MCP adapter for local agent runtime.
|
|
2
|
+
|
|
3
|
+
This module defines the abstract base class for MCP adapters.
|
|
4
|
+
Different backends (LangGraph, Google ADK) implement their own adapters.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseMCPAdapter(ABC):
|
|
17
|
+
"""Abstract base class for MCP adapters.
|
|
18
|
+
|
|
19
|
+
One Interface, Multiple Implementations:
|
|
20
|
+
- LangChainMCPAdapter: Adapts to aip-agents mcp_config format
|
|
21
|
+
- GoogleADKMCPAdapter: Adapts to Google ADK format (future)
|
|
22
|
+
|
|
23
|
+
Each backend implements this interface to adapt glaip-sdk MCPs
|
|
24
|
+
to their specific MCP configuration format.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def adapt_mcps(self, mcp_refs: list[Any]) -> dict[str, Any]:
|
|
29
|
+
"""Adapt glaip-sdk MCP references to backend-specific format.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
mcp_refs: List of MCP references from Agent definition.
|
|
33
|
+
Can be: MCP class instances, MCP.from_native() refs, etc.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Backend-specific MCP configuration.
|
|
37
|
+
For LangChain/aip-agents: dict[str, dict[str, Any]] mapping server names to config.
|
|
38
|
+
For Google ADK: dict in Google ADK MCP format.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If MCP is not supported by this backend.
|
|
42
|
+
"""
|
|
43
|
+
...
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""LangChain MCP adapter for local agent runtime.
|
|
2
|
+
|
|
3
|
+
This module handles adaptation of glaip-sdk MCP references to aip-agents
|
|
4
|
+
MCP configuration format for local execution.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from glaip_sdk.runner.mcp_adapter.base_mcp_adapter import BaseMCPAdapter
|
|
13
|
+
from glaip_sdk.runner.mcp_adapter.mcp_config_builder import MCPConfigBuilder
|
|
14
|
+
from gllm_core.utils import LoggerManager
|
|
15
|
+
|
|
16
|
+
logger = LoggerManager().get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LangChainMCPAdapter(BaseMCPAdapter):
|
|
20
|
+
"""Adapts glaip-sdk MCPs to aip-agents mcp_config dict format.
|
|
21
|
+
|
|
22
|
+
Handles:
|
|
23
|
+
- MCP class with http transport → convert to mcp_config
|
|
24
|
+
- MCP class with sse transport → convert to mcp_config
|
|
25
|
+
- MCP class with stdio transport → convert to mcp_config
|
|
26
|
+
|
|
27
|
+
Rejects:
|
|
28
|
+
- MCP.from_native() → platform-specific
|
|
29
|
+
- String MCP references → platform-specific
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def adapt_mcps(self, mcp_refs: list[Any]) -> dict[str, Any]:
|
|
33
|
+
"""Adapt MCP references to aip-agents mcp_config format.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
mcp_refs: List of MCP references from Agent definition.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Dictionary mapping server names to configuration dicts.
|
|
40
|
+
Format: {server_name: {transport: ..., url: ..., ...}}
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: If MCP is not supported in local mode.
|
|
44
|
+
"""
|
|
45
|
+
mcp_configs = {}
|
|
46
|
+
|
|
47
|
+
for mcp_ref in mcp_refs:
|
|
48
|
+
server_name, config = self._adapt_single_mcp(mcp_ref)
|
|
49
|
+
mcp_configs[server_name] = config
|
|
50
|
+
|
|
51
|
+
logger.debug("Adapted %d MCPs to aip-agents format", len(mcp_configs))
|
|
52
|
+
return mcp_configs
|
|
53
|
+
|
|
54
|
+
def _adapt_single_mcp(self, mcp_ref: Any) -> tuple[str, dict[str, Any]]:
|
|
55
|
+
"""Adapt a single MCP reference.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
mcp_ref: Single MCP reference to adapt.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Tuple of (server_name, config_dict).
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: If MCP is not supported.
|
|
65
|
+
"""
|
|
66
|
+
# 1. String references (not supported)
|
|
67
|
+
if isinstance(mcp_ref, str):
|
|
68
|
+
raise ValueError(self._get_platform_mcp_error(mcp_ref))
|
|
69
|
+
|
|
70
|
+
# 2. MCP instance - check if it's local or platform
|
|
71
|
+
if self._is_local_mcp(mcp_ref):
|
|
72
|
+
config = self._convert_mcp_config(mcp_ref)
|
|
73
|
+
return mcp_ref.name, config
|
|
74
|
+
|
|
75
|
+
# 3. Platform MCP (from_native, from_id)
|
|
76
|
+
if self._is_platform_mcp(mcp_ref):
|
|
77
|
+
raise ValueError(self._get_platform_mcp_error(mcp_ref))
|
|
78
|
+
|
|
79
|
+
# 4. Unknown type
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Unsupported MCP type for local mode: {type(mcp_ref)}. "
|
|
82
|
+
"Local mode only supports MCP class instances with http/sse/stdio transport."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def _is_local_mcp(self, ref: Any) -> bool:
|
|
86
|
+
"""Check if ref is a local MCP (has transport config)."""
|
|
87
|
+
return (
|
|
88
|
+
hasattr(ref, "transport")
|
|
89
|
+
and hasattr(ref, "name")
|
|
90
|
+
and getattr(ref, "transport", None) in ("http", "sse", "stdio")
|
|
91
|
+
and not self._is_lookup_only(ref)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _is_lookup_only(self, ref: Any) -> bool:
|
|
95
|
+
"""Check if MCP is lookup-only (platform reference)."""
|
|
96
|
+
return hasattr(ref, "_lookup_only") and getattr(ref, "_lookup_only", False)
|
|
97
|
+
|
|
98
|
+
def _convert_mcp_config(self, mcp: Any) -> dict[str, Any]:
|
|
99
|
+
"""Convert glaip-sdk MCP to aip-agents mcp_config format.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
mcp: glaip-sdk MCP instance.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
aip-agents compatible MCP config dict.
|
|
106
|
+
"""
|
|
107
|
+
# Start with user-provided config
|
|
108
|
+
config = mcp.config.copy() if mcp.config else {}
|
|
109
|
+
|
|
110
|
+
# Ensure transport is set
|
|
111
|
+
config["transport"] = mcp.transport
|
|
112
|
+
|
|
113
|
+
# Map server_url to url if needed (aip-agents uses 'url')
|
|
114
|
+
if "server_url" in config and "url" not in config:
|
|
115
|
+
config["url"] = config.pop("server_url")
|
|
116
|
+
|
|
117
|
+
self._validate_converted_config(
|
|
118
|
+
mcp_name=mcp.name,
|
|
119
|
+
transport=mcp.transport,
|
|
120
|
+
config=config,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Convert authentication to headers using MCPConfigBuilder
|
|
124
|
+
# Merge with existing headers (auth headers take precedence for conflicts)
|
|
125
|
+
if hasattr(mcp, "authentication") and mcp.authentication:
|
|
126
|
+
auth_headers = MCPConfigBuilder.build_headers_from_auth(mcp.authentication)
|
|
127
|
+
if auth_headers:
|
|
128
|
+
existing_headers = config.get("headers", {})
|
|
129
|
+
config["headers"] = {**existing_headers, **auth_headers}
|
|
130
|
+
else:
|
|
131
|
+
logger.warning("Failed to build headers from authentication for MCP '%s'", mcp.name)
|
|
132
|
+
|
|
133
|
+
logger.debug("Converted MCP '%s' with transport '%s'", mcp.name, mcp.transport)
|
|
134
|
+
return config
|
|
135
|
+
|
|
136
|
+
def _validate_converted_config(self, mcp_name: str, transport: str, config: dict[str, Any]) -> None:
|
|
137
|
+
"""Validate converted MCP config matches aip-agents schema expectations.
|
|
138
|
+
|
|
139
|
+
This method performs transport-specific validation after the glaip-sdk MCP
|
|
140
|
+
has been converted into the `aip-agents` `mcp_config` dictionary.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
mcp_name: The MCP server name.
|
|
144
|
+
transport: The MCP transport type.
|
|
145
|
+
config: The converted MCP configuration dictionary.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ValueError: If the configuration is invalid for the chosen transport.
|
|
149
|
+
"""
|
|
150
|
+
self._validate_transport_config(mcp_name, transport)
|
|
151
|
+
if transport in ("http", "sse"):
|
|
152
|
+
self._validate_http_sse_config(
|
|
153
|
+
mcp_name=mcp_name,
|
|
154
|
+
transport=transport,
|
|
155
|
+
config=config,
|
|
156
|
+
)
|
|
157
|
+
return
|
|
158
|
+
if transport == "stdio":
|
|
159
|
+
self._validate_stdio_config(
|
|
160
|
+
mcp_name=mcp_name,
|
|
161
|
+
config=config,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def _validate_transport_config(self, mcp_name: str, transport: str) -> None:
|
|
165
|
+
"""Validate that the MCP transport is supported by local mode.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
mcp_name: The MCP server name.
|
|
169
|
+
transport: The MCP transport type.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ValueError: If the transport is not one of 'http', 'sse', or 'stdio'.
|
|
173
|
+
"""
|
|
174
|
+
if transport not in ("http", "sse", "stdio"):
|
|
175
|
+
raise ValueError(
|
|
176
|
+
f"Invalid MCP config for '{mcp_name}': transport must be one of "
|
|
177
|
+
f"'http', 'sse', or 'stdio'. Got: {transport!r}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def _validate_http_sse_config(self, mcp_name: str, transport: str, config: dict[str, Any]) -> None:
|
|
181
|
+
"""Validate http/sse config has a usable URL.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
mcp_name: The MCP server name.
|
|
185
|
+
transport: The MCP transport type ('http' or 'sse').
|
|
186
|
+
config: The converted MCP configuration dictionary.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
ValueError: If url is missing/empty or does not use http(s) scheme.
|
|
190
|
+
"""
|
|
191
|
+
url = config.get("url")
|
|
192
|
+
if not isinstance(url, str) or not url:
|
|
193
|
+
raise ValueError(
|
|
194
|
+
f"Invalid MCP config for '{mcp_name}': transport='{transport}' "
|
|
195
|
+
"requires config['url'] as a non-empty string."
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if not (url.startswith("http://") or url.startswith("https://")):
|
|
199
|
+
raise ValueError(
|
|
200
|
+
f"Invalid MCP config for '{mcp_name}': config['url'] must start with "
|
|
201
|
+
f"'http://' or 'https://'. Got: {url!r}"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def _validate_stdio_config(self, mcp_name: str, config: dict[str, Any]) -> None:
|
|
205
|
+
"""Validate stdio config has a usable command and optional args list.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
mcp_name: The MCP server name.
|
|
209
|
+
config: The converted MCP configuration dictionary.
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
ValueError: If command is missing/empty or args is not a list of strings.
|
|
213
|
+
"""
|
|
214
|
+
command = config.get("command")
|
|
215
|
+
if not isinstance(command, str) or not command:
|
|
216
|
+
raise ValueError(
|
|
217
|
+
f"Invalid MCP config for '{mcp_name}': transport='stdio' "
|
|
218
|
+
"requires config['command'] as a non-empty string."
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
args = config.get("args")
|
|
222
|
+
if args is not None and (not isinstance(args, list) or any(not isinstance(x, str) for x in args)):
|
|
223
|
+
raise ValueError(
|
|
224
|
+
f"Invalid MCP config for '{mcp_name}': transport='stdio' expects "
|
|
225
|
+
"config['args'] to be a list[str] if provided."
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def _is_platform_mcp(self, ref: Any) -> bool:
|
|
229
|
+
"""Check if ref is platform-specific (not supported locally)."""
|
|
230
|
+
# MCP.from_native() or MCP.from_id() instances
|
|
231
|
+
if self._is_lookup_only(ref):
|
|
232
|
+
return True
|
|
233
|
+
|
|
234
|
+
# MCP with ID but no local transport
|
|
235
|
+
if hasattr(ref, "id") and getattr(ref, "id") and not self._is_local_mcp(ref):
|
|
236
|
+
return True
|
|
237
|
+
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
def _get_platform_mcp_error(self, ref: Any) -> str:
|
|
241
|
+
"""Get error message for platform MCPs."""
|
|
242
|
+
if isinstance(ref, str):
|
|
243
|
+
mcp_name = ref
|
|
244
|
+
else:
|
|
245
|
+
mcp_name = getattr(ref, "name", "<unknown>")
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
f"MCP '{mcp_name}' is not supported in local mode.\n\n"
|
|
249
|
+
"Local mode only supports MCPs with local transport configurations:\n"
|
|
250
|
+
" - MCP(name='...', transport='http', config={{'url': '...'}})\n"
|
|
251
|
+
" - MCP(name='...', transport='sse', config={{'url': '...'}})\n"
|
|
252
|
+
" - MCP(name='...', transport='stdio', config={{'command': '...'}})\n\n"
|
|
253
|
+
"Alternatives:\n"
|
|
254
|
+
" 1. Configure MCP with a local server URL\n"
|
|
255
|
+
" 2. Deploy the agent to use platform MCPs: agent.deploy()\n"
|
|
256
|
+
" 3. Remove MCP for local testing"
|
|
257
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""MCP configuration builder helper.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for building MCP configurations,
|
|
4
|
+
particularly for handling authentication conversion to headers.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from gllm_core.utils import LoggerManager
|
|
13
|
+
|
|
14
|
+
logger = LoggerManager().get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MCPConfigBuilder:
|
|
18
|
+
"""Helper class for building MCP configurations.
|
|
19
|
+
|
|
20
|
+
Handles authentication-to-headers conversion and configuration validation.
|
|
21
|
+
Simplified version focused on local MCP support needs.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def build_headers_from_auth(authentication: dict[str, Any]) -> dict[str, str] | None:
|
|
26
|
+
"""Build HTTP headers from authentication configuration.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
authentication: Authentication configuration dict with 'type' and auth-specific fields.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
dict[str, str] | None: HTTP headers or None if invalid/no-auth.
|
|
33
|
+
"""
|
|
34
|
+
if not authentication or "type" not in authentication:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
auth_type = str(authentication["type"]).lower()
|
|
38
|
+
|
|
39
|
+
# Dispatch to type-specific handlers
|
|
40
|
+
handlers = {
|
|
41
|
+
"no-auth": MCPConfigBuilder._handle_no_auth,
|
|
42
|
+
"custom-header": MCPConfigBuilder._handle_custom_header,
|
|
43
|
+
"bearer-token": MCPConfigBuilder._handle_bearer_token,
|
|
44
|
+
"api-key": MCPConfigBuilder._handle_api_key,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
handler = handlers.get(auth_type)
|
|
48
|
+
if handler:
|
|
49
|
+
return handler(authentication)
|
|
50
|
+
|
|
51
|
+
logger.warning("Unsupported authentication type: %s", auth_type)
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def _handle_no_auth(authentication: dict[str, Any]) -> None: # noqa: ARG004
|
|
56
|
+
"""Handle no-auth type."""
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def _handle_custom_header(authentication: dict[str, Any]) -> dict[str, str] | None:
|
|
61
|
+
"""Handle custom-header auth type."""
|
|
62
|
+
headers = authentication.get("headers")
|
|
63
|
+
if isinstance(headers, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in headers.items()):
|
|
64
|
+
return headers
|
|
65
|
+
logger.warning("custom-header auth requires 'headers' dict with string keys/values")
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def _handle_bearer_token(authentication: dict[str, Any]) -> dict[str, str] | None:
|
|
70
|
+
"""Handle bearer-token auth type."""
|
|
71
|
+
# Check if headers provided directly
|
|
72
|
+
headers = authentication.get("headers")
|
|
73
|
+
if isinstance(headers, dict):
|
|
74
|
+
return headers
|
|
75
|
+
# Otherwise build from token
|
|
76
|
+
token = authentication.get("token")
|
|
77
|
+
if token:
|
|
78
|
+
return {"Authorization": f"Bearer {token}"}
|
|
79
|
+
logger.warning("bearer-token auth requires 'token' field or 'headers' dict")
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _handle_api_key(authentication: dict[str, Any]) -> dict[str, str] | None:
|
|
84
|
+
"""Handle api-key auth type."""
|
|
85
|
+
# Check if headers provided directly
|
|
86
|
+
headers = authentication.get("headers")
|
|
87
|
+
if isinstance(headers, dict):
|
|
88
|
+
return headers
|
|
89
|
+
# Otherwise build from key/value
|
|
90
|
+
key = authentication.get("key")
|
|
91
|
+
value = authentication.get("value")
|
|
92
|
+
if key and value:
|
|
93
|
+
return {str(key): str(value)}
|
|
94
|
+
logger.warning("api-key auth requires 'key' and 'value' fields or 'headers' dict")
|
|
95
|
+
return None
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Tool adapter for local agent runtime.
|
|
2
|
+
|
|
3
|
+
This package provides adapters to convert glaip-sdk tool references
|
|
4
|
+
to backend-specific formats for local execution.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from glaip_sdk.runner.tool_adapter.base_tool_adapter import BaseToolAdapter
|
|
11
|
+
from glaip_sdk.runner.tool_adapter.langchain_tool_adapter import (
|
|
12
|
+
LangChainToolAdapter,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BaseToolAdapter",
|
|
17
|
+
"LangChainToolAdapter",
|
|
18
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Base tool adapter for local agent runtime.
|
|
2
|
+
|
|
3
|
+
This module defines the abstract base class for tool adapters.
|
|
4
|
+
Different backends (LangGraph, Google ADK) implement their own adapters.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseToolAdapter(ABC):
|
|
17
|
+
"""Abstract base class for tool adapters.
|
|
18
|
+
|
|
19
|
+
One Interface, Multiple Implementations:
|
|
20
|
+
- LangChainToolAdapter: Adapts to LangChain BaseTool (for aip-agents)
|
|
21
|
+
- GoogleADKToolAdapter: Adapts to Google ADK format (future)
|
|
22
|
+
|
|
23
|
+
Each backend implements this interface to adapt glaip-sdk tools
|
|
24
|
+
to their specific tool format.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def adapt_tools(self, tool_refs: list[Any]) -> list[Any]:
|
|
29
|
+
"""Adapt glaip-sdk tool references to backend-specific format.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
tool_refs: List of tool references from Agent definition.
|
|
33
|
+
Can be: LangChain classes/instances, Tool.from_langchain(),
|
|
34
|
+
Tool.from_native(), string names, etc.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of tools in backend-specific format.
|
|
38
|
+
For LangChain: list of BaseTool instances.
|
|
39
|
+
For Google ADK: list of Google ADK tool objects.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: If tool is not supported by this backend.
|
|
43
|
+
"""
|
|
44
|
+
...
|