beamlit 0.0.22rc11__py3-none-any.whl → 0.0.23__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
beamlit/agents/chat.py CHANGED
@@ -37,12 +37,13 @@ def get_chat_model(agent_model: AgentDeployment):
37
37
  headers = get_authentication_headers(settings)
38
38
  headers["X-Beamlit-Environment"] = agent_model.environment
39
39
 
40
- jwt = headers.pop("X-Beamlit-Authorization").replace("Bearer ", "")
40
+ jwt = headers.get("X-Beamlit-Authorization", "").replace("Bearer ", "")
41
41
  params = {"environment": agent_model.environment}
42
42
  chat_classes = {
43
43
  "openai": {
44
44
  "func": get_openai_chat_model,
45
45
  "kwargs": {
46
+ "http_async_client": client.get_async_httpx_client(),
46
47
  "http_client": client.get_httpx_client(),
47
48
  },
48
49
  },
@@ -1,17 +1,18 @@
1
1
  # Import necessary modules
2
2
  import ast
3
+ import asyncio
3
4
  import importlib
4
5
  import os
5
6
  from logging import getLogger
6
7
 
7
- from langgraph.checkpoint.memory import MemorySaver
8
- from langgraph.prebuilt import create_react_agent
9
-
10
8
  from beamlit.api.models import get_model_deployment
11
9
  from beamlit.authentication import new_client
12
10
  from beamlit.common.settings import get_settings, init
13
11
  from beamlit.errors import UnexpectedStatus
14
12
  from beamlit.models import AgentDeployment
13
+ from langchain_core.tools import Tool
14
+ from langgraph.checkpoint.memory import MemorySaver
15
+ from langgraph.prebuilt import create_react_agent
15
16
 
16
17
  from .chat import get_chat_model
17
18
 
@@ -35,7 +36,7 @@ def get_functions(dir="src/functions", from_decorator="function"):
35
36
  # Look for function definitions with decorators
36
37
  for node in ast.walk(tree):
37
38
  if (
38
- not isinstance(node, ast.FunctionDef)
39
+ (not isinstance(node, ast.FunctionDef) and not isinstance(node, ast.AsyncFunctionDef))
39
40
  or len(node.decorator_list) == 0
40
41
  ):
41
42
  continue
@@ -73,7 +74,10 @@ def get_functions(dir="src/functions", from_decorator="function"):
73
74
  # Get the decorated function
74
75
  if not is_kit and hasattr(module, func_name):
75
76
  func = getattr(module, func_name)
76
- functions.append(func)
77
+ if asyncio.iscoroutinefunction(func):
78
+ functions.append(Tool(name=func.__name__, description=func.__doc__, func=func, coroutine=func))
79
+ else:
80
+ functions.append(Tool(name=func.__name__, description=func.__doc__, func=func))
77
81
  except Exception as e:
78
82
  logger.warning(f"Error processing {file_path}: {e!s}")
79
83
  return functions
@@ -51,7 +51,6 @@ def new_client():
51
51
  else:
52
52
  settings = get_settings()
53
53
  credentials = load_credentials_from_settings(settings)
54
-
55
54
  client_config = RunClientWithCredentials(
56
55
  credentials=credentials,
57
56
  workspace=settings.workspace,
@@ -130,7 +130,7 @@ def load_credentials(workspace_name: str) -> Credentials:
130
130
  def load_credentials_from_settings(settings: Settings) -> Credentials:
131
131
  return Credentials(
132
132
  api_key=settings.authentication.api_key,
133
- client_credentials=settings.authentication.client_credentials,
133
+ client_credentials=settings.authentication.client.credentials,
134
134
  )
135
135
 
136
136
 
beamlit/client.py CHANGED
@@ -248,7 +248,6 @@ class AuthenticatedClient:
248
248
  def get_async_httpx_client(self) -> httpx.AsyncClient:
249
249
  """Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
250
250
  if self._async_client is None:
251
- self._headers[self.auth_header_name] = f"{self.prefix} {self.token}" if self.prefix else self.token
252
251
  self._async_client = httpx.AsyncClient(
253
252
  base_url=self._base_url,
254
253
  cookies=self._cookies,
@@ -2,16 +2,6 @@ import os
2
2
  from logging import getLogger
3
3
  from typing import List, Tuple, Type, Union
4
4
 
5
- from langchain_core.language_models.chat_models import BaseChatModel
6
- from langgraph.graph.graph import CompiledGraph
7
- from pydantic import Field
8
- from pydantic_settings import (
9
- BaseSettings,
10
- PydanticBaseSettingsSource,
11
- SettingsConfigDict,
12
- YamlConfigSettingsSource,
13
- )
14
-
15
5
  from beamlit.api.functions import get_function_deployment
16
6
  from beamlit.api.models import get_model_deployment
17
7
  from beamlit.client import AuthenticatedClient
@@ -20,6 +10,11 @@ from beamlit.models.agent_deployment import AgentDeployment
20
10
  from beamlit.models.function_deployment import FunctionDeployment
21
11
  from beamlit.models.model_deployment import ModelDeployment
22
12
  from beamlit.types import UNSET, Unset
13
+ from langchain_core.language_models.chat_models import BaseChatModel
14
+ from langgraph.graph.graph import CompiledGraph
15
+ from pydantic import Field
16
+ from pydantic_settings import (BaseSettings, PydanticBaseSettingsSource,
17
+ SettingsConfigDict, YamlConfigSettingsSource)
23
18
 
24
19
  global SETTINGS
25
20
  SETTINGS = None
@@ -39,10 +34,14 @@ class SettingsAgent(BaseSettings):
39
34
  module: str = Field(default="main.main")
40
35
 
41
36
 
37
+ class SettingsAuthenticationClient(BaseSettings):
38
+ credentials: Union[None, str] = None
39
+
40
+
42
41
  class SettingsAuthentication(BaseSettings):
43
42
  api_key: Union[None, str] = None
44
43
  jwt: Union[None, str] = None
45
- client_credentials: Union[None, str] = None
44
+ client: SettingsAuthenticationClient = SettingsAuthenticationClient()
46
45
 
47
46
 
48
47
  class SettingsServer(BaseSettings):
@@ -56,14 +55,17 @@ class Settings(BaseSettings):
56
55
  yaml_file="beamlit.yaml",
57
56
  env_prefix="bl_",
58
57
  env_nested_delimiter="_",
58
+ extra="ignore",
59
59
  )
60
60
 
61
61
  workspace: str
62
62
  environment: str = Field(default="production")
63
+ remote: bool = Field(default=False)
63
64
  type: str = Field(default="agent")
64
65
  name: str = Field(default="beamlit-agent")
65
66
  base_url: str = Field(default="https://api.beamlit.dev/v0")
66
67
  run_url: str = Field(default="https://run.beamlit.dev")
68
+ registry_url: str = Field(default="https://serverless-registry-production.beamlit.workers.dev")
67
69
  log_level: str = Field(default="INFO")
68
70
  agent: SettingsAgent = SettingsAgent()
69
71
  server: SettingsServer = SettingsServer()
@@ -0,0 +1,3 @@
1
+ from .deploy import generate_beamlit_deployment
2
+
3
+ __all__ = ["generate_beamlit_deployment"]
@@ -0,0 +1,503 @@
1
+ import ast
2
+ import importlib
3
+ import json
4
+ import os
5
+ import sys
6
+ from dataclasses import dataclass
7
+ from logging import getLogger
8
+ from typing import Callable, Literal
9
+
10
+ from beamlit.common.settings import Settings, get_settings, init
11
+ from beamlit.models import (AgentChain, AgentDeployment, Flavor,
12
+ FunctionDeployment, Runtime,
13
+ StoreFunctionParameter)
14
+
15
+ sys.path.insert(0, os.getcwd())
16
+ sys.path.insert(0, os.path.join(os.getcwd(), "src"))
17
+
18
+
19
+ @dataclass
20
+ class Resource:
21
+ type: Literal["agent", "function"]
22
+ module: Callable
23
+ name: str
24
+ decorator: ast.Call
25
+ func: Callable
26
+
27
+ def get_resources(from_decorator, dir="src") -> list[Resource]:
28
+ """
29
+ Scans through Python files in a directory to find functions decorated with a specific decorator.
30
+
31
+ Args:
32
+ from_decorator (str): The name of the decorator to search for
33
+ dir (str): The directory to scan, defaults to "src"
34
+
35
+ Returns:
36
+ list[Resource]: List of Resource objects containing information about decorated functions
37
+ """
38
+ resources = []
39
+ logger = getLogger(__name__)
40
+
41
+ # Walk through all Python files in resources directory and subdirectories
42
+ for root, _, files in os.walk(dir):
43
+ for file in files:
44
+ if file.endswith(".py"):
45
+ file_path = os.path.join(root, file)
46
+ # Read and compile the file content
47
+ with open(file_path) as f:
48
+ try:
49
+ file_content = f.read()
50
+ # Parse the file content to find decorated resources
51
+ tree = ast.parse(file_content)
52
+
53
+ # Look for function definitions with decorators
54
+ for node in ast.walk(tree):
55
+ if (
56
+ not isinstance(node, ast.FunctionDef) and not isinstance(node, ast.AsyncFunctionDef)
57
+ ) or len(node.decorator_list) == 0:
58
+ continue
59
+ decorator = node.decorator_list[0]
60
+
61
+ decorator_name = ""
62
+ if isinstance(decorator, ast.Call):
63
+ decorator_name = decorator.func.id
64
+ if isinstance(decorator, ast.Name):
65
+ decorator_name = decorator.id
66
+ if decorator_name == from_decorator:
67
+ # Get the function name and decorator name
68
+ func_name = node.name
69
+
70
+ # Import the module to get the actual function
71
+ spec = importlib.util.spec_from_file_location(func_name, file_path)
72
+ module = importlib.util.module_from_spec(spec)
73
+ spec.loader.exec_module(module)
74
+ # Check if kit=True in the decorator arguments
75
+
76
+ # Get the decorated function
77
+ if hasattr(module, func_name) and isinstance(decorator, ast.Call):
78
+
79
+ resources.append(
80
+ Resource(
81
+ type=decorator_name,
82
+ module=module,
83
+ name=func_name,
84
+ func=getattr(module, func_name),
85
+ decorator=decorator,
86
+ )
87
+ )
88
+ except Exception as e:
89
+ logger.warning(f"Error processing {file_path}: {e!s}")
90
+ return resources
91
+
92
+
93
+ def get_parameters(resource: Resource) -> list[StoreFunctionParameter]:
94
+ """
95
+ Extracts parameter information from a function's signature and docstring.
96
+
97
+ Args:
98
+ resource (Resource): The resource object containing the function to analyze
99
+
100
+ Returns:
101
+ list[StoreFunctionParameter]: List of parameter objects with name, type, required status, and description
102
+ """
103
+ parameters = []
104
+ # Get function signature
105
+ import inspect
106
+ sig = inspect.signature(resource.func)
107
+ # Get docstring for parameter descriptions
108
+ docstring = inspect.getdoc(resource.func)
109
+ param_descriptions = {}
110
+ if docstring:
111
+ # Parse docstring for parameter descriptions
112
+ lines = docstring.split('\n')
113
+ for line in lines:
114
+ line = line.strip().lower()
115
+ if line.startswith(':param '):
116
+ # Extract parameter name and description
117
+ param_line = line[7:].split(':', 1)
118
+ if len(param_line) == 2:
119
+ param_name = param_line[0].strip()
120
+ param_desc = param_line[1].strip()
121
+ param_descriptions[param_name] = param_desc
122
+ for name, param in sig.parameters.items():
123
+ # Skip *args and **kwargs parameters
124
+ if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
125
+
126
+ continue
127
+
128
+ param_type = "string" # Default type
129
+ type_mapping = {
130
+ 'str': 'string',
131
+ 'int': 'integer',
132
+ 'float': 'number',
133
+ 'bool': 'boolean',
134
+ 'list': 'array',
135
+ 'dict': 'object',
136
+ 'none': 'null'
137
+ }
138
+ if param.annotation != inspect.Parameter.empty:
139
+ # Map Python types to OpenAPI types
140
+ if hasattr(param.annotation, "__name__"):
141
+ param_type = param.annotation.__name__.lower()
142
+ else:
143
+ # Handle special types like Union, Optional etc
144
+ param_type = str(param.annotation).lower()
145
+ parameter = StoreFunctionParameter(
146
+ name=name,
147
+ type_=type_mapping.get(param_type, "string"),
148
+ required=param.default == inspect.Parameter.empty,
149
+ description=param_descriptions.get(name, f"Parameter {name}")
150
+ )
151
+ parameters.append(parameter)
152
+
153
+ return parameters
154
+
155
+
156
+ def get_description(description: str | None, resource: Resource) -> str:
157
+ """
158
+ Gets the description of a function from either a provided description or the function's docstring.
159
+
160
+ Args:
161
+ description (str | None): Optional explicit description
162
+ resource (Resource): The resource object containing the function
163
+
164
+ Returns:
165
+ str: The function description
166
+ """
167
+ if description:
168
+ return description
169
+ doc = resource.func.__doc__
170
+ if doc:
171
+ # Split docstring into sections and get only the description part
172
+ doc_lines = doc.split('\n')
173
+ description_lines = []
174
+ for line in doc_lines:
175
+ line = line.strip()
176
+ # Stop when we hit param/return sections
177
+ if line.startswith(':param') or line.startswith(':return'):
178
+ break
179
+ if line:
180
+ description_lines.append(line)
181
+ return ' '.join(description_lines).strip()
182
+ return ""
183
+
184
+ def get_kwargs(arg: ast.Call) -> dict:
185
+ """
186
+ Extracts keyword arguments from an AST Call node.
187
+
188
+ Args:
189
+ arg (ast.Call): The AST Call node to process
190
+
191
+ Returns:
192
+ dict: Dictionary of keyword arguments and their values
193
+ """
194
+ kwargs = {}
195
+ for keyword in arg.keywords:
196
+ if isinstance(keyword.value, ast.Constant):
197
+ kwargs[keyword.arg] = keyword.value.value
198
+ elif isinstance(keyword.value, (ast.List, ast.Tuple)):
199
+ kwargs[keyword.arg] = [
200
+ AgentChain(**get_kwargs(elem)) if isinstance(elem, ast.Call) and isinstance(elem.func, ast.Name) and elem.func.id == "AgentChain"
201
+ else elem.value if isinstance(elem, ast.Constant) else elem
202
+ for elem in keyword.value.elts
203
+ ]
204
+ elif isinstance(keyword.value, ast.Dict):
205
+ kwargs[keyword.arg] = {}
206
+ for k, v in zip(keyword.value.keys, keyword.value.values):
207
+ if isinstance(k, ast.Constant) and isinstance(v, ast.Constant):
208
+ kwargs[keyword.arg][k.value] = v.value
209
+ if isinstance(k, ast.Constant) and isinstance(v, ast.Call):
210
+ kwargs[keyword.arg][k.value] = get_kwargs(v)
211
+ return kwargs
212
+
213
+ def get_runtime(type: str, name: str) -> Runtime:
214
+ settings = get_settings()
215
+ registry_url = settings.registry_url.replace("https://", "").replace("http://", "")
216
+ image = f"{registry_url}/{settings.workspace}/{type}s/{name}"
217
+ return Runtime(image=image)
218
+
219
+ def get_beamlit_deployment_from_resource(resource: Resource) -> AgentDeployment | FunctionDeployment:
220
+ """
221
+ Creates a deployment configuration from a resource.
222
+
223
+ Args:
224
+ resource (Resource): The resource to create a deployment for
225
+
226
+ Returns:
227
+ AgentDeployment | FunctionDeployment: The deployment configuration
228
+ """
229
+ for arg in resource.decorator.args:
230
+ if isinstance(arg, ast.Call):
231
+ if isinstance(arg.func, ast.Name) and arg.func.id == "AgentDeployment":
232
+ kwargs = get_kwargs(arg)
233
+ description = kwargs.pop("description", None)
234
+ return AgentDeployment(
235
+ **kwargs,
236
+ description=get_description(description, resource),
237
+ runtime=get_runtime("agent", kwargs.get("agent", resource.name))
238
+ )
239
+ if isinstance(arg.func, ast.Name) and arg.func.id == "FunctionDeployment":
240
+ kwargs = get_kwargs(arg)
241
+ description = kwargs.pop("description", None)
242
+ return FunctionDeployment(
243
+ **kwargs,
244
+ parameters=get_parameters(resource),
245
+ description=get_description(description, resource),
246
+ runtime=get_runtime("function", kwargs.get("function", resource.name))
247
+ )
248
+ for arg in resource.decorator.keywords:
249
+ if isinstance(arg.value, ast.Call):
250
+ if isinstance(arg.value.func, ast.Name) and arg.value.func.id == "AgentDeployment":
251
+ kwargs = get_kwargs(arg.value)
252
+ description = kwargs.pop("description", None)
253
+ return AgentDeployment(
254
+ **kwargs,
255
+ description=get_description(description, resource),
256
+ runtime=get_runtime("agent", kwargs.get("agent", resource.name))
257
+ )
258
+ if isinstance(arg.value.func, ast.Name) and arg.value.func.id == "FunctionDeployment":
259
+ kwargs = get_kwargs(arg.value)
260
+ description = kwargs.pop("description", None)
261
+ return FunctionDeployment(
262
+ **kwargs,
263
+ parameters=get_parameters(resource),
264
+ description=get_description(description, resource),
265
+ runtime=get_runtime("function", kwargs.get("function", resource.name))
266
+ )
267
+ if resource.type == "agent":
268
+ return AgentDeployment(
269
+ agent=resource.name,
270
+ description=get_description(None,resource),
271
+ runtime=get_runtime("agent", resource.name)
272
+ )
273
+ if resource.type == "function":
274
+ return FunctionDeployment(
275
+ function=resource.name,
276
+ parameters=get_parameters(resource),
277
+ description=get_description(None,resource),
278
+ runtime=get_runtime("function", resource.name)
279
+ )
280
+ return None
281
+
282
+
283
+ def get_flavors(flavors: list[Flavor]) -> str:
284
+ """
285
+ Converts a list of Flavor objects to JSON string.
286
+
287
+ Args:
288
+ flavors (list[Flavor]): List of Flavor objects
289
+
290
+ Returns:
291
+ str: JSON string representation of flavors
292
+ """
293
+ if not flavors:
294
+ return "[]"
295
+ return json.dumps([flavor.to_dict() for flavor in flavors])
296
+
297
+ def format_parameters(parameters: list[StoreFunctionParameter]) -> str:
298
+ """
299
+ Formats function parameters into YAML-compatible string.
300
+
301
+ Args:
302
+ parameters (list[StoreFunctionParameter]): List of parameter objects
303
+
304
+ Returns:
305
+ str: YAML-formatted string of parameters
306
+ """
307
+ if not parameters:
308
+ return "[]"
309
+
310
+ formatted = []
311
+ for param in parameters:
312
+ formatted.append(f"""
313
+ - name: {param.name}
314
+ type: {param.type_}
315
+ required: {str(param.required).lower()}
316
+ description: {param.description}""")
317
+
318
+ return "\n".join(formatted)
319
+
320
+ def format_agent_chain(agent_chain: list[AgentChain]) -> str:
321
+ """
322
+ Formats agent chain configuration into YAML-compatible string.
323
+
324
+ Args:
325
+ agent_chain (list[AgentChain]): List of agent chain configurations
326
+
327
+ Returns:
328
+ str: YAML-formatted string of agent chain
329
+ """
330
+ if not agent_chain:
331
+ return "[]"
332
+ formatted = []
333
+
334
+ for agent in agent_chain:
335
+ formatted.append(f"""
336
+ - agent: {agent.name}
337
+ enabled: {agent.enabled}""")
338
+ if agent.description:
339
+ formatted.append(f" description: {agent.description}")
340
+ return "\n".join(formatted)
341
+
342
+ def get_agent_yaml(agent: AgentDeployment, functions: list[tuple[Resource, FunctionDeployment]], settings: Settings) -> str:
343
+ """
344
+ Generates YAML configuration for an agent deployment.
345
+
346
+ Args:
347
+ agent (AgentDeployment): Agent deployment configuration
348
+ functions (list[tuple[Resource, FunctionDeployment]]): List of associated functions
349
+ settings (Settings): Application settings
350
+
351
+ Returns:
352
+ str: YAML configuration string
353
+ """
354
+ template = f"""
355
+ apiVersion: beamlit.com/v1alpha1
356
+ kind: Agent
357
+ metadata:
358
+ name: {agent.agent}
359
+ spec:
360
+ display_name: {agent.agent}
361
+ deployments:
362
+ - environment: {settings.environment}
363
+ enabled: true
364
+ policies: [{", ".join(agent.policies or [])}]
365
+ functions: [{", ".join([f"{function.function}" for (_, function) in functions])}]
366
+ agent_chain: {format_agent_chain(agent.agent_chain)}
367
+ model: {agent.model}
368
+ runtime:
369
+ image: {agent.runtime.image}
370
+ """
371
+ if agent.description:
372
+ template += f""" description: |
373
+ {agent.description}"""
374
+ return template
375
+
376
+ def get_function_yaml(function: FunctionDeployment, settings: Settings) -> str:
377
+ """
378
+ Generates YAML configuration for a function deployment.
379
+
380
+ Args:
381
+ function (FunctionDeployment): Function deployment configuration
382
+ settings (Settings): Application settings
383
+
384
+ Returns:
385
+ str: YAML configuration string
386
+ """
387
+ return f"""
388
+ apiVersion: beamlit.com/v1alpha1
389
+ kind: Function
390
+ metadata:
391
+ name: {function.function}
392
+ spec:
393
+ display_name: {function.function}
394
+ deployments:
395
+ - environment: {settings.environment}
396
+ enabled: true
397
+ policies: [{", ".join(function.policies or [])}]
398
+ description: |
399
+ {function.description}
400
+ parameters: {format_parameters(function.parameters)}
401
+ runtime:
402
+ image: {function.runtime.image}
403
+ """
404
+
405
+ def dockerfile(type: Literal["agent", "function"], resource: Resource, deployment: AgentDeployment | FunctionDeployment) -> str:
406
+ """
407
+ Generates Dockerfile content for agent or function deployment.
408
+
409
+ Args:
410
+ type (Literal["agent", "function"]): Type of deployment
411
+ resource (Resource): Resource to be deployed
412
+ deployment (AgentDeployment | FunctionDeployment): Deployment configuration
413
+
414
+ Returns:
415
+ str: Dockerfile content
416
+ """
417
+ if type == "agent":
418
+ module = f"{resource.module.__file__.split('/')[-1].replace('.py', '')}.{resource.module.__name__}"
419
+ else:
420
+ module = f"functions.{resource.module.__name__}.{resource.func.__name__}"
421
+ cmd = ["bl", "serve", "--port", "80", "--module", module]
422
+ if type == "agent":
423
+ cmd.append("--remote")
424
+ cmd_str = ','.join([f'"{c}"' for c in cmd])
425
+
426
+ return f"""
427
+ FROM python:3.12-slim
428
+
429
+ ARG UV_VERSION="latest"
430
+ RUN apt update && apt install -y curl
431
+
432
+ # Install uv.
433
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
434
+ RUN curl -fsSL https://raw.githubusercontent.com/beamlit/toolkit/main/install.sh | BINDIR=/bin sh
435
+ WORKDIR /beamlit
436
+
437
+ # Install the application dependencies.
438
+ COPY pyproject.toml /beamlit/pyproject.toml
439
+ COPY uv.lock /beamlit/uv.lock
440
+ RUN uv sync --no-cache
441
+
442
+ COPY README.md /beamlit/README.md
443
+ COPY LICENSE /beamlit/LICENSE
444
+ COPY src /beamlit/src
445
+
446
+ ENV PATH="/beamlit/.venv/bin:$PATH"
447
+
448
+ ENTRYPOINT [{cmd_str}]
449
+ """
450
+
451
+ def generate_beamlit_deployment(directory: str):
452
+ """
453
+ Generates all necessary deployment files for Beamlit agents and functions.
454
+
455
+ Args:
456
+ directory (str): Target directory for generated files
457
+
458
+ Creates:
459
+ - Agent and function YAML configurations
460
+ - Dockerfiles for each deployment
461
+ - Directory structure for agents and functions
462
+ """
463
+ settings = init()
464
+ logger = getLogger(__name__)
465
+ logger.info(f"Importing server module: {settings.server.module}")
466
+ functions: list[tuple[Resource, FunctionDeployment]] = []
467
+ agents: list[tuple[Resource, AgentDeployment]] = []
468
+ for agent in get_resources("agent"):
469
+ agent_deployment = get_beamlit_deployment_from_resource(agent)
470
+ if agent_deployment:
471
+ agents.append((agent, agent_deployment))
472
+ for function in get_resources("function"):
473
+ function_deployment = get_beamlit_deployment_from_resource(function)
474
+ if function_deployment:
475
+ functions.append((function, function_deployment))
476
+
477
+ agents_dir = os.path.join(directory, "agents")
478
+ functions_dir = os.path.join(directory, "functions")
479
+ # Create directory if it doesn't exist
480
+ os.makedirs(agents_dir, exist_ok=True)
481
+ os.makedirs(functions_dir, exist_ok=True)
482
+ for (resource, agent) in agents:
483
+ # write deployment file
484
+ agent_dir = os.path.join(agents_dir, agent.agent)
485
+ os.makedirs(agent_dir, exist_ok=True)
486
+ with open(os.path.join(agent_dir, f"agent.yaml"), "w") as f:
487
+ content = get_agent_yaml(agent, functions, settings)
488
+ f.write(content)
489
+ # write dockerfile for build
490
+ with open(os.path.join(agent_dir, f"Dockerfile"), "w") as f:
491
+ content = dockerfile("agent", resource, agent)
492
+ f.write(content)
493
+ for (resource, function) in functions:
494
+ # write deployment file
495
+ function_dir = os.path.join(functions_dir, function.function)
496
+ os.makedirs(function_dir, exist_ok=True)
497
+ with open(os.path.join(function_dir, f"function.yaml"), "w") as f:
498
+ content = get_function_yaml(function, settings)
499
+ f.write(content)
500
+ # write dockerfile for build
501
+ with open(os.path.join(function_dir, f"Dockerfile"), "w") as f:
502
+ content = dockerfile("function", resource, function)
503
+ f.write(content)
@@ -3,12 +3,11 @@
3
3
  from collections.abc import Callable
4
4
  from logging import getLogger
5
5
 
6
- from langchain_core.tools import create_schema_from_function, tool
7
-
8
6
  from beamlit.authentication import new_client
9
7
  from beamlit.common.settings import get_settings
10
8
  from beamlit.models import FunctionDeployment, FunctionKit
11
9
  from beamlit.run import RunClient
10
+ from langchain_core.tools import create_schema_from_function
12
11
 
13
12
  logger = getLogger(__name__)
14
13
 
@@ -58,7 +57,7 @@ def kit(bl_kit: FunctionKit = None, **kwargs: dict) -> Callable:
58
57
  def wrapper(func: Callable) -> Callable:
59
58
  if bl_kit and not func.__doc__ and bl_kit.description:
60
59
  func.__doc__ = bl_kit.description
61
- return tool(func, **kwargs)
60
+ return func
62
61
 
63
62
  return wrapper
64
63
 
@@ -72,10 +71,7 @@ def function(
72
71
  def wrapper(func: Callable) -> Callable:
73
72
  if bl_function and not func.__doc__ and bl_function.description:
74
73
  func.__doc__ = bl_function.description
75
- if (
76
- settings.environment == "development"
77
- or settings.environment == "production"
78
- ):
74
+ if settings.remote:
79
75
  remote_func = get_remote_function(func, bl_function)
80
76
  if not kwargs.get("args_schema"):
81
77
  kwargs["args_schema"] = create_schema_from_function(
@@ -83,8 +79,7 @@ def function(
83
79
  func,
84
80
  parse_docstring=func.__doc__,
85
81
  )
86
- return tool(remote_func, **kwargs)
87
- return tool(func, **kwargs)
82
+ return remote_func
83
+ return func
88
84
 
89
85
  return wrapper
90
- return wrapper
beamlit/serve/app.py CHANGED
@@ -16,7 +16,6 @@ from .middlewares import AccessLogMiddleware, AddProcessTimeHeader
16
16
  sys.path.insert(0, os.getcwd())
17
17
  sys.path.insert(0, os.path.join(os.getcwd(), "src"))
18
18
 
19
-
20
19
  def import_module():
21
20
  settings = get_settings()
22
21
  main_module = importlib.import_module(".".join(settings.server.module.split(".")[0:-1]))
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: beamlit
3
- Version: 0.0.22rc11
3
+ Version: 0.0.23
4
4
  Summary: Add your description here
5
5
  Author-email: cploujoux <ch.ploujoux@gmail.com>
6
6
  Requires-Python: >=3.12
@@ -1,12 +1,12 @@
1
1
  beamlit/__init__.py,sha256=545gFC-wLLwUktWcOAjUWe_Glha40tBetRTOYSfHnbI,164
2
- beamlit/client.py,sha256=vwvjAkUKHRySnA2tOVzXI8xtm9s1k2sEklCRE4j1Vc8,12543
2
+ beamlit/client.py,sha256=OdRbs5VVHF32HUd5RMcnkhe8YxamnAmgGlxO6pm1Xac,12431
3
3
  beamlit/errors.py,sha256=gO8GBmKqmSNgAg-E5oT-oOyxztvp7V_6XG7OUTT15q0,546
4
4
  beamlit/py.typed,sha256=8ZJUsxZiuOy1oJeVhsTWQhTG_6pTVHVXk5hJL79ebTk,25
5
5
  beamlit/run.py,sha256=y61iDBaR0917ihj5q-cJ_r3BFW1Rn5K_kDAISw5O6aU,1339
6
6
  beamlit/types.py,sha256=E1hhDh_zXfsSQ0NCt9-uw90_Mr5iIlsdfnfvxv5HarU,1005
7
7
  beamlit/agents/__init__.py,sha256=nf1iwQwGtCG6nDqyVhxfWoqR6dv6X3bvSpCeqkTCFaM,101
8
- beamlit/agents/chat.py,sha256=8KsUvIB-eaUApfKclT76_4HQu3VBa9nifMqmq_AAvcM,2630
9
- beamlit/agents/decorator.py,sha256=naeOsvfto74TZTcc15Ro98Hl8Bbe6Uhflof_SdyUkwY,6054
8
+ beamlit/agents/chat.py,sha256=aI7pObyywRyg3dBpubzHAUWTbTk1nwtxvpY7iIP1RLY,2704
9
+ beamlit/agents/decorator.py,sha256=XDrWN-Xx4czSy1sIkfQY4t9-UnsM21vMll58CP1TRLw,6468
10
10
  beamlit/api/__init__.py,sha256=zTSiG_ujSjAqWPyc435YXaX9XTlpMjiJWBbV-f-YtdA,45
11
11
  beamlit/api/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  beamlit/api/agents/create_agent.py,sha256=HFExosu02JZqZz7I6U6WjN81TERz6p2i8CzQCyiRYXo,4112
@@ -130,18 +130,20 @@ beamlit/api/workspaces/update_workspace.py,sha256=qa5DV2UJSUYuB_ibALb4E9ghKpT1Ha
130
130
  beamlit/api/workspaces/update_workspace_user_role.py,sha256=Yn9iuJ4tKtauzBiJyU4-wYUMS9g98X2Om8zs7UkzrY8,4917
131
131
  beamlit/authentication/__init__.py,sha256=wiXqRbc7E-ulrH_ueA9duOGFvXeo7-RvhSD1XbFogMo,1020
132
132
  beamlit/authentication/apikey.py,sha256=jnz1FMRauI5qAInqeeDER8aCONx4O8ZPZGedvi3Ap_o,659
133
- beamlit/authentication/authentication.py,sha256=om26AteY2cCV9ctqbOCynX6PgS8YO-aCreNOFSnnWKc,3121
133
+ beamlit/authentication/authentication.py,sha256=tZu8GoVueKDuq1RLXMvtHcV95XLikmQ19PCxLWBn2Ek,3120
134
134
  beamlit/authentication/clientcredentials.py,sha256=6kbfTjwUkXUArJX8XZLe9ZzbEicQc19tSXBvsTpiXMk,3954
135
- beamlit/authentication/credentials.py,sha256=DBw598T6it7EgXKnS_qfsBM0mkC33iRDadWA70bg5X0,5349
135
+ beamlit/authentication/credentials.py,sha256=DlfiF_FfOosPVsRoa39JSR3XoICmhBqTlHXc6b4zWtE,5349
136
136
  beamlit/authentication/device_mode.py,sha256=oQVBCDsq-pdeXF31WSTAAEdaX6eACV7SYcOSyf3ea_Q,3728
137
137
  beamlit/common/__init__.py,sha256=yDoMJDKj-xjTGl7U1YI59KpWxiOV65HSiUulgO8xdTA,277
138
138
  beamlit/common/generate.py,sha256=VJ_MiRDulXdQdnlKdM4_Bod6CO6DOGlFiosGXOLuLGs,7227
139
139
  beamlit/common/logger.py,sha256=ayabnsoHS8ncXm8EpBS01FkvSe0XRcaNdQjKVuPI5z4,1025
140
140
  beamlit/common/secrets.py,sha256=sid81bOe3LflkMKDHwBsBs9nIju8bp5-v9qU9gkyNMc,212
141
- beamlit/common/settings.py,sha256=7PDfLPzTPN5Yeh2NnmAlSzpBi_u-qza2lUIX-i6N_lg,5252
141
+ beamlit/common/settings.py,sha256=N0VzveNQ3vAE6ZJWMnWrIVi2hXidLtkRZkAXQTGo40A,5546
142
142
  beamlit/common/utils.py,sha256=jouz5igBvT37Xn_e94-foCHyQczVim-UzVcoIF6RWJ4,657
143
+ beamlit/deploy/__init__.py,sha256=GS7l7Jtm2yKs7iNLKcfjYO-rAhUzggQ3xiYSf3oxLBY,91
144
+ beamlit/deploy/deploy.py,sha256=bWUXG_5b_XhFcG5t75vel2EbEfZPIdg_YLXtzkfgXUs,18799
143
145
  beamlit/functions/__init__.py,sha256=_RPG1Bfg54JGdIPnViAU6n9zD7E1cDNsdXi8oYGskzE,138
144
- beamlit/functions/decorator.py,sha256=-2newMBztweIgFuh0ABKOdxCfUzWaRxf0ym-YAgggJI,3168
146
+ beamlit/functions/decorator.py,sha256=uYZOVxD-7ZNHORTQfCIn0qdNKPIZupsr7_QdUmWEKu0,2996
145
147
  beamlit/functions/github/__init__.py,sha256=gYnUkeegukOfbymdabuuJkScvH-_ZJygX05BoqkPn0o,49
146
148
  beamlit/functions/github/github.py,sha256=FajzLCNkpXcwfgnC0l9rOGT2eSPLCz8-qrMzK9N_ZNc,598
147
149
  beamlit/functions/github/kit/__init__.py,sha256=jBwPqZv6C23_utukohxqXZwrlicNlI7PYPUj0Den7Cw,136
@@ -292,10 +294,10 @@ beamlit/models/websocket_channel.py,sha256=tyNtsVR0cOwd6BK--ehBCH8bIjxtyPhiAkrxY
292
294
  beamlit/models/workspace.py,sha256=s7wS6ibswosB0FdUb3ry3BnlLa325axBdYPLI3ipe0Q,3986
293
295
  beamlit/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8fWFfkCf7hzCTiNp4A,1246
294
296
  beamlit/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
295
- beamlit/serve/app.py,sha256=-CvUU7pqFDigndHpA01IR4_mxXaT8c0VsnVjMfIw6K8,2399
297
+ beamlit/serve/app.py,sha256=_0ZesKcczd1sYm8vs3ulbXO1M1boO_5DhFf3jSmjM4g,2398
296
298
  beamlit/serve/middlewares/__init__.py,sha256=1dVmnOmhAQWvWktqHkKSIX-YoF6fmMU8xkUQuhg_rJU,148
297
299
  beamlit/serve/middlewares/accesslog.py,sha256=wM52-hcwtO-_hdM1pnsEJzerzJf1MzEyN5m85BdDccE,609
298
300
  beamlit/serve/middlewares/processtime.py,sha256=lDAaIasZ4bwvN-HKHvZpaD9r-yrkVNZYx4abvbjbrCg,411
299
- beamlit-0.0.22rc11.dist-info/METADATA,sha256=AOUr4a-4i_HzlZwKbn12EyaSvGR2BJFEFdzs4WXPqRw,2027
300
- beamlit-0.0.22rc11.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
301
- beamlit-0.0.22rc11.dist-info/RECORD,,
301
+ beamlit-0.0.23.dist-info/METADATA,sha256=3o33Ckt9YG7lMNqHL9niFXXNSeQCLZUWX7BavDh7ZjI,2023
302
+ beamlit-0.0.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
303
+ beamlit-0.0.23.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any