beamlit 0.0.34rc60__py3-none-any.whl → 0.0.34rc62__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/chat.py +4 -1
- beamlit/agents/decorator.py +13 -140
- beamlit/api/service_accounts/create_workspace_service_account.py +3 -1
- beamlit/api/service_accounts/delete_workspace_service_account.py +3 -1
- beamlit/api/service_accounts/get_workspace_service_accounts.py +3 -1
- beamlit/api/service_accounts/update_workspace_service_account.py +3 -1
- beamlit/deploy/deploy.py +60 -58
- beamlit/deploy/format.py +10 -0
- beamlit/functions/__init__.py +2 -2
- beamlit/functions/decorator.py +147 -1
- beamlit/models/__init__.py +3 -1
- beamlit/serve/app.py +1 -1
- {beamlit-0.0.34rc60.dist-info → beamlit-0.0.34rc62.dist-info}/METADATA +1 -1
- {beamlit-0.0.34rc60.dist-info → beamlit-0.0.34rc62.dist-info}/RECORD +15 -15
- {beamlit-0.0.34rc60.dist-info → beamlit-0.0.34rc62.dist-info}/WHEEL +0 -0
beamlit/agents/chat.py
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
from logging import getLogger
|
2
|
+
from typing import Tuple
|
3
|
+
|
4
|
+
from langchain_core.language_models import BaseChatModel
|
2
5
|
|
3
6
|
from beamlit.authentication import get_authentication_headers, new_client
|
4
7
|
from beamlit.common.settings import get_settings
|
@@ -39,7 +42,7 @@ def get_cohere_chat_model(**kwargs):
|
|
39
42
|
|
40
43
|
return ChatCohere(**kwargs)
|
41
44
|
|
42
|
-
def get_chat_model(name: str, agent_model: Model):
|
45
|
+
def get_chat_model(name: str, agent_model: Model) -> Tuple[BaseChatModel, str, str]:
|
43
46
|
settings = get_settings()
|
44
47
|
client = new_client()
|
45
48
|
|
beamlit/agents/decorator.py
CHANGED
@@ -1,122 +1,20 @@
|
|
1
1
|
# Import necessary modules
|
2
|
-
import ast
|
3
|
-
import asyncio
|
4
2
|
import functools
|
5
|
-
import importlib
|
6
|
-
import os
|
7
3
|
from logging import getLogger
|
8
4
|
|
9
|
-
from langchain_core.tools import StructuredTool
|
10
5
|
from langgraph.checkpoint.memory import MemorySaver
|
11
6
|
from langgraph.prebuilt import create_react_agent
|
12
|
-
from langchain_core.tools.base import create_schema_from_function
|
13
7
|
|
14
8
|
from beamlit.api.models import get_model
|
15
9
|
from beamlit.authentication import new_client
|
16
|
-
from beamlit.common import
|
17
|
-
from beamlit.common.settings import get_settings, init
|
10
|
+
from beamlit.common.settings import init
|
18
11
|
from beamlit.errors import UnexpectedStatus
|
19
|
-
from beamlit.functions
|
20
|
-
from beamlit.functions.remote.remote import RemoteToolkit
|
12
|
+
from beamlit.functions import get_functions
|
21
13
|
from beamlit.models import Agent, AgentMetadata, AgentSpec
|
22
14
|
|
23
|
-
from .chain import ChainToolkit
|
24
15
|
from .chat import get_chat_model
|
25
16
|
|
26
17
|
|
27
|
-
def get_functions(client, dir="src/functions", from_decorator="function", remote_functions_empty=True):
|
28
|
-
functions = []
|
29
|
-
logger = getLogger(__name__)
|
30
|
-
settings = get_settings()
|
31
|
-
|
32
|
-
# Walk through all Python files in functions directory and subdirectories
|
33
|
-
if not os.path.exists(dir):
|
34
|
-
if remote_functions_empty:
|
35
|
-
logger.warn(f"Functions directory {dir} not found")
|
36
|
-
return []
|
37
|
-
for root, _, files in os.walk(dir):
|
38
|
-
for file in files:
|
39
|
-
if file.endswith(".py"):
|
40
|
-
file_path = os.path.join(root, file)
|
41
|
-
# Read and compile the file content
|
42
|
-
with open(file_path) as f:
|
43
|
-
try:
|
44
|
-
file_content = f.read()
|
45
|
-
# Parse the file content to find decorated functions
|
46
|
-
tree = ast.parse(file_content)
|
47
|
-
|
48
|
-
# Look for function definitions with decorators
|
49
|
-
for node in ast.walk(tree):
|
50
|
-
if (
|
51
|
-
not isinstance(node, ast.FunctionDef)
|
52
|
-
and not isinstance(node, ast.AsyncFunctionDef)
|
53
|
-
) or len(node.decorator_list) == 0:
|
54
|
-
continue
|
55
|
-
decorator = node.decorator_list[0]
|
56
|
-
|
57
|
-
decorator_name = ""
|
58
|
-
if isinstance(decorator, ast.Call):
|
59
|
-
decorator_name = decorator.func.id
|
60
|
-
if isinstance(decorator, ast.Name):
|
61
|
-
decorator_name = decorator.id
|
62
|
-
|
63
|
-
if decorator_name == from_decorator:
|
64
|
-
# Get the function name and decorator name
|
65
|
-
func_name = node.name
|
66
|
-
|
67
|
-
# Import the module to get the actual function
|
68
|
-
spec = importlib.util.spec_from_file_location(func_name, file_path)
|
69
|
-
module = importlib.util.module_from_spec(spec)
|
70
|
-
spec.loader.exec_module(module)
|
71
|
-
# Check if kit=True in the decorator arguments
|
72
|
-
is_kit = False
|
73
|
-
if isinstance(decorator, ast.Call):
|
74
|
-
for keyword in decorator.keywords:
|
75
|
-
if keyword.arg == "kit" and isinstance(
|
76
|
-
keyword.value, ast.Constant
|
77
|
-
):
|
78
|
-
is_kit = keyword.value.value
|
79
|
-
if is_kit and not settings.remote:
|
80
|
-
kit_functions = get_functions(
|
81
|
-
client,
|
82
|
-
dir=os.path.join(root),
|
83
|
-
from_decorator="kit",
|
84
|
-
remote_functions_empty=remote_functions_empty,
|
85
|
-
)
|
86
|
-
functions.extend(kit_functions)
|
87
|
-
|
88
|
-
# Get the decorated function
|
89
|
-
if not is_kit and hasattr(module, func_name):
|
90
|
-
func = getattr(module, func_name)
|
91
|
-
if settings.remote:
|
92
|
-
toolkit = RemoteToolkit(client, slugify(func.__name__))
|
93
|
-
toolkit.initialize()
|
94
|
-
functions.extend(toolkit.get_tools())
|
95
|
-
else:
|
96
|
-
if asyncio.iscoroutinefunction(func):
|
97
|
-
functions.append(
|
98
|
-
StructuredTool(
|
99
|
-
name=func.__name__,
|
100
|
-
description=func.__doc__,
|
101
|
-
func=func,
|
102
|
-
coroutine=func,
|
103
|
-
args_schema=create_schema_from_function(func.__name__, func)
|
104
|
-
)
|
105
|
-
)
|
106
|
-
else:
|
107
|
-
functions.append(
|
108
|
-
StructuredTool(
|
109
|
-
name=func.__name__,
|
110
|
-
description=func.__doc__,
|
111
|
-
func=func,
|
112
|
-
args_schema=create_schema_from_function(func.__name__, func)
|
113
|
-
)
|
114
|
-
)
|
115
|
-
except Exception as e:
|
116
|
-
logger.warning(f"Error processing {file_path}: {e!s}")
|
117
|
-
return functions
|
118
|
-
|
119
|
-
|
120
18
|
def agent(
|
121
19
|
agent: Agent | dict = None,
|
122
20
|
override_model=None,
|
@@ -148,15 +46,6 @@ def agent(
|
|
148
46
|
|
149
47
|
return wrapped
|
150
48
|
|
151
|
-
# Initialize functions array to store decorated functions
|
152
|
-
functions = get_functions(
|
153
|
-
client,
|
154
|
-
dir=settings.agent.functions_directory,
|
155
|
-
remote_functions_empty=not remote_functions,
|
156
|
-
)
|
157
|
-
|
158
|
-
|
159
|
-
|
160
49
|
if agent is not None:
|
161
50
|
metadata = AgentMetadata(**agent.get("metadata", {}))
|
162
51
|
spec = AgentSpec(**agent.get("spec", {}))
|
@@ -187,32 +76,16 @@ def agent(
|
|
187
76
|
settings.agent.chat_model = chat_model
|
188
77
|
logger.info(f"Chat model configured, using: {provider}:{model}")
|
189
78
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
logger.warn(f"Failed to initialize MCP server {server}: {e!s}")
|
199
|
-
|
200
|
-
if remote_functions:
|
201
|
-
for function in remote_functions:
|
202
|
-
try:
|
203
|
-
toolkit = RemoteToolkit(client, function)
|
204
|
-
toolkit.initialize()
|
205
|
-
functions.extend(toolkit.get_tools())
|
206
|
-
except Exception as e:
|
207
|
-
logger.warn(f"Failed to initialize remote function {function}: {e!s}")
|
208
|
-
|
209
|
-
if agent.spec.agent_chain:
|
210
|
-
toolkit = ChainToolkit(client, agent.spec.agent_chain)
|
211
|
-
toolkit.initialize()
|
212
|
-
functions.extend(toolkit.get_tools())
|
213
|
-
|
79
|
+
functions = get_functions(
|
80
|
+
client=client,
|
81
|
+
dir=settings.agent.functions_directory,
|
82
|
+
mcp_hub=mcp_hub,
|
83
|
+
remote_functions=remote_functions,
|
84
|
+
chain=agent.spec.agent_chain,
|
85
|
+
remote_functions_empty=not remote_functions,
|
86
|
+
)
|
214
87
|
settings.agent.functions = functions
|
215
|
-
|
88
|
+
|
216
89
|
if override_agent is None and len(functions) == 0:
|
217
90
|
raise ValueError(
|
218
91
|
"You must define at least one function, you can define this function in directory "
|
@@ -225,8 +98,8 @@ def agent(
|
|
225
98
|
|
226
99
|
if override_agent is None and chat_model is not None:
|
227
100
|
memory = MemorySaver()
|
228
|
-
|
229
|
-
settings.agent.agent =
|
101
|
+
_agent = create_react_agent(chat_model, functions, checkpointer=memory)
|
102
|
+
settings.agent.agent = _agent
|
230
103
|
else:
|
231
104
|
settings.agent.agent = override_agent
|
232
105
|
return wrapper
|
@@ -6,7 +6,9 @@ import httpx
|
|
6
6
|
from ... import errors
|
7
7
|
from ...client import AuthenticatedClient, Client
|
8
8
|
from ...models.create_workspace_service_account_body import CreateWorkspaceServiceAccountBody
|
9
|
-
from ...models.create_workspace_service_account_response_200 import
|
9
|
+
from ...models.create_workspace_service_account_response_200 import (
|
10
|
+
CreateWorkspaceServiceAccountResponse200,
|
11
|
+
)
|
10
12
|
from ...types import Response
|
11
13
|
|
12
14
|
|
@@ -5,7 +5,9 @@ import httpx
|
|
5
5
|
|
6
6
|
from ... import errors
|
7
7
|
from ...client import AuthenticatedClient, Client
|
8
|
-
from ...models.delete_workspace_service_account_response_200 import
|
8
|
+
from ...models.delete_workspace_service_account_response_200 import (
|
9
|
+
DeleteWorkspaceServiceAccountResponse200,
|
10
|
+
)
|
9
11
|
from ...types import Response
|
10
12
|
|
11
13
|
|
@@ -5,7 +5,9 @@ import httpx
|
|
5
5
|
|
6
6
|
from ... import errors
|
7
7
|
from ...client import AuthenticatedClient, Client
|
8
|
-
from ...models.get_workspace_service_accounts_response_200_item import
|
8
|
+
from ...models.get_workspace_service_accounts_response_200_item import (
|
9
|
+
GetWorkspaceServiceAccountsResponse200Item,
|
10
|
+
)
|
9
11
|
from ...types import Response
|
10
12
|
|
11
13
|
|
@@ -6,7 +6,9 @@ import httpx
|
|
6
6
|
from ... import errors
|
7
7
|
from ...client import AuthenticatedClient, Client
|
8
8
|
from ...models.update_workspace_service_account_body import UpdateWorkspaceServiceAccountBody
|
9
|
-
from ...models.update_workspace_service_account_response_200 import
|
9
|
+
from ...models.update_workspace_service_account_response_200 import (
|
10
|
+
UpdateWorkspaceServiceAccountResponse200,
|
11
|
+
)
|
10
12
|
from ...types import Response
|
11
13
|
|
12
14
|
|
beamlit/deploy/deploy.py
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
import ast
|
2
2
|
import json
|
3
3
|
import os
|
4
|
+
import shutil
|
4
5
|
import sys
|
5
|
-
import uuid
|
6
6
|
from logging import getLogger
|
7
|
+
from pathlib import Path
|
7
8
|
from typing import Literal
|
8
9
|
|
10
|
+
import yaml
|
11
|
+
|
12
|
+
from beamlit.api.agents import get_agent
|
13
|
+
from beamlit.authentication import new_client
|
14
|
+
from beamlit.client import AuthenticatedClient
|
9
15
|
from beamlit.common import slugify
|
10
16
|
from beamlit.common.settings import Settings, get_settings, init
|
11
17
|
from beamlit.models import (
|
@@ -15,26 +21,15 @@ from beamlit.models import (
|
|
15
21
|
Flavor,
|
16
22
|
Function,
|
17
23
|
FunctionSpec,
|
18
|
-
|
24
|
+
MetadataLabels,
|
19
25
|
)
|
20
26
|
|
21
|
-
from .format import arg_to_dict
|
27
|
+
from .format import arg_to_dict
|
22
28
|
from .parser import Resource, get_description, get_parameters, get_resources
|
23
29
|
|
24
30
|
sys.path.insert(0, os.getcwd())
|
25
31
|
sys.path.insert(0, os.path.join(os.getcwd(), "src"))
|
26
32
|
|
27
|
-
random_id = str(uuid.uuid4())[:8]
|
28
|
-
|
29
|
-
def get_runtime_image(type: str, name: str) -> str:
|
30
|
-
settings = get_settings()
|
31
|
-
registry_url = settings.registry_url.replace("https://", "").replace("http://", "")
|
32
|
-
image = f"{registry_url}/{settings.workspace}/{type}s/{name}"
|
33
|
-
# Generate a random ID to ensure unique image tags
|
34
|
-
image = f"{image}:{random_id}"
|
35
|
-
return image
|
36
|
-
|
37
|
-
|
38
33
|
def set_default_values(resource: Resource, deployment: Agent | Function):
|
39
34
|
settings = get_settings()
|
40
35
|
deployment.metadata.workspace = settings.workspace
|
@@ -45,10 +40,6 @@ def set_default_values(resource: Resource, deployment: Agent | Function):
|
|
45
40
|
deployment.metadata.display_name = deployment.metadata.name
|
46
41
|
if not deployment.spec.description:
|
47
42
|
deployment.spec.description = get_description(None, resource)
|
48
|
-
if not deployment.spec.runtime:
|
49
|
-
deployment.spec.runtime = Runtime()
|
50
|
-
if not deployment.spec.runtime.image:
|
51
|
-
deployment.spec.runtime.image = get_runtime_image(resource.type, deployment.metadata.name)
|
52
43
|
return deployment
|
53
44
|
|
54
45
|
def get_beamlit_deployment_from_resource(
|
@@ -105,7 +96,7 @@ def get_flavors(flavors: list[Flavor]) -> str:
|
|
105
96
|
return json.dumps([flavor.to_dict() for flavor in flavors])
|
106
97
|
|
107
98
|
def get_agent_yaml(
|
108
|
-
agent: Agent, functions: list[tuple[Resource, Function]], settings: Settings
|
99
|
+
agent: Agent, functions: list[tuple[Resource, Function]], settings: Settings, client: AuthenticatedClient
|
109
100
|
) -> str:
|
110
101
|
"""
|
111
102
|
Generates YAML configuration for an agent deployment.
|
@@ -118,30 +109,24 @@ def get_agent_yaml(
|
|
118
109
|
Returns:
|
119
110
|
str: YAML configuration string
|
120
111
|
"""
|
112
|
+
try:
|
113
|
+
agent_response = get_agent.sync(agent.metadata.name, client=client)
|
114
|
+
agent.spec.repository = agent_response.spec.repository
|
115
|
+
except Exception:
|
116
|
+
pass
|
117
|
+
agent.spec.functions = [slugify(function.metadata.name) for (_, function) in functions]
|
118
|
+
agent.metadata.labels = agent.metadata.labels and MetadataLabels.from_dict(agent.metadata.labels) or MetadataLabels()
|
119
|
+
agent.metadata.labels["x-beamlit-auto-generated"] = "true"
|
120
|
+
agent_yaml = yaml.dump(agent.to_dict())
|
121
121
|
template = f"""
|
122
122
|
apiVersion: beamlit.com/v1alpha1
|
123
123
|
kind: Agent
|
124
|
-
|
125
|
-
name: {slugify(agent.metadata.name)}
|
126
|
-
displayName: {agent.metadata.display_name or agent.metadata.name}
|
127
|
-
environment: {settings.environment}
|
128
|
-
workspace: {settings.workspace}
|
129
|
-
labels:
|
130
|
-
x-beamlit-auto-generated: "true"
|
131
|
-
spec:
|
132
|
-
enabled: true
|
133
|
-
policies: [{", ".join(agent.spec.policies or [])}]
|
134
|
-
functions: [{", ".join([f"{slugify(function.metadata.name)}" for (_, function) in functions])}]
|
135
|
-
agentChain: {format_agent_chain(agent.spec.agent_chain)}
|
136
|
-
model: {agent.spec.model}
|
124
|
+
{agent_yaml}
|
137
125
|
"""
|
138
|
-
if agent.spec.description:
|
139
|
-
template += f""" description: |
|
140
|
-
{agent.spec.description}"""
|
141
126
|
return template
|
142
127
|
|
143
128
|
|
144
|
-
def get_function_yaml(function: Function, settings: Settings) -> str:
|
129
|
+
def get_function_yaml(function: Function, settings: Settings, client: AuthenticatedClient) -> str:
|
145
130
|
"""
|
146
131
|
Generates YAML configuration for a function deployment.
|
147
132
|
|
@@ -152,21 +137,13 @@ def get_function_yaml(function: Function, settings: Settings) -> str:
|
|
152
137
|
Returns:
|
153
138
|
str: YAML configuration string
|
154
139
|
"""
|
140
|
+
function.metadata.labels = function.metadata.labels and MetadataLabels.from_dict(function.metadata.labels) or MetadataLabels()
|
141
|
+
function.metadata.labels["x-beamlit-auto-generated"] = "true"
|
142
|
+
function_yaml = yaml.dump(function.to_dict())
|
155
143
|
return f"""
|
156
144
|
apiVersion: beamlit.com/v1alpha1
|
157
145
|
kind: Function
|
158
|
-
|
159
|
-
name: {slugify(function.metadata.name)}
|
160
|
-
displayName: {function.metadata.display_name or function.metadata.name}
|
161
|
-
environment: {settings.environment}
|
162
|
-
labels:
|
163
|
-
x-beamlit-auto-generated: "true"
|
164
|
-
spec:
|
165
|
-
enabled: true
|
166
|
-
policies: [{", ".join(function.spec.policies or [])}]
|
167
|
-
description: |
|
168
|
-
{function.spec.description}
|
169
|
-
parameters: {format_parameters(function.spec.parameters)}
|
146
|
+
{function_yaml}
|
170
147
|
"""
|
171
148
|
|
172
149
|
|
@@ -220,6 +197,35 @@ ENV PATH="/beamlit/.venv/bin:$PATH"
|
|
220
197
|
ENTRYPOINT [{cmd_str}]
|
221
198
|
"""
|
222
199
|
|
200
|
+
def clean_auto_generated(
|
201
|
+
directory: str,
|
202
|
+
type: Literal["agent", "function"],
|
203
|
+
deployments: list[tuple[Resource, Agent | Function]]
|
204
|
+
):
|
205
|
+
"""
|
206
|
+
Cleans up auto-generated deployments of a specific type.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
directory (str): Base directory containing deployments
|
210
|
+
type (str): Type of deployment ("agent" or "function")
|
211
|
+
deployments (list[tuple[Resource, Agent | Function]]): List of deployment resources and configurations
|
212
|
+
"""
|
213
|
+
|
214
|
+
deploy_dir = Path(directory) / f"{type}s"
|
215
|
+
deploy_names = [d.metadata.name for (_, d) in deployments]
|
216
|
+
|
217
|
+
if deploy_dir.exists():
|
218
|
+
for item_dir in deploy_dir.iterdir():
|
219
|
+
if item_dir.is_dir() and item_dir.name not in deploy_names:
|
220
|
+
yaml_file = item_dir / f"{type}.yaml"
|
221
|
+
if yaml_file.exists():
|
222
|
+
with open(yaml_file) as f:
|
223
|
+
try:
|
224
|
+
content = yaml.safe_load(f)
|
225
|
+
if content.get("metadata", {}).get("labels", {}).get("x-beamlit-auto-generated") == "true":
|
226
|
+
shutil.rmtree(item_dir)
|
227
|
+
except yaml.YAMLError:
|
228
|
+
continue
|
223
229
|
|
224
230
|
def generate_beamlit_deployment(directory: str):
|
225
231
|
"""
|
@@ -234,6 +240,7 @@ def generate_beamlit_deployment(directory: str):
|
|
234
240
|
- Directory structure for agents and functions
|
235
241
|
"""
|
236
242
|
settings = init()
|
243
|
+
client = new_client()
|
237
244
|
logger = getLogger(__name__)
|
238
245
|
logger.info(f"Importing server module: {settings.server.module}")
|
239
246
|
functions: list[tuple[Resource, Function]] = []
|
@@ -257,28 +264,23 @@ def generate_beamlit_deployment(directory: str):
|
|
257
264
|
agent_dir = os.path.join(agents_dir, agent.metadata.name)
|
258
265
|
os.makedirs(agent_dir, exist_ok=True)
|
259
266
|
with open(os.path.join(agent_dir, "agent.yaml"), "w") as f:
|
260
|
-
content = get_agent_yaml(agent, functions, settings)
|
267
|
+
content = get_agent_yaml(agent, functions, settings, client)
|
261
268
|
f.write(content)
|
262
269
|
# write dockerfile for build
|
263
270
|
with open(os.path.join(agent_dir, "Dockerfile"), "w") as f:
|
264
271
|
content = dockerfile("agent", resource, agent)
|
265
272
|
f.write(content)
|
266
|
-
# write destination docker
|
267
|
-
with open(os.path.join(agent_dir, "destination.txt"), "w") as f:
|
268
|
-
content = agent.spec.runtime.image
|
269
|
-
f.write(content)
|
270
273
|
for resource, function in functions:
|
271
274
|
# write deployment file
|
272
275
|
function_dir = os.path.join(functions_dir, function.metadata.name)
|
273
276
|
os.makedirs(function_dir, exist_ok=True)
|
274
277
|
with open(os.path.join(function_dir, "function.yaml"), "w") as f:
|
275
|
-
content = get_function_yaml(function, settings)
|
278
|
+
content = get_function_yaml(function, settings, client)
|
276
279
|
f.write(content)
|
277
280
|
# write dockerfile for build
|
278
281
|
with open(os.path.join(function_dir, "Dockerfile"), "w") as f:
|
279
282
|
content = dockerfile("function", resource, function)
|
280
283
|
f.write(content)
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
f.write(content)
|
284
|
+
|
285
|
+
clean_auto_generated(directory, "agent", agents)
|
286
|
+
clean_auto_generated(directory, "function", functions)
|
beamlit/deploy/format.py
CHANGED
@@ -48,6 +48,16 @@ def format_parameters(parameters: list[StoreFunctionParameter]) -> str:
|
|
48
48
|
|
49
49
|
return "\n".join(formatted)
|
50
50
|
|
51
|
+
def format_dict(obj: dict) -> str:
|
52
|
+
if not obj:
|
53
|
+
return "null"
|
54
|
+
ret = ""
|
55
|
+
for k, v in obj.items():
|
56
|
+
if not v:
|
57
|
+
ret += f"{k}: null\n"
|
58
|
+
else:
|
59
|
+
ret += f"{k}: {v}\n"
|
60
|
+
return ret
|
51
61
|
|
52
62
|
def format_agent_chain(agentChain: list[AgentChain]) -> str:
|
53
63
|
"""
|
beamlit/functions/__init__.py
CHANGED
beamlit/functions/decorator.py
CHANGED
@@ -1,15 +1,161 @@
|
|
1
1
|
"""Decorators for creating function tools with Beamlit and LangChain integration."""
|
2
|
+
import ast
|
2
3
|
import asyncio
|
3
4
|
import functools
|
5
|
+
import importlib.util
|
6
|
+
import os
|
4
7
|
from collections.abc import Callable
|
5
8
|
from logging import getLogger
|
9
|
+
from typing import Union
|
6
10
|
|
7
11
|
from fastapi import Request
|
12
|
+
from langchain_core.tools import StructuredTool
|
13
|
+
from langchain_core.tools.base import create_schema_from_function
|
8
14
|
|
9
|
-
from beamlit.
|
15
|
+
from beamlit.agents.chain import ChainToolkit
|
16
|
+
from beamlit.authentication import new_client
|
17
|
+
from beamlit.client import AuthenticatedClient
|
18
|
+
from beamlit.common import slugify
|
19
|
+
from beamlit.common.settings import get_settings
|
20
|
+
from beamlit.functions.mcp.mcp import MCPClient, MCPToolkit
|
21
|
+
from beamlit.functions.remote.remote import RemoteToolkit
|
22
|
+
from beamlit.models import AgentChain, Function, FunctionKit
|
10
23
|
|
11
24
|
logger = getLogger(__name__)
|
12
25
|
|
26
|
+
def get_functions(
|
27
|
+
client:Union[AuthenticatedClient, None]=None,
|
28
|
+
dir:Union[str, None]=None,
|
29
|
+
mcp_hub:Union[list[str], None]=None,
|
30
|
+
remote_functions:Union[list[str], None]=None,
|
31
|
+
chain:Union[list[AgentChain], None]=None,
|
32
|
+
remote_functions_empty:bool=True,
|
33
|
+
from_decorator:str="function",
|
34
|
+
):
|
35
|
+
settings = get_settings()
|
36
|
+
if client is None:
|
37
|
+
client = new_client()
|
38
|
+
if dir is None:
|
39
|
+
dir = settings.agent.functions_directory
|
40
|
+
|
41
|
+
functions = []
|
42
|
+
logger = getLogger(__name__)
|
43
|
+
settings = get_settings()
|
44
|
+
|
45
|
+
# Walk through all Python files in functions directory and subdirectories
|
46
|
+
if not os.path.exists(dir):
|
47
|
+
if remote_functions_empty:
|
48
|
+
logger.warn(f"Functions directory {dir} not found")
|
49
|
+
return []
|
50
|
+
for root, _, files in os.walk(dir):
|
51
|
+
for file in files:
|
52
|
+
if file.endswith(".py"):
|
53
|
+
file_path = os.path.join(root, file)
|
54
|
+
# Read and compile the file content
|
55
|
+
with open(file_path) as f:
|
56
|
+
try:
|
57
|
+
file_content = f.read()
|
58
|
+
# Parse the file content to find decorated functions
|
59
|
+
tree = ast.parse(file_content)
|
60
|
+
|
61
|
+
# Look for function definitions with decorators
|
62
|
+
for node in ast.walk(tree):
|
63
|
+
if (
|
64
|
+
not isinstance(node, ast.FunctionDef)
|
65
|
+
and not isinstance(node, ast.AsyncFunctionDef)
|
66
|
+
) or len(node.decorator_list) == 0:
|
67
|
+
continue
|
68
|
+
decorator = node.decorator_list[0]
|
69
|
+
|
70
|
+
decorator_name = ""
|
71
|
+
if isinstance(decorator, ast.Call):
|
72
|
+
decorator_name = decorator.func.id
|
73
|
+
if isinstance(decorator, ast.Name):
|
74
|
+
decorator_name = decorator.id
|
75
|
+
if decorator_name == from_decorator:
|
76
|
+
# Get the function name and decorator name
|
77
|
+
func_name = node.name
|
78
|
+
|
79
|
+
# Import the module to get the actual function
|
80
|
+
spec = importlib.util.spec_from_file_location(func_name, file_path)
|
81
|
+
module = importlib.util.module_from_spec(spec)
|
82
|
+
spec.loader.exec_module(module)
|
83
|
+
# Check if kit=True in the decorator arguments
|
84
|
+
is_kit = False
|
85
|
+
if isinstance(decorator, ast.Call):
|
86
|
+
for keyword in decorator.keywords:
|
87
|
+
if keyword.arg == "kit" and isinstance(
|
88
|
+
keyword.value, ast.Constant
|
89
|
+
):
|
90
|
+
is_kit = keyword.value.value
|
91
|
+
if is_kit and not settings.remote:
|
92
|
+
kit_functions = get_functions(
|
93
|
+
client=client,
|
94
|
+
dir=os.path.join(root),
|
95
|
+
remote_functions_empty=remote_functions_empty,
|
96
|
+
from_decorator="kit",
|
97
|
+
)
|
98
|
+
functions.extend(kit_functions)
|
99
|
+
|
100
|
+
# Get the decorated function
|
101
|
+
if not is_kit and hasattr(module, func_name):
|
102
|
+
func = getattr(module, func_name)
|
103
|
+
if settings.remote:
|
104
|
+
toolkit = RemoteToolkit(client, slugify(func.__name__))
|
105
|
+
toolkit.initialize()
|
106
|
+
functions.extend(toolkit.get_tools())
|
107
|
+
else:
|
108
|
+
if asyncio.iscoroutinefunction(func):
|
109
|
+
functions.append(
|
110
|
+
StructuredTool(
|
111
|
+
name=func.__name__,
|
112
|
+
description=func.__doc__,
|
113
|
+
func=func,
|
114
|
+
coroutine=func,
|
115
|
+
args_schema=create_schema_from_function(func.__name__, func)
|
116
|
+
)
|
117
|
+
)
|
118
|
+
else:
|
119
|
+
|
120
|
+
functions.append(
|
121
|
+
StructuredTool(
|
122
|
+
name=func.__name__,
|
123
|
+
description=func.__doc__,
|
124
|
+
func=func,
|
125
|
+
args_schema=create_schema_from_function(func.__name__, func)
|
126
|
+
)
|
127
|
+
)
|
128
|
+
except Exception as e:
|
129
|
+
logger.warning(f"Error processing {file_path}: {e!s}")
|
130
|
+
|
131
|
+
if mcp_hub:
|
132
|
+
for server in mcp_hub:
|
133
|
+
try:
|
134
|
+
mcp_client = MCPClient(client, server)
|
135
|
+
toolkit = MCPToolkit(client=mcp_client)
|
136
|
+
toolkit.initialize()
|
137
|
+
functions.extend(toolkit.get_tools())
|
138
|
+
except Exception as e:
|
139
|
+
logger.warn(f"Failed to initialize MCP server {server}: {e!s}")
|
140
|
+
|
141
|
+
if remote_functions:
|
142
|
+
for function in remote_functions:
|
143
|
+
try:
|
144
|
+
toolkit = RemoteToolkit(client, function)
|
145
|
+
toolkit.initialize()
|
146
|
+
functions.extend(toolkit.get_tools())
|
147
|
+
except Exception as e:
|
148
|
+
logger.warn(f"Failed to initialize remote function {function}: {e!s}")
|
149
|
+
|
150
|
+
if chain:
|
151
|
+
toolkit = ChainToolkit(client, chain)
|
152
|
+
toolkit.initialize()
|
153
|
+
functions.extend(toolkit.get_tools())
|
154
|
+
|
155
|
+
return functions
|
156
|
+
|
157
|
+
|
158
|
+
|
13
159
|
def kit(bl_kit: FunctionKit = None, **kwargs: dict) -> Callable:
|
14
160
|
"""Create function tools with Beamlit and LangChain integration."""
|
15
161
|
|
beamlit/models/__init__.py
CHANGED
@@ -33,7 +33,9 @@ from .function_spec import FunctionSpec
|
|
33
33
|
from .get_trace_ids_response_200 import GetTraceIdsResponse200
|
34
34
|
from .get_trace_logs_response_200 import GetTraceLogsResponse200
|
35
35
|
from .get_trace_response_200 import GetTraceResponse200
|
36
|
-
from .get_workspace_service_accounts_response_200_item import
|
36
|
+
from .get_workspace_service_accounts_response_200_item import (
|
37
|
+
GetWorkspaceServiceAccountsResponse200Item,
|
38
|
+
)
|
37
39
|
from .increase_and_rate_metric import IncreaseAndRateMetric
|
38
40
|
from .integration_config import IntegrationConfig
|
39
41
|
from .integration_connection import IntegrationConnection
|
beamlit/serve/app.py
CHANGED
@@ -16,8 +16,8 @@ from beamlit.common import HTTPError, get_settings, init
|
|
16
16
|
from beamlit.common.instrumentation import (
|
17
17
|
get_resource_attributes,
|
18
18
|
get_span_exporter,
|
19
|
-
shutdown_instrumentation,
|
20
19
|
instrument_app,
|
20
|
+
shutdown_instrumentation,
|
21
21
|
)
|
22
22
|
|
23
23
|
from .middlewares import AccessLogMiddleware, AddProcessTimeHeader
|
@@ -6,8 +6,8 @@ beamlit/run.py,sha256=HtDYDjD7oVfQ8r3T5_t4qN5UDJOJfsQILi45Z21ArAg,1446
|
|
6
6
|
beamlit/types.py,sha256=E1hhDh_zXfsSQ0NCt9-uw90_Mr5iIlsdfnfvxv5HarU,1005
|
7
7
|
beamlit/agents/__init__.py,sha256=nf1iwQwGtCG6nDqyVhxfWoqR6dv6X3bvSpCeqkTCFaM,101
|
8
8
|
beamlit/agents/chain.py,sha256=vfCjiFHuu02uTTGicxMlFzjyICQkIjpXrBGs-7uJEsg,2826
|
9
|
-
beamlit/agents/chat.py,sha256=
|
10
|
-
beamlit/agents/decorator.py,sha256=
|
9
|
+
beamlit/agents/chat.py,sha256=eSpLhZkfL8Llf7TZeDnRT7HH70buBJO1DOwKm9UNeHs,3675
|
10
|
+
beamlit/agents/decorator.py,sha256=YU8FHrn9bYfgKuGtt42iPzOqiVvWC9GzbFLmhTacIsU,4183
|
11
11
|
beamlit/api/__init__.py,sha256=zTSiG_ujSjAqWPyc435YXaX9XTlpMjiJWBbV-f-YtdA,45
|
12
12
|
beamlit/api/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
beamlit/api/agents/create_agent.py,sha256=t5Pr62My2EhQlcIY71MrI73-0_q5Djr3a_Ybt9MIiQQ,3587
|
@@ -98,12 +98,12 @@ beamlit/api/privateclusters/update_private_cluster.py,sha256=Urb5GGuVcSwQy2WBlru
|
|
98
98
|
beamlit/api/privateclusters/update_private_cluster_health.py,sha256=PxCyl5ZEIqpQ_PKIr9ErqjZcoTSKlTZS1YgTT1JwhCg,2572
|
99
99
|
beamlit/api/service_accounts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
100
100
|
beamlit/api/service_accounts/create_api_key_for_service_account.py,sha256=Ee9CXzYqVh9OTJeai8bKaclznuWpJiPd74aPHZYZdeQ,4525
|
101
|
-
beamlit/api/service_accounts/create_workspace_service_account.py,sha256=
|
101
|
+
beamlit/api/service_accounts/create_workspace_service_account.py,sha256=D3NkGoPZyMi6ibj29F7xbJd5aLI2jw2vLSl1qDRGhW4,4648
|
102
102
|
beamlit/api/service_accounts/delete_api_key_for_service_account.py,sha256=bS2OWtO7Dn8FU2uaTe9h7VCxiobti8Ka2NtTUKBd31Q,2621
|
103
|
-
beamlit/api/service_accounts/delete_workspace_service_account.py,sha256=
|
104
|
-
beamlit/api/service_accounts/get_workspace_service_accounts.py,sha256=
|
103
|
+
beamlit/api/service_accounts/delete_workspace_service_account.py,sha256=XEz6-DkWZTmuaTOMooaywKvBsGilBYR-xG32RsXP5Mw,4136
|
104
|
+
beamlit/api/service_accounts/get_workspace_service_accounts.py,sha256=6c7T_WsXCuotQomm4zkAkAkhdLEcgVyvon9aYHPiEiI,4151
|
105
105
|
beamlit/api/service_accounts/list_api_keys_for_service_account.py,sha256=skp0PmjxfW7EGAyRcVAye_SKfryKjvsTuhSRxeIaXDo,4066
|
106
|
-
beamlit/api/service_accounts/update_workspace_service_account.py,sha256=
|
106
|
+
beamlit/api/service_accounts/update_workspace_service_account.py,sha256=Y4rQL8Yx8yRZSzTJz5-ESP5zQ0juVhuoX6xwpg5ZM00,4912
|
107
107
|
beamlit/api/store/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
108
108
|
beamlit/api/store/get_store_agent.py,sha256=UjvsRXyFsXZLf4utQjpiOiPM7KxlRj4C-x_ac5EXoLk,3624
|
109
109
|
beamlit/api/store/get_store_function.py,sha256=kEbg91jJh0XAXmipjzwg931mxkMCZ820PxiJ89zYbso,3756
|
@@ -137,11 +137,11 @@ beamlit/common/settings.py,sha256=b2rvby-ufG3M0AB1ReoWFM-1EzF1LaE-gbokO9HvQDI,38
|
|
137
137
|
beamlit/common/slugify.py,sha256=nR29r37IdWS2i44ZC6ZsXRgqKPYmvMGtFQ7BuIQUTlc,90
|
138
138
|
beamlit/common/utils.py,sha256=jouz5igBvT37Xn_e94-foCHyQczVim-UzVcoIF6RWJ4,657
|
139
139
|
beamlit/deploy/__init__.py,sha256=GS7l7Jtm2yKs7iNLKcfjYO-rAhUzggQ3xiYSf3oxLBY,91
|
140
|
-
beamlit/deploy/deploy.py,sha256=
|
141
|
-
beamlit/deploy/format.py,sha256=
|
140
|
+
beamlit/deploy/deploy.py,sha256=h8ugvkSGcMKLqM-_uD5BZUzng5tJRp-Gea1Jup6QzTE,10257
|
141
|
+
beamlit/deploy/format.py,sha256=U6UZEFAYLnGJJ7O2YmSdlUUFhnWNGAv6NZ-DW4KTgvI,2049
|
142
142
|
beamlit/deploy/parser.py,sha256=Ga0poCZkoRnuTw082QnTcNGCBJncoRAnVsn8-1FsaJE,6907
|
143
|
-
beamlit/functions/__init__.py,sha256=
|
144
|
-
beamlit/functions/decorator.py,sha256=
|
143
|
+
beamlit/functions/__init__.py,sha256=NcQPZZNfWhAJ1T1F6Xn21LFPMbZ7aMR2Sve3uZOkBCQ,170
|
144
|
+
beamlit/functions/decorator.py,sha256=6Sc-ZX5g-p8GlmdI2MmTW-XhVa8eeMawmwSQlndTLCE,8718
|
145
145
|
beamlit/functions/github/__init__.py,sha256=gYnUkeegukOfbymdabuuJkScvH-_ZJygX05BoqkPn0o,49
|
146
146
|
beamlit/functions/github/github.py,sha256=FajzLCNkpXcwfgnC0l9rOGT2eSPLCz8-qrMzK9N_ZNc,598
|
147
147
|
beamlit/functions/github/kit/__init__.py,sha256=jBwPqZv6C23_utukohxqXZwrlicNlI7PYPUj0Den7Cw,136
|
@@ -152,7 +152,7 @@ beamlit/functions/mcp/mcp.py,sha256=-LL7O35vTlcYfF1MSlEY83rBKKShJTaHB-y9MRPAEiU,
|
|
152
152
|
beamlit/functions/remote/remote.py,sha256=AL8WhD7yXZ18h3iU4vE9E4JtJt0n_i-ZwlbBDoolnEs,3767
|
153
153
|
beamlit/functions/search/__init__.py,sha256=5NAthQ9PBwrkNg1FpLRx4flauvv0HyWuwaVS589c1Pw,49
|
154
154
|
beamlit/functions/search/search.py,sha256=8s9ECltq7YE17j6rTxb12uY2EQY4_eTLHmwlIMThI0w,515
|
155
|
-
beamlit/models/__init__.py,sha256=
|
155
|
+
beamlit/models/__init__.py,sha256=YMCHuYVLqV03yE_D8TB15I07F7wS1DYV022owTeMRYA,7897
|
156
156
|
beamlit/models/acl.py,sha256=tH67gsl_BMaviSbTaaIkO1g9cWZgJ6VgAnYVjQSzGZY,3952
|
157
157
|
beamlit/models/agent.py,sha256=nGRGNghN5RID5q8oikPVzj1_Aahh9GmDhVPMIA901zc,4372
|
158
158
|
beamlit/models/agent_chain.py,sha256=8PN8wVSayS-LoBN2nahZsOmr6r3t62H_LPDK_8fnkM8,2255
|
@@ -258,10 +258,10 @@ beamlit/models/websocket_channel.py,sha256=jg3vN7yS_oOIwGtndtIUr1LsyEA58RXLXahqS
|
|
258
258
|
beamlit/models/workspace.py,sha256=7G3Q9_D9n7_AczznYOToAsEGjWgMJwhnFb3flYNg2ro,4245
|
259
259
|
beamlit/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8fWFfkCf7hzCTiNp4A,1246
|
260
260
|
beamlit/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
|
261
|
-
beamlit/serve/app.py,sha256=
|
261
|
+
beamlit/serve/app.py,sha256=_aG2UVQ3Y85rUW3ehu9TlzLnowkfh54IIz558ftqOMw,3638
|
262
262
|
beamlit/serve/middlewares/__init__.py,sha256=1dVmnOmhAQWvWktqHkKSIX-YoF6fmMU8xkUQuhg_rJU,148
|
263
263
|
beamlit/serve/middlewares/accesslog.py,sha256=Mu4T4_9OvHybjA0ApzZFpgi2C8f3X1NbUk-76v634XM,631
|
264
264
|
beamlit/serve/middlewares/processtime.py,sha256=lDAaIasZ4bwvN-HKHvZpaD9r-yrkVNZYx4abvbjbrCg,411
|
265
|
-
beamlit-0.0.
|
266
|
-
beamlit-0.0.
|
267
|
-
beamlit-0.0.
|
265
|
+
beamlit-0.0.34rc62.dist-info/METADATA,sha256=a5Vc8440X2LFgBPnIwx0SqImcwaKO3_OaUVLfTl7K7M,2412
|
266
|
+
beamlit-0.0.34rc62.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
267
|
+
beamlit-0.0.34rc62.dist-info/RECORD,,
|
File without changes
|