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 +2 -2
- beamlit/agents/decorator.py +35 -17
- beamlit/authentication/authentication.py +9 -7
- beamlit/authentication/credentials.py +12 -6
- beamlit/common/generate.py +6 -6
- beamlit/common/settings.py +56 -26
- beamlit/serve/app.py +73 -0
- beamlit/serve/middlewares/__init__.py +4 -0
- beamlit/serve/middlewares/accesslog.py +14 -0
- beamlit/serve/middlewares/processtime.py +12 -0
- {beamlit-0.0.20rc6.dist-info → beamlit-0.0.20rc7.dist-info}/METADATA +7 -5
- {beamlit-0.0.20rc6.dist-info → beamlit-0.0.20rc7.dist-info}/RECORD +13 -10
- beamlit/serve.py +0 -120
- {beamlit-0.0.20rc6.dist-info → beamlit-0.0.20rc7.dist-info}/WHEEL +0 -0
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.
|
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.
|
70
|
+
provider = agent_model.runtime.type_
|
71
71
|
model = agent_model.runtime.model
|
72
72
|
|
73
73
|
kwargs = {
|
beamlit/agents/decorator.py
CHANGED
@@ -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
|
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(
|
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.
|
92
|
-
settings.
|
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.
|
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(
|
107
|
-
|
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(
|
112
|
-
|
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 =
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
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(
|
124
|
-
return 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
154
|
+
logger.info("No credentials to save, error")
|
149
155
|
return
|
150
156
|
|
151
157
|
config = load_config()
|
beamlit/common/generate.py
CHANGED
@@ -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.
|
159
|
-
for function_config in settings.
|
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.
|
169
|
-
for agent in settings.
|
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.
|
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.
|
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
|
beamlit/common/settings.py
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
import os
|
2
2
|
from logging import getLogger
|
3
|
-
from typing import
|
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
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
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
|
99
|
+
if settings.agent.functions or settings.agent.chain:
|
82
100
|
return
|
83
101
|
|
84
102
|
# Init configuration from beamlit control plane
|
85
|
-
name =
|
86
|
-
env =
|
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
|
-
|
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
|
-
|
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
|
-
|
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 (
|
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
|
-
|
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,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.
|
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
|
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]
|
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-
|
12
|
-
Requires-Dist:
|
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=
|
10
|
-
beamlit/agents/decorator.py,sha256=
|
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=
|
133
|
+
beamlit/authentication/authentication.py,sha256=om26AteY2cCV9ctqbOCynX6PgS8YO-aCreNOFSnnWKc,3121
|
135
134
|
beamlit/authentication/clientcredentials.py,sha256=6kbfTjwUkXUArJX8XZLe9ZzbEicQc19tSXBvsTpiXMk,3954
|
136
|
-
beamlit/authentication/credentials.py,sha256=
|
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=
|
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=
|
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
|
289
|
-
beamlit
|
290
|
-
beamlit
|
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()
|
File without changes
|