blaxel 0.64.0__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.
- blaxel/__init__.py +8 -0
- blaxel/agents/__init__.py +5 -0
- blaxel/agents/chain.py +153 -0
- blaxel/agents/chat.py +286 -0
- blaxel/agents/decorator.py +208 -0
- blaxel/agents/thread.py +24 -0
- blaxel/agents/voice/openai.py +255 -0
- blaxel/agents/voice/utils.py +25 -0
- blaxel/api/__init__.py +1 -0
- blaxel/api/agents/__init__.py +0 -0
- blaxel/api/agents/create_agent.py +155 -0
- blaxel/api/agents/delete_agent.py +146 -0
- blaxel/api/agents/get_agent.py +146 -0
- blaxel/api/agents/get_agent_logs.py +151 -0
- blaxel/api/agents/get_agent_metrics.py +150 -0
- blaxel/api/agents/get_agent_trace_ids.py +201 -0
- blaxel/api/agents/list_agent_revisions.py +155 -0
- blaxel/api/agents/list_agents.py +127 -0
- blaxel/api/agents/update_agent.py +168 -0
- blaxel/api/configurations/__init__.py +0 -0
- blaxel/api/configurations/get_configuration.py +122 -0
- blaxel/api/default/__init__.py +0 -0
- blaxel/api/default/get_trace.py +150 -0
- blaxel/api/default/get_trace_ids.py +218 -0
- blaxel/api/default/get_trace_logs.py +186 -0
- blaxel/api/default/list_mcp_hub_definitions.py +127 -0
- blaxel/api/functions/__init__.py +0 -0
- blaxel/api/functions/create_function.py +155 -0
- blaxel/api/functions/delete_function.py +146 -0
- blaxel/api/functions/get_function.py +146 -0
- blaxel/api/functions/get_function_logs.py +151 -0
- blaxel/api/functions/get_function_metrics.py +150 -0
- blaxel/api/functions/get_function_trace_ids.py +201 -0
- blaxel/api/functions/list_function_revisions.py +158 -0
- blaxel/api/functions/list_functions.py +131 -0
- blaxel/api/functions/update_function.py +168 -0
- blaxel/api/integrations/__init__.py +0 -0
- blaxel/api/integrations/create_integration_connection.py +167 -0
- blaxel/api/integrations/delete_integration_connection.py +158 -0
- blaxel/api/integrations/get_integration.py +97 -0
- blaxel/api/integrations/get_integration_connection.py +158 -0
- blaxel/api/integrations/get_integration_connection_model.py +104 -0
- blaxel/api/integrations/get_integration_connection_model_endpoint_configurations.py +97 -0
- blaxel/api/integrations/list_integration_connection_models.py +97 -0
- blaxel/api/integrations/list_integration_connections.py +139 -0
- blaxel/api/integrations/update_integration_connection.py +180 -0
- blaxel/api/invitations/__init__.py +0 -0
- blaxel/api/invitations/list_all_pending_invitations.py +142 -0
- blaxel/api/knowledgebases/__init__.py +0 -0
- blaxel/api/knowledgebases/create_knowledgebase.py +163 -0
- blaxel/api/knowledgebases/delete_knowledgebase.py +154 -0
- blaxel/api/knowledgebases/get_knowledgebase.py +154 -0
- blaxel/api/knowledgebases/list_knowledgebase_revisions.py +158 -0
- blaxel/api/knowledgebases/list_knowledgebases.py +139 -0
- blaxel/api/knowledgebases/update_knowledgebase.py +176 -0
- blaxel/api/locations/__init__.py +0 -0
- blaxel/api/locations/list_locations.py +139 -0
- blaxel/api/metrics/__init__.py +0 -0
- blaxel/api/metrics/get_metrics.py +130 -0
- blaxel/api/models/__init__.py +0 -0
- blaxel/api/models/create_model.py +163 -0
- blaxel/api/models/delete_model.py +154 -0
- blaxel/api/models/get_model.py +154 -0
- blaxel/api/models/get_model_logs.py +155 -0
- blaxel/api/models/get_model_metrics.py +158 -0
- blaxel/api/models/get_model_trace_ids.py +201 -0
- blaxel/api/models/list_model_revisions.py +158 -0
- blaxel/api/models/list_models.py +135 -0
- blaxel/api/models/update_model.py +176 -0
- blaxel/api/policies/__init__.py +0 -0
- blaxel/api/policies/create_policy.py +167 -0
- blaxel/api/policies/delete_policy.py +154 -0
- blaxel/api/policies/get_policy.py +154 -0
- blaxel/api/policies/list_policies.py +139 -0
- blaxel/api/policies/update_policy.py +180 -0
- blaxel/api/privateclusters/__init__.py +0 -0
- blaxel/api/privateclusters/create_private_cluster.py +132 -0
- blaxel/api/privateclusters/delete_private_cluster.py +156 -0
- blaxel/api/privateclusters/get_private_cluster.py +159 -0
- blaxel/api/privateclusters/get_private_cluster_health.py +97 -0
- blaxel/api/privateclusters/list_private_clusters.py +140 -0
- blaxel/api/privateclusters/update_private_cluster.py +156 -0
- blaxel/api/privateclusters/update_private_cluster_health.py +97 -0
- blaxel/api/service_accounts/__init__.py +0 -0
- blaxel/api/service_accounts/create_api_key_for_service_account.py +177 -0
- blaxel/api/service_accounts/create_workspace_service_account.py +170 -0
- blaxel/api/service_accounts/delete_api_key_for_service_account.py +104 -0
- blaxel/api/service_accounts/delete_workspace_service_account.py +160 -0
- blaxel/api/service_accounts/get_workspace_service_accounts.py +141 -0
- blaxel/api/service_accounts/list_api_keys_for_service_account.py +163 -0
- blaxel/api/service_accounts/update_workspace_service_account.py +183 -0
- blaxel/api/store/__init__.py +0 -0
- blaxel/api/store/get_store_agent.py +146 -0
- blaxel/api/store/get_store_function.py +146 -0
- blaxel/api/store/list_store_agents.py +131 -0
- blaxel/api/store/list_store_functions.py +131 -0
- blaxel/api/workspaces/__init__.py +0 -0
- blaxel/api/workspaces/accept_workspace_invitation.py +161 -0
- blaxel/api/workspaces/create_worspace.py +163 -0
- blaxel/api/workspaces/decline_workspace_invitation.py +158 -0
- blaxel/api/workspaces/delete_workspace.py +154 -0
- blaxel/api/workspaces/get_workspace.py +154 -0
- blaxel/api/workspaces/invite_workspace_user.py +174 -0
- blaxel/api/workspaces/leave_workspace.py +161 -0
- blaxel/api/workspaces/list_workspace_users.py +139 -0
- blaxel/api/workspaces/list_workspaces.py +139 -0
- blaxel/api/workspaces/remove_workspace_user.py +101 -0
- blaxel/api/workspaces/update_workspace.py +176 -0
- blaxel/api/workspaces/update_workspace_user_role.py +187 -0
- blaxel/authentication/__init__.py +45 -0
- blaxel/authentication/apikey.py +50 -0
- blaxel/authentication/authentication.py +176 -0
- blaxel/authentication/clientcredentials.py +103 -0
- blaxel/authentication/credentials.py +295 -0
- blaxel/authentication/device_mode.py +197 -0
- blaxel/client.py +281 -0
- blaxel/common/__init__.py +17 -0
- blaxel/common/error.py +27 -0
- blaxel/common/instrumentation.py +317 -0
- blaxel/common/logger.py +60 -0
- blaxel/common/secrets.py +39 -0
- blaxel/common/settings.py +150 -0
- blaxel/common/slugify.py +18 -0
- blaxel/common/utils.py +34 -0
- blaxel/deploy/__init__.py +8 -0
- blaxel/deploy/deploy.py +316 -0
- blaxel/deploy/format.py +46 -0
- blaxel/deploy/parser.py +192 -0
- blaxel/errors.py +16 -0
- blaxel/functions/__init__.py +7 -0
- blaxel/functions/common.py +228 -0
- blaxel/functions/decorator.py +64 -0
- blaxel/functions/local/local.py +48 -0
- blaxel/functions/mcp/client.py +96 -0
- blaxel/functions/mcp/mcp.py +168 -0
- blaxel/functions/mcp/utils.py +56 -0
- blaxel/functions/remote/remote.py +183 -0
- blaxel/models/__init__.py +233 -0
- blaxel/models/acl.py +133 -0
- blaxel/models/agent.py +126 -0
- blaxel/models/agent_chain.py +88 -0
- blaxel/models/agent_spec.py +346 -0
- blaxel/models/api_key.py +142 -0
- blaxel/models/configuration.py +85 -0
- blaxel/models/continent.py +70 -0
- blaxel/models/core_event.py +97 -0
- blaxel/models/core_spec.py +249 -0
- blaxel/models/core_spec_configurations.py +77 -0
- blaxel/models/country.py +70 -0
- blaxel/models/create_api_key_for_service_account_body.py +69 -0
- blaxel/models/create_workspace_service_account_body.py +71 -0
- blaxel/models/create_workspace_service_account_response_200.py +105 -0
- blaxel/models/delete_workspace_service_account_response_200.py +96 -0
- blaxel/models/entrypoint.py +96 -0
- blaxel/models/entrypoint_env.py +45 -0
- blaxel/models/flavor.py +70 -0
- blaxel/models/form.py +120 -0
- blaxel/models/form_config.py +45 -0
- blaxel/models/form_oauthomitempty.py +45 -0
- blaxel/models/form_secrets.py +45 -0
- blaxel/models/function.py +126 -0
- blaxel/models/function_kit.py +97 -0
- blaxel/models/function_spec.py +310 -0
- blaxel/models/get_trace_ids_response_200.py +45 -0
- blaxel/models/get_trace_logs_response_200.py +45 -0
- blaxel/models/get_trace_response_200.py +45 -0
- blaxel/models/get_workspace_service_accounts_response_200_item.py +96 -0
- blaxel/models/histogram_bucket.py +79 -0
- blaxel/models/histogram_stats.py +88 -0
- blaxel/models/integration_connection.py +96 -0
- blaxel/models/integration_connection_spec.py +114 -0
- blaxel/models/integration_connection_spec_config.py +45 -0
- blaxel/models/integration_connection_spec_secret.py +45 -0
- blaxel/models/integration_model.py +162 -0
- blaxel/models/integration_repository.py +88 -0
- blaxel/models/invite_workspace_user_body.py +60 -0
- blaxel/models/knowledgebase.py +126 -0
- blaxel/models/knowledgebase_spec.py +163 -0
- blaxel/models/knowledgebase_spec_options.py +45 -0
- blaxel/models/last_n_requests_metric.py +79 -0
- blaxel/models/latency_metric.py +144 -0
- blaxel/models/location_response.py +113 -0
- blaxel/models/mcp_definition.py +188 -0
- blaxel/models/mcp_definition_entrypoint.py +45 -0
- blaxel/models/mcp_definition_form.py +45 -0
- blaxel/models/metadata.py +139 -0
- blaxel/models/metadata_labels.py +45 -0
- blaxel/models/metric.py +79 -0
- blaxel/models/metrics.py +169 -0
- blaxel/models/metrics_models.py +45 -0
- blaxel/models/metrics_request_total_per_code.py +45 -0
- blaxel/models/metrics_rps_per_code.py +45 -0
- blaxel/models/model.py +126 -0
- blaxel/models/model_private_cluster.py +79 -0
- blaxel/models/model_spec.py +249 -0
- blaxel/models/o_auth.py +72 -0
- blaxel/models/owner_fields.py +70 -0
- blaxel/models/pending_invitation.py +124 -0
- blaxel/models/pending_invitation_accept.py +85 -0
- blaxel/models/pending_invitation_render.py +147 -0
- blaxel/models/pending_invitation_render_invited_by.py +88 -0
- blaxel/models/pending_invitation_render_workspace.py +70 -0
- blaxel/models/pending_invitation_workspace_details.py +72 -0
- blaxel/models/pod_template_spec.py +45 -0
- blaxel/models/policy.py +96 -0
- blaxel/models/policy_location.py +70 -0
- blaxel/models/policy_max_tokens.py +106 -0
- blaxel/models/policy_spec.py +151 -0
- blaxel/models/private_cluster.py +183 -0
- blaxel/models/private_location.py +61 -0
- blaxel/models/repository.py +70 -0
- blaxel/models/request_duration_over_time_metric.py +97 -0
- blaxel/models/request_duration_over_time_metrics.py +80 -0
- blaxel/models/request_total_by_origin_metric.py +115 -0
- blaxel/models/request_total_by_origin_metric_request_total_by_origin.py +45 -0
- blaxel/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +45 -0
- blaxel/models/request_total_metric.py +123 -0
- blaxel/models/request_total_metric_request_total_per_code.py +45 -0
- blaxel/models/request_total_metric_rps_per_code.py +45 -0
- blaxel/models/resource_log.py +79 -0
- blaxel/models/resource_metrics.py +270 -0
- blaxel/models/resource_metrics_request_total_per_code.py +45 -0
- blaxel/models/resource_metrics_rps_per_code.py +45 -0
- blaxel/models/revision_configuration.py +97 -0
- blaxel/models/revision_metadata.py +124 -0
- blaxel/models/runtime.py +196 -0
- blaxel/models/runtime_startup_probe.py +45 -0
- blaxel/models/serverless_config.py +80 -0
- blaxel/models/spec_configuration.py +70 -0
- blaxel/models/store_agent.py +178 -0
- blaxel/models/store_agent_labels.py +45 -0
- blaxel/models/store_configuration.py +151 -0
- blaxel/models/store_configuration_option.py +79 -0
- blaxel/models/store_function.py +211 -0
- blaxel/models/store_function_kit.py +97 -0
- blaxel/models/store_function_labels.py +45 -0
- blaxel/models/store_function_parameter.py +88 -0
- blaxel/models/time_fields.py +70 -0
- blaxel/models/token_rate_metric.py +88 -0
- blaxel/models/token_rate_metrics.py +120 -0
- blaxel/models/token_total_metric.py +106 -0
- blaxel/models/trace_ids_response.py +45 -0
- blaxel/models/update_workspace_service_account_body.py +69 -0
- blaxel/models/update_workspace_service_account_response_200.py +96 -0
- blaxel/models/update_workspace_user_role_body.py +60 -0
- blaxel/models/websocket_channel.py +88 -0
- blaxel/models/workspace.py +148 -0
- blaxel/models/workspace_labels.py +45 -0
- blaxel/models/workspace_user.py +115 -0
- blaxel/py.typed +1 -0
- blaxel/run.py +108 -0
- blaxel/serve/app.py +131 -0
- blaxel/serve/middlewares/__init__.py +10 -0
- blaxel/serve/middlewares/accesslog.py +32 -0
- blaxel/serve/middlewares/processtime.py +28 -0
- blaxel/types.py +46 -0
- blaxel-0.64.0.dist-info/METADATA +96 -0
- blaxel-0.64.0.dist-info/RECORD +261 -0
- blaxel-0.64.0.dist-info/WHEEL +4 -0
- blaxel-0.64.0.dist-info/entry_points.txt +2 -0
- blaxel-0.64.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,168 @@
|
|
1
|
+
"""
|
2
|
+
This module provides functionalities to interact with MCP (Multi-Client Platform) servers.
|
3
|
+
It includes classes for managing MCP clients, creating dynamic schemas, and integrating MCP tools into Blaxel.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import asyncio
|
7
|
+
import logging
|
8
|
+
import warnings
|
9
|
+
from typing import Any, AsyncIterator, Callable
|
10
|
+
|
11
|
+
import pydantic
|
12
|
+
import pydantic_core
|
13
|
+
import requests
|
14
|
+
import typing_extensions as t
|
15
|
+
from langchain_core.tools.base import BaseTool, BaseToolkit, ToolException
|
16
|
+
from mcp import ClientSession
|
17
|
+
from mcp.types import CallToolResult, ListToolsResult
|
18
|
+
|
19
|
+
from blaxel.aimon.settings import get_settings
|
20
|
+
from blaxel.authentication import get_authentication_headers
|
21
|
+
from blaxel.authentication.authentication import AuthenticatedClient
|
22
|
+
from blaxel.functions.mcp.client import websocket_client
|
23
|
+
|
24
|
+
from .utils import create_schema_model
|
25
|
+
|
26
|
+
settings = get_settings()
|
27
|
+
|
28
|
+
logger = logging.getLogger(__name__)
|
29
|
+
|
30
|
+
|
31
|
+
class MCPClient:
|
32
|
+
def __init__(self, client: AuthenticatedClient, url: str, fallback_url: str | None = None):
|
33
|
+
self.client = client
|
34
|
+
self.url = url
|
35
|
+
self.fallback_url = fallback_url
|
36
|
+
|
37
|
+
async def list_ws_tools(self, is_fallback: bool = False) -> ListToolsResult:
|
38
|
+
if is_fallback:
|
39
|
+
url = self.fallback_url
|
40
|
+
else:
|
41
|
+
url = self.url
|
42
|
+
try:
|
43
|
+
async with websocket_client(url, headers=get_authentication_headers(settings)) as (read_stream, write_stream):
|
44
|
+
logger.debug("WebSocket connection established")
|
45
|
+
async with ClientSession(read_stream, write_stream) as client:
|
46
|
+
await client.initialize()
|
47
|
+
response = await client.list_tools()
|
48
|
+
logger.debug(f"WebSocket tools: {response}")
|
49
|
+
return response
|
50
|
+
except Exception as e:
|
51
|
+
logger.error(f"Error listing tools: {e}")
|
52
|
+
logger.debug("WebSocket not available, trying HTTP")
|
53
|
+
return None # Signal to list_tools() to try HTTP instead
|
54
|
+
|
55
|
+
async def list_tools(self) -> ListToolsResult:
|
56
|
+
logger.debug(f"Listing tools for {self.url}")
|
57
|
+
try:
|
58
|
+
result = await self.list_ws_tools(is_fallback=False)
|
59
|
+
return result
|
60
|
+
except Exception as e: # Fallback to Public endpoint
|
61
|
+
if self.fallback_url:
|
62
|
+
try:
|
63
|
+
result = await self.list_ws_tools(is_fallback=True)
|
64
|
+
return result
|
65
|
+
except Exception as e:
|
66
|
+
raise e
|
67
|
+
else:
|
68
|
+
raise e
|
69
|
+
|
70
|
+
|
71
|
+
async def call_tool(
|
72
|
+
self,
|
73
|
+
tool_name: str,
|
74
|
+
arguments: dict[str, Any] = None,
|
75
|
+
is_fallback: bool = False,
|
76
|
+
) -> requests.Response | AsyncIterator[CallToolResult]:
|
77
|
+
if is_fallback:
|
78
|
+
url = self.fallback_url
|
79
|
+
else:
|
80
|
+
url = self.url
|
81
|
+
try:
|
82
|
+
async with websocket_client(url, headers=get_authentication_headers(settings)) as (read_stream, write_stream):
|
83
|
+
async with ClientSession(read_stream, write_stream) as session:
|
84
|
+
await session.initialize()
|
85
|
+
response = await session.call_tool(tool_name, arguments or {})
|
86
|
+
content = pydantic_core.to_json(response).decode()
|
87
|
+
if response.isError:
|
88
|
+
raise ToolException(content)
|
89
|
+
return content
|
90
|
+
except Exception as e:
|
91
|
+
raise e
|
92
|
+
|
93
|
+
|
94
|
+
class MCPTool(BaseTool):
|
95
|
+
"""
|
96
|
+
Tool for interacting with MCP server-hosted tools.
|
97
|
+
|
98
|
+
Attributes:
|
99
|
+
client (MCPClient): The MCP client instance.
|
100
|
+
handle_tool_error (bool | str | Callable[[ToolException], str] | None): Error handling strategy.
|
101
|
+
"""
|
102
|
+
|
103
|
+
client: MCPClient
|
104
|
+
handle_tool_error: bool | str | Callable[[ToolException], str] | None = True
|
105
|
+
|
106
|
+
@t.override
|
107
|
+
def _run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
108
|
+
warnings.warn(
|
109
|
+
"Invoke this tool asynchronousely using `ainvoke`. This method exists only to satisfy standard tests.",
|
110
|
+
stacklevel=1,
|
111
|
+
)
|
112
|
+
return asyncio.run(self._arun(*args, **kwargs))
|
113
|
+
|
114
|
+
@t.override
|
115
|
+
async def _arun(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
116
|
+
try:
|
117
|
+
return await self.client.call_tool(self.name, arguments=kwargs)
|
118
|
+
except Exception as e:
|
119
|
+
if self.client.fallback_url:
|
120
|
+
try:
|
121
|
+
return await self.client.call_tool(self.name, arguments=kwargs, is_fallback=True) # Fallback to Public endpoint
|
122
|
+
except Exception as e:
|
123
|
+
raise e
|
124
|
+
else:
|
125
|
+
raise e
|
126
|
+
|
127
|
+
@t.override
|
128
|
+
@property
|
129
|
+
def tool_call_schema(self) -> type[pydantic.BaseModel]:
|
130
|
+
assert self.args_schema is not None # noqa: S101
|
131
|
+
return self.args_schema
|
132
|
+
|
133
|
+
class MCPToolkit(BaseToolkit):
|
134
|
+
"""
|
135
|
+
Toolkit for managing MCP server tools.
|
136
|
+
|
137
|
+
Attributes:
|
138
|
+
client (MCPClient): The MCP client instance.
|
139
|
+
_tools (ListToolsResult | None): Cached list of tools from the MCP server.
|
140
|
+
"""
|
141
|
+
|
142
|
+
client: MCPClient
|
143
|
+
"""The MCP session used to obtain the tools"""
|
144
|
+
|
145
|
+
_tools: ListToolsResult | None = None
|
146
|
+
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
|
147
|
+
|
148
|
+
async def initialize(self) -> None:
|
149
|
+
"""Initialize the session and retrieve tools list"""
|
150
|
+
if self._tools is None:
|
151
|
+
response = await self.client.list_tools()
|
152
|
+
self._tools = response
|
153
|
+
|
154
|
+
@t.override
|
155
|
+
async def get_tools(self) -> list[BaseTool]:
|
156
|
+
if self._tools is None:
|
157
|
+
raise RuntimeError("Must initialize the toolkit first")
|
158
|
+
|
159
|
+
return [
|
160
|
+
MCPTool(
|
161
|
+
client=self.client,
|
162
|
+
name=tool.name,
|
163
|
+
description=tool.description or "",
|
164
|
+
args_schema=create_schema_model(tool.name, tool.inputSchema),
|
165
|
+
)
|
166
|
+
# list_tools returns a PaginatedResult, but I don't see a way to pass the cursor to retrieve more tools
|
167
|
+
for tool in self._tools.tools
|
168
|
+
]
|
@@ -0,0 +1,56 @@
|
|
1
|
+
"""
|
2
|
+
This module provides functionalities to interact with MCP (Multi-Client Platform) servers.
|
3
|
+
It includes classes for managing MCP clients, creating dynamic schemas, and integrating MCP tools into Blaxel.
|
4
|
+
"""
|
5
|
+
import pydantic
|
6
|
+
import typing_extensions as t
|
7
|
+
from pydantic.json_schema import JsonSchemaValue
|
8
|
+
from pydantic_core import core_schema as cs
|
9
|
+
|
10
|
+
TYPE_MAP = {
|
11
|
+
"integer": int,
|
12
|
+
"number": float,
|
13
|
+
"array": list,
|
14
|
+
"object": dict,
|
15
|
+
"boolean": bool,
|
16
|
+
"string": str,
|
17
|
+
"null": type(None),
|
18
|
+
}
|
19
|
+
|
20
|
+
FIELD_DEFAULTS = {
|
21
|
+
int: 0,
|
22
|
+
float: 0.0,
|
23
|
+
list: [],
|
24
|
+
bool: False,
|
25
|
+
str: "",
|
26
|
+
type(None): None,
|
27
|
+
}
|
28
|
+
|
29
|
+
def configure_field(name: str, type_: dict[str, t.Any], required: list[str]) -> tuple[type, t.Any]:
|
30
|
+
field_type = TYPE_MAP[type_["type"]]
|
31
|
+
default_ = FIELD_DEFAULTS.get(field_type) if name not in required else ...
|
32
|
+
return field_type, default_
|
33
|
+
|
34
|
+
def create_schema_model(name: str, schema: dict[str, t.Any]) -> type[pydantic.BaseModel]:
|
35
|
+
# Create a new model class that returns our JSON schema.
|
36
|
+
# LangChain requires a BaseModel class.
|
37
|
+
class SchemaBase(pydantic.BaseModel):
|
38
|
+
model_config = pydantic.ConfigDict(extra="allow")
|
39
|
+
|
40
|
+
@t.override
|
41
|
+
@classmethod
|
42
|
+
def __get_pydantic_json_schema__(
|
43
|
+
cls, core_schema: cs.CoreSchema, handler: pydantic.GetJsonSchemaHandler
|
44
|
+
) -> JsonSchemaValue:
|
45
|
+
return schema
|
46
|
+
|
47
|
+
# Since this langchain patch, we need to synthesize pydantic fields from the schema
|
48
|
+
# https://github.com/langchain-ai/langchain/commit/033ac417609297369eb0525794d8b48a425b8b33
|
49
|
+
required = schema.get("required", [])
|
50
|
+
fields: dict[str, t.Any] = {
|
51
|
+
name: configure_field(name, type_, required) for name, type_ in schema["properties"].items()
|
52
|
+
}
|
53
|
+
|
54
|
+
return pydantic.create_model(f"{name}Schema", __base__=SchemaBase, **fields)
|
55
|
+
|
56
|
+
|
@@ -0,0 +1,183 @@
|
|
1
|
+
"""
|
2
|
+
This module provides functionalities to integrate remote functions into Blaxel.
|
3
|
+
It includes classes for creating dynamic schemas based on function parameters and managing remote toolkits.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import asyncio
|
7
|
+
import os
|
8
|
+
import warnings
|
9
|
+
from dataclasses import dataclass
|
10
|
+
from typing import Callable
|
11
|
+
|
12
|
+
import pydantic
|
13
|
+
import typing_extensions as t
|
14
|
+
from langchain_core.tools.base import BaseTool, ToolException
|
15
|
+
|
16
|
+
from blaxel.aimon.settings import get_settings
|
17
|
+
from blaxel.api.functions import get_function, list_functions
|
18
|
+
from blaxel.authentication.authentication import AuthenticatedClient
|
19
|
+
from blaxel.errors import UnexpectedStatus
|
20
|
+
from blaxel.functions.mcp.mcp import MCPClient, MCPToolkit
|
21
|
+
from blaxel.models import Function, StoreFunctionParameter
|
22
|
+
from blaxel.run import RunClient
|
23
|
+
|
24
|
+
|
25
|
+
def create_dynamic_schema(name: str, parameters: list[StoreFunctionParameter]) -> type[pydantic.BaseModel]:
|
26
|
+
"""
|
27
|
+
Creates a dynamic Pydantic schema based on function parameters.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
name (str): The name of the schema.
|
31
|
+
parameters (list[StoreFunctionParameter]): List of parameter objects.
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
type[pydantic.BaseModel]: The dynamically created Pydantic model.
|
35
|
+
"""
|
36
|
+
field_definitions = {}
|
37
|
+
for param in parameters:
|
38
|
+
field_type = str
|
39
|
+
if param.type_ == "number":
|
40
|
+
field_type = float
|
41
|
+
elif param.type_ == "integer":
|
42
|
+
field_type = int
|
43
|
+
elif param.type_ == "boolean":
|
44
|
+
field_type = bool
|
45
|
+
|
46
|
+
field_definitions[param.name] = (
|
47
|
+
field_type,
|
48
|
+
pydantic.Field(description=param.description or "")
|
49
|
+
)
|
50
|
+
return pydantic.create_model(
|
51
|
+
f"{name}Schema",
|
52
|
+
**field_definitions
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
class RemoteTool(BaseTool):
|
57
|
+
"""
|
58
|
+
Tool for interacting with remote functions.
|
59
|
+
|
60
|
+
Attributes:
|
61
|
+
client (RunClient): The client used to execute remote function calls.
|
62
|
+
resource_name (str): The name of the remote resource.
|
63
|
+
kit (bool): Indicates whether the tool is part of a function kit.
|
64
|
+
handle_tool_error (bool | str | Callable[[ToolException], str] | None): Error handling strategy.
|
65
|
+
"""
|
66
|
+
|
67
|
+
client: RunClient
|
68
|
+
resource_name: str
|
69
|
+
kit: bool = False
|
70
|
+
handle_tool_error: bool | str | Callable[[ToolException], str] | None = True
|
71
|
+
service_name: str | None = None
|
72
|
+
cloud: bool = False
|
73
|
+
@t.override
|
74
|
+
def _run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
75
|
+
warnings.warn(
|
76
|
+
"Invoke this tool asynchronousely using `ainvoke`. This method exists only to satisfy standard tests.",
|
77
|
+
stacklevel=1,
|
78
|
+
)
|
79
|
+
return asyncio.run(self._arun(*args, **kwargs))
|
80
|
+
|
81
|
+
@t.override
|
82
|
+
async def _arun(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
83
|
+
body = {**kwargs}
|
84
|
+
if self.kit:
|
85
|
+
body["name"] = self.name
|
86
|
+
result = self.client.run(
|
87
|
+
"function",
|
88
|
+
self.resource_name,
|
89
|
+
"POST",
|
90
|
+
cloud=self.cloud,
|
91
|
+
service_name=self.service_name,
|
92
|
+
json=body,
|
93
|
+
)
|
94
|
+
return result.text
|
95
|
+
|
96
|
+
@t.override
|
97
|
+
@property
|
98
|
+
def tool_call_schema(self) -> type[pydantic.BaseModel]:
|
99
|
+
assert self.args_schema is not None # noqa: S101
|
100
|
+
return self.args_schema
|
101
|
+
|
102
|
+
@dataclass
|
103
|
+
class RemoteToolkit:
|
104
|
+
"""
|
105
|
+
Toolkit for managing remote function tools.
|
106
|
+
|
107
|
+
Attributes:
|
108
|
+
client (AuthenticatedClient): The authenticated client instance.
|
109
|
+
function (str): The name of the remote function to integrate.
|
110
|
+
_function (Function | None): Cached Function object after initialization.
|
111
|
+
"""
|
112
|
+
client: AuthenticatedClient
|
113
|
+
function: str
|
114
|
+
_function: Function | None = None
|
115
|
+
_service_name: str | None = None
|
116
|
+
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
|
117
|
+
|
118
|
+
async def initialize(self) -> None:
|
119
|
+
"""Initialize the session and retrieve the remote function details."""
|
120
|
+
if self._function is None:
|
121
|
+
try:
|
122
|
+
response = get_function.sync_detailed(self.function, client=self.client)
|
123
|
+
function_name = self.function.upper().replace("-", "_")
|
124
|
+
if os.getenv(f"BL_FUNCTION_{function_name}_SERVICE_NAME"):
|
125
|
+
self._service_name = os.getenv(f"BL_FUNCTION_{function_name}_SERVICE_NAME")
|
126
|
+
self._function = response.parsed
|
127
|
+
except UnexpectedStatus as e:
|
128
|
+
functions = list_functions.sync_detailed(
|
129
|
+
client=self.client,
|
130
|
+
).parsed
|
131
|
+
names = [
|
132
|
+
f.metadata.name
|
133
|
+
for f in functions
|
134
|
+
]
|
135
|
+
raise RuntimeError(
|
136
|
+
f"error: {e.status_code}. Available functions: {', '.join(names)}"
|
137
|
+
)
|
138
|
+
|
139
|
+
async def get_tools(self) -> list[BaseTool]:
|
140
|
+
settings = get_settings()
|
141
|
+
if self._function is None:
|
142
|
+
raise RuntimeError("Must initialize the toolkit first")
|
143
|
+
|
144
|
+
if self._function.spec.integration_connections:
|
145
|
+
fallback_url = None
|
146
|
+
url = f"{settings.run_url}/{settings.workspace}/functions/{self._function.metadata.name}"
|
147
|
+
if self._service_name:
|
148
|
+
fallback_url = f"https://{self._service_name}.{settings.run_internal_hostname}"
|
149
|
+
url = f"https://{self._service_name}.{settings.run_internal_hostname}"
|
150
|
+
mcp_client = MCPClient(self.client, url, fallback_url)
|
151
|
+
mcp_toolkit = MCPToolkit(client=mcp_client, url=url)
|
152
|
+
await mcp_toolkit.initialize()
|
153
|
+
return await mcp_toolkit.get_tools()
|
154
|
+
|
155
|
+
if self._function.spec.kit:
|
156
|
+
return [
|
157
|
+
RemoteTool(
|
158
|
+
client=RunClient(self.client),
|
159
|
+
name=func.name,
|
160
|
+
resource_name=self._function.metadata.name,
|
161
|
+
kit=True,
|
162
|
+
description=func.description or "",
|
163
|
+
args_schema=create_dynamic_schema(func.name, func.parameters),
|
164
|
+
cloud=settings.cloud,
|
165
|
+
service_name=self._service_name,
|
166
|
+
)
|
167
|
+
for func in self._function.spec.kit
|
168
|
+
]
|
169
|
+
|
170
|
+
return [
|
171
|
+
RemoteTool(
|
172
|
+
client=RunClient(self.client),
|
173
|
+
name=self._function.metadata.name,
|
174
|
+
resource_name=self._function.metadata.name,
|
175
|
+
description=self._function.spec.description or "",
|
176
|
+
args_schema=create_dynamic_schema(
|
177
|
+
self._function.metadata.name,
|
178
|
+
self._function.spec.parameters
|
179
|
+
),
|
180
|
+
cloud=settings.cloud,
|
181
|
+
service_name=self._service_name,
|
182
|
+
)
|
183
|
+
]
|
@@ -0,0 +1,233 @@
|
|
1
|
+
"""Contains all the data models used in inputs/outputs"""
|
2
|
+
|
3
|
+
from .acl import ACL
|
4
|
+
from .agent import Agent
|
5
|
+
from .agent_chain import AgentChain
|
6
|
+
from .agent_spec import AgentSpec
|
7
|
+
from .api_key import ApiKey
|
8
|
+
from .configuration import Configuration
|
9
|
+
from .continent import Continent
|
10
|
+
from .core_event import CoreEvent
|
11
|
+
from .core_spec import CoreSpec
|
12
|
+
from .core_spec_configurations import CoreSpecConfigurations
|
13
|
+
from .country import Country
|
14
|
+
from .create_api_key_for_service_account_body import CreateApiKeyForServiceAccountBody
|
15
|
+
from .create_workspace_service_account_body import CreateWorkspaceServiceAccountBody
|
16
|
+
from .create_workspace_service_account_response_200 import CreateWorkspaceServiceAccountResponse200
|
17
|
+
from .delete_workspace_service_account_response_200 import DeleteWorkspaceServiceAccountResponse200
|
18
|
+
from .entrypoint import Entrypoint
|
19
|
+
from .entrypoint_env import EntrypointEnv
|
20
|
+
from .flavor import Flavor
|
21
|
+
from .form import Form
|
22
|
+
from .form_config import FormConfig
|
23
|
+
from .form_oauthomitempty import FormOauthomitempty
|
24
|
+
from .form_secrets import FormSecrets
|
25
|
+
from .function import Function
|
26
|
+
from .function_kit import FunctionKit
|
27
|
+
from .function_spec import FunctionSpec
|
28
|
+
from .get_trace_ids_response_200 import GetTraceIdsResponse200
|
29
|
+
from .get_trace_logs_response_200 import GetTraceLogsResponse200
|
30
|
+
from .get_trace_response_200 import GetTraceResponse200
|
31
|
+
from .get_workspace_service_accounts_response_200_item import (
|
32
|
+
GetWorkspaceServiceAccountsResponse200Item,
|
33
|
+
)
|
34
|
+
from .histogram_bucket import HistogramBucket
|
35
|
+
from .histogram_stats import HistogramStats
|
36
|
+
from .integration_connection import IntegrationConnection
|
37
|
+
from .integration_connection_spec import IntegrationConnectionSpec
|
38
|
+
from .integration_connection_spec_config import IntegrationConnectionSpecConfig
|
39
|
+
from .integration_connection_spec_secret import IntegrationConnectionSpecSecret
|
40
|
+
from .integration_model import IntegrationModel
|
41
|
+
from .integration_repository import IntegrationRepository
|
42
|
+
from .invite_workspace_user_body import InviteWorkspaceUserBody
|
43
|
+
from .knowledgebase import Knowledgebase
|
44
|
+
from .knowledgebase_spec import KnowledgebaseSpec
|
45
|
+
from .knowledgebase_spec_options import KnowledgebaseSpecOptions
|
46
|
+
from .last_n_requests_metric import LastNRequestsMetric
|
47
|
+
from .latency_metric import LatencyMetric
|
48
|
+
from .location_response import LocationResponse
|
49
|
+
from .mcp_definition import MCPDefinition
|
50
|
+
from .mcp_definition_entrypoint import MCPDefinitionEntrypoint
|
51
|
+
from .mcp_definition_form import MCPDefinitionForm
|
52
|
+
from .metadata import Metadata
|
53
|
+
from .metadata_labels import MetadataLabels
|
54
|
+
from .metric import Metric
|
55
|
+
from .metrics import Metrics
|
56
|
+
from .metrics_models import MetricsModels
|
57
|
+
from .metrics_request_total_per_code import MetricsRequestTotalPerCode
|
58
|
+
from .metrics_rps_per_code import MetricsRpsPerCode
|
59
|
+
from .model import Model
|
60
|
+
from .model_private_cluster import ModelPrivateCluster
|
61
|
+
from .model_spec import ModelSpec
|
62
|
+
from .o_auth import OAuth
|
63
|
+
from .owner_fields import OwnerFields
|
64
|
+
from .pending_invitation import PendingInvitation
|
65
|
+
from .pending_invitation_accept import PendingInvitationAccept
|
66
|
+
from .pending_invitation_render import PendingInvitationRender
|
67
|
+
from .pending_invitation_render_invited_by import PendingInvitationRenderInvitedBy
|
68
|
+
from .pending_invitation_render_workspace import PendingInvitationRenderWorkspace
|
69
|
+
from .pending_invitation_workspace_details import PendingInvitationWorkspaceDetails
|
70
|
+
from .pod_template_spec import PodTemplateSpec
|
71
|
+
from .policy import Policy
|
72
|
+
from .policy_location import PolicyLocation
|
73
|
+
from .policy_max_tokens import PolicyMaxTokens
|
74
|
+
from .policy_spec import PolicySpec
|
75
|
+
from .private_cluster import PrivateCluster
|
76
|
+
from .private_location import PrivateLocation
|
77
|
+
from .repository import Repository
|
78
|
+
from .request_duration_over_time_metric import RequestDurationOverTimeMetric
|
79
|
+
from .request_duration_over_time_metrics import RequestDurationOverTimeMetrics
|
80
|
+
from .request_total_by_origin_metric import RequestTotalByOriginMetric
|
81
|
+
from .request_total_by_origin_metric_request_total_by_origin import (
|
82
|
+
RequestTotalByOriginMetricRequestTotalByOrigin,
|
83
|
+
)
|
84
|
+
from .request_total_by_origin_metric_request_total_by_origin_and_code import (
|
85
|
+
RequestTotalByOriginMetricRequestTotalByOriginAndCode,
|
86
|
+
)
|
87
|
+
from .request_total_metric import RequestTotalMetric
|
88
|
+
from .request_total_metric_request_total_per_code import RequestTotalMetricRequestTotalPerCode
|
89
|
+
from .request_total_metric_rps_per_code import RequestTotalMetricRpsPerCode
|
90
|
+
from .resource_log import ResourceLog
|
91
|
+
from .resource_metrics import ResourceMetrics
|
92
|
+
from .resource_metrics_request_total_per_code import ResourceMetricsRequestTotalPerCode
|
93
|
+
from .resource_metrics_rps_per_code import ResourceMetricsRpsPerCode
|
94
|
+
from .revision_configuration import RevisionConfiguration
|
95
|
+
from .revision_metadata import RevisionMetadata
|
96
|
+
from .runtime import Runtime
|
97
|
+
from .runtime_startup_probe import RuntimeStartupProbe
|
98
|
+
from .serverless_config import ServerlessConfig
|
99
|
+
from .spec_configuration import SpecConfiguration
|
100
|
+
from .store_agent import StoreAgent
|
101
|
+
from .store_agent_labels import StoreAgentLabels
|
102
|
+
from .store_configuration import StoreConfiguration
|
103
|
+
from .store_configuration_option import StoreConfigurationOption
|
104
|
+
from .store_function import StoreFunction
|
105
|
+
from .store_function_kit import StoreFunctionKit
|
106
|
+
from .store_function_labels import StoreFunctionLabels
|
107
|
+
from .store_function_parameter import StoreFunctionParameter
|
108
|
+
from .time_fields import TimeFields
|
109
|
+
from .token_rate_metric import TokenRateMetric
|
110
|
+
from .token_rate_metrics import TokenRateMetrics
|
111
|
+
from .token_total_metric import TokenTotalMetric
|
112
|
+
from .trace_ids_response import TraceIdsResponse
|
113
|
+
from .update_workspace_service_account_body import UpdateWorkspaceServiceAccountBody
|
114
|
+
from .update_workspace_service_account_response_200 import UpdateWorkspaceServiceAccountResponse200
|
115
|
+
from .update_workspace_user_role_body import UpdateWorkspaceUserRoleBody
|
116
|
+
from .websocket_channel import WebsocketChannel
|
117
|
+
from .workspace import Workspace
|
118
|
+
from .workspace_labels import WorkspaceLabels
|
119
|
+
from .workspace_user import WorkspaceUser
|
120
|
+
|
121
|
+
__all__ = (
|
122
|
+
"ACL",
|
123
|
+
"Agent",
|
124
|
+
"AgentChain",
|
125
|
+
"AgentSpec",
|
126
|
+
"ApiKey",
|
127
|
+
"Configuration",
|
128
|
+
"Continent",
|
129
|
+
"CoreEvent",
|
130
|
+
"CoreSpec",
|
131
|
+
"CoreSpecConfigurations",
|
132
|
+
"Country",
|
133
|
+
"CreateApiKeyForServiceAccountBody",
|
134
|
+
"CreateWorkspaceServiceAccountBody",
|
135
|
+
"CreateWorkspaceServiceAccountResponse200",
|
136
|
+
"DeleteWorkspaceServiceAccountResponse200",
|
137
|
+
"Entrypoint",
|
138
|
+
"EntrypointEnv",
|
139
|
+
"Flavor",
|
140
|
+
"Form",
|
141
|
+
"FormConfig",
|
142
|
+
"FormOauthomitempty",
|
143
|
+
"FormSecrets",
|
144
|
+
"Function",
|
145
|
+
"FunctionKit",
|
146
|
+
"FunctionSpec",
|
147
|
+
"GetTraceIdsResponse200",
|
148
|
+
"GetTraceLogsResponse200",
|
149
|
+
"GetTraceResponse200",
|
150
|
+
"GetWorkspaceServiceAccountsResponse200Item",
|
151
|
+
"HistogramBucket",
|
152
|
+
"HistogramStats",
|
153
|
+
"IntegrationConnection",
|
154
|
+
"IntegrationConnectionSpec",
|
155
|
+
"IntegrationConnectionSpecConfig",
|
156
|
+
"IntegrationConnectionSpecSecret",
|
157
|
+
"IntegrationModel",
|
158
|
+
"IntegrationRepository",
|
159
|
+
"InviteWorkspaceUserBody",
|
160
|
+
"Knowledgebase",
|
161
|
+
"KnowledgebaseSpec",
|
162
|
+
"KnowledgebaseSpecOptions",
|
163
|
+
"LastNRequestsMetric",
|
164
|
+
"LatencyMetric",
|
165
|
+
"LocationResponse",
|
166
|
+
"MCPDefinition",
|
167
|
+
"MCPDefinitionEntrypoint",
|
168
|
+
"MCPDefinitionForm",
|
169
|
+
"Metadata",
|
170
|
+
"MetadataLabels",
|
171
|
+
"Metric",
|
172
|
+
"Metrics",
|
173
|
+
"MetricsModels",
|
174
|
+
"MetricsRequestTotalPerCode",
|
175
|
+
"MetricsRpsPerCode",
|
176
|
+
"Model",
|
177
|
+
"ModelPrivateCluster",
|
178
|
+
"ModelSpec",
|
179
|
+
"OAuth",
|
180
|
+
"OwnerFields",
|
181
|
+
"PendingInvitation",
|
182
|
+
"PendingInvitationAccept",
|
183
|
+
"PendingInvitationRender",
|
184
|
+
"PendingInvitationRenderInvitedBy",
|
185
|
+
"PendingInvitationRenderWorkspace",
|
186
|
+
"PendingInvitationWorkspaceDetails",
|
187
|
+
"PodTemplateSpec",
|
188
|
+
"Policy",
|
189
|
+
"PolicyLocation",
|
190
|
+
"PolicyMaxTokens",
|
191
|
+
"PolicySpec",
|
192
|
+
"PrivateCluster",
|
193
|
+
"PrivateLocation",
|
194
|
+
"Repository",
|
195
|
+
"RequestDurationOverTimeMetric",
|
196
|
+
"RequestDurationOverTimeMetrics",
|
197
|
+
"RequestTotalByOriginMetric",
|
198
|
+
"RequestTotalByOriginMetricRequestTotalByOrigin",
|
199
|
+
"RequestTotalByOriginMetricRequestTotalByOriginAndCode",
|
200
|
+
"RequestTotalMetric",
|
201
|
+
"RequestTotalMetricRequestTotalPerCode",
|
202
|
+
"RequestTotalMetricRpsPerCode",
|
203
|
+
"ResourceLog",
|
204
|
+
"ResourceMetrics",
|
205
|
+
"ResourceMetricsRequestTotalPerCode",
|
206
|
+
"ResourceMetricsRpsPerCode",
|
207
|
+
"RevisionConfiguration",
|
208
|
+
"RevisionMetadata",
|
209
|
+
"Runtime",
|
210
|
+
"RuntimeStartupProbe",
|
211
|
+
"ServerlessConfig",
|
212
|
+
"SpecConfiguration",
|
213
|
+
"StoreAgent",
|
214
|
+
"StoreAgentLabels",
|
215
|
+
"StoreConfiguration",
|
216
|
+
"StoreConfigurationOption",
|
217
|
+
"StoreFunction",
|
218
|
+
"StoreFunctionKit",
|
219
|
+
"StoreFunctionLabels",
|
220
|
+
"StoreFunctionParameter",
|
221
|
+
"TimeFields",
|
222
|
+
"TokenRateMetric",
|
223
|
+
"TokenRateMetrics",
|
224
|
+
"TokenTotalMetric",
|
225
|
+
"TraceIdsResponse",
|
226
|
+
"UpdateWorkspaceServiceAccountBody",
|
227
|
+
"UpdateWorkspaceServiceAccountResponse200",
|
228
|
+
"UpdateWorkspaceUserRoleBody",
|
229
|
+
"WebsocketChannel",
|
230
|
+
"Workspace",
|
231
|
+
"WorkspaceLabels",
|
232
|
+
"WorkspaceUser",
|
233
|
+
)
|