beamlit 0.0.31rc42__py3-none-any.whl → 0.0.33__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.
@@ -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