beamlit 0.0.31rc42__py3-none-any.whl → 0.0.33__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,13 +6,15 @@ import importlib
6
6
  import os
7
7
  from logging import getLogger
8
8
 
9
- from langchain_core.tools import Tool
9
+ from langchain_core.tools import StructuredTool
10
10
  from langgraph.checkpoint.memory import MemorySaver
11
11
  from langgraph.prebuilt import create_react_agent
12
+ from langchain_core.tools.base import create_schema_from_function
12
13
 
13
14
  from beamlit.api.models import get_model
14
15
  from beamlit.authentication import new_client
15
- from beamlit.common.settings import init
16
+ from beamlit.common import slugify
17
+ from beamlit.common.settings import get_settings, init
16
18
  from beamlit.errors import UnexpectedStatus
17
19
  from beamlit.functions.mcp.mcp import MCPClient, MCPToolkit
18
20
  from beamlit.functions.remote.remote import RemoteToolkit
@@ -22,9 +24,10 @@ from .chain import ChainToolkit
22
24
  from .chat import get_chat_model
23
25
 
24
26
 
25
- def get_functions(dir="src/functions", from_decorator="function", remote_functions_empty=True):
27
+ def get_functions(client, dir="src/functions", from_decorator="function", remote_functions_empty=True):
26
28
  functions = []
27
29
  logger = getLogger(__name__)
30
+ settings = get_settings()
28
31
 
29
32
  # Walk through all Python files in functions directory and subdirectories
30
33
  if not os.path.exists(dir):
@@ -73,8 +76,9 @@ def get_functions(dir="src/functions", from_decorator="function", remote_functio
73
76
  keyword.value, ast.Constant
74
77
  ):
75
78
  is_kit = keyword.value.value
76
- if is_kit:
79
+ if is_kit and not settings.remote:
77
80
  kit_functions = get_functions(
81
+ client,
78
82
  dir=os.path.join(root),
79
83
  from_decorator="kit",
80
84
  remote_functions_empty=remote_functions_empty,
@@ -84,23 +88,30 @@ def get_functions(dir="src/functions", from_decorator="function", remote_functio
84
88
  # Get the decorated function
85
89
  if not is_kit and hasattr(module, func_name):
86
90
  func = getattr(module, func_name)
87
- if asyncio.iscoroutinefunction(func):
88
- functions.append(
89
- Tool(
90
- name=func.__name__,
91
- description=func.__doc__,
92
- func=func,
93
- coroutine=func,
94
- )
95
- )
91
+ if settings.remote:
92
+ toolkit = RemoteToolkit(client, slugify(func.__name__))
93
+ toolkit.initialize()
94
+ functions.extend(toolkit.get_tools())
96
95
  else:
97
- functions.append(
98
- Tool(
99
- name=func.__name__,
100
- description=func.__doc__,
101
- func=func,
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
+ )
102
114
  )
103
- )
104
115
  except Exception as e:
105
116
  logger.warning(f"Error processing {file_path}: {e!s}")
106
117
  return functions
@@ -139,9 +150,11 @@ def agent(
139
150
 
140
151
  # Initialize functions array to store decorated functions
141
152
  functions = get_functions(
153
+ client,
142
154
  dir=settings.agent.functions_directory,
143
155
  remote_functions_empty=not remote_functions,
144
156
  )
157
+
145
158
  settings.agent.functions = functions
146
159
 
147
160
  if agent is not None:
@@ -1,16 +1,17 @@
1
1
  from .error import HTTPError
2
2
  from .logger import init as init_logger
3
3
  from .secrets import Secret
4
- from .settings import Settings, get_settings, init, init_agent
4
+ from .settings import Settings, get_settings, init
5
+ from .slugify import slugify
5
6
  from .utils import copy_folder
6
7
 
7
8
  __all__ = [
8
9
  "Secret",
9
10
  "Settings",
10
11
  "get_settings",
11
- "init_agent",
12
12
  "init",
13
13
  "copy_folder",
14
14
  "init_logger",
15
15
  "HTTPError",
16
+ "slugify"
16
17
  ]
@@ -1,5 +1,4 @@
1
1
  import os
2
- from logging import getLogger
3
2
  from typing import Tuple, Type, Union
4
3
 
5
4
  from langchain_core.language_models.chat_models import BaseChatModel
@@ -12,22 +11,17 @@ from pydantic_settings import (
12
11
  YamlConfigSettingsSource,
13
12
  )
14
13
 
15
- from beamlit.api.agents import get_agent
16
- from beamlit.api.functions import get_function
17
- from beamlit.api.models import get_model
18
- from beamlit.client import AuthenticatedClient
19
14
  from beamlit.common.logger import init as init_logger
20
15
  from beamlit.models import Agent, Function, Model
21
- from beamlit.types import UNSET, Unset
22
16
 
23
17
  global SETTINGS
24
18
  SETTINGS = None
25
19
 
26
20
  class SettingsAgent(BaseSettings):
27
- agent: Union[None, CompiledGraph, BaseChatModel] = None
28
- chain: Union[Unset, list[Agent]] = UNSET
29
- model: Union[Unset, Model] = UNSET
30
- functions: Union[Unset, list[Function]] = UNSET
21
+ agent: Union[None, CompiledGraph] = None
22
+ chain: Union[None, list[Agent]] = None
23
+ model: Union[None, Model] = None
24
+ functions: Union[None, list[Function]] = None
31
25
  functions_directory: str = Field(default="src/functions")
32
26
  chat_model: Union[None, BaseChatModel] = None
33
27
  module: str = Field(default="main.main")
@@ -100,59 +94,6 @@ class Settings(BaseSettings):
100
94
  def get_settings() -> Settings:
101
95
  return SETTINGS
102
96
 
103
-
104
- def init_agent(
105
- client: AuthenticatedClient,
106
- destination: str = f"{os.getcwd()}/src/beamlit_generated.py",
107
- ):
108
- from beamlit.common.generate import generate
109
-
110
- logger = getLogger(__name__)
111
- settings = get_settings()
112
- # Init configuration from environment variables
113
- if settings.agent.functions or settings.agent.chain:
114
- return
115
-
116
- # Init configuration from beamlit control plane
117
- name = settings.name
118
- env = settings.environment
119
-
120
- agent = get_agent.sync(name, environment=env, client=client)
121
- if not agent:
122
- raise ValueError(f"Agent {name} not found")
123
- functions: list[Function] = []
124
- agents_chain: list[Agent] = []
125
- if agent.spec.functions:
126
- for function in agent.spec.functions:
127
- function = get_function.sync(function, environment=env, client=client)
128
- if function:
129
- functions.append(function)
130
- settings.agent.functions = functions
131
-
132
- if agent.spec.agentChain:
133
- for chain in agent.spec.agentChain:
134
- if chain.enabled:
135
- agentChain = get_agent.sync(chain.name, environment=env, client=client)
136
- if chain.description:
137
- agentChain.spec.description = chain.description
138
- agents_chain.append(agentChain)
139
- settings.agent.chain = agents_chain
140
- if agent.spec.model:
141
- model = get_model.sync(agent.spec.model, environment=env, client=client)
142
- settings.agent.model = model
143
-
144
- content_generate = generate(destination, dry_run=True)
145
- compared_content = None
146
- if os.path.exists(destination):
147
- compared_content = open(destination).read()
148
-
149
- if not os.path.exists(destination) or (
150
- compared_content and content_generate != compared_content
151
- ):
152
- logger.info("Generating agent code")
153
- generate(destination)
154
-
155
-
156
97
  def init() -> Settings:
157
98
  """Parse the beamlit.yaml file to get configurations."""
158
99
  from beamlit.authentication.credentials import current_context
@@ -0,0 +1,2 @@
1
+ def slugify(name: str) -> str:
2
+ return name.lower().replace(" ", "-").replace("_", "-")
beamlit/deploy/deploy.py CHANGED
@@ -6,6 +6,7 @@ import uuid
6
6
  from logging import getLogger
7
7
  from typing import Literal
8
8
 
9
+ from beamlit.common import slugify
9
10
  from beamlit.common.settings import Settings, get_settings, init
10
11
  from beamlit.models import (
11
12
  Agent,
@@ -24,8 +25,6 @@ sys.path.insert(0, os.getcwd())
24
25
  sys.path.insert(0, os.path.join(os.getcwd(), "src"))
25
26
 
26
27
  random_id = str(uuid.uuid4())[:8]
27
- def slugify(name: str) -> str:
28
- return name.lower().replace(" ", "-").replace("_", "-")
29
28
 
30
29
  def get_runtime_image(type: str, name: str) -> str:
31
30
  settings = get_settings()
@@ -130,7 +129,7 @@ metadata:
130
129
  spec:
131
130
  enabled: true
132
131
  policies: [{", ".join(agent.spec.policies or [])}]
133
- functions: [{", ".join([f"{function.metadata.name}" for (_, function) in functions])}]
132
+ functions: [{", ".join([f"{slugify(function.metadata.name)}" for (_, function) in functions])}]
134
133
  agentChain: {format_agent_chain(agent.spec.agent_chain)}
135
134
  model: {agent.spec.model}
136
135
  runtime:
@@ -1,62 +1,15 @@
1
1
  """Decorators for creating function tools with Beamlit and LangChain integration."""
2
-
3
- import json
2
+ import asyncio
3
+ import functools
4
4
  from collections.abc import Callable
5
5
  from logging import getLogger
6
6
 
7
- from langchain_core.tools import create_schema_from_function
7
+ from fastapi import Request
8
8
 
9
- from beamlit.authentication import new_client
10
- from beamlit.common.settings import get_settings
11
9
  from beamlit.models import Function, FunctionKit
12
- from beamlit.run import RunClient
13
10
 
14
11
  logger = getLogger(__name__)
15
12
 
16
-
17
- def get_remote_function(func: Callable, function: Function):
18
- settings = get_settings()
19
- name = (function and function.metadata and function.metadata.name) or func.__name__
20
-
21
- def _partial(*args, **kwargs):
22
- # Get function signature parameters
23
- try:
24
- client = new_client()
25
- run_client = RunClient(client)
26
- logger.debug(
27
- f"Calling remote function: NAME={name}"
28
- f" PARAMS={kwargs} ENVIRONMENT={settings.environment}"
29
- )
30
- response = run_client.run(
31
- resource_type="function",
32
- resource_name=name,
33
- environment=settings.environment,
34
- method="POST",
35
- headers={"Content-Type": "application/json"},
36
- data=json.dumps(kwargs),
37
- )
38
- content = response.text
39
- if response.status_code >= 400:
40
- content = f"{response.status_code}:{response.text}"
41
- logger.error(f"Error calling remote function: {content}")
42
- return f"Error calling remote function: {content}"
43
- logger.debug(
44
- f"Response from remote function: NAME={name}"
45
- f" RESPONSE={content} ENVIRONMENT={settings.environment}"
46
- )
47
- if response.headers.get("content-type") == "application/json":
48
- return response.json()
49
- return content
50
- except Exception as e:
51
- logger.error(f"Error calling function {name}: {e}")
52
- raise e
53
-
54
- remote_func = _partial
55
- remote_func.__name__ = func.__name__
56
- remote_func.__doc__ = func.__doc__
57
- return remote_func
58
-
59
-
60
13
  def kit(bl_kit: FunctionKit = None, **kwargs: dict) -> Callable:
61
14
  """Create function tools with Beamlit and LangChain integration."""
62
15
 
@@ -70,26 +23,23 @@ def kit(bl_kit: FunctionKit = None, **kwargs: dict) -> Callable:
70
23
 
71
24
  def function(*args, function: Function | dict = None, kit=False, **kwargs: dict) -> Callable:
72
25
  """Create function tools with Beamlit and LangChain integration."""
73
- settings = get_settings()
74
26
  if function is not None and not isinstance(function, dict):
75
27
  raise Exception(
76
28
  'function must be a dictionary, example: @function(function={"metadata": {"name": "my_function"}})'
77
29
  )
78
30
  if isinstance(function, dict):
79
31
  function = Function(**function)
80
-
32
+
81
33
  def wrapper(func: Callable) -> Callable:
82
34
  if function and not func.__doc__ and function.spec and function.spec.description:
83
35
  func.__doc__ = function.spec.description
84
- if settings.remote:
85
- remote_func = get_remote_function(func, function)
86
- if not kwargs.get("args_schema"):
87
- kwargs["args_schema"] = create_schema_from_function(
88
- func.__name__,
89
- func,
90
- parse_docstring=func.__doc__,
91
- )
92
- return remote_func
93
- return func
36
+
37
+ @functools.wraps(func)
38
+ async def wrapped(*args, **kwargs):
39
+ if len(args) > 0 and isinstance(args[0], Request):
40
+ body = await args[0].json()
41
+ args = [body.get(param) for param in func.__code__.co_varnames[:func.__code__.co_argcount]]
42
+ return await func(*args, **kwargs) if asyncio.iscoroutinefunction(func) else func(*args, **kwargs)
43
+ return wrapped
94
44
 
95
45
  return wrapper
beamlit/serve/app.py CHANGED
@@ -71,13 +71,11 @@ async def root(request: Request):
71
71
  settings = get_settings()
72
72
  logger = getLogger(__name__)
73
73
  try:
74
- body = await request.json()
75
-
76
74
  original_func = getattr(func, "__wrapped__", func)
77
75
  if asyncio.iscoroutinefunction(func) or asyncio.iscoroutinefunction(original_func):
78
- response = await func(body)
76
+ response = await func(request)
79
77
  else:
80
- response = func(body)
78
+ response = func(request)
81
79
 
82
80
  if isinstance(response, Response):
83
81
  return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beamlit
3
- Version: 0.0.31rc42
3
+ Version: 0.0.33
4
4
  Summary: Add your description here
5
5
  Author-email: cploujoux <ch.ploujoux@gmail.com>
6
6
  Requires-Python: >=3.12
@@ -7,7 +7,7 @@ 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
9
  beamlit/agents/chat.py,sha256=gVyv4FGBdQTDhdutX8l64OUNa6Fdqaw4eCfEDRH0IPQ,3558
10
- beamlit/agents/decorator.py,sha256=RAhe7wHKJnjMpmU7KWKcM6EZjdm3pgUBhQQUP2T6GZY,9716
10
+ beamlit/agents/decorator.py,sha256=n3sETKu8V2g3GDJ0f17kUowyqaRJvpg3n_BGRRVuNGY,10619
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
@@ -128,20 +128,20 @@ beamlit/authentication/authentication.py,sha256=ODQCc00RvCOZNaiGG5ctylNnE-JVCuge
128
128
  beamlit/authentication/clientcredentials.py,sha256=cxZPPu--CgizwqX0pdfFQ91gJt1EFKwyy-aBB_dXX7I,3990
129
129
  beamlit/authentication/credentials.py,sha256=p_1xenabCbQuRz7BiFk7oTK4uCxAt_zoyku5o-jcKGE,5343
130
130
  beamlit/authentication/device_mode.py,sha256=tmr22gllKOZwBRub_QjF5pYa425x-nE8tQNpZ_EGR6g,3644
131
- beamlit/common/__init__.py,sha256=vj4_boIBVitMsaQR8BqBqE2eupOIh6MWBAYlYyCCH98,341
131
+ beamlit/common/__init__.py,sha256=saX5X3hRCJ9erSlXuSkZ2VGgquvpgdcofAU_9sM4bCE,354
132
132
  beamlit/common/error.py,sha256=f9oJDFxhoHK-vpjxBgEp0NwWIk0N_THPemUI7uQxVzU,270
133
- beamlit/common/generate.py,sha256=LtdCju_QayRS4lZrrb_0VHqWWvTcv4Mbf-iV1TB_Qko,7522
134
133
  beamlit/common/instrumentation.py,sha256=GVYeat7qCcqzDoKSYig3s8ZCC172R9JiQIr3Evv3kik,4293
135
134
  beamlit/common/logger.py,sha256=nN_dSOl4bs13QU3Rod-w3e3jYOnlSrHx3_bs-ACY6Aw,1115
136
135
  beamlit/common/secrets.py,sha256=sid81bOe3LflkMKDHwBsBs9nIju8bp5-v9qU9gkyNMc,212
137
- beamlit/common/settings.py,sha256=_4oCVrJZOMaTZoK2Zzo2DWqtUyBsspuOH3iAJ_nU0tw,5923
136
+ beamlit/common/settings.py,sha256=b2rvby-ufG3M0AB1ReoWFM-1EzF1LaE-gbokO9HvQDI,3810
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=xo2fAdF6jR1cG75sXMzpqsJlG_qGAKVnMwjuDa_8sk8,9936
140
+ beamlit/deploy/deploy.py,sha256=YKY1iN0ZofQr_n9efvDJ5pK9HpyLZO9wyHmh8W9PAaE,9889
141
141
  beamlit/deploy/format.py,sha256=PJ8kU7Y1pwiS3tqqyvFaag9LO3jju-4ua574163VPk4,1820
142
142
  beamlit/deploy/parser.py,sha256=Ga0poCZkoRnuTw082QnTcNGCBJncoRAnVsn8-1FsaJE,6907
143
143
  beamlit/functions/__init__.py,sha256=_RPG1Bfg54JGdIPnViAU6n9zD7E1cDNsdXi8oYGskzE,138
144
- beamlit/functions/decorator.py,sha256=YNNU8MZXV2C6gIjxaYhp-FSWGLc9I_XLsIE24zQ3fr8,3541
144
+ beamlit/functions/decorator.py,sha256=ZtSQsPLI70WKwi2jPhA7DaqREQUINpqt9BDVugeV_sg,1714
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
@@ -252,10 +252,10 @@ beamlit/models/websocket_channel.py,sha256=jg3vN7yS_oOIwGtndtIUr1LsyEA58RXLXahqS
252
252
  beamlit/models/workspace.py,sha256=l__bIpbA4oJvxXo7UbEoCcqkvu9MiNt5aXXpZ3bgwHg,4309
253
253
  beamlit/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8fWFfkCf7hzCTiNp4A,1246
254
254
  beamlit/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
255
- beamlit/serve/app.py,sha256=QzReZZjGFQWhNA1XBDq81rsn9zpEihtLZLrfZi9ThT0,3554
255
+ beamlit/serve/app.py,sha256=DXWxQoMeuA5FYvBMyLrP94OEWQbwLf4GZk3I9fkwSPA,3523
256
256
  beamlit/serve/middlewares/__init__.py,sha256=1dVmnOmhAQWvWktqHkKSIX-YoF6fmMU8xkUQuhg_rJU,148
257
257
  beamlit/serve/middlewares/accesslog.py,sha256=Mu4T4_9OvHybjA0ApzZFpgi2C8f3X1NbUk-76v634XM,631
258
258
  beamlit/serve/middlewares/processtime.py,sha256=lDAaIasZ4bwvN-HKHvZpaD9r-yrkVNZYx4abvbjbrCg,411
259
- beamlit-0.0.31rc42.dist-info/METADATA,sha256=9DQPwNYvv6Q3ailPx5xQTQCxOyzOKlD8xuLp6qcBhck,2405
260
- beamlit-0.0.31rc42.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
261
- beamlit-0.0.31rc42.dist-info/RECORD,,
259
+ beamlit-0.0.33.dist-info/METADATA,sha256=4AMW5iTcsR-ZCtdCDjbHpt7mN-1OFd_Haj7dOWztBFw,2401
260
+ beamlit-0.0.33.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
261
+ beamlit-0.0.33.dist-info/RECORD,,
@@ -1,196 +0,0 @@
1
- from typing import Tuple
2
-
3
- from beamlit.common.settings import Settings, get_settings
4
- from beamlit.models import Agent, Function, FunctionMetadata, FunctionSpec
5
- from beamlit.models.function_kit import FunctionKit
6
-
7
-
8
- def get_titles_name(name: str) -> str:
9
- return name.title().replace("-", "").replace("_", "")
10
-
11
-
12
- def generate_kit_function_code(
13
- settings: Settings, function: Function, kit: FunctionKit
14
- ) -> Tuple[str, str]:
15
- export_code = ""
16
- code = ""
17
- for kit in kit:
18
- fn = Function(
19
- metadata=FunctionMetadata(
20
- name=kit.name,
21
- workspace=settings.workspace,
22
- environment=settings.environment,
23
- ),
24
- spec=FunctionSpec(
25
- parameters=kit.parameters,
26
- description=kit.description,
27
- ),
28
- )
29
- new_code, export = generate_function_code(
30
- settings, fn, force_name_in_endpoint=function.metadata.name, kit=True
31
- )
32
- code += new_code
33
- export_code += export
34
- return code, export_code
35
-
36
-
37
- def generate_function_code(
38
- settings: Settings,
39
- function: Function,
40
- force_name_in_endpoint: str = "",
41
- kit: bool = False,
42
- ) -> Tuple[str, str]:
43
- name = get_titles_name(function.metadata.name)
44
- if function.spec.parameters and len(function.spec.parameters) > 0:
45
- args_list = ", ".join(f"{param.name}: str" for param in function.spec.parameters)
46
- args_list += ", "
47
- else:
48
- args_list = ""
49
- args_schema = ""
50
- if function.spec.parameters:
51
- for param in function.spec.parameters:
52
- args_schema += f'{param.name}: str = Field(description="""{param.description}""")\n '
53
- if len(args_schema) == 0:
54
- args_schema = "pass"
55
-
56
- # TODO: add return direct in function configuration
57
- return_direct = False
58
- endpoint_name = force_name_in_endpoint or function.metadata.name
59
- body = "{}"
60
- if function.spec.parameters:
61
- body = f'{", ".join(f'"{param.name}": {param.name}' for param in function.spec.parameters)}'
62
- if kit is True:
63
- has_name = False
64
- if function.spec.parameters:
65
- for param in function.spec.parameters:
66
- if param.name == "name":
67
- has_name = True
68
- break
69
- if not has_name:
70
- if len(body) > 0:
71
- body += ", "
72
- body += f'"name": "{function.metadata.name}"'
73
- return (
74
- f'''
75
-
76
- class Beamlit{name}Input(BaseModel):
77
- {args_schema}
78
-
79
- class Beamlit{name}(BaseTool):
80
- name: str = "beamlit_{function.metadata.name.replace("-", "_")}"
81
- description: str = """{function.spec.description}"""
82
- args_schema: Type[BaseModel] = Beamlit{name}Input
83
-
84
- response_format: Literal["content_and_artifact"] = "content_and_artifact"
85
- return_direct: bool = {return_direct}
86
-
87
- def _run(self, {args_list} run_manager: Optional[CallbackManagerForToolRun] = None) -> Tuple[Union[List[Dict[str, str]], str], Dict]:
88
- try:
89
- params = self.metadata.get("params", {{}})
90
- response = run_client.run("function", "{endpoint_name}", settings.environment, "POST", json={{{body}}})
91
- if response.status_code >= 400:
92
- logger.error(f"Failed to run function {name}, {{response.status_code}}::{{response.text}}")
93
- raise Exception(f"Failed to run function {name}, {{response.status_code}}::{{response.text}}")
94
- return response.json(), {{}}
95
- except Exception as e:
96
- return repr(e), {{}}
97
- ''',
98
- f"Beamlit{get_titles_name(function.metadata.name)},",
99
- )
100
-
101
-
102
- def generate_chain_code(settings: Settings, agent: Agent) -> Tuple[str, str]:
103
- name = get_titles_name(agent.metadata.name)
104
- # TODO: add return direct in agent configuration
105
- return_direct = False
106
- return (
107
- f'''
108
- class BeamlitChain{name}Input(BaseModel):
109
- input: str = Field(description='{agent.spec.description}')
110
-
111
- class BeamlitChain{name}(BaseTool):
112
- name: str = "beamlit_chain_{agent.metadata.name.replace("-", "_")}"
113
- description: str = """{agent.spec.description}"""
114
- args_schema: Type[BaseModel] = BeamlitChain{name}Input
115
-
116
- response_format: Literal["content_and_artifact"] = "content_and_artifact"
117
- return_direct: bool = {return_direct}
118
-
119
- def _run(
120
- self,
121
- input: str,
122
- run_manager: Optional[CallbackManagerForToolRun] = None,
123
- ) -> Tuple[Union[List[Dict[str, str]], str], Dict]:
124
- try:
125
- params = self.metadata.get("params", {{}})
126
- response = run_client.run("agent", "{agent.metadata.name}", settings.environment, "POST", json={{"input": input}})
127
- if response.status_code >= 400:
128
- logger.error(f"Failed to run tool {agent.metadata.name}, {{response.status_code}}::{{response.text}}")
129
- raise Exception(f"Failed to run tool {agent.metadata.name}, {{response.status_code}}::{{response.text}}")
130
- if response.headers.get("Content-Type") == "application/json":
131
- return response.json(), {{}}
132
- else:
133
- return response.text, {{}}
134
- except Exception as e:
135
- return repr(e), {{}}
136
- ''',
137
- f"BeamlitChain{name},",
138
- )
139
-
140
-
141
- def generate(destination: str, dry_run: bool = False):
142
- imports = """from logging import getLogger
143
- from typing import Dict, List, Literal, Optional, Tuple, Type, Union
144
-
145
- from langchain_core.callbacks import CallbackManagerForToolRun
146
- from langchain_core.tools import BaseTool
147
- from pydantic import BaseModel, Field
148
- from beamlit.authentication import (RunClientWithCredentials,
149
- load_credentials_from_settings,
150
- new_client_with_credentials)
151
- from beamlit.common.settings import get_settings
152
- from beamlit.run import RunClient
153
-
154
- logger = getLogger(__name__)
155
- settings = get_settings()
156
- credentials = load_credentials_from_settings(settings)
157
-
158
- client_config = RunClientWithCredentials(
159
- credentials=credentials,
160
- workspace=settings.workspace,
161
- )
162
- client = new_client_with_credentials(client_config)
163
- run_client = RunClient(client=client)
164
- """
165
- settings = get_settings()
166
- export_code = "\n\nfunctions = ["
167
- export_chain = "\n\nchains = ["
168
- code = imports
169
- if settings.agent.functions and len(settings.agent.functions) > 0:
170
- for function_config in settings.agent.functions:
171
- if function_config.spec.kit and len(function_config.spec.kit) > 0:
172
- new_code, export = generate_kit_function_code(
173
- settings, function_config, function_config.spec.kit
174
- )
175
- code += new_code
176
- export_code += export
177
- else:
178
- new_code, export = generate_function_code(settings, function_config)
179
- code += new_code
180
- export_code += export
181
- if settings.agent.chain and len(settings.agent.chain) > 0:
182
- for agent in settings.agent.chain:
183
- new_code, export = generate_chain_code(settings, agent)
184
- code += new_code
185
- export_chain += export
186
- if settings.agent.functions and len(settings.agent.functions) > 0:
187
- export_code = export_code[:-1]
188
- export_code += "]"
189
- if settings.agent.chain and len(settings.agent.chain) > 0:
190
- export_chain = export_chain[:-1]
191
- export_chain += "]"
192
- content = code + export_code + export_chain
193
- if not dry_run:
194
- with open(destination, "w") as f:
195
- f.write(content)
196
- return content