beamlit 0.0.55rc103__py3-none-any.whl → 0.0.56rc105__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.
- beamlit/agents/chain.py +14 -0
- beamlit/agents/decorator.py +6 -3
- beamlit/api/default/list_mcp_hub_definitions.py +127 -0
- beamlit/api/generation/__init__.py +0 -0
- beamlit/api/generation/run_information_generation_agent.py +168 -0
- beamlit/api/knowledgebases/delete_knowledgebase.py +18 -18
- beamlit/api/knowledgebases/get_knowledgebase.py +18 -18
- beamlit/api/knowledgebases/update_knowledgebase.py +14 -14
- beamlit/common/settings.py +2 -0
- beamlit/deploy/deploy.py +5 -2
- beamlit/functions/common.py +16 -0
- beamlit/functions/local/local.py +49 -0
- beamlit/functions/mcp/mcp.py +64 -74
- beamlit/functions/mcp/utils.py +56 -0
- beamlit/functions/remote/remote.py +16 -2
- beamlit/models/__init__.py +30 -0
- beamlit/models/agent_information_request.py +63 -0
- beamlit/models/agent_information_response.py +88 -0
- beamlit/models/agent_spec.py +9 -0
- beamlit/models/entrypoint.py +96 -0
- beamlit/models/entrypoint_env.py +45 -0
- beamlit/models/form.py +120 -0
- beamlit/models/form_config.py +45 -0
- beamlit/models/form_oauthomitempty.py +45 -0
- beamlit/models/form_secrets.py +45 -0
- beamlit/models/mcp_definition.py +188 -0
- beamlit/models/mcp_definition_entrypoint.py +45 -0
- beamlit/models/mcp_definition_form.py +45 -0
- beamlit/models/mcp_hub_artifact.py +188 -0
- beamlit/models/mcp_hub_artifact_entrypoint.py +45 -0
- beamlit/models/mcp_hub_artifact_form.py +45 -0
- beamlit/models/model_spec.py +0 -9
- beamlit/models/o_auth.py +72 -0
- beamlit/models/workspace.py +9 -9
- beamlit/run.py +25 -9
- {beamlit-0.0.55rc103.dist-info → beamlit-0.0.56rc105.dist-info}/METADATA +1 -1
- {beamlit-0.0.55rc103.dist-info → beamlit-0.0.56rc105.dist-info}/RECORD +40 -21
- beamlit/api/workspaces/workspace_quotas_request.py +0 -97
- {beamlit-0.0.55rc103.dist-info → beamlit-0.0.56rc105.dist-info}/WHEEL +0 -0
- {beamlit-0.0.55rc103.dist-info → beamlit-0.0.56rc105.dist-info}/entry_points.txt +0 -0
- {beamlit-0.0.55rc103.dist-info → beamlit-0.0.56rc105.dist-info}/licenses/LICENSE +0 -0
@@ -10,7 +10,7 @@ from ...types import Response
|
|
10
10
|
|
11
11
|
|
12
12
|
def _get_kwargs(
|
13
|
-
|
13
|
+
knowledgebase_name: str,
|
14
14
|
*,
|
15
15
|
body: Knowledgebase,
|
16
16
|
) -> dict[str, Any]:
|
@@ -18,7 +18,7 @@ def _get_kwargs(
|
|
18
18
|
|
19
19
|
_kwargs: dict[str, Any] = {
|
20
20
|
"method": "put",
|
21
|
-
"url": f"/knowledgebases/{
|
21
|
+
"url": f"/knowledgebases/{knowledgebase_name}",
|
22
22
|
}
|
23
23
|
|
24
24
|
_body = body.to_dict()
|
@@ -51,7 +51,7 @@ def _build_response(*, client: Union[AuthenticatedClient, Client], response: htt
|
|
51
51
|
|
52
52
|
|
53
53
|
def sync_detailed(
|
54
|
-
|
54
|
+
knowledgebase_name: str,
|
55
55
|
*,
|
56
56
|
client: AuthenticatedClient,
|
57
57
|
body: Knowledgebase,
|
@@ -61,7 +61,7 @@ def sync_detailed(
|
|
61
61
|
Updates an knowledgebase.
|
62
62
|
|
63
63
|
Args:
|
64
|
-
|
64
|
+
knowledgebase_name (str):
|
65
65
|
body (Knowledgebase): Knowledgebase
|
66
66
|
|
67
67
|
Raises:
|
@@ -73,7 +73,7 @@ def sync_detailed(
|
|
73
73
|
"""
|
74
74
|
|
75
75
|
kwargs = _get_kwargs(
|
76
|
-
|
76
|
+
knowledgebase_name=knowledgebase_name,
|
77
77
|
body=body,
|
78
78
|
)
|
79
79
|
|
@@ -85,7 +85,7 @@ def sync_detailed(
|
|
85
85
|
|
86
86
|
|
87
87
|
def sync(
|
88
|
-
|
88
|
+
knowledgebase_name: str,
|
89
89
|
*,
|
90
90
|
client: AuthenticatedClient,
|
91
91
|
body: Knowledgebase,
|
@@ -95,7 +95,7 @@ def sync(
|
|
95
95
|
Updates an knowledgebase.
|
96
96
|
|
97
97
|
Args:
|
98
|
-
|
98
|
+
knowledgebase_name (str):
|
99
99
|
body (Knowledgebase): Knowledgebase
|
100
100
|
|
101
101
|
Raises:
|
@@ -107,14 +107,14 @@ def sync(
|
|
107
107
|
"""
|
108
108
|
|
109
109
|
return sync_detailed(
|
110
|
-
|
110
|
+
knowledgebase_name=knowledgebase_name,
|
111
111
|
client=client,
|
112
112
|
body=body,
|
113
113
|
).parsed
|
114
114
|
|
115
115
|
|
116
116
|
async def asyncio_detailed(
|
117
|
-
|
117
|
+
knowledgebase_name: str,
|
118
118
|
*,
|
119
119
|
client: AuthenticatedClient,
|
120
120
|
body: Knowledgebase,
|
@@ -124,7 +124,7 @@ async def asyncio_detailed(
|
|
124
124
|
Updates an knowledgebase.
|
125
125
|
|
126
126
|
Args:
|
127
|
-
|
127
|
+
knowledgebase_name (str):
|
128
128
|
body (Knowledgebase): Knowledgebase
|
129
129
|
|
130
130
|
Raises:
|
@@ -136,7 +136,7 @@ async def asyncio_detailed(
|
|
136
136
|
"""
|
137
137
|
|
138
138
|
kwargs = _get_kwargs(
|
139
|
-
|
139
|
+
knowledgebase_name=knowledgebase_name,
|
140
140
|
body=body,
|
141
141
|
)
|
142
142
|
|
@@ -146,7 +146,7 @@ async def asyncio_detailed(
|
|
146
146
|
|
147
147
|
|
148
148
|
async def asyncio(
|
149
|
-
|
149
|
+
knowledgebase_name: str,
|
150
150
|
*,
|
151
151
|
client: AuthenticatedClient,
|
152
152
|
body: Knowledgebase,
|
@@ -156,7 +156,7 @@ async def asyncio(
|
|
156
156
|
Updates an knowledgebase.
|
157
157
|
|
158
158
|
Args:
|
159
|
-
|
159
|
+
knowledgebase_name (str):
|
160
160
|
body (Knowledgebase): Knowledgebase
|
161
161
|
|
162
162
|
Raises:
|
@@ -169,7 +169,7 @@ async def asyncio(
|
|
169
169
|
|
170
170
|
return (
|
171
171
|
await asyncio_detailed(
|
172
|
-
|
172
|
+
knowledgebase_name=knowledgebase_name,
|
173
173
|
client=client,
|
174
174
|
body=body,
|
175
175
|
)
|
beamlit/common/settings.py
CHANGED
@@ -83,9 +83,11 @@ class Settings(BaseSettings):
|
|
83
83
|
base_url: str = Field(default="https://api.beamlit.com/v0")
|
84
84
|
app_url: str = Field(default="https://app.beamlit.com")
|
85
85
|
run_url: str = Field(default="https://run.beamlit.com")
|
86
|
+
run_internal_hostname: str = Field(default="internal.run.beamlit.com")
|
86
87
|
registry_url: str = Field(default="https://us.registry.beamlit.com")
|
87
88
|
log_level: str = Field(default="INFO")
|
88
89
|
enable_opentelemetry: bool = Field(default=False)
|
90
|
+
cloud: bool = Field(default=False)
|
89
91
|
agent: SettingsAgent = SettingsAgent()
|
90
92
|
server: SettingsServer = SettingsServer()
|
91
93
|
authentication: SettingsAuthentication = SettingsAuthentication()
|
beamlit/deploy/deploy.py
CHANGED
@@ -59,6 +59,7 @@ def set_default_values(resource: Resource, deployment: Agent | Function):
|
|
59
59
|
return deployment
|
60
60
|
|
61
61
|
def get_beamlit_deployment_from_resource(
|
62
|
+
settings: Settings,
|
62
63
|
resource: Resource,
|
63
64
|
) -> Agent | Function:
|
64
65
|
"""
|
@@ -75,6 +76,7 @@ def get_beamlit_deployment_from_resource(
|
|
75
76
|
if isinstance(arg.value, ast.Dict):
|
76
77
|
value = arg_to_dict(arg.value)
|
77
78
|
metadata = EnvironmentMetadata(**value.get("metadata", {}))
|
79
|
+
metadata.environment = settings.environment
|
78
80
|
spec = AgentSpec(**value.get("spec", {}))
|
79
81
|
agent = Agent(metadata=metadata, spec=spec)
|
80
82
|
if not agent.spec.prompt:
|
@@ -84,6 +86,7 @@ def get_beamlit_deployment_from_resource(
|
|
84
86
|
if isinstance(arg.value, ast.Dict):
|
85
87
|
value = arg_to_dict(arg.value)
|
86
88
|
metadata = EnvironmentMetadata(**value.get("metadata", {}))
|
89
|
+
metadata.environment = settings.environment
|
87
90
|
spec = FunctionSpec(**value.get("spec", {}))
|
88
91
|
func = Function(metadata=metadata, spec=spec)
|
89
92
|
if not func.spec.parameters:
|
@@ -265,13 +268,13 @@ def generate_beamlit_deployment(directory: str, name: str):
|
|
265
268
|
functions: list[tuple[Resource, Function]] = []
|
266
269
|
agents: list[tuple[Resource, Agent]] = []
|
267
270
|
for resource in get_resources("agent", settings.server.directory):
|
268
|
-
agent = get_beamlit_deployment_from_resource(resource)
|
271
|
+
agent = get_beamlit_deployment_from_resource(settings, resource)
|
269
272
|
if name and agent.metadata.name != name:
|
270
273
|
agent.metadata.name = slugify(name)
|
271
274
|
if agent:
|
272
275
|
agents.append((resource, agent))
|
273
276
|
for resource in get_resources("function", settings.server.directory):
|
274
|
-
function = get_beamlit_deployment_from_resource(resource)
|
277
|
+
function = get_beamlit_deployment_from_resource(settings, resource)
|
275
278
|
if function:
|
276
279
|
functions.append((resource, function))
|
277
280
|
|
beamlit/functions/common.py
CHANGED
@@ -28,6 +28,7 @@ from beamlit.authentication import new_client
|
|
28
28
|
from beamlit.client import AuthenticatedClient
|
29
29
|
from beamlit.common import slugify
|
30
30
|
from beamlit.common.settings import get_settings
|
31
|
+
from beamlit.functions.local.local import LocalToolKit
|
31
32
|
from beamlit.functions.remote.remote import RemoteToolkit
|
32
33
|
from beamlit.models import AgentChain
|
33
34
|
|
@@ -35,10 +36,12 @@ logger = getLogger(__name__)
|
|
35
36
|
|
36
37
|
def get_functions(
|
37
38
|
remote_functions: Union[list[str], None] = None,
|
39
|
+
local_functions: Union[list[dict], None] = None,
|
38
40
|
client: Union[AuthenticatedClient, None] = None,
|
39
41
|
dir: Union[str, None] = None,
|
40
42
|
chain: Union[list[AgentChain], None] = None,
|
41
43
|
remote_functions_empty: bool = True,
|
44
|
+
local_functions_empty: bool = True,
|
42
45
|
from_decorator: str = "function",
|
43
46
|
warning: bool = True,
|
44
47
|
):
|
@@ -140,6 +143,7 @@ def get_functions(
|
|
140
143
|
client=client,
|
141
144
|
dir=os.path.join(root),
|
142
145
|
remote_functions_empty=remote_functions_empty,
|
146
|
+
local_functions_empty=local_functions_empty,
|
143
147
|
from_decorator="kit",
|
144
148
|
)
|
145
149
|
functions.extend(kit_functions)
|
@@ -187,6 +191,18 @@ def get_functions(
|
|
187
191
|
f"Traceback:\n{traceback.format_exc()}"
|
188
192
|
)
|
189
193
|
logger.warn(f"Failed to initialize remote function {function}: {e!s}")
|
194
|
+
if local_functions:
|
195
|
+
for function in local_functions:
|
196
|
+
try:
|
197
|
+
toolkit = LocalToolKit(client, function)
|
198
|
+
toolkit.initialize()
|
199
|
+
functions.extend(toolkit.get_tools())
|
200
|
+
except Exception as e:
|
201
|
+
logger.debug(
|
202
|
+
f"Failed to initialize local function {function}: {e!s}\n"
|
203
|
+
f"Traceback:\n{traceback.format_exc()}"
|
204
|
+
)
|
205
|
+
logger.warn(f"Failed to initialize local function {function}: {e!s}")
|
190
206
|
|
191
207
|
if chain:
|
192
208
|
toolkit = ChainToolkit(client, chain)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
import pydantic
|
4
|
+
from langchain_core.tools.base import BaseTool
|
5
|
+
|
6
|
+
from beamlit.authentication.authentication import AuthenticatedClient
|
7
|
+
from beamlit.functions.mcp.mcp import MCPClient, MCPToolkit
|
8
|
+
from beamlit.models import Function
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class LocalToolKit:
|
13
|
+
"""
|
14
|
+
Toolkit for managing local tools.
|
15
|
+
|
16
|
+
Attributes:
|
17
|
+
client (AuthenticatedClient): The authenticated client instance.
|
18
|
+
function (str): The name of the local function to integrate.
|
19
|
+
_function (Function | None): Cached Function object after initialization.
|
20
|
+
"""
|
21
|
+
client: AuthenticatedClient
|
22
|
+
local_function: dict
|
23
|
+
_function: Function | None = None
|
24
|
+
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
|
25
|
+
|
26
|
+
def initialize(self) -> None:
|
27
|
+
"""Initialize the session and retrieve the local function details."""
|
28
|
+
if self._function is None:
|
29
|
+
try:
|
30
|
+
# For local functions, we directly create the Function object
|
31
|
+
# based on the local function name
|
32
|
+
self._function = Function(
|
33
|
+
metadata={"name": self.local_function['name']},
|
34
|
+
spec={
|
35
|
+
"configurations": {
|
36
|
+
"url": self.local_function['url'],
|
37
|
+
"sse": self.local_function['sse'],
|
38
|
+
},
|
39
|
+
"description": self.local_function['description'] or "",
|
40
|
+
}
|
41
|
+
)
|
42
|
+
except Exception as e:
|
43
|
+
raise RuntimeError(f"Failed to initialize local function: {e}")
|
44
|
+
|
45
|
+
def get_tools(self) -> list[BaseTool]:
|
46
|
+
mcp_client = MCPClient(self.client, self._function.spec["configurations"]["url"], sse=self._function.spec["configurations"]["sse"])
|
47
|
+
mcp_toolkit = MCPToolkit(client=mcp_client)
|
48
|
+
mcp_toolkit.initialize()
|
49
|
+
return mcp_toolkit.get_tools()
|
beamlit/functions/mcp/mcp.py
CHANGED
@@ -4,91 +4,87 @@ It includes classes for managing MCP clients, creating dynamic schemas, and inte
|
|
4
4
|
"""
|
5
5
|
|
6
6
|
import asyncio
|
7
|
+
import logging
|
7
8
|
import warnings
|
8
|
-
from typing import Any, Callable
|
9
|
+
from typing import Any, AsyncIterator, Callable
|
9
10
|
|
10
11
|
import pydantic
|
11
12
|
import pydantic_core
|
12
13
|
import requests
|
13
14
|
import typing_extensions as t
|
14
15
|
from langchain_core.tools.base import BaseTool, BaseToolkit, ToolException
|
16
|
+
from mcp import ClientSession
|
17
|
+
from mcp.client.sse import sse_client
|
15
18
|
from mcp.types import CallToolResult, ListToolsResult
|
16
|
-
from pydantic.json_schema import JsonSchemaValue
|
17
|
-
from pydantic_core import core_schema as cs
|
18
19
|
|
19
20
|
from beamlit.authentication.authentication import AuthenticatedClient
|
20
21
|
from beamlit.common.settings import get_settings
|
21
22
|
|
22
|
-
|
23
|
+
from .utils import create_schema_model
|
23
24
|
|
24
|
-
|
25
|
-
"integer": int,
|
26
|
-
"number": float,
|
27
|
-
"array": list,
|
28
|
-
"object": dict,
|
29
|
-
"boolean": bool,
|
30
|
-
"string": str,
|
31
|
-
"null": type(None),
|
32
|
-
}
|
33
|
-
|
34
|
-
FIELD_DEFAULTS = {
|
35
|
-
int: 0,
|
36
|
-
float: 0.0,
|
37
|
-
list: [],
|
38
|
-
bool: False,
|
39
|
-
str: "",
|
40
|
-
type(None): None,
|
41
|
-
}
|
42
|
-
|
43
|
-
def configure_field(name: str, type_: dict[str, t.Any], required: list[str]) -> tuple[type, t.Any]:
|
44
|
-
field_type = TYPE_MAP[type_["type"]]
|
45
|
-
default_ = FIELD_DEFAULTS.get(field_type) if name not in required else ...
|
46
|
-
return field_type, default_
|
47
|
-
|
48
|
-
def create_schema_model(name: str, schema: dict[str, t.Any]) -> type[pydantic.BaseModel]:
|
49
|
-
# Create a new model class that returns our JSON schema.
|
50
|
-
# LangChain requires a BaseModel class.
|
51
|
-
class SchemaBase(pydantic.BaseModel):
|
52
|
-
model_config = pydantic.ConfigDict(extra="allow")
|
53
|
-
|
54
|
-
@t.override
|
55
|
-
@classmethod
|
56
|
-
def __get_pydantic_json_schema__(
|
57
|
-
cls, core_schema: cs.CoreSchema, handler: pydantic.GetJsonSchemaHandler
|
58
|
-
) -> JsonSchemaValue:
|
59
|
-
return schema
|
60
|
-
|
61
|
-
# Since this langchain patch, we need to synthesize pydantic fields from the schema
|
62
|
-
# https://github.com/langchain-ai/langchain/commit/033ac417609297369eb0525794d8b48a425b8b33
|
63
|
-
required = schema.get("required", [])
|
64
|
-
fields: dict[str, t.Any] = {
|
65
|
-
name: configure_field(name, type_, required) for name, type_ in schema["properties"].items()
|
66
|
-
}
|
67
|
-
|
68
|
-
return pydantic.create_model(f"{name}Schema", __base__=SchemaBase, **fields)
|
25
|
+
settings = get_settings()
|
69
26
|
|
27
|
+
logger = logging.getLogger(__name__)
|
70
28
|
|
71
29
|
|
72
30
|
class MCPClient:
|
73
31
|
def __init__(self, client: AuthenticatedClient, url: str):
|
74
32
|
self.client = client
|
75
33
|
self.url = url
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
34
|
+
self._sse = False
|
35
|
+
|
36
|
+
async def list_sse_tools(self) -> ListToolsResult:
|
37
|
+
# Create a new context for each SSE connection
|
38
|
+
try:
|
39
|
+
async with sse_client(f"{self.url}/sse") as (read_stream, write_stream):
|
40
|
+
async with ClientSession(read_stream, write_stream) as session:
|
41
|
+
await session.initialize()
|
42
|
+
response = await session.list_tools()
|
43
|
+
return response
|
44
|
+
except Exception:
|
45
|
+
self._sse = False
|
46
|
+
logger.info("SSE not available, trying HTTP")
|
47
|
+
return None # Signal to list_tools() to try HTTP instead
|
48
|
+
|
49
|
+
def list_tools(self) -> ListToolsResult:
|
50
|
+
try:
|
51
|
+
loop = asyncio.get_event_loop()
|
52
|
+
result = loop.run_until_complete(self.list_sse_tools())
|
53
|
+
if result is None: # SSE failed, try HTTP
|
54
|
+
raise Exception("SSE failed")
|
55
|
+
self._sse = True
|
56
|
+
return result
|
57
|
+
except Exception: # Fallback to HTTP
|
58
|
+
client = self.client.get_httpx_client()
|
59
|
+
response = client.request("GET", f"{self.url}/tools/list")
|
60
|
+
response.raise_for_status()
|
61
|
+
return ListToolsResult(**response.json())
|
62
|
+
|
63
|
+
async def call_tool(
|
64
|
+
self,
|
65
|
+
tool_name: str,
|
66
|
+
arguments: dict[str, Any] = None,
|
67
|
+
) -> requests.Response | AsyncIterator[CallToolResult]:
|
68
|
+
if self._sse:
|
69
|
+
async with sse_client(f"{self.url}/sse") as (read_stream, write_stream):
|
70
|
+
async with ClientSession(read_stream, write_stream) as session:
|
71
|
+
await session.initialize()
|
72
|
+
response = await session.call_tool(tool_name, arguments or {})
|
73
|
+
content = pydantic_core.to_json(response).decode()
|
74
|
+
return content
|
75
|
+
else: # Fallback to HTTP
|
76
|
+
client = self.client.get_httpx_client()
|
77
|
+
response = client.request(
|
78
|
+
"POST",
|
79
|
+
f"{self.url}/tools/call",
|
80
|
+
json={"name": tool_name, "arguments": arguments},
|
81
|
+
)
|
82
|
+
response.raise_for_status()
|
83
|
+
result = CallToolResult(response.json())
|
84
|
+
if result.isError:
|
85
|
+
raise ToolException(result.content)
|
86
|
+
content = pydantic_core.to_json(result.content).decode()
|
87
|
+
return content
|
92
88
|
|
93
89
|
class MCPTool(BaseTool):
|
94
90
|
"""
|
@@ -97,6 +93,7 @@ class MCPTool(BaseTool):
|
|
97
93
|
Attributes:
|
98
94
|
client (MCPClient): The MCP client instance.
|
99
95
|
handle_tool_error (bool | str | Callable[[ToolException], str] | None): Error handling strategy.
|
96
|
+
sse (bool): Whether to use SSE streaming for responses.
|
100
97
|
"""
|
101
98
|
|
102
99
|
client: MCPClient
|
@@ -112,13 +109,7 @@ class MCPTool(BaseTool):
|
|
112
109
|
|
113
110
|
@t.override
|
114
111
|
async def _arun(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
115
|
-
|
116
|
-
response = result.json()
|
117
|
-
result = CallToolResult(**response)
|
118
|
-
if result.isError:
|
119
|
-
raise ToolException(result.content)
|
120
|
-
content = pydantic_core.to_json(result.content).decode()
|
121
|
-
return content
|
112
|
+
return await self.client.call_tool(self.name, arguments=kwargs)
|
122
113
|
|
123
114
|
@t.override
|
124
115
|
@property
|
@@ -139,14 +130,13 @@ class MCPToolkit(BaseToolkit):
|
|
139
130
|
"""The MCP session used to obtain the tools"""
|
140
131
|
|
141
132
|
_tools: ListToolsResult | None = None
|
142
|
-
|
143
133
|
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
|
144
134
|
|
145
135
|
def initialize(self) -> None:
|
146
136
|
"""Initialize the session and retrieve tools list"""
|
147
137
|
if self._tools is None:
|
148
138
|
response = self.client.list_tools()
|
149
|
-
self._tools =
|
139
|
+
self._tools = response
|
150
140
|
|
151
141
|
@t.override
|
152
142
|
def get_tools(self) -> list[BaseTool]:
|
@@ -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 Beamlit.
|
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
|
+
|
@@ -4,6 +4,7 @@ It includes classes for creating dynamic schemas based on function parameters an
|
|
4
4
|
"""
|
5
5
|
|
6
6
|
import asyncio
|
7
|
+
import os
|
7
8
|
import warnings
|
8
9
|
from dataclasses import dataclass
|
9
10
|
from typing import Callable
|
@@ -67,7 +68,8 @@ class RemoteTool(BaseTool):
|
|
67
68
|
resource_name: str
|
68
69
|
kit: bool = False
|
69
70
|
handle_tool_error: bool | str | Callable[[ToolException], str] | None = True
|
70
|
-
|
71
|
+
service_name: str | None = None
|
72
|
+
cloud: bool = False
|
71
73
|
@t.override
|
72
74
|
def _run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
73
75
|
warnings.warn(
|
@@ -87,7 +89,9 @@ class RemoteTool(BaseTool):
|
|
87
89
|
self.resource_name,
|
88
90
|
settings.environment,
|
89
91
|
"POST",
|
90
|
-
|
92
|
+
cloud=self.cloud,
|
93
|
+
service_name=self.service_name,
|
94
|
+
json=body,
|
91
95
|
)
|
92
96
|
return result.text
|
93
97
|
|
@@ -110,6 +114,7 @@ class RemoteToolkit:
|
|
110
114
|
client: AuthenticatedClient
|
111
115
|
function: str
|
112
116
|
_function: Function | None = None
|
117
|
+
_service_name: str | None = None
|
113
118
|
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
|
114
119
|
|
115
120
|
def initialize(self) -> None:
|
@@ -117,6 +122,9 @@ class RemoteToolkit:
|
|
117
122
|
if self._function is None:
|
118
123
|
try:
|
119
124
|
response = get_function.sync_detailed(self.function, client=self.client)
|
125
|
+
function_name = self.function.upper().replace("-", "_")
|
126
|
+
if os.getenv(f"BL_FUNCTION_{function_name}_SERVICE_NAME"):
|
127
|
+
self._service_name = os.getenv(f"BL_FUNCTION_{function_name}_SERVICE_NAME")
|
120
128
|
self._function = response.parsed
|
121
129
|
except UnexpectedStatus as e:
|
122
130
|
settings = get_settings()
|
@@ -139,6 +147,8 @@ class RemoteToolkit:
|
|
139
147
|
|
140
148
|
if self._function.spec.integration_connections:
|
141
149
|
url = f"{settings.run_url}/{settings.workspace}/functions/{self._function.metadata.name}"
|
150
|
+
if self._service_name:
|
151
|
+
url = f"https://{self._service_name}.{settings.run_internal_hostname}"
|
142
152
|
mcp_client = MCPClient(self.client, url)
|
143
153
|
mcp_toolkit = MCPToolkit(client=mcp_client)
|
144
154
|
mcp_toolkit.initialize()
|
@@ -153,6 +163,8 @@ class RemoteToolkit:
|
|
153
163
|
kit=True,
|
154
164
|
description=func.description or "",
|
155
165
|
args_schema=create_dynamic_schema(func.name, func.parameters),
|
166
|
+
cloud=settings.cloud,
|
167
|
+
service_name=self._service_name,
|
156
168
|
)
|
157
169
|
for func in self._function.spec.kit
|
158
170
|
]
|
@@ -167,5 +179,7 @@ class RemoteToolkit:
|
|
167
179
|
self._function.metadata.name,
|
168
180
|
self._function.spec.parameters
|
169
181
|
),
|
182
|
+
cloud=settings.cloud,
|
183
|
+
service_name=self._service_name,
|
170
184
|
)
|
171
185
|
]
|
beamlit/models/__init__.py
CHANGED
@@ -5,6 +5,8 @@ from .agent import Agent
|
|
5
5
|
from .agent_chain import AgentChain
|
6
6
|
from .agent_history import AgentHistory
|
7
7
|
from .agent_history_event import AgentHistoryEvent
|
8
|
+
from .agent_information_request import AgentInformationRequest
|
9
|
+
from .agent_information_response import AgentInformationResponse
|
8
10
|
from .agent_release import AgentRelease
|
9
11
|
from .agent_spec import AgentSpec
|
10
12
|
from .api_key import ApiKey
|
@@ -18,11 +20,17 @@ from .create_api_key_for_service_account_body import CreateApiKeyForServiceAccou
|
|
18
20
|
from .create_workspace_service_account_body import CreateWorkspaceServiceAccountBody
|
19
21
|
from .create_workspace_service_account_response_200 import CreateWorkspaceServiceAccountResponse200
|
20
22
|
from .delete_workspace_service_account_response_200 import DeleteWorkspaceServiceAccountResponse200
|
23
|
+
from .entrypoint import Entrypoint
|
24
|
+
from .entrypoint_env import EntrypointEnv
|
21
25
|
from .environment import Environment
|
22
26
|
from .environment_metadata import EnvironmentMetadata
|
23
27
|
from .environment_metrics import EnvironmentMetrics
|
24
28
|
from .environment_spec import EnvironmentSpec
|
25
29
|
from .flavor import Flavor
|
30
|
+
from .form import Form
|
31
|
+
from .form_config import FormConfig
|
32
|
+
from .form_oauthomitempty import FormOauthomitempty
|
33
|
+
from .form_secrets import FormSecrets
|
26
34
|
from .function import Function
|
27
35
|
from .function_kit import FunctionKit
|
28
36
|
from .function_release import FunctionRelease
|
@@ -49,6 +57,12 @@ from .knowledgebase_spec_options import KnowledgebaseSpecOptions
|
|
49
57
|
from .last_n_requests_metric import LastNRequestsMetric
|
50
58
|
from .latency_metric import LatencyMetric
|
51
59
|
from .location_response import LocationResponse
|
60
|
+
from .mcp_definition import MCPDefinition
|
61
|
+
from .mcp_definition_entrypoint import MCPDefinitionEntrypoint
|
62
|
+
from .mcp_definition_form import MCPDefinitionForm
|
63
|
+
from .mcp_hub_artifact import MCPHubArtifact
|
64
|
+
from .mcp_hub_artifact_entrypoint import MCPHubArtifactEntrypoint
|
65
|
+
from .mcp_hub_artifact_form import MCPHubArtifactForm
|
52
66
|
from .metadata import Metadata
|
53
67
|
from .metadata_labels import MetadataLabels
|
54
68
|
from .metric import Metric
|
@@ -60,6 +74,7 @@ from .model import Model
|
|
60
74
|
from .model_private_cluster import ModelPrivateCluster
|
61
75
|
from .model_release import ModelRelease
|
62
76
|
from .model_spec import ModelSpec
|
77
|
+
from .o_auth import OAuth
|
63
78
|
from .owner_fields import OwnerFields
|
64
79
|
from .pending_invitation import PendingInvitation
|
65
80
|
from .pending_invitation_accept import PendingInvitationAccept
|
@@ -125,6 +140,8 @@ __all__ = (
|
|
125
140
|
"AgentChain",
|
126
141
|
"AgentHistory",
|
127
142
|
"AgentHistoryEvent",
|
143
|
+
"AgentInformationRequest",
|
144
|
+
"AgentInformationResponse",
|
128
145
|
"AgentRelease",
|
129
146
|
"AgentSpec",
|
130
147
|
"ApiKey",
|
@@ -138,11 +155,17 @@ __all__ = (
|
|
138
155
|
"CreateWorkspaceServiceAccountBody",
|
139
156
|
"CreateWorkspaceServiceAccountResponse200",
|
140
157
|
"DeleteWorkspaceServiceAccountResponse200",
|
158
|
+
"Entrypoint",
|
159
|
+
"EntrypointEnv",
|
141
160
|
"Environment",
|
142
161
|
"EnvironmentMetadata",
|
143
162
|
"EnvironmentMetrics",
|
144
163
|
"EnvironmentSpec",
|
145
164
|
"Flavor",
|
165
|
+
"Form",
|
166
|
+
"FormConfig",
|
167
|
+
"FormOauthomitempty",
|
168
|
+
"FormSecrets",
|
146
169
|
"Function",
|
147
170
|
"FunctionKit",
|
148
171
|
"FunctionRelease",
|
@@ -167,6 +190,12 @@ __all__ = (
|
|
167
190
|
"LastNRequestsMetric",
|
168
191
|
"LatencyMetric",
|
169
192
|
"LocationResponse",
|
193
|
+
"MCPDefinition",
|
194
|
+
"MCPDefinitionEntrypoint",
|
195
|
+
"MCPDefinitionForm",
|
196
|
+
"MCPHubArtifact",
|
197
|
+
"MCPHubArtifactEntrypoint",
|
198
|
+
"MCPHubArtifactForm",
|
170
199
|
"Metadata",
|
171
200
|
"MetadataLabels",
|
172
201
|
"Metric",
|
@@ -178,6 +207,7 @@ __all__ = (
|
|
178
207
|
"ModelPrivateCluster",
|
179
208
|
"ModelRelease",
|
180
209
|
"ModelSpec",
|
210
|
+
"OAuth",
|
181
211
|
"OwnerFields",
|
182
212
|
"PendingInvitation",
|
183
213
|
"PendingInvitationAccept",
|