beamlit 0.0.49rc96__py3-none-any.whl → 0.0.50rc98__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 +13 -1
- beamlit/agents/decorator.py +17 -14
- beamlit/agents/voice/openai.py +257 -0
- beamlit/agents/voice/utils.py +25 -0
- beamlit/deploy/deploy.py +1 -1
- beamlit/serve/app.py +53 -39
- {beamlit-0.0.49rc96.dist-info → beamlit-0.0.50rc98.dist-info}/METADATA +2 -1
- {beamlit-0.0.49rc96.dist-info → beamlit-0.0.50rc98.dist-info}/RECORD +10 -8
- {beamlit-0.0.49rc96.dist-info → beamlit-0.0.50rc98.dist-info}/WHEEL +0 -0
- {beamlit-0.0.49rc96.dist-info → beamlit-0.0.50rc98.dist-info}/licenses/LICENSE +0 -0
beamlit/agents/chat.py
CHANGED
@@ -7,6 +7,7 @@ from beamlit.api.models import get_model
|
|
7
7
|
from beamlit.authentication import get_authentication_headers, new_client
|
8
8
|
from beamlit.common.settings import get_settings
|
9
9
|
from beamlit.models import Model
|
10
|
+
from .voice.openai import OpenAIVoiceReactAgent
|
10
11
|
|
11
12
|
logger = getLogger(__name__)
|
12
13
|
|
@@ -57,7 +58,7 @@ def get_azure_ai_inference_chat_model(**kwargs):
|
|
57
58
|
|
58
59
|
def get_azure_marketplace_chat_model(**kwargs):
|
59
60
|
from langchain_openai import OpenAI # type: ignore
|
60
|
-
|
61
|
+
|
61
62
|
return OpenAI(
|
62
63
|
**kwargs
|
63
64
|
) # It seems to use a compatible endpoint, so we can use the classic OpenAI interface
|
@@ -156,6 +157,17 @@ def get_chat_model_full(name: str, agent_model: Union[Model, None] = None) -> Tu
|
|
156
157
|
logger.warning("Model not found in agent model, defaulting to gpt-4o-mini")
|
157
158
|
model = "gpt-4o-mini"
|
158
159
|
|
160
|
+
if provider == "openai" and "realtime" in model:
|
161
|
+
logger.info("Starting OpenAI Realtime Agent")
|
162
|
+
return (
|
163
|
+
OpenAIVoiceReactAgent(
|
164
|
+
url=get_base_url(name),
|
165
|
+
model=model,
|
166
|
+
headers=headers
|
167
|
+
),
|
168
|
+
provider,
|
169
|
+
model
|
170
|
+
)
|
159
171
|
kwargs = {
|
160
172
|
"model": model,
|
161
173
|
"base_url": get_base_url(name),
|
beamlit/agents/decorator.py
CHANGED
@@ -12,7 +12,7 @@ from beamlit.common.settings import init
|
|
12
12
|
from beamlit.errors import UnexpectedStatus
|
13
13
|
from beamlit.functions import get_functions
|
14
14
|
from beamlit.models import Agent, AgentSpec, EnvironmentMetadata
|
15
|
-
|
15
|
+
from .voice.openai import OpenAIVoiceReactAgent
|
16
16
|
from .chat import get_chat_model_full
|
17
17
|
|
18
18
|
|
@@ -128,19 +128,22 @@ def agent(
|
|
128
128
|
f" \"description\": \"{agent.spec.description}\",\n"
|
129
129
|
" },\n"
|
130
130
|
"}")
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
"
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
131
|
+
if isinstance(chat_model, OpenAIVoiceReactAgent):
|
132
|
+
_agent = chat_model
|
133
|
+
else:
|
134
|
+
memory = MemorySaver()
|
135
|
+
if len(functions) == 0:
|
136
|
+
raise ValueError("You can define this function in directory "
|
137
|
+
f'"{settings.agent.functions_directory}". Here is a sample function you can use:\n\n'
|
138
|
+
"from beamlit.functions import function\n\n"
|
139
|
+
"@function()\n"
|
140
|
+
"def hello_world(query: str):\n"
|
141
|
+
" return 'Hello, world!'\n")
|
142
|
+
try:
|
143
|
+
_agent = create_react_agent(chat_model, functions, checkpointer=memory)
|
144
|
+
except AttributeError: # special case for azure-marketplace where it uses the old OpenAI interface (no tools)
|
145
|
+
logger.warning("Using the old OpenAI interface for Azure Marketplace, no tools available")
|
146
|
+
_agent = create_react_agent(chat_model, [], checkpointer=memory)
|
144
147
|
|
145
148
|
settings.agent.agent = _agent
|
146
149
|
else:
|
@@ -0,0 +1,257 @@
|
|
1
|
+
import asyncio
|
2
|
+
import json
|
3
|
+
import websockets
|
4
|
+
|
5
|
+
from contextlib import asynccontextmanager
|
6
|
+
from typing import AsyncGenerator, AsyncIterator, Any, Callable, Coroutine
|
7
|
+
from .utils import amerge
|
8
|
+
|
9
|
+
from langchain_core.tools import BaseTool
|
10
|
+
from langchain_core._api import beta
|
11
|
+
from langchain_core.utils import secret_from_env
|
12
|
+
|
13
|
+
from pydantic import BaseModel, Field, SecretStr, PrivateAttr
|
14
|
+
|
15
|
+
EVENTS_TO_IGNORE = {
|
16
|
+
"response.function_call_arguments.delta",
|
17
|
+
"rate_limits.updated",
|
18
|
+
"response.audio_transcript.delta",
|
19
|
+
"response.created",
|
20
|
+
"response.content_part.added",
|
21
|
+
"response.content_part.done",
|
22
|
+
"conversation.item.created",
|
23
|
+
"response.audio.done",
|
24
|
+
"session.created",
|
25
|
+
"session.updated",
|
26
|
+
"response.done",
|
27
|
+
"response.output_item.done",
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
@asynccontextmanager
|
32
|
+
async def connect(*, headers: dict[str, Any], model: str, url: str) -> AsyncGenerator[
|
33
|
+
tuple[
|
34
|
+
Callable[[dict[str, Any] | str], Coroutine[Any, Any, None]],
|
35
|
+
AsyncIterator[dict[str, Any]],
|
36
|
+
],
|
37
|
+
None,
|
38
|
+
]:
|
39
|
+
"""
|
40
|
+
async with connect(model="gpt-4o-realtime-preview-2024-10-01") as websocket:
|
41
|
+
await websocket.send("Hello, world!")
|
42
|
+
async for message in websocket:
|
43
|
+
print(message)
|
44
|
+
"""
|
45
|
+
url += f"?model={model}"
|
46
|
+
|
47
|
+
websocket = await websockets.connect(url, extra_headers={**headers, "OpenAI-Beta": "realtime=v1"})
|
48
|
+
|
49
|
+
try:
|
50
|
+
|
51
|
+
async def send_event(event: dict[str, Any] | str) -> None:
|
52
|
+
formatted_event = json.dumps(event) if isinstance(event, dict) else event
|
53
|
+
await websocket.send(formatted_event)
|
54
|
+
|
55
|
+
async def event_stream() -> AsyncIterator[dict[str, Any]]:
|
56
|
+
async for raw_event in websocket:
|
57
|
+
yield json.loads(raw_event)
|
58
|
+
|
59
|
+
stream: AsyncIterator[dict[str, Any]] = event_stream()
|
60
|
+
|
61
|
+
yield send_event, stream
|
62
|
+
finally:
|
63
|
+
await websocket.close()
|
64
|
+
|
65
|
+
|
66
|
+
class VoiceToolExecutor(BaseModel):
|
67
|
+
"""
|
68
|
+
Can accept function calls and emits function call outputs to a stream.
|
69
|
+
"""
|
70
|
+
|
71
|
+
tools_by_name: dict[str, BaseTool]
|
72
|
+
_trigger_future: asyncio.Future = PrivateAttr(default_factory=asyncio.Future)
|
73
|
+
_lock: asyncio.Lock = PrivateAttr(default_factory=asyncio.Lock)
|
74
|
+
|
75
|
+
async def _trigger_func(self) -> dict: # returns a tool call
|
76
|
+
return await self._trigger_future
|
77
|
+
|
78
|
+
async def add_tool_call(self, tool_call: dict) -> None:
|
79
|
+
# lock to avoid simultaneous tool calls racing and missing
|
80
|
+
# _trigger_future being
|
81
|
+
async with self._lock:
|
82
|
+
if self._trigger_future.done():
|
83
|
+
# TODO: handle simultaneous tool calls better
|
84
|
+
raise ValueError("Tool call adding already in progress")
|
85
|
+
|
86
|
+
self._trigger_future.set_result(tool_call)
|
87
|
+
|
88
|
+
async def _create_tool_call_task(self, tool_call: dict) -> asyncio.Task[dict]:
|
89
|
+
tool = self.tools_by_name.get(tool_call["name"])
|
90
|
+
if tool is None:
|
91
|
+
# immediately yield error, do not add task
|
92
|
+
raise ValueError(
|
93
|
+
f"tool {tool_call['name']} not found. "
|
94
|
+
f"Must be one of {list(self.tools_by_name.keys())}"
|
95
|
+
)
|
96
|
+
|
97
|
+
# try to parse args
|
98
|
+
try:
|
99
|
+
args = json.loads(tool_call["arguments"])
|
100
|
+
except json.JSONDecodeError:
|
101
|
+
raise ValueError(
|
102
|
+
f"failed to parse arguments `{tool_call['arguments']}`. Must be valid JSON."
|
103
|
+
)
|
104
|
+
|
105
|
+
async def run_tool() -> dict:
|
106
|
+
result = await tool.ainvoke(args)
|
107
|
+
try:
|
108
|
+
result_str = json.dumps(result)
|
109
|
+
except TypeError:
|
110
|
+
# not json serializable, use str
|
111
|
+
result_str = str(result)
|
112
|
+
return {
|
113
|
+
"type": "conversation.item.create",
|
114
|
+
"item": {
|
115
|
+
"id": tool_call["call_id"],
|
116
|
+
"call_id": tool_call["call_id"],
|
117
|
+
"type": "function_call_output",
|
118
|
+
"output": result_str,
|
119
|
+
},
|
120
|
+
}
|
121
|
+
|
122
|
+
task = asyncio.create_task(run_tool())
|
123
|
+
return task
|
124
|
+
|
125
|
+
async def output_iterator(self) -> AsyncIterator[dict]: # yield events
|
126
|
+
trigger_task = asyncio.create_task(self._trigger_func())
|
127
|
+
tasks = set([trigger_task])
|
128
|
+
while True:
|
129
|
+
done, _ = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
|
130
|
+
for task in done:
|
131
|
+
tasks.remove(task)
|
132
|
+
if task == trigger_task:
|
133
|
+
async with self._lock:
|
134
|
+
self._trigger_future = asyncio.Future()
|
135
|
+
trigger_task = asyncio.create_task(self._trigger_func())
|
136
|
+
tasks.add(trigger_task)
|
137
|
+
tool_call = task.result()
|
138
|
+
try:
|
139
|
+
new_task = await self._create_tool_call_task(tool_call)
|
140
|
+
tasks.add(new_task)
|
141
|
+
except ValueError as e:
|
142
|
+
yield {
|
143
|
+
"type": "conversation.item.create",
|
144
|
+
"item": {
|
145
|
+
"id": tool_call["call_id"],
|
146
|
+
"call_id": tool_call["call_id"],
|
147
|
+
"type": "function_call_output",
|
148
|
+
"output": (f"Error: {str(e)}"),
|
149
|
+
},
|
150
|
+
}
|
151
|
+
else:
|
152
|
+
yield task.result()
|
153
|
+
|
154
|
+
|
155
|
+
@beta()
|
156
|
+
class OpenAIVoiceReactAgent(BaseModel):
|
157
|
+
model: str
|
158
|
+
headers: dict[str, Any] = Field()
|
159
|
+
instructions: str | None = None
|
160
|
+
tools: list[BaseTool] | None = None
|
161
|
+
url: str = Field()
|
162
|
+
|
163
|
+
def bind_tools(self, tools: list[BaseTool]) -> None:
|
164
|
+
self.tools = tools
|
165
|
+
|
166
|
+
async def aconnect(
|
167
|
+
self,
|
168
|
+
input_stream: AsyncIterator[str],
|
169
|
+
send_output_chunk: Callable[[str], Coroutine[Any, Any, None]],
|
170
|
+
) -> None:
|
171
|
+
"""
|
172
|
+
Connect to the OpenAI API and send and receive messages.
|
173
|
+
|
174
|
+
input_stream: AsyncIterator[str]
|
175
|
+
Stream of input events to send to the model. Usually transports input_audio_buffer.append events from the microphone.
|
176
|
+
output: Callable[[str], None]
|
177
|
+
Callback to receive output events from the model. Usually sends response.audio.delta events to the speaker.
|
178
|
+
|
179
|
+
"""
|
180
|
+
# formatted_tools: list[BaseTool] = [
|
181
|
+
# tool if isinstance(tool, BaseTool) else tool_converter.wr(tool) # type: ignore
|
182
|
+
# for tool in self.tools or []
|
183
|
+
# ]
|
184
|
+
tools_by_name = {tool.name: tool for tool in self.tools}
|
185
|
+
tool_executor = VoiceToolExecutor(tools_by_name=tools_by_name)
|
186
|
+
|
187
|
+
async with connect(
|
188
|
+
model=self.model, headers=self.headers, url=self.url
|
189
|
+
) as (
|
190
|
+
model_send,
|
191
|
+
model_receive_stream,
|
192
|
+
):
|
193
|
+
# sent tools and instructions with initial chunk
|
194
|
+
tool_defs = [
|
195
|
+
{
|
196
|
+
"type": "function",
|
197
|
+
"name": tool.name,
|
198
|
+
"description": tool.description,
|
199
|
+
"parameters": {"type": "object", "properties": tool.args},
|
200
|
+
}
|
201
|
+
for tool in tools_by_name.values()
|
202
|
+
]
|
203
|
+
await model_send(
|
204
|
+
{
|
205
|
+
"type": "session.update",
|
206
|
+
"session": {
|
207
|
+
"instructions": self.instructions,
|
208
|
+
"input_audio_transcription": {
|
209
|
+
"model": "whisper-1",
|
210
|
+
},
|
211
|
+
"tools": tool_defs,
|
212
|
+
},
|
213
|
+
}
|
214
|
+
)
|
215
|
+
async for stream_key, data_raw in amerge(
|
216
|
+
input_mic=input_stream,
|
217
|
+
output_speaker=model_receive_stream,
|
218
|
+
tool_outputs=tool_executor.output_iterator(),
|
219
|
+
):
|
220
|
+
try:
|
221
|
+
data = (
|
222
|
+
json.loads(data_raw) if isinstance(data_raw, str) else data_raw
|
223
|
+
)
|
224
|
+
except json.JSONDecodeError:
|
225
|
+
print("error decoding data:", data_raw)
|
226
|
+
continue
|
227
|
+
|
228
|
+
if stream_key == "input_mic":
|
229
|
+
await model_send(data)
|
230
|
+
elif stream_key == "tool_outputs":
|
231
|
+
print("tool output", data)
|
232
|
+
await model_send(data)
|
233
|
+
await model_send({"type": "response.create", "response": {}})
|
234
|
+
elif stream_key == "output_speaker":
|
235
|
+
|
236
|
+
t = data["type"]
|
237
|
+
if t == "response.audio.delta":
|
238
|
+
await send_output_chunk(json.dumps(data))
|
239
|
+
elif t == "input_audio_buffer.speech_started":
|
240
|
+
print("interrupt")
|
241
|
+
send_output_chunk(json.dumps(data))
|
242
|
+
elif t == "error":
|
243
|
+
print("error:", data)
|
244
|
+
elif t == "response.function_call_arguments.done":
|
245
|
+
print("tool call", data)
|
246
|
+
await tool_executor.add_tool_call(data)
|
247
|
+
elif t == "response.audio_transcript.done":
|
248
|
+
print("model:", data["transcript"])
|
249
|
+
elif t == "conversation.item.input_audio_transcription.completed":
|
250
|
+
print("user:", data["transcript"])
|
251
|
+
elif t in EVENTS_TO_IGNORE:
|
252
|
+
pass
|
253
|
+
else:
|
254
|
+
print(t)
|
255
|
+
|
256
|
+
|
257
|
+
__all__ = ["OpenAIVoiceReactAgent"]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import asyncio
|
2
|
+
from typing import AsyncIterator, TypeVar
|
3
|
+
|
4
|
+
T = TypeVar("T")
|
5
|
+
|
6
|
+
|
7
|
+
async def amerge(**streams: AsyncIterator[T]) -> AsyncIterator[tuple[str, T]]:
|
8
|
+
"""Merge multiple streams into one stream."""
|
9
|
+
nexts: dict[asyncio.Task, str] = {
|
10
|
+
asyncio.create_task(anext(stream)): key for key, stream in streams.items()
|
11
|
+
}
|
12
|
+
while nexts:
|
13
|
+
done, _ = await asyncio.wait(nexts, return_when=asyncio.FIRST_COMPLETED)
|
14
|
+
for task in done:
|
15
|
+
key = nexts.pop(task)
|
16
|
+
stream = streams[key]
|
17
|
+
try:
|
18
|
+
yield key, task.result()
|
19
|
+
nexts[asyncio.create_task(anext(stream))] = key
|
20
|
+
except StopAsyncIteration:
|
21
|
+
pass
|
22
|
+
except Exception as e:
|
23
|
+
for task in nexts:
|
24
|
+
task.cancel()
|
25
|
+
raise e
|
beamlit/deploy/deploy.py
CHANGED
beamlit/serve/app.py
CHANGED
@@ -3,12 +3,13 @@ import importlib
|
|
3
3
|
import os
|
4
4
|
import sys
|
5
5
|
import traceback
|
6
|
+
import inspect
|
6
7
|
from contextlib import asynccontextmanager
|
7
8
|
from logging import getLogger
|
8
9
|
from uuid import uuid4
|
9
10
|
|
10
11
|
from asgi_correlation_id import CorrelationIdMiddleware
|
11
|
-
from fastapi import FastAPI, Request, Response
|
12
|
+
from fastapi import FastAPI, Request, Response, WebSocket
|
12
13
|
from fastapi.responses import JSONResponse
|
13
14
|
|
14
15
|
from beamlit.common import HTTPError, get_settings, init
|
@@ -38,7 +39,10 @@ logger.info(
|
|
38
39
|
f"Running server with environment {settings.environment}"
|
39
40
|
f" on {settings.server.host}:{settings.server.port}"
|
40
41
|
)
|
41
|
-
|
42
|
+
func_params = inspect.signature(func).parameters
|
43
|
+
websocket_detected = False
|
44
|
+
if "websocket" in func_params:
|
45
|
+
websocket_detected = True
|
42
46
|
|
43
47
|
@asynccontextmanager
|
44
48
|
async def lifespan(app: FastAPI):
|
@@ -56,48 +60,58 @@ app.add_middleware(AddProcessTimeHeader)
|
|
56
60
|
app.add_middleware(AccessLogMiddleware)
|
57
61
|
instrument_app(app)
|
58
62
|
|
59
|
-
|
60
63
|
@app.get("/health")
|
61
64
|
async def health():
|
62
65
|
return {"status": "ok"}
|
63
66
|
|
67
|
+
if websocket_detected:
|
68
|
+
@app.websocket("/ws")
|
69
|
+
async def websocket_endpoint(websocket: WebSocket):
|
70
|
+
await websocket.accept()
|
64
71
|
|
65
|
-
@app.post("/")
|
66
|
-
async def root(request: Request):
|
67
|
-
settings = get_settings()
|
68
|
-
logger = getLogger(__name__)
|
69
|
-
try:
|
70
72
|
original_func = getattr(func, "__wrapped__", func)
|
71
73
|
if asyncio.iscoroutinefunction(func) or asyncio.iscoroutinefunction(original_func):
|
72
|
-
|
74
|
+
await func(websocket)
|
73
75
|
else:
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
76
|
+
func(websocket)
|
77
|
+
|
78
|
+
else:
|
79
|
+
@app.post("/")
|
80
|
+
async def root(request: Request):
|
81
|
+
settings = get_settings()
|
82
|
+
logger = getLogger(__name__)
|
83
|
+
try:
|
84
|
+
original_func = getattr(func, "__wrapped__", func)
|
85
|
+
if asyncio.iscoroutinefunction(func) or asyncio.iscoroutinefunction(original_func):
|
86
|
+
response = await func(request)
|
87
|
+
else:
|
88
|
+
response = func(request)
|
89
|
+
|
90
|
+
if isinstance(response, Response):
|
91
|
+
return response
|
92
|
+
if type(response) is str:
|
93
|
+
return Response(
|
94
|
+
content=response,
|
95
|
+
headers={"Content-Type": "text/plain"},
|
96
|
+
media_type="text/plain",
|
97
|
+
status_code=200,
|
98
|
+
)
|
99
|
+
return JSONResponse(status_code=200, content=response)
|
100
|
+
except ValueError as e:
|
101
|
+
content = {"error": str(e)}
|
102
|
+
if settings.environment == "development":
|
103
|
+
content["traceback"] = str(traceback.format_exc())
|
104
|
+
logger.error(str(traceback.format_exc()))
|
105
|
+
return JSONResponse(status_code=400, content=content)
|
106
|
+
except HTTPError as e:
|
107
|
+
content = {"error": e.message, "status_code": e.status_code}
|
108
|
+
if settings.environment == "development":
|
109
|
+
content["traceback"] = str(traceback.format_exc())
|
110
|
+
logger.error(f"{e.status_code} {str(traceback.format_exc())}")
|
111
|
+
return JSONResponse(status_code=e.status_code, content=content)
|
112
|
+
except Exception as e:
|
113
|
+
content = {"error": f"Internal server error, {e}"}
|
114
|
+
if settings.environment == "development":
|
115
|
+
content["traceback"] = str(traceback.format_exc())
|
116
|
+
logger.error(str(traceback.format_exc()))
|
117
|
+
return JSONResponse(status_code=500, content=content)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: beamlit
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.50rc98
|
4
4
|
Summary: Add your description here
|
5
5
|
Author-email: cploujoux <ch.ploujoux@gmail.com>
|
6
6
|
License-File: LICENSE
|
@@ -43,6 +43,7 @@ Requires-Dist: pyjwt>=2.10.1
|
|
43
43
|
Requires-Dist: python-dateutil>=2.8.0
|
44
44
|
Requires-Dist: pyyaml<6.1.0,>=6.0.2
|
45
45
|
Requires-Dist: requests<2.33.0,>=2.32.3
|
46
|
+
Requires-Dist: websockets>=14.1
|
46
47
|
Description-Content-Type: text/markdown
|
47
48
|
|
48
49
|
# beamlit
|
@@ -6,9 +6,11 @@ beamlit/run.py,sha256=HtDYDjD7oVfQ8r3T5_t4qN5UDJOJfsQILi45Z21ArAg,1446
|
|
6
6
|
beamlit/types.py,sha256=E1hhDh_zXfsSQ0NCt9-uw90_Mr5iIlsdfnfvxv5HarU,1005
|
7
7
|
beamlit/agents/__init__.py,sha256=bWsFaXUbAps3IsL3Prti89m1s714vICXodbQi77h3vY,206
|
8
8
|
beamlit/agents/chain.py,sha256=vfCjiFHuu02uTTGicxMlFzjyICQkIjpXrBGs-7uJEsg,2826
|
9
|
-
beamlit/agents/chat.py,sha256=
|
10
|
-
beamlit/agents/decorator.py,sha256=
|
9
|
+
beamlit/agents/chat.py,sha256=wkzhRNWeKmNlaKZQVRLwdjTEtdOTiUnpj0fTeRe2ePk,5877
|
10
|
+
beamlit/agents/decorator.py,sha256=5bEnVYEYTiFmCyQkFtN4XamrfTli6KYr98nFukPgOZE,6573
|
11
11
|
beamlit/agents/thread.py,sha256=LN5Ss-uOf5_hdB0WV1dqpn-N-pDJB3C2hUvlCzdqtdk,519
|
12
|
+
beamlit/agents/voice/openai.py,sha256=hmZQlAGQCQtR2MsXX1kC1DMUeiFctb-FiYqKDG7NGOo,9540
|
13
|
+
beamlit/agents/voice/utils.py,sha256=tQidyM40Ewuy12wKqpvJLvfJgneQ0sZf50dqnerPGHg,836
|
12
14
|
beamlit/api/__init__.py,sha256=zTSiG_ujSjAqWPyc435YXaX9XTlpMjiJWBbV-f-YtdA,45
|
13
15
|
beamlit/api/accounts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
16
|
beamlit/api/accounts/delete_account.py,sha256=Mrbhuxl1IF1GDclSixIAvj3YHa7trlFg3JNmllqpBRA,3864
|
@@ -139,7 +141,7 @@ beamlit/common/settings.py,sha256=C7iZrLLvl6ap5VCVCYOoK20WdSFqacmd6HtaeZ7SF6s,37
|
|
139
141
|
beamlit/common/slugify.py,sha256=nR29r37IdWS2i44ZC6ZsXRgqKPYmvMGtFQ7BuIQUTlc,90
|
140
142
|
beamlit/common/utils.py,sha256=jouz5igBvT37Xn_e94-foCHyQczVim-UzVcoIF6RWJ4,657
|
141
143
|
beamlit/deploy/__init__.py,sha256=GS7l7Jtm2yKs7iNLKcfjYO-rAhUzggQ3xiYSf3oxLBY,91
|
142
|
-
beamlit/deploy/deploy.py,sha256=
|
144
|
+
beamlit/deploy/deploy.py,sha256=0snAN62iZGVYdH4Nh1H2-QTQKSN_-WpaEUCkwTo6USc,10380
|
143
145
|
beamlit/deploy/format.py,sha256=U6UZEFAYLnGJJ7O2YmSdlUUFhnWNGAv6NZ-DW4KTgvI,2049
|
144
146
|
beamlit/deploy/parser.py,sha256=Ga0poCZkoRnuTw082QnTcNGCBJncoRAnVsn8-1FsaJE,6907
|
145
147
|
beamlit/functions/__init__.py,sha256=oejZ6GVd4-2kGKsOwRUq1u2AuSyrrn9kWIuH6WxMqhE,189
|
@@ -259,11 +261,11 @@ beamlit/models/websocket_channel.py,sha256=jg3vN7yS_oOIwGtndtIUr1LsyEA58RXLXahqS
|
|
259
261
|
beamlit/models/workspace.py,sha256=nsck1BNzNoF_SalMqyVtkRoC_P-6-b4gzxKgKu3HwDw,5065
|
260
262
|
beamlit/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8fWFfkCf7hzCTiNp4A,1246
|
261
263
|
beamlit/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
|
262
|
-
beamlit/serve/app.py,sha256=
|
264
|
+
beamlit/serve/app.py,sha256=BhEFQx8l2pv4eeVRGnvaC6el0tcgrzu-YiXpyL8oY6Q,3970
|
263
265
|
beamlit/serve/middlewares/__init__.py,sha256=1dVmnOmhAQWvWktqHkKSIX-YoF6fmMU8xkUQuhg_rJU,148
|
264
266
|
beamlit/serve/middlewares/accesslog.py,sha256=Mu4T4_9OvHybjA0ApzZFpgi2C8f3X1NbUk-76v634XM,631
|
265
267
|
beamlit/serve/middlewares/processtime.py,sha256=lDAaIasZ4bwvN-HKHvZpaD9r-yrkVNZYx4abvbjbrCg,411
|
266
|
-
beamlit-0.0.
|
267
|
-
beamlit-0.0.
|
268
|
-
beamlit-0.0.
|
269
|
-
beamlit-0.0.
|
268
|
+
beamlit-0.0.50rc98.dist-info/METADATA,sha256=8tFF9JJsKQfaiy_El_dSMgkvAers9GIgsMsLcX13MNc,3514
|
269
|
+
beamlit-0.0.50rc98.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
270
|
+
beamlit-0.0.50rc98.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
|
271
|
+
beamlit-0.0.50rc98.dist-info/RECORD,,
|
File without changes
|
File without changes
|