beamlit 0.0.20rc6__py3-none-any.whl → 0.0.20rc7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
beamlit/agents/chat.py CHANGED
@@ -62,12 +62,12 @@ def get_chat_model(agent_model: AgentDeployment):
62
62
  raise ValueError("agent_model not found in configuration")
63
63
  if agent_model.runtime is None:
64
64
  raise ValueError("runtime not found in agent model")
65
- if agent_model.runtime.type is None:
65
+ if agent_model.runtime.type_ is None:
66
66
  raise ValueError("type not found in runtime")
67
67
  if agent_model.runtime.model is None:
68
68
  raise ValueError("model not found in runtime")
69
69
 
70
- provider = agent_model.runtime.type
70
+ provider = agent_model.runtime.type_
71
71
  model = agent_model.runtime.model
72
72
 
73
73
  kwargs = {
@@ -34,7 +34,10 @@ def get_functions(dir="src/functions", from_decorator="function"):
34
34
 
35
35
  # Look for function definitions with decorators
36
36
  for node in ast.walk(tree):
37
- if not isinstance(node, ast.FunctionDef) or len(node.decorator_list) == 0:
37
+ if (
38
+ not isinstance(node, ast.FunctionDef)
39
+ or len(node.decorator_list) == 0
40
+ ):
38
41
  continue
39
42
  decorator = node.decorator_list[0]
40
43
 
@@ -56,7 +59,9 @@ def get_functions(dir="src/functions", from_decorator="function"):
56
59
  is_kit = False
57
60
  if isinstance(decorator, ast.Call):
58
61
  for keyword in decorator.keywords:
59
- if keyword.arg == "kit" and isinstance(keyword.value, ast.Constant):
62
+ if keyword.arg == "kit" and isinstance(
63
+ keyword.value, ast.Constant
64
+ ):
60
65
  is_kit = keyword.value.value
61
66
  if is_kit:
62
67
  kit_functions = get_functions(
@@ -87,9 +92,9 @@ def agent(
87
92
 
88
93
  def wrapped(*args, **kwargs):
89
94
  return func(
90
- settings.agent,
91
- settings.agent_chat_model,
92
- settings.agent_functions,
95
+ settings.agent.agent,
96
+ settings.agent.chat_model,
97
+ settings.agent.functions,
93
98
  *args,
94
99
  **kwargs,
95
100
  )
@@ -97,33 +102,46 @@ def agent(
97
102
  return wrapped
98
103
 
99
104
  # Initialize functions array to store decorated functions
100
- functions = get_functions()
101
- settings.agent_functions = functions
105
+ functions = get_functions(dir=settings.agent.functions_directory)
106
+ settings.agent.functions = functions
102
107
 
103
108
  if bl_agent.model and chat_model is None:
104
109
  client = new_client()
105
110
  try:
106
- response = get_model_deployment.sync_detailed(bl_agent.model, settings.environment, client=client)
107
- settings.agent_model = response.parsed
111
+ response = get_model_deployment.sync_detailed(
112
+ bl_agent.model, settings.environment, client=client
113
+ )
114
+ settings.agent.model = response.parsed
108
115
  except UnexpectedStatus as e:
109
116
  if e.status_code == 404 and settings.environment != "production":
110
117
  try:
111
- response = get_model_deployment.sync_detailed(bl_agent.model, "production", client=client)
112
- settings.agent_model = response.parsed
118
+ response = get_model_deployment.sync_detailed(
119
+ bl_agent.model, "production", client=client
120
+ )
121
+ settings.agent.model = response.parsed
113
122
  except UnexpectedStatus as e:
114
123
  if e.status_code == 404:
115
124
  raise ValueError(f"Model {bl_agent.model} not found")
116
125
  else:
117
126
  raise e
118
-
119
- chat_model = get_chat_model(settings.agent_model)
120
- settings.agent_chat_model = chat_model
121
- runtime = settings.agent_model.runtime
122
- logger.info(f"Chat model configured, using: {runtime.type}:{runtime.model}")
127
+ chat_model = get_chat_model(settings.agent.model)
128
+ settings.agent.chat_model = chat_model
129
+ runtime = settings.agent.model.runtime
130
+ logger.info(f"Chat model configured, using: {runtime.type_}:{runtime.model}")
131
+
132
+ if len(functions) == 0:
133
+ raise ValueError(
134
+ "You must define at least one function, you can define this function in directory "
135
+ f'"{settings.agent.functions_directory}". Here is a sample function you can use:\n\n'
136
+ "from beamlit.functions import function\n\n"
137
+ "@function()\n"
138
+ "def hello_world(query: str):\n"
139
+ " return 'Hello, world!'\n"
140
+ )
123
141
 
124
142
  if agent is None and chat_model is not None:
125
143
  memory = MemorySaver()
126
144
  agent = create_react_agent(chat_model, functions, checkpointer=memory)
127
- settings.agent = agent
145
+ settings.agent.agent = agent
128
146
 
129
147
  return wrapper
@@ -66,9 +66,7 @@ def new_client_with_credentials(config: RunClientWithCredentials):
66
66
  elif config.credentials.access_token:
67
67
  provider = BearerToken(config.credentials, config.workspace, config.api_url)
68
68
  elif config.credentials.client_credentials:
69
- provider = ClientCredentials(
70
- config.credentials, config.workspace, config.api_url
71
- )
69
+ provider = ClientCredentials(config.credentials, config.workspace, config.api_url)
72
70
  else:
73
71
  provider = PublicProvider()
74
72
 
@@ -76,7 +74,13 @@ def new_client_with_credentials(config: RunClientWithCredentials):
76
74
 
77
75
 
78
76
  def get_authentication_headers(settings: Settings) -> Dict[str, str]:
79
- credentials = load_credentials_from_settings(settings)
77
+ context = current_context()
78
+ if context.workspace:
79
+ credentials = load_credentials(context.workspace)
80
+ else:
81
+ settings = get_settings()
82
+ credentials = load_credentials_from_settings(settings)
83
+
80
84
  config = RunClientWithCredentials(
81
85
  credentials=credentials,
82
86
  workspace=settings.workspace,
@@ -87,9 +91,7 @@ def get_authentication_headers(settings: Settings) -> Dict[str, str]:
87
91
  elif config.credentials.access_token:
88
92
  provider = BearerToken(config.credentials, config.workspace, config.api_url)
89
93
  elif config.credentials.client_credentials:
90
- provider = ClientCredentials(
91
- config.credentials, config.workspace, config.api_url
92
- )
94
+ provider = ClientCredentials(config.credentials, config.workspace, config.api_url)
93
95
 
94
96
  if provider is None:
95
97
  return None
@@ -1,4 +1,5 @@
1
1
  from dataclasses import dataclass
2
+ from logging import getLogger
2
3
  from pathlib import Path
3
4
  from typing import List
4
5
 
@@ -6,6 +7,8 @@ import yaml
6
7
 
7
8
  from beamlit.common.settings import Settings
8
9
 
10
+ logger = getLogger(__name__)
11
+
9
12
 
10
13
  @dataclass
11
14
  class Credentials:
@@ -120,32 +123,35 @@ def load_credentials(workspace_name: str) -> Credentials:
120
123
  return Credentials()
121
124
 
122
125
 
123
- def load_credentials_from_settings(config: Settings) -> Credentials:
124
- return Credentials(api_key=config.api_key, client_credentials=config.client_credentials)
126
+ def load_credentials_from_settings(settings: Settings) -> Credentials:
127
+ return Credentials(
128
+ api_key=settings.authentication.api_key,
129
+ client_credentials=settings.authentication.client_credentials,
130
+ )
125
131
 
126
132
 
127
133
  def create_home_dir_if_missing():
128
134
  home_dir = Path.home()
129
135
  if not home_dir:
130
- print("Error getting home directory")
136
+ logger.error("Error getting home directory")
131
137
  return
132
138
 
133
139
  credentials_dir = home_dir / ".beamlit"
134
140
  credentials_file = credentials_dir / "credentials.json"
135
141
 
136
142
  if credentials_file.exists():
137
- print("You are already logged in. Enter a new API key to overwrite it.")
143
+ logger.warning("You are already logged in. Enter a new API key to overwrite it.")
138
144
  else:
139
145
  try:
140
146
  credentials_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
141
147
  except Exception as e:
142
- print(f"Error creating credentials directory: {e}")
148
+ logger.error(f"Error creating credentials directory: {e}")
143
149
 
144
150
 
145
151
  def save_credentials(workspace_name: str, credentials: Credentials):
146
152
  create_home_dir_if_missing()
147
153
  if not credentials.access_token and not credentials.api_key:
148
- print("No credentials to save, error")
154
+ logger.info("No credentials to save, error")
149
155
  return
150
156
 
151
157
  config = load_config()
@@ -155,8 +155,8 @@ run_client = RunClient(client=client)
155
155
  export_code = "\n\nfunctions = ["
156
156
  export_chain = "\n\nchains = ["
157
157
  code = imports
158
- if settings.agent_functions and len(settings.agent_functions) > 0:
159
- for function_config in settings.agent_functions:
158
+ if settings.agent.functions and len(settings.agent.functions) > 0:
159
+ for function_config in settings.agent.functions:
160
160
  if function_config.kit and len(function_config.kit) > 0:
161
161
  new_code, export = generate_kit_function_code(settings, function_config, function_config.kit)
162
162
  code += new_code
@@ -165,15 +165,15 @@ run_client = RunClient(client=client)
165
165
  new_code, export = generate_function_code(settings, function_config)
166
166
  code += new_code
167
167
  export_code += export
168
- if settings.agent_chain and len(settings.agent_chain) > 0:
169
- for agent in settings.agent_chain:
168
+ if settings.agent.chain and len(settings.agent.chain) > 0:
169
+ for agent in settings.agent.chain:
170
170
  new_code, export = generate_chain_code(settings, agent)
171
171
  code += new_code
172
172
  export_chain += export
173
- if settings.agent_functions and len(settings.agent_functions) > 0:
173
+ if settings.agent.functions and len(settings.agent.functions) > 0:
174
174
  export_code = export_code[:-1]
175
175
  export_code += "]"
176
- if settings.agent_chain and len(settings.agent_chain) > 0:
176
+ if settings.agent.chain and len(settings.agent.chain) > 0:
177
177
  export_chain = export_chain[:-1]
178
178
  export_chain += "]"
179
179
  content = code + export_code + export_chain
@@ -1,11 +1,16 @@
1
1
  import os
2
2
  from logging import getLogger
3
- from typing import Any, List, Tuple, Type, Union
3
+ from typing import List, Tuple, Type, Union
4
4
 
5
5
  from langchain_core.language_models.chat_models import BaseChatModel
6
6
  from langgraph.graph.graph import CompiledGraph
7
7
  from pydantic import Field
8
- from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, YamlConfigSettingsSource
8
+ from pydantic_settings import (
9
+ BaseSettings,
10
+ PydanticBaseSettingsSource,
11
+ SettingsConfigDict,
12
+ YamlConfigSettingsSource,
13
+ )
9
14
 
10
15
  from beamlit.api.functions import get_function_deployment
11
16
  from beamlit.api.models import get_model_deployment
@@ -24,6 +29,28 @@ def get_settings():
24
29
  return SETTINGS
25
30
 
26
31
 
32
+ class SettingsAgent(BaseSettings):
33
+ agent: Union[None, CompiledGraph, BaseChatModel] = None
34
+ chain: Union[Unset, List[AgentDeployment]] = UNSET
35
+ model: Union[Unset, ModelDeployment] = UNSET
36
+ functions: Union[Unset, List[FunctionDeployment]] = UNSET
37
+ functions_directory: str = Field(default="src/functions")
38
+ chat_model: Union[None, BaseChatModel] = None
39
+ module: str = Field(default="main.main")
40
+
41
+
42
+ class SettingsAuthentication(BaseSettings):
43
+ api_key: Union[None, str] = None
44
+ jwt: Union[None, str] = None
45
+ client_credentials: Union[None, str] = None
46
+
47
+
48
+ class SettingsServer(BaseSettings):
49
+ module: str = Field(default="main.main")
50
+ port: int = Field(default=80)
51
+ host: str = Field(default="0.0.0.0")
52
+
53
+
27
54
  class Settings(BaseSettings):
28
55
  model_config = SettingsConfigDict(
29
56
  yaml_file="beamlit.yaml",
@@ -31,24 +58,15 @@ class Settings(BaseSettings):
31
58
  )
32
59
 
33
60
  workspace: str
34
- type: str
35
- name: str
36
- environment: str = Field(default="production")
61
+ environment: str
62
+ type: str = Field(default="agent")
63
+ name: str = Field(default="beamlit-agent")
37
64
  base_url: str = Field(default="https://api.beamlit.dev/v0")
38
65
  run_url: str = Field(default="https://run.beamlit.dev")
39
- port: int = Field(default=80)
40
- host: str = Field(default="0.0.0.0")
41
66
  log_level: str = Field(default="INFO")
42
- api_key: Union[None, str] = None
43
- jwt: Union[None, str] = None
44
- client_credentials: Union[None, str] = None
45
- agent_module: str = Field(default="main.main")
46
- agent_chain: Union[Unset, List[AgentDeployment]] = UNSET
47
- agent_functions: Union[Unset, List[FunctionDeployment]] = UNSET
48
- agent_model: Union[Unset, ModelDeployment] = UNSET
49
- agent: Union[None, CompiledGraph, BaseChatModel] = None
50
- agent_chat_model: Union[None, BaseChatModel] = None
51
- agent_functions: Union[None, List[Any]] = None
67
+ agent: SettingsAgent = SettingsAgent()
68
+ server: SettingsServer = SettingsServer()
69
+ authentication: SettingsAuthentication = SettingsAuthentication()
52
70
 
53
71
  @classmethod
54
72
  def settings_customise_sources(
@@ -76,14 +94,14 @@ def init_agent(
76
94
  from beamlit.common.generate import generate
77
95
 
78
96
  logger = getLogger(__name__)
79
-
97
+ settings = get_settings()
80
98
  # Init configuration from environment variables
81
- if SETTINGS.agent_functions or SETTINGS.agent_chain:
99
+ if settings.agent.functions or settings.agent.chain:
82
100
  return
83
101
 
84
102
  # Init configuration from beamlit control plane
85
- name = SETTINGS.name
86
- env = SETTINGS.environment
103
+ name = settings.name
104
+ env = settings.environment
87
105
 
88
106
  agent_deployment = get_agent_deployment.sync(name, env, client=client)
89
107
  function_deployments = []
@@ -92,7 +110,7 @@ def init_agent(
92
110
  for function in agent_deployment.functions:
93
111
  function_deployment = get_function_deployment.sync(function, env, client=client)
94
112
  function_deployments.append(function_deployment)
95
- SETTINGS.agent_functions = function_deployments
113
+ settings.agent.functions = function_deployments
96
114
 
97
115
  if agent_deployment.agent_chain:
98
116
  for chain in agent_deployment.agent_chain:
@@ -101,25 +119,37 @@ def init_agent(
101
119
  if chain.description:
102
120
  agent_deployment.description = chain.description
103
121
  agent_chain_deployments.append(agent_deployment)
104
- SETTINGS.agent_chain = agent_chain_deployments
122
+ settings.agent.chain = agent_chain_deployments
105
123
  if agent_deployment.model:
106
124
  model_deployment = get_model_deployment.sync(agent_deployment.model, env, client=client)
107
- SETTINGS.agent_model = model_deployment
125
+ settings.agent.model = model_deployment
108
126
 
109
127
  content_generate = generate(destination, dry_run=True)
110
128
  compared_content = None
111
129
  if os.path.exists(destination):
112
130
  compared_content = open(destination).read()
113
131
 
114
- if not os.path.exists(destination) or (compared_content and content_generate != compared_content):
132
+ if not os.path.exists(destination) or (
133
+ compared_content and content_generate != compared_content
134
+ ):
115
135
  logger.info("Generating agent code")
116
136
  generate(destination)
117
137
 
118
138
 
119
139
  def init() -> Settings:
120
140
  """Parse the beamlit.yaml file to get configurations."""
141
+ from beamlit.authentication.credentials import current_context
142
+
121
143
  global SETTINGS
122
144
 
123
- SETTINGS = Settings()
145
+ context = current_context()
146
+ kwargs = {}
147
+ if context.workspace:
148
+ kwargs["workspace"] = context.workspace
149
+ if context.environment:
150
+ kwargs["environment"] = context.environment
151
+
152
+ SETTINGS = Settings(**kwargs)
124
153
  init_logger(SETTINGS.log_level)
154
+
125
155
  return SETTINGS
beamlit/serve/app.py ADDED
@@ -0,0 +1,73 @@
1
+ import importlib
2
+ import traceback
3
+ from logging import getLogger
4
+ from uuid import uuid4
5
+
6
+ from asgi_correlation_id import CorrelationIdMiddleware
7
+ from fastapi import FastAPI, Request, Response
8
+ from fastapi.responses import JSONResponse
9
+
10
+ from beamlit.common.settings import get_settings, init
11
+
12
+ from .middlewares import AccessLogMiddleware, AddProcessTimeHeader
13
+
14
+
15
+ def import_module():
16
+ settings = get_settings()
17
+ main_module = importlib.import_module(".".join(settings.server.module.split(".")[0:-1]))
18
+ func = getattr(main_module, settings.server.module.split(".")[-1])
19
+ return func
20
+
21
+
22
+ settings = init()
23
+ logger = getLogger(__name__)
24
+ logger.info(f"Importing server module: {settings.server.module}")
25
+ func = import_module()
26
+ logger.info(
27
+ f"Running server with environment {settings.environment}"
28
+ f" on {settings.server.host}:{settings.server.port}"
29
+ )
30
+
31
+ app = FastAPI(docs_url=None, redoc_url=None)
32
+ app.add_middleware(
33
+ CorrelationIdMiddleware,
34
+ header_name="x-beamlit-request-id",
35
+ generator=lambda: str(uuid4()),
36
+ )
37
+ app.add_middleware(AddProcessTimeHeader)
38
+ app.add_middleware(AccessLogMiddleware)
39
+
40
+
41
+ @app.get("/health")
42
+ async def health():
43
+ return {"status": "ok"}
44
+
45
+
46
+ @app.post("/")
47
+ async def root(request: Request):
48
+ settings = get_settings()
49
+ logger = getLogger(__name__)
50
+ try:
51
+ body = await request.json()
52
+ response = await func(body)
53
+ if isinstance(response, Response):
54
+ return response
55
+ if type(response) is str:
56
+ return Response(
57
+ content=response,
58
+ headers={"Content-Type": "text/plain"},
59
+ media_type="text/plain",
60
+ status_code=200,
61
+ )
62
+ return JSONResponse(status_code=200, content=response)
63
+ except ValueError as e:
64
+ content = {"error": str(e)}
65
+ if settings.environment == "development":
66
+ content["traceback"] = str(traceback.format_exc())
67
+ logger.error(f"{content}")
68
+ return JSONResponse(status_code=400, content=content)
69
+ except Exception as e:
70
+ content = {"error": f"Internal server error, {e}"}
71
+ if settings.environment == "development":
72
+ content["traceback"] = str(traceback.format_exc())
73
+ return JSONResponse(status_code=500, content=content)
@@ -0,0 +1,4 @@
1
+ from .accesslog import AccessLogMiddleware
2
+ from .processtime import AddProcessTimeHeader
3
+
4
+ __all__ = ["AccessLogMiddleware", "AddProcessTimeHeader"]
@@ -0,0 +1,14 @@
1
+ from logging import getLogger
2
+
3
+ from starlette.middleware.base import BaseHTTPMiddleware
4
+
5
+
6
+ class AccessLogMiddleware(BaseHTTPMiddleware):
7
+ async def dispatch(self, request, call_next):
8
+ logger = getLogger(__name__)
9
+ response = await call_next(request)
10
+ process_time = response.headers.get("X-Process-Time")
11
+ rid_header = response.headers.get("X-Request-Id")
12
+ request_id = rid_header or response.headers.get("X-Beamlit-Request-Id")
13
+ logger.info(f"{request.method} {request.url.path} {response.status_code} {process_time}ms rid={request_id}")
14
+ return response
@@ -0,0 +1,12 @@
1
+ import time
2
+
3
+ from starlette.middleware.base import BaseHTTPMiddleware
4
+
5
+
6
+ class AddProcessTimeHeader(BaseHTTPMiddleware):
7
+ async def dispatch(self, request, call_next):
8
+ start_time = time.perf_counter()
9
+ response = await call_next(request)
10
+ process_time = (time.perf_counter() - start_time) * 1000
11
+ response.headers["X-Process-Time"] = f"{process_time:.2f}"
12
+ return response
@@ -1,15 +1,17 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: beamlit
3
- Version: 0.0.20rc6
3
+ Version: 0.0.20rc7
4
4
  Summary: Add your description here
5
5
  Author-email: cploujoux <ch.ploujoux@gmail.com>
6
6
  Requires-Python: >=3.12
7
- Requires-Dist: asgi-correlation-id==4.3.4
7
+ Requires-Dist: asgi-correlation-id<5.0.0,>=4.3.4
8
8
  Requires-Dist: attrs>=21.3.0
9
- Requires-Dist: fastapi[standard]==0.115.4
9
+ Requires-Dist: fastapi[standard]<0.116.0,>=0.115.4
10
10
  Requires-Dist: httpx<0.28.0,>=0.20.0
11
- Requires-Dist: langchain-core==0.3.13
12
- Requires-Dist: langgraph==0.2.40
11
+ Requires-Dist: langchain-community<0.4.0,>=0.3.3
12
+ Requires-Dist: langchain-core<0.4.0,>=0.3.13
13
+ Requires-Dist: langchain-openai<0.3.0,>=0.2.4
14
+ Requires-Dist: langgraph<0.3.0,>=0.2.40
13
15
  Requires-Dist: pydantic-settings<2.7.0,>=2.6.1
14
16
  Requires-Dist: pydantic<2.11.0,>=2.10.3
15
17
  Requires-Dist: python-dateutil>=2.8.0
@@ -3,11 +3,10 @@ beamlit/client.py,sha256=vwvjAkUKHRySnA2tOVzXI8xtm9s1k2sEklCRE4j1Vc8,12543
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
- beamlit/serve.py,sha256=WFByzkLYYtgR0fl0zqXg9sFbs7edOlRWWIzQFyzO_mA,3757
7
6
  beamlit/types.py,sha256=E1hhDh_zXfsSQ0NCt9-uw90_Mr5iIlsdfnfvxv5HarU,1005
8
7
  beamlit/agents/__init__.py,sha256=nf1iwQwGtCG6nDqyVhxfWoqR6dv6X3bvSpCeqkTCFaM,101
9
- beamlit/agents/chat.py,sha256=1DRpz9RdXL1fzkf8nVagB0gNC0c6aaID91YXao3uW34,2628
10
- beamlit/agents/decorator.py,sha256=vW-dFOvdch38lYwqgLv4v9Fq1LmBYVAprNl_4-bHsZ0,5314
8
+ beamlit/agents/chat.py,sha256=8KsUvIB-eaUApfKclT76_4HQu3VBa9nifMqmq_AAvcM,2630
9
+ beamlit/agents/decorator.py,sha256=naeOsvfto74TZTcc15Ro98Hl8Bbe6Uhflof_SdyUkwY,6054
11
10
  beamlit/api/__init__.py,sha256=zTSiG_ujSjAqWPyc435YXaX9XTlpMjiJWBbV-f-YtdA,45
12
11
  beamlit/api/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
12
  beamlit/api/agents/create_agent.py,sha256=HFExosu02JZqZz7I6U6WjN81TERz6p2i8CzQCyiRYXo,4112
@@ -131,15 +130,15 @@ beamlit/api/workspaces/update_workspace.py,sha256=qa5DV2UJSUYuB_ibALb4E9ghKpT1Ha
131
130
  beamlit/api/workspaces/update_workspace_user_role.py,sha256=Yn9iuJ4tKtauzBiJyU4-wYUMS9g98X2Om8zs7UkzrY8,4917
132
131
  beamlit/authentication/__init__.py,sha256=wiXqRbc7E-ulrH_ueA9duOGFvXeo7-RvhSD1XbFogMo,1020
133
132
  beamlit/authentication/apikey.py,sha256=jnz1FMRauI5qAInqeeDER8aCONx4O8ZPZGedvi3Ap_o,659
134
- beamlit/authentication/authentication.py,sha256=Mv_PiHKTEVAYsLNnE0DMOtqoD8P9SWVbPXk-HCpGhac,3000
133
+ beamlit/authentication/authentication.py,sha256=om26AteY2cCV9ctqbOCynX6PgS8YO-aCreNOFSnnWKc,3121
135
134
  beamlit/authentication/clientcredentials.py,sha256=6kbfTjwUkXUArJX8XZLe9ZzbEicQc19tSXBvsTpiXMk,3954
136
- beamlit/authentication/credentials.py,sha256=rNYZioSSiVK4-hGp8wQ-awlcDxLpA6Ag3SG6lUt7W-k,4817
135
+ beamlit/authentication/credentials.py,sha256=_Bjj49jGeo-JTvO2GPS1yXxh5vS1NmX0haxTfg0KiEk,4965
137
136
  beamlit/authentication/device_mode.py,sha256=oQVBCDsq-pdeXF31WSTAAEdaX6eACV7SYcOSyf3ea_Q,3728
138
137
  beamlit/common/__init__.py,sha256=yDoMJDKj-xjTGl7U1YI59KpWxiOV65HSiUulgO8xdTA,277
139
- beamlit/common/generate.py,sha256=eLEJz6hRQ1Jxq9VG1oFG2odqEhLJM3L3dkHb0-K-5KU,7227
138
+ beamlit/common/generate.py,sha256=VJ_MiRDulXdQdnlKdM4_Bod6CO6DOGlFiosGXOLuLGs,7227
140
139
  beamlit/common/logger.py,sha256=ayabnsoHS8ncXm8EpBS01FkvSe0XRcaNdQjKVuPI5z4,1025
141
140
  beamlit/common/secrets.py,sha256=sid81bOe3LflkMKDHwBsBs9nIju8bp5-v9qU9gkyNMc,212
142
- beamlit/common/settings.py,sha256=1QDxhGjnYJzeqhtWvLZgnQRoU4J3uRQFvhCkA8ePNwM,4515
141
+ beamlit/common/settings.py,sha256=WF7_BztXwfHC1LY7DXtj7plYNFgYnCx_hk_wO1DUPJ8,5188
143
142
  beamlit/common/utils.py,sha256=jouz5igBvT37Xn_e94-foCHyQczVim-UzVcoIF6RWJ4,657
144
143
  beamlit/functions/__init__.py,sha256=_RPG1Bfg54JGdIPnViAU6n9zD7E1cDNsdXi8oYGskzE,138
145
144
  beamlit/functions/decorator.py,sha256=-2newMBztweIgFuh0ABKOdxCfUzWaRxf0ym-YAgggJI,3168
@@ -285,6 +284,10 @@ beamlit/models/websocket_channel.py,sha256=tyNtsVR0cOwd6BK--ehBCH8bIjxtyPhiAkrxY
285
284
  beamlit/models/workspace.py,sha256=s7wS6ibswosB0FdUb3ry3BnlLa325axBdYPLI3ipe0Q,3986
286
285
  beamlit/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8fWFfkCf7hzCTiNp4A,1246
287
286
  beamlit/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
288
- beamlit-0.0.20rc6.dist-info/METADATA,sha256=0t45przJ2szSLSOPIO5rXalb6Uf-3xAw4H62jsWoH-E,1901
289
- beamlit-0.0.20rc6.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
290
- beamlit-0.0.20rc6.dist-info/RECORD,,
287
+ beamlit/serve/app.py,sha256=riZWmczexN5t-PT172WxB5YZkoORDEl0ucS-2vxdWHg,2292
288
+ beamlit/serve/middlewares/__init__.py,sha256=1dVmnOmhAQWvWktqHkKSIX-YoF6fmMU8xkUQuhg_rJU,148
289
+ beamlit/serve/middlewares/accesslog.py,sha256=wM52-hcwtO-_hdM1pnsEJzerzJf1MzEyN5m85BdDccE,609
290
+ beamlit/serve/middlewares/processtime.py,sha256=lDAaIasZ4bwvN-HKHvZpaD9r-yrkVNZYx4abvbjbrCg,411
291
+ beamlit-0.0.20rc7.dist-info/METADATA,sha256=ib3u7kmMzztuEi5w1_vsiZ59yPJGVsZh4_KUTfZImCM,2026
292
+ beamlit-0.0.20rc7.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
293
+ beamlit-0.0.20rc7.dist-info/RECORD,,
beamlit/serve.py DELETED
@@ -1,120 +0,0 @@
1
- import importlib
2
- import time
3
- import traceback
4
- from contextlib import asynccontextmanager
5
- from logging import getLogger
6
- from uuid import uuid4
7
-
8
- import uvicorn
9
- from asgi_correlation_id import CorrelationIdMiddleware
10
- from fastapi import FastAPI, Request, Response
11
- from fastapi.responses import JSONResponse
12
- from starlette.middleware.base import BaseHTTPMiddleware
13
-
14
- from beamlit.common.settings import get_settings, init
15
-
16
- main_agent = None
17
-
18
-
19
- class AccessLogMiddleware(BaseHTTPMiddleware):
20
- async def dispatch(self, request, call_next):
21
- logger = getLogger(__name__)
22
- response = await call_next(request)
23
- process_time = response.headers.get("X-Process-Time")
24
- rid_header = response.headers.get("X-Request-Id")
25
- request_id = rid_header or response.headers.get("X-Beamlit-Request-Id")
26
- logger.info(f"{request.method} {request.url.path} {response.status_code} {process_time}ms rid={request_id}")
27
- return response
28
-
29
-
30
- class AddProcessTimeHeader(BaseHTTPMiddleware):
31
- async def dispatch(self, request, call_next):
32
- start_time = time.perf_counter()
33
- response = await call_next(request)
34
- process_time = (time.perf_counter() - start_time) * 1000
35
- response.headers["X-Process-Time"] = f"{process_time:.2f}"
36
- return response
37
-
38
-
39
- @asynccontextmanager
40
- async def lifespan(app: FastAPI):
41
- try:
42
- is_main = __name__ == "main"
43
- if not is_main:
44
- init()
45
-
46
- logger = getLogger(__name__)
47
- settings = get_settings()
48
-
49
- # Import the agent
50
- global main_agent
51
- main_agent = importlib.import_module(".".join(settings.agent_module.split(".")[0:-1]))
52
- # Log the server is running
53
- if is_main:
54
- logger.info(f"Server running on http://{settings.host}:{settings.port}")
55
- yield
56
- except Exception as e:
57
- logger = getLogger(__name__)
58
- logger.error(f"Error initializing agent: {e}", exc_info=True)
59
- raise e
60
-
61
-
62
- app = FastAPI(lifespan=lifespan, docs_url=None, redoc_url=None)
63
- app.add_middleware(
64
- CorrelationIdMiddleware,
65
- header_name="x-beamlit-request-id",
66
- generator=lambda: str(uuid4()),
67
- )
68
- app.add_middleware(AddProcessTimeHeader)
69
- app.add_middleware(AccessLogMiddleware)
70
-
71
-
72
- @app.get("/health")
73
- async def health():
74
- return {"status": "ok"}
75
-
76
-
77
- @app.post("/")
78
- async def root(request: Request):
79
- settings = get_settings()
80
- logger = getLogger(__name__)
81
- try:
82
- func = getattr(main_agent, settings.agent_module.split(".")[-1])
83
- body = await request.json()
84
- response = await func(body)
85
- if isinstance(response, Response):
86
- return response
87
- if type(response) is str:
88
- return Response(
89
- content=response,
90
- headers={"Content-Type": "text/plain"},
91
- media_type="text/plain",
92
- status_code=200,
93
- )
94
- return JSONResponse(status_code=200, content=response)
95
- except ValueError as e:
96
- content = {"error": str(e)}
97
- if settings.environment == "development":
98
- content["traceback"] = str(traceback.format_exc())
99
- logger.error(f"{content}")
100
- return JSONResponse(status_code=400, content=content)
101
- except Exception as e:
102
- content = {"error": f"Internal server error, {e}"}
103
- if settings.environment == "development":
104
- content["traceback"] = str(traceback.format_exc())
105
- return JSONResponse(status_code=500, content=content)
106
-
107
-
108
- def main():
109
- settings = init()
110
- uvicorn.run(
111
- f"{__name__}:app",
112
- host=settings.host,
113
- port=settings.port,
114
- log_level="critical",
115
- reload=settings.environment != "production",
116
- )
117
-
118
-
119
- if __name__ == "__main__":
120
- main()