beamlit 0.0.55rc103__py3-none-any.whl → 0.0.56rc104__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 CHANGED
@@ -2,7 +2,7 @@ import asyncio
2
2
  import warnings
3
3
  from dataclasses import dataclass
4
4
  from typing import Callable
5
-
5
+ import os
6
6
  import pydantic
7
7
  import typing_extensions as t
8
8
  from langchain_core.tools.base import BaseTool, ToolException
@@ -21,6 +21,8 @@ class ChainTool(BaseTool):
21
21
 
22
22
  client: RunClient
23
23
  handle_tool_error: bool | str | Callable[[ToolException], str] | None = True
24
+ _cloud: bool = False
25
+ _service_name: str | None = None
24
26
 
25
27
  @t.override
26
28
  def _run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
@@ -58,6 +60,8 @@ class ChainTool(BaseTool):
58
60
  self.name,
59
61
  settings.environment,
60
62
  "POST",
63
+ cloud=self._cloud,
64
+ service_name=self._service_name,
61
65
  json=kwargs,
62
66
  )
63
67
  return result.text
@@ -91,6 +95,8 @@ class ChainToolkit:
91
95
 
92
96
  client: AuthenticatedClient
93
97
  chain: list[AgentChain]
98
+ _cloud: bool = False
99
+ _service_name: str | None = None
94
100
  _chain: list[Agent] | None = None
95
101
 
96
102
  model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
@@ -103,6 +109,8 @@ class ChainToolkit:
103
109
  RuntimeError: If initialization fails due to missing agents.
104
110
  """
105
111
  """Initialize the session and retrieve tools list"""
112
+ settings = get_settings()
113
+ self._cloud = settings.cloud
106
114
  if self._chain is None:
107
115
  agents = list_agents.sync_detailed(
108
116
  client=self.client,
@@ -111,6 +119,9 @@ class ChainToolkit:
111
119
  agents_chain = []
112
120
  for chain in chain_enabled:
113
121
  agent = [agent for agent in agents if agent.metadata.name == chain.name]
122
+ agent_name = agent[0].metadata.name.upper().replace("-", "_")
123
+ if os.getenv(f"BL_AGENT_{agent_name}_SERVICE_NAME"):
124
+ self._service_name = os.getenv(f"BL_AGENT_{agent_name}_SERVICE_NAME")
114
125
  if agent:
115
126
  agent[0].spec.prompt = chain.prompt or agent[0].spec.prompt
116
127
  agent[0].spec.description = chain.description or agent[0].spec.description
@@ -136,6 +147,8 @@ class ChainToolkit:
136
147
  name=agent.metadata.name,
137
148
  description=agent.spec.description or agent.spec.prompt or "",
138
149
  args_schema=ChainInput,
150
+ cloud=self._cloud,
151
+ service_name=self._service_name,
139
152
  )
140
153
  for agent in self._chain
141
154
  ]
@@ -29,6 +29,7 @@ def agent(
29
29
  override_agent=None,
30
30
  override_functions=None,
31
31
  remote_functions=None,
32
+ local_functions=None,
32
33
  ) -> Callable:
33
34
  """
34
35
  A decorator factory that configures and wraps functions to integrate with Beamlit agents.
@@ -40,6 +41,7 @@ def agent(
40
41
  override_agent (Any, optional): An optional agent instance to override the default agent.
41
42
  mcp_hub (Any, optional): An optional MCP hub configuration.
42
43
  remote_functions (Any, optional): An optional list of remote functions to be integrated.
44
+ local_functions (Any, optional): An optional list of local functions to be integrated.
43
45
 
44
46
  Returns:
45
47
  Callable: A decorator that wraps the target function, injecting agent-related configurations and dependencies.
@@ -130,9 +132,10 @@ def agent(
130
132
  dir=settings.agent.functions_directory,
131
133
  remote_functions=remote_functions,
132
134
  chain=agent.spec.agent_chain,
133
- remote_functions_empty=not remote_functions,
134
- warning=chat_model is not None,
135
- )
135
+ local_functions=local_functions,
136
+ remote_functions_empty=not remote_functions,
137
+ warning=chat_model is not None,
138
+ )
136
139
 
137
140
  settings.agent.functions = functions
138
141
 
@@ -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()
@@ -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()
@@ -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
- settings = get_settings()
23
+ from .utils import create_schema_model
23
24
 
24
- TYPE_MAP = {
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
- def list_tools(self) -> requests.Response:
78
- client = self.client.get_httpx_client()
79
- response = client.request("GET", f"{self.url}/tools/list")
80
- response.raise_for_status()
81
- return response
82
-
83
- def call_tool(self, tool_name: str, arguments: dict[str, Any] = None) -> requests.Response:
84
- client = self.client.get_httpx_client()
85
- response = client.request(
86
- "POST",
87
- f"{self.url}/tools/call",
88
- json={"name": tool_name, "arguments": arguments},
89
- )
90
- response.raise_for_status()
91
- return response
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
- result = self.client.call_tool(self.name, arguments=kwargs)
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 = ListToolsResult(**response.json())
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
+
@@ -7,11 +7,10 @@ import asyncio
7
7
  import warnings
8
8
  from dataclasses import dataclass
9
9
  from typing import Callable
10
+ import os
10
11
 
11
12
  import pydantic
12
13
  import typing_extensions as t
13
- from langchain_core.tools.base import BaseTool, ToolException
14
-
15
14
  from beamlit.api.functions import get_function, list_functions
16
15
  from beamlit.authentication.authentication import AuthenticatedClient
17
16
  from beamlit.common.settings import get_settings
@@ -19,6 +18,7 @@ from beamlit.errors import UnexpectedStatus
19
18
  from beamlit.functions.mcp.mcp import MCPClient, MCPToolkit
20
19
  from beamlit.models import Function, StoreFunctionParameter
21
20
  from beamlit.run import RunClient
21
+ from langchain_core.tools.base import BaseTool, ToolException
22
22
 
23
23
 
24
24
  def create_dynamic_schema(name: str, parameters: list[StoreFunctionParameter]) -> type[pydantic.BaseModel]:
@@ -67,7 +67,8 @@ class RemoteTool(BaseTool):
67
67
  resource_name: str
68
68
  kit: bool = False
69
69
  handle_tool_error: bool | str | Callable[[ToolException], str] | None = True
70
-
70
+ service_name: str | None = None
71
+ cloud: bool = False
71
72
  @t.override
72
73
  def _run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
73
74
  warnings.warn(
@@ -87,7 +88,9 @@ class RemoteTool(BaseTool):
87
88
  self.resource_name,
88
89
  settings.environment,
89
90
  "POST",
90
- json=body
91
+ cloud=self.cloud,
92
+ service_name=self.service_name,
93
+ json=body,
91
94
  )
92
95
  return result.text
93
96
 
@@ -110,6 +113,7 @@ class RemoteToolkit:
110
113
  client: AuthenticatedClient
111
114
  function: str
112
115
  _function: Function | None = None
116
+ _service_name: str | None = None
113
117
  model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
114
118
 
115
119
  def initialize(self) -> None:
@@ -117,6 +121,9 @@ class RemoteToolkit:
117
121
  if self._function is None:
118
122
  try:
119
123
  response = get_function.sync_detailed(self.function, client=self.client)
124
+ function_name = self.function.upper().replace("-", "_")
125
+ if os.getenv(f"BL_FUNCTION_{function_name}_SERVICE_NAME"):
126
+ self._service_name = os.getenv(f"BL_FUNCTION_{function_name}_SERVICE_NAME")
120
127
  self._function = response.parsed
121
128
  except UnexpectedStatus as e:
122
129
  settings = get_settings()
@@ -139,6 +146,8 @@ class RemoteToolkit:
139
146
 
140
147
  if self._function.spec.integration_connections:
141
148
  url = f"{settings.run_url}/{settings.workspace}/functions/{self._function.metadata.name}"
149
+ if self._service_name:
150
+ url = f"https://{self._service_name}.{settings.run_internal_hostname}"
142
151
  mcp_client = MCPClient(self.client, url)
143
152
  mcp_toolkit = MCPToolkit(client=mcp_client)
144
153
  mcp_toolkit.initialize()
@@ -153,6 +162,8 @@ class RemoteToolkit:
153
162
  kit=True,
154
163
  description=func.description or "",
155
164
  args_schema=create_dynamic_schema(func.name, func.parameters),
165
+ cloud=settings.cloud,
166
+ service_name=self._service_name,
156
167
  )
157
168
  for func in self._function.spec.kit
158
169
  ]
@@ -167,5 +178,7 @@ class RemoteToolkit:
167
178
  self._function.metadata.name,
168
179
  self._function.spec.parameters
169
180
  ),
181
+ cloud=settings.cloud,
182
+ service_name=self._service_name,
170
183
  )
171
184
  ]
beamlit/run.py CHANGED
@@ -48,6 +48,8 @@ class RunClient:
48
48
  json: dict[str, Any] | None = None,
49
49
  data: str | None = None,
50
50
  params: dict[str, str] | None = None,
51
+ cloud: bool = False,
52
+ service_name: str | None = None,
51
53
  ) -> requests.Response:
52
54
  """Execute an HTTP request against a Beamlit resource.
53
55
 
@@ -61,6 +63,8 @@ class RunClient:
61
63
  json (dict[str, Any] | None, optional): JSON payload to send with the request. Defaults to None.
62
64
  data (str | None, optional): Raw data to send with the request. Defaults to None.
63
65
  params (dict[str, str] | None, optional): Query parameters to include in the URL. Defaults to None.
66
+ cloud (bool, optional): Whether to use the cloud endpoint. Defaults to False.
67
+ service_name (str | None, optional): The name of the service to use. Defaults to None.
64
68
 
65
69
  Returns:
66
70
  requests.Response: The HTTP response from the server.
@@ -72,14 +76,17 @@ class RunClient:
72
76
  headers = headers or {}
73
77
  params = params or {}
74
78
 
75
- # Build the path
76
- if path:
77
- path = f"{settings.workspace}/{resource_type}s/{resource_name}/{path}"
78
- else:
79
- path = f"{settings.workspace}/{resource_type}s/{resource_name}"
79
+ if cloud and path and service_name:
80
+ url = f"https://{service_name}.{settings.run_internal_hostname}/{path}"
81
+
82
+ if cloud and not path and service_name:
83
+ url = f"https://{service_name}.{settings.run_internal_hostname}"
80
84
 
81
- client = self.client.get_httpx_client()
82
- url = urllib.parse.urljoin(settings.run_url, path)
85
+ if not cloud and path:
86
+ url = urllib.parse.urljoin(settings.run_url, f"{settings.workspace}/{resource_type}s/{resource_name}/{path}")
87
+
88
+ if not cloud and not path:
89
+ url = urllib.parse.urljoin(settings.run_url, f"{settings.workspace}/{resource_type}s/{resource_name}")
83
90
 
84
91
  kwargs = {
85
92
  "headers": headers,
@@ -90,7 +97,16 @@ class RunClient:
90
97
  if json:
91
98
  kwargs["json"] = json
92
99
 
93
- response = client.request(method, url, **kwargs)
94
- if response.status_code >= 400:
100
+ response = self.client.request(method, url, **kwargs)
101
+ if response.status_code >= 400 and not cloud:
95
102
  raise HTTPError(response.status_code, response.text)
103
+ if response.status_code >= 400 and cloud: # Redirect to the public endpoint if the resource is in the cloud and the request fails
104
+ if path:
105
+ url = urllib.parse.urljoin(settings.run_url, f"{settings.workspace}/{resource_type}s/{resource_name}/{path}")
106
+ else:
107
+ url = urllib.parse.urljoin(settings.run_url, f"{settings.workspace}/{resource_type}s/{resource_name}")
108
+ response = self.client.request(method, url, **kwargs)
109
+ if response.status_code >= 400:
110
+ raise HTTPError(response.status_code, response.text)
111
+ return response
96
112
  return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beamlit
3
- Version: 0.0.55rc103
3
+ Version: 0.0.56rc104
4
4
  Summary: Add your description here
5
5
  Author-email: cploujoux <ch.ploujoux@gmail.com>
6
6
  License-File: LICENSE
@@ -2,12 +2,12 @@ beamlit/__init__.py,sha256=545gFC-wLLwUktWcOAjUWe_Glha40tBetRTOYSfHnbI,164
2
2
  beamlit/client.py,sha256=PnR6ybZk5dLIJPnDKAf2epHOeQC_7yL0fG4muvphHjA,12695
3
3
  beamlit/errors.py,sha256=gO8GBmKqmSNgAg-E5oT-oOyxztvp7V_6XG7OUTT15q0,546
4
4
  beamlit/py.typed,sha256=8ZJUsxZiuOy1oJeVhsTWQhTG_6pTVHVXk5hJL79ebTk,25
5
- beamlit/run.py,sha256=ERZ0tA7TsocV_2LC81QbxPxCokNCSmrZv4kqh9sGEkc,3494
5
+ beamlit/run.py,sha256=PNkL1JEd_gZ6BMo_SA0u8TaczRyFz001qcmIOdkw5NM,4645
6
6
  beamlit/types.py,sha256=E1hhDh_zXfsSQ0NCt9-uw90_Mr5iIlsdfnfvxv5HarU,1005
7
7
  beamlit/agents/__init__.py,sha256=bWsFaXUbAps3IsL3Prti89m1s714vICXodbQi77h3vY,206
8
- beamlit/agents/chain.py,sha256=xtOTOFQR9Wml-ZwW06cKZB4dIPmkQxpBx1dPyaE3PKk,4333
8
+ beamlit/agents/chain.py,sha256=DrVRdXI8Opa0qfXLAtoNB7oqwtftygN28pxwB1UiWUg,4934
9
9
  beamlit/agents/chat.py,sha256=ItuS9zXkALgXVa6IevNpCVKvsicwg9OcUf4_VqO1x5Q,8603
10
- beamlit/agents/decorator.py,sha256=KdOKKf_svJc7Pa-eyP-_sXqEndZrEIQCV5Yfgz-zoQE,8253
10
+ beamlit/agents/decorator.py,sha256=dz0XPS_RQFrCdX6VB_dnFc6f_1z8sszJzgaxlwd9E8Q,8435
11
11
  beamlit/agents/thread.py,sha256=XNqj2WI3W2roQB7J5mpF1wEuGb3VtfvKQvWaEPx-sx8,1014
12
12
  beamlit/agents/voice/openai.py,sha256=-RDBwl16i4TbUhFo5-77Ci3zmI3Y8U2yf2MmvXR2haQ,9479
13
13
  beamlit/agents/voice/utils.py,sha256=tQidyM40Ewuy12wKqpvJLvfJgneQ0sZf50dqnerPGHg,836
@@ -136,7 +136,7 @@ beamlit/common/error.py,sha256=eOftZvd6XsAcQIYl9V7B1msGnTZf-HO0hGxBw1uIxaA,763
136
136
  beamlit/common/instrumentation.py,sha256=a3wuXMOFx4x_M82I7NkzXwg0-vE5LZBNxV03YD-wJh4,10345
137
137
  beamlit/common/logger.py,sha256=YBsVZXKApr7CCvZdAeNWkUX1lplt1SZVfv9Q4DbgRhQ,2092
138
138
  beamlit/common/secrets.py,sha256=_qsc_HZMsIVVf03yyHUDN6j19Fgd_m1YgFumVS2sgxk,1200
139
- beamlit/common/settings.py,sha256=Zf43TPYGVIeqi2WiKXvIfbHVuR6V9cbTeMqjz4WN--Q,5306
139
+ beamlit/common/settings.py,sha256=EX1AYSR-3FL0jFGOBssOWSUhLOfU85c01hkxMy5gJ70,5420
140
140
  beamlit/common/slugify.py,sha256=QPJqa1VrSjRle931RN37t24rUjAbyZEg1UFAp2fLgac,679
141
141
  beamlit/common/utils.py,sha256=eG201z9gMRnhoHkaZGNtfFUbCzfg_Y59JR4ciMgidW8,1465
142
142
  beamlit/deploy/__init__.py,sha256=uRsI_-gTbbki59LlvubeTfG6wfI3o2XqZODW0QXA-Ao,292
@@ -144,10 +144,12 @@ beamlit/deploy/deploy.py,sha256=V07m0GKEKvQKjyw5r5UR2IsXQxhgyH6OeSgexj3AAtY,1123
144
144
  beamlit/deploy/format.py,sha256=W3ESUHyFv-iZDjVnHOf9YFDDXZSXYIFFbwCoL1GInE0,1162
145
145
  beamlit/deploy/parser.py,sha256=gjRUhOVtfKnc1UNc_FhXsEfj9zrMNuq8W93pNsJBpo0,7586
146
146
  beamlit/functions/__init__.py,sha256=Mnoqpa1dm7TXwjodBbF_40JyD78aXsOYWmqjDSnA1lU,317
147
- beamlit/functions/common.py,sha256=IkdxQYrvu6H4cPgqc0OeXquCInMtGvmq6GP2FA1rh7w,9221
147
+ beamlit/functions/common.py,sha256=4SK1N0VoQiydUT4frGT9OWMRxgHgMHUcsBhicMkcMmc,9996
148
148
  beamlit/functions/decorator.py,sha256=iQbLwUo0K83DFJ3ub8O5jKtkbSINnku6GZcKJ9h7-5E,2292
149
- beamlit/functions/mcp/mcp.py,sha256=M6SAGnBo5xpZcPKB6YE0R2Xp73hHOlbZXc0Q2kFSYuU,5458
150
- beamlit/functions/remote/remote.py,sha256=g0M909Ow9GLPgtistrWkUuhcITUwA2iHIOQNpUsp28g,5989
149
+ beamlit/functions/local/local.py,sha256=KjkHWBxGlG9fliXnOOwZQEop1g2o10IaLiPo4Zu_XAk,1929
150
+ beamlit/functions/mcp/mcp.py,sha256=6pHWTPbglw1-SlqIztAigN284OyYR7ovn29u0IuF73E,5611
151
+ beamlit/functions/mcp/utils.py,sha256=V7bah6cymdtjJ_LJUrNcHDeApDHA6uXvaGVeFJGKj2U,1850
152
+ beamlit/functions/remote/remote.py,sha256=0orVJHpdvXWo18mmk-a_vBwpcClus6bk0kP1lNVoNpg,6716
151
153
  beamlit/models/__init__.py,sha256=2yOQDeO7hQ_85q0WTU7ikAe-IfGr7PwlnHXJnLRSuB0,9036
152
154
  beamlit/models/acl.py,sha256=tH67gsl_BMaviSbTaaIkO1g9cWZgJ6VgAnYVjQSzGZY,3952
153
155
  beamlit/models/agent.py,sha256=pkFemfg0OUAuiqebiT3PurXhjAKvgCa_YOPuyqFVE2o,4264
@@ -263,8 +265,8 @@ beamlit/serve/app.py,sha256=lM59fdUtfkfAYNPWSCU9pkXIPBnhgVGvvgfoMkSVtks,4531
263
265
  beamlit/serve/middlewares/__init__.py,sha256=O7fyfE1DIYmajFY9WWdzxCgeAQWZzJfeUjzHGbpWaAk,309
264
266
  beamlit/serve/middlewares/accesslog.py,sha256=lcu33j4epFSHRBaeTpyt8deNb3kaM3K91-andw4fp80,1112
265
267
  beamlit/serve/middlewares/processtime.py,sha256=3x5w1yQexB0xFNKK6fgLbINxT-eLLunfZ6UDV0bIIF4,944
266
- beamlit-0.0.55rc103.dist-info/METADATA,sha256=Id4qMO4WgYlFRDVc9Tm5d9-JjwzqIP_U7-TwHL695cQ,3515
267
- beamlit-0.0.55rc103.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
268
- beamlit-0.0.55rc103.dist-info/entry_points.txt,sha256=zxhgdn7SP-Otk4rEv7LMPAAa9w4TUCLbu9TJi9-K3xg,115
269
- beamlit-0.0.55rc103.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
270
- beamlit-0.0.55rc103.dist-info/RECORD,,
268
+ beamlit-0.0.56rc104.dist-info/METADATA,sha256=uym9lJtWUtDt9LG3E1dzGs8Clj2TGE3B3gIqzCOeJsg,3515
269
+ beamlit-0.0.56rc104.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
270
+ beamlit-0.0.56rc104.dist-info/entry_points.txt,sha256=zxhgdn7SP-Otk4rEv7LMPAAa9w4TUCLbu9TJi9-K3xg,115
271
+ beamlit-0.0.56rc104.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
272
+ beamlit-0.0.56rc104.dist-info/RECORD,,