beamlit 0.0.22rc12__py3-none-any.whl → 0.0.23rc14__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/common/settings.py +6 -10
- beamlit/deploy/__init__.py +3 -0
- beamlit/deploy/deploy.py +345 -0
- beamlit/functions/decorator.py +2 -6
- {beamlit-0.0.22rc12.dist-info → beamlit-0.0.23rc14.dist-info}/METADATA +1 -1
- {beamlit-0.0.22rc12.dist-info → beamlit-0.0.23rc14.dist-info}/RECORD +7 -5
- {beamlit-0.0.22rc12.dist-info → beamlit-0.0.23rc14.dist-info}/WHEEL +0 -0
beamlit/common/settings.py
CHANGED
@@ -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
|
@@ -65,6 +60,7 @@ class Settings(BaseSettings):
|
|
65
60
|
|
66
61
|
workspace: str
|
67
62
|
environment: str = Field(default="production")
|
63
|
+
remote: bool = Field(default=False)
|
68
64
|
type: str = Field(default="agent")
|
69
65
|
name: str = Field(default="beamlit-agent")
|
70
66
|
base_url: str = Field(default="https://api.beamlit.dev/v0")
|
beamlit/deploy/deploy.py
ADDED
@@ -0,0 +1,345 @@
|
|
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, init
|
11
|
+
from beamlit.models import (AgentChain, AgentDeployment, Flavor,
|
12
|
+
FunctionDeployment, StoreFunctionParameter)
|
13
|
+
|
14
|
+
sys.path.insert(0, os.getcwd())
|
15
|
+
sys.path.insert(0, os.path.join(os.getcwd(), "src"))
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass
|
19
|
+
class Resource:
|
20
|
+
type: Literal["agent", "function"]
|
21
|
+
module: Callable
|
22
|
+
name: str
|
23
|
+
decorator: ast.Call
|
24
|
+
func: Callable
|
25
|
+
|
26
|
+
def get_resources(from_decorator, dir="src") -> list[Resource]:
|
27
|
+
resources = []
|
28
|
+
logger = getLogger(__name__)
|
29
|
+
|
30
|
+
# Walk through all Python files in resources directory and subdirectories
|
31
|
+
for root, _, files in os.walk(dir):
|
32
|
+
for file in files:
|
33
|
+
if file.endswith(".py"):
|
34
|
+
file_path = os.path.join(root, file)
|
35
|
+
# Read and compile the file content
|
36
|
+
with open(file_path) as f:
|
37
|
+
try:
|
38
|
+
file_content = f.read()
|
39
|
+
# Parse the file content to find decorated resources
|
40
|
+
tree = ast.parse(file_content)
|
41
|
+
|
42
|
+
# Look for function definitions with decorators
|
43
|
+
for node in ast.walk(tree):
|
44
|
+
if (
|
45
|
+
not isinstance(node, ast.FunctionDef) and not isinstance(node, ast.AsyncFunctionDef)
|
46
|
+
) or len(node.decorator_list) == 0:
|
47
|
+
continue
|
48
|
+
decorator = node.decorator_list[0]
|
49
|
+
|
50
|
+
decorator_name = ""
|
51
|
+
if isinstance(decorator, ast.Call):
|
52
|
+
decorator_name = decorator.func.id
|
53
|
+
if isinstance(decorator, ast.Name):
|
54
|
+
decorator_name = decorator.id
|
55
|
+
if decorator_name == from_decorator:
|
56
|
+
# Get the function name and decorator name
|
57
|
+
func_name = node.name
|
58
|
+
|
59
|
+
# Import the module to get the actual function
|
60
|
+
spec = importlib.util.spec_from_file_location(func_name, file_path)
|
61
|
+
module = importlib.util.module_from_spec(spec)
|
62
|
+
spec.loader.exec_module(module)
|
63
|
+
# Check if kit=True in the decorator arguments
|
64
|
+
|
65
|
+
# Get the decorated function
|
66
|
+
if hasattr(module, func_name) and isinstance(decorator, ast.Call):
|
67
|
+
|
68
|
+
resources.append(
|
69
|
+
Resource(
|
70
|
+
type=decorator_name,
|
71
|
+
module=module,
|
72
|
+
name=func_name,
|
73
|
+
func=getattr(module, func_name),
|
74
|
+
decorator=decorator,
|
75
|
+
)
|
76
|
+
)
|
77
|
+
except Exception as e:
|
78
|
+
logger.warning(f"Error processing {file_path}: {e!s}")
|
79
|
+
return resources
|
80
|
+
|
81
|
+
|
82
|
+
def get_parameters(resource: Resource) -> list[StoreFunctionParameter]:
|
83
|
+
parameters = []
|
84
|
+
# Get function signature
|
85
|
+
import inspect
|
86
|
+
sig = inspect.signature(resource.func)
|
87
|
+
# Get docstring for parameter descriptions
|
88
|
+
docstring = inspect.getdoc(resource.func)
|
89
|
+
param_descriptions = {}
|
90
|
+
if docstring:
|
91
|
+
# Parse docstring for parameter descriptions
|
92
|
+
lines = docstring.split('\n')
|
93
|
+
for line in lines:
|
94
|
+
line = line.strip().lower()
|
95
|
+
if line.startswith(':param '):
|
96
|
+
# Extract parameter name and description
|
97
|
+
param_line = line[7:].split(':', 1)
|
98
|
+
if len(param_line) == 2:
|
99
|
+
param_name = param_line[0].strip()
|
100
|
+
param_desc = param_line[1].strip()
|
101
|
+
param_descriptions[param_name] = param_desc
|
102
|
+
for name, param in sig.parameters.items():
|
103
|
+
# Skip *args and **kwargs parameters
|
104
|
+
if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
|
105
|
+
|
106
|
+
continue
|
107
|
+
|
108
|
+
param_type = "string" # Default type
|
109
|
+
type_mapping = {
|
110
|
+
'str': 'string',
|
111
|
+
'int': 'integer',
|
112
|
+
'float': 'number',
|
113
|
+
'bool': 'boolean',
|
114
|
+
'list': 'array',
|
115
|
+
'dict': 'object',
|
116
|
+
'none': 'null'
|
117
|
+
}
|
118
|
+
if param.annotation != inspect.Parameter.empty:
|
119
|
+
# Map Python types to OpenAPI types
|
120
|
+
if hasattr(param.annotation, "__name__"):
|
121
|
+
param_type = param.annotation.__name__.lower()
|
122
|
+
else:
|
123
|
+
# Handle special types like Union, Optional etc
|
124
|
+
param_type = str(param.annotation).lower()
|
125
|
+
parameter = StoreFunctionParameter(
|
126
|
+
name=name,
|
127
|
+
type_=type_mapping.get(param_type, "string"),
|
128
|
+
required=param.default == inspect.Parameter.empty,
|
129
|
+
description=param_descriptions.get(name, f"Parameter {name}")
|
130
|
+
)
|
131
|
+
parameters.append(parameter)
|
132
|
+
|
133
|
+
return parameters
|
134
|
+
|
135
|
+
|
136
|
+
def get_description(description: str | None, resource: Resource) -> str:
|
137
|
+
if description:
|
138
|
+
return description
|
139
|
+
doc = resource.func.__doc__
|
140
|
+
if doc:
|
141
|
+
# Split docstring into sections and get only the description part
|
142
|
+
doc_lines = doc.split('\n')
|
143
|
+
description_lines = []
|
144
|
+
for line in doc_lines:
|
145
|
+
line = line.strip()
|
146
|
+
# Stop when we hit param/return sections
|
147
|
+
if line.startswith(':param') or line.startswith(':return'):
|
148
|
+
break
|
149
|
+
if line:
|
150
|
+
description_lines.append(line)
|
151
|
+
return ' '.join(description_lines).strip()
|
152
|
+
return ""
|
153
|
+
|
154
|
+
def get_kwargs(arg: ast.Call) -> dict:
|
155
|
+
kwargs = {}
|
156
|
+
for keyword in arg.keywords:
|
157
|
+
if isinstance(keyword.value, ast.Constant):
|
158
|
+
kwargs[keyword.arg] = keyword.value.value
|
159
|
+
elif isinstance(keyword.value, (ast.List, ast.Tuple)):
|
160
|
+
kwargs[keyword.arg] = [
|
161
|
+
AgentChain(**get_kwargs(elem)) if isinstance(elem, ast.Call) and isinstance(elem.func, ast.Name) and elem.func.id == "AgentChain"
|
162
|
+
else elem.value if isinstance(elem, ast.Constant) else elem
|
163
|
+
for elem in keyword.value.elts
|
164
|
+
]
|
165
|
+
elif isinstance(keyword.value, ast.Dict):
|
166
|
+
kwargs[keyword.arg] = {}
|
167
|
+
for k, v in zip(keyword.value.keys, keyword.value.values):
|
168
|
+
if isinstance(k, ast.Constant) and isinstance(v, ast.Constant):
|
169
|
+
kwargs[keyword.arg][k.value] = v.value
|
170
|
+
if isinstance(k, ast.Constant) and isinstance(v, ast.Call):
|
171
|
+
kwargs[keyword.arg][k.value] = get_kwargs(v)
|
172
|
+
return kwargs
|
173
|
+
|
174
|
+
def get_beamlit_deployment_from_resource(resource: Resource) -> AgentDeployment | FunctionDeployment:
|
175
|
+
for arg in resource.decorator.args:
|
176
|
+
if isinstance(arg, ast.Call):
|
177
|
+
if isinstance(arg.func, ast.Name) and arg.func.id == "AgentDeployment":
|
178
|
+
kwargs = get_kwargs(arg)
|
179
|
+
description = kwargs.pop("description", None)
|
180
|
+
return AgentDeployment(**kwargs, description=get_description(description, resource))
|
181
|
+
if isinstance(arg.func, ast.Name) and arg.func.id == "FunctionDeployment":
|
182
|
+
kwargs = get_kwargs(arg)
|
183
|
+
description = kwargs.pop("description", None)
|
184
|
+
return FunctionDeployment(**kwargs, parameters=get_parameters(resource), description=get_description(description, resource))
|
185
|
+
if resource.type == "agent":
|
186
|
+
return AgentDeployment(
|
187
|
+
agent=resource.name,
|
188
|
+
description=get_description(None,resource)
|
189
|
+
)
|
190
|
+
if resource.type == "function":
|
191
|
+
return FunctionDeployment(
|
192
|
+
function=resource.name,
|
193
|
+
parameters=get_parameters(resource),
|
194
|
+
description=get_description(None,resource)
|
195
|
+
)
|
196
|
+
return None
|
197
|
+
|
198
|
+
|
199
|
+
def get_flavors(flavors: list[Flavor]) -> str:
|
200
|
+
if not flavors:
|
201
|
+
return "[]"
|
202
|
+
return json.dumps([flavor.to_dict() for flavor in flavors])
|
203
|
+
|
204
|
+
def format_parameters(parameters: list[StoreFunctionParameter]) -> str:
|
205
|
+
if not parameters:
|
206
|
+
return "[]"
|
207
|
+
|
208
|
+
formatted = []
|
209
|
+
for param in parameters:
|
210
|
+
formatted.append(f"""
|
211
|
+
- name: {param.name}
|
212
|
+
type: {param.type_}
|
213
|
+
required: {str(param.required).lower()}
|
214
|
+
description: {param.description}""")
|
215
|
+
|
216
|
+
return "\n".join(formatted)
|
217
|
+
|
218
|
+
def format_agent_chain(agent_chain: list[AgentChain]) -> str:
|
219
|
+
if not agent_chain:
|
220
|
+
return "[]"
|
221
|
+
formatted = []
|
222
|
+
|
223
|
+
for agent in agent_chain:
|
224
|
+
formatted.append(f"""
|
225
|
+
- agent: {agent.name}
|
226
|
+
enabled: {agent.enabled}""")
|
227
|
+
if agent.description:
|
228
|
+
formatted.append(f" description: {agent.description}")
|
229
|
+
return "\n".join(formatted)
|
230
|
+
|
231
|
+
def get_agent_yaml(agent: AgentDeployment, functions: list[tuple[Resource, FunctionDeployment]], settings: Settings) -> str:
|
232
|
+
template = f"""
|
233
|
+
apiVersion: beamlit.com/v1alpha1
|
234
|
+
kind: Agent
|
235
|
+
metadata:
|
236
|
+
name: {agent.agent}
|
237
|
+
spec:
|
238
|
+
display_name: Agent - {agent.agent}
|
239
|
+
deployments:
|
240
|
+
- environment: production
|
241
|
+
enabled: true
|
242
|
+
policies: [{", ".join(agent.policies or [])}]
|
243
|
+
functions: [{", ".join([f"{function.function}" for (_, function) in functions])}]
|
244
|
+
agent_chain: {format_agent_chain(agent.agent_chain)}
|
245
|
+
model: {agent.model}
|
246
|
+
"""
|
247
|
+
if agent.description:
|
248
|
+
template += f""" description: |
|
249
|
+
{agent.description}"""
|
250
|
+
return template
|
251
|
+
|
252
|
+
def get_function_yaml(function: FunctionDeployment, settings: Settings) -> str:
|
253
|
+
return f"""
|
254
|
+
apiVersion: beamlit.com/v1alpha1
|
255
|
+
kind: Function
|
256
|
+
metadata:
|
257
|
+
name: {function.function}
|
258
|
+
spec:
|
259
|
+
display_name: {function.function}
|
260
|
+
deployments:
|
261
|
+
- environment: {settings.environment}
|
262
|
+
enabled: true
|
263
|
+
policies: [{", ".join(function.policies or [])}]
|
264
|
+
description: |
|
265
|
+
{function.description}
|
266
|
+
parameters: {format_parameters(function.parameters)}
|
267
|
+
"""
|
268
|
+
|
269
|
+
def dockerfile(type: Literal["agent", "function"], resource: Resource, deployment: AgentDeployment | FunctionDeployment):
|
270
|
+
if type == "agent":
|
271
|
+
module = f"{resource.module.__file__.split('/')[-1].replace('.py', '')}.{resource.module.__name__}"
|
272
|
+
else:
|
273
|
+
module = f"functions.{resource.module.__name__}.{resource.func.__name__}"
|
274
|
+
cmd = ["bl", "serve", "--port", "80", "--module", module]
|
275
|
+
if type == "agent":
|
276
|
+
cmd.append("--remote")
|
277
|
+
cmd_str = ','.join([f'"{c}"' for c in cmd])
|
278
|
+
|
279
|
+
return f"""
|
280
|
+
FROM python:3.12-slim
|
281
|
+
|
282
|
+
ARG UV_VERSION="latest"
|
283
|
+
RUN apt update && apt install -y curl
|
284
|
+
|
285
|
+
# Install uv.
|
286
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
287
|
+
RUN curl -fsSL https://raw.githubusercontent.com/beamlit/toolkit/main/install.sh | BINDIR=/bin sh
|
288
|
+
WORKDIR /beamlit
|
289
|
+
|
290
|
+
# Install the application dependencies.
|
291
|
+
COPY pyproject.toml /beamlit/pyproject.toml
|
292
|
+
COPY uv.lock /beamlit/uv.lock
|
293
|
+
RUN uv sync --no-cache
|
294
|
+
|
295
|
+
COPY README.md /beamlit/README.md
|
296
|
+
COPY LICENSE /beamlit/LICENSE
|
297
|
+
COPY src /beamlit/src
|
298
|
+
|
299
|
+
ENV PATH="/beamlit/.venv/bin:$PATH"
|
300
|
+
|
301
|
+
ENTRYPOINT [{cmd_str}]
|
302
|
+
"""
|
303
|
+
|
304
|
+
def generate_beamlit_deployment(directory: str):
|
305
|
+
settings = init()
|
306
|
+
logger = getLogger(__name__)
|
307
|
+
logger.info(f"Importing server module: {settings.server.module}")
|
308
|
+
functions: list[tuple[Resource, FunctionDeployment]] = []
|
309
|
+
agents: list[tuple[Resource, AgentDeployment]] = []
|
310
|
+
for agent in get_resources("agent"):
|
311
|
+
agent_deployment = get_beamlit_deployment_from_resource(agent)
|
312
|
+
if agent_deployment:
|
313
|
+
agents.append((agent, agent_deployment))
|
314
|
+
for function in get_resources("function"):
|
315
|
+
function_deployment = get_beamlit_deployment_from_resource(function)
|
316
|
+
if function_deployment:
|
317
|
+
functions.append((function, function_deployment))
|
318
|
+
|
319
|
+
agents_dir = os.path.join(directory, "agents")
|
320
|
+
functions_dir = os.path.join(directory, "functions")
|
321
|
+
# Create directory if it doesn't exist
|
322
|
+
os.makedirs(agents_dir, exist_ok=True)
|
323
|
+
os.makedirs(functions_dir, exist_ok=True)
|
324
|
+
for (resource, agent) in agents:
|
325
|
+
# write deployment file
|
326
|
+
agent_dir = os.path.join(agents_dir, agent.agent)
|
327
|
+
os.makedirs(agent_dir, exist_ok=True)
|
328
|
+
with open(os.path.join(agent_dir, f"agent.yaml"), "w") as f:
|
329
|
+
content = get_agent_yaml(agent, functions, settings)
|
330
|
+
f.write(content)
|
331
|
+
# write dockerfile for build
|
332
|
+
with open(os.path.join(agent_dir, f"Dockerfile"), "w") as f:
|
333
|
+
content = dockerfile("agent", resource, agent)
|
334
|
+
f.write(content)
|
335
|
+
for (resource, function) in functions:
|
336
|
+
# write deployment file
|
337
|
+
function_dir = os.path.join(functions_dir, function.function)
|
338
|
+
os.makedirs(function_dir, exist_ok=True)
|
339
|
+
with open(os.path.join(function_dir, f"function.yaml"), "w") as f:
|
340
|
+
content = get_function_yaml(function, settings)
|
341
|
+
f.write(content)
|
342
|
+
# write dockerfile for build
|
343
|
+
with open(os.path.join(function_dir, f"Dockerfile"), "w") as f:
|
344
|
+
content = dockerfile("function", resource, function)
|
345
|
+
f.write(content)
|
beamlit/functions/decorator.py
CHANGED
@@ -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
|
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
|
|
@@ -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(
|
@@ -138,10 +138,12 @@ beamlit/common/__init__.py,sha256=yDoMJDKj-xjTGl7U1YI59KpWxiOV65HSiUulgO8xdTA,27
|
|
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=
|
141
|
+
beamlit/common/settings.py,sha256=cL5HAg6atxnTJXL9Rxz5Xs4iApNCCYY5ijha8UM3PW4,5446
|
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=F79JRGhZV-5wIvux66UC6ATSnqQkTfL2MvD8ayVy6-o,13487
|
143
145
|
beamlit/functions/__init__.py,sha256=_RPG1Bfg54JGdIPnViAU6n9zD7E1cDNsdXi8oYGskzE,138
|
144
|
-
beamlit/functions/decorator.py,sha256=
|
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
|
@@ -296,6 +298,6 @@ 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.
|
300
|
-
beamlit-0.0.
|
301
|
-
beamlit-0.0.
|
301
|
+
beamlit-0.0.23rc14.dist-info/METADATA,sha256=l7YaLMfPwUhKPfEiD2-Ve1OtVt6U3dkdR9rEh243w5Y,2027
|
302
|
+
beamlit-0.0.23rc14.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
303
|
+
beamlit-0.0.23rc14.dist-info/RECORD,,
|
File without changes
|