Skip to content

Commit a43a422

Browse files
committed
restore playground/ to main branch version
1 parent 16c7f1c commit a43a422

17 files changed

Lines changed: 572 additions & 662 deletions

File tree

playground/app/core/configuration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class ConfigFile(ConfigBaseModel):
103103
The following parameters allow you to configure the Playground application. The configuration file can be shared with the API, as the sections are
104104
identical and compatible. Some parameters are common to both the API and the Playground (for example, `app_title`).
105105
106-
For Playground deployment, some environment variables are required to be set, like Reflex backend URL. See
106+
For Plagroud deployment, some environment variables are required to be set, like Reflex backend URL. See
107107
[Environment variables](/configuration/environment_variable/#playground) for more information.
108108
"""
109109

playground/app/features/chat/state.py

Lines changed: 66 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
from typing import Any
32

43
import httpx
@@ -126,136 +125,142 @@ def set_stop_sequences(self, sequences: str):
126125
"""Set the stop sequences."""
127126
self.stop_sequences = sequences
128127

129-
@rx.event(background=True)
128+
@rx.event
130129
async def process_question(self, form_data: dict[str, Any]):
130+
# Get the question from the form
131131
question = form_data["question"]
132+
133+
# Check if the question is empty
132134
if not question:
133135
return
134136

135-
# Check auth, set up initial state, and snapshot all needed values
136-
async with self:
137-
if not self.is_authenticated or not self.api_key:
138-
return
139-
140-
qa = QA(question=question, answer="")
141-
self._messages.append(qa)
142-
self.processing = True
143-
144-
url = self.opengatellm_url
145-
api_key = self.api_key
146-
model = self.model
147-
temperature = self.temperature
148-
top_p = self.top_p
149-
max_completion_tokens = self.max_completion_tokens
150-
frequency_penalty = self.frequency_penalty
151-
presence_penalty = self.presence_penalty
152-
stream = self.stream
153-
seed_str = self.seed_str
154-
stop_sequences_str = self.stop_sequences
155-
messages_snapshot = list(self._messages)
156-
yield
137+
async for value in self.api_process_question(question):
138+
yield value
139+
140+
@rx.event
141+
async def api_process_question(self, question: str):
142+
"""Get the response from the API.
143+
144+
Args:
145+
question: The user's question.
146+
"""
147+
148+
# Check if authenticated
149+
if not self.is_authenticated or not self.api_key:
150+
return
157151

158-
# Build messages outside the lock
152+
# Add the question to the list of questions.
153+
qa = QA(question=question, answer="")
154+
self._messages.append(qa)
155+
156+
# Clear the input and start the processing.
157+
self.processing = True
158+
yield
159+
160+
# Build the messages.
159161
messages = []
160-
for qa in messages_snapshot:
162+
for qa in self._messages:
161163
messages.append({"role": "user", "content": qa["question"]})
162164
if qa["answer"]:
163165
messages.append({"role": "assistant", "content": qa["answer"]})
164166

167+
# Remove the last empty answer.
165168
if messages and messages[-1]["role"] == "assistant" and not messages[-1]["content"]:
166169
messages = messages[:-1]
167170

171+
# Prepare the request payload
168172
payload = {
169-
"model": model,
173+
"model": self.model,
170174
"messages": messages,
171-
"temperature": temperature,
172-
"top_p": top_p,
173-
"max_completion_tokens": max_completion_tokens,
174-
"frequency_penalty": frequency_penalty,
175-
"presence_penalty": presence_penalty,
176-
"stream": stream,
175+
"temperature": self.temperature,
176+
"top_p": self.top_p,
177+
"max_completion_tokens": self.max_completion_tokens,
178+
"frequency_penalty": self.frequency_penalty,
179+
"presence_penalty": self.presence_penalty,
180+
"stream": self.stream,
177181
}
178182

179-
if seed_str:
183+
# Add optional parameters
184+
if self.seed_str:
180185
try:
181-
payload["seed"] = int(seed_str)
186+
payload["seed"] = int(self.seed_str)
182187
except ValueError:
183188
pass
184189

185-
if stop_sequences_str:
186-
stop_list = [s.strip() for s in stop_sequences_str.split("\n") if s.strip()]
190+
if self.stop_sequences:
191+
stop_list = [s.strip() for s in self.stop_sequences.split("\n") if s.strip()]
187192
if stop_list:
188193
payload["stop"] = stop_list
189194

190195
try:
191-
if stream:
196+
if self.stream:
197+
# Streaming response
192198
async with httpx.AsyncClient() as client:
193199
async with client.stream(
194200
"POST",
195-
f"{url}/v1/chat/completions",
201+
f"{self.opengatellm_url}/v1/chat/completions",
196202
headers={
197-
"Authorization": f"Bearer {api_key}",
203+
"Authorization": f"Bearer {self.api_key}",
198204
"Content-Type": "application/json",
199205
},
200206
json=payload,
201207
timeout=configuration.settings.playground_opengatellm_timeout,
202208
) as response:
203209
if response.status_code != 200:
204210
error_text = await response.aread()
205-
async with self:
206-
self._messages[-1]["answer"] = f"Error: {error_text.decode()}"
207-
self.processing = False
208-
yield
211+
self._messages[-1]["answer"] = f"Error: {error_text.decode()}"
212+
self.processing = False
213+
yield
209214
return
210215

211216
async for line in response.aiter_lines():
212217
if line.startswith("data: "):
213218
data = line[6:]
214219
if data == "[DONE]":
215220
break
221+
216222
try:
223+
import json
224+
217225
chunk = json.loads(data)
218226
if chunk.get("choices") and len(chunk["choices"]) > 0:
219227
delta = chunk["choices"][0].get("delta", {})
220228
content = delta.get("content")
221229
if content:
222-
async with self:
223-
self._messages[-1]["answer"] += content
224-
self._messages = self._messages
225-
yield
230+
self._messages[-1]["answer"] += content
231+
self._messages = self._messages
232+
yield
226233
except Exception:
227234
continue
228235
else:
236+
# Non-streaming response
229237
async with httpx.AsyncClient() as client:
230238
response = await client.post(
231-
f"{url}/v1/chat/completions",
239+
f"{self.opengatellm_url}/v1/chat/completions",
232240
headers={
233-
"Authorization": f"Bearer {api_key}",
241+
"Authorization": f"Bearer {self.api_key}",
234242
"Content-Type": "application/json",
235243
},
236244
json=payload,
237245
timeout=configuration.settings.playground_opengatellm_timeout,
238246
)
239247

240-
async with self:
241248
if response.status_code != 200:
242249
self._messages[-1]["answer"] = f"Error: {response.text}"
243250
else:
244251
data = response.json()
245252
if data.get("choices") and len(data["choices"]) > 0:
246253
content = data["choices"][0]["message"]["content"]
247254
self._messages[-1]["answer"] = content
255+
248256
yield
249257

250258
except httpx.TimeoutException:
251-
async with self:
252-
self._messages[-1]["answer"] = "Error: Request timeout"
253-
yield
259+
self._messages[-1]["answer"] = "Error: Request timeout"
260+
yield
254261
except Exception as e:
255-
async with self:
256-
self._messages[-1]["answer"] = f"Error: {str(e)}"
257-
yield
258-
finally:
259-
async with self:
260-
self.processing = False
261-
yield
262+
self._messages[-1]["answer"] = f"Error: {str(e)}"
263+
yield
264+
265+
# Toggle the processing flag.
266+
self.processing = False

playground/app/features/keys/state.py

Lines changed: 53 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -33,62 +33,46 @@ def keys(self) -> list[Key]:
3333
"""Get keys list with correct typing for Reflex."""
3434
return self.entities
3535

36-
@rx.event(background=True)
36+
@rx.event
3737
async def load_entities(self):
3838
"""Load entities."""
39-
async with self:
40-
if not self.is_authenticated or not self.api_key:
41-
return
42-
self.entities_loading = True
43-
url = self.opengatellm_url
44-
api_key = self.api_key
45-
page = self.page
46-
per_page = self.per_page
47-
order_by = self.order_by_value
48-
order_direction = self.order_direction_value
49-
yield
39+
if not self.is_authenticated or not self.api_key:
40+
return
41+
42+
self.entities_loading = True
43+
yield
5044

5145
params = {
52-
"offset": (page - 1) * per_page,
53-
"limit": per_page,
54-
"order_by": order_by,
55-
"order_direction": order_direction,
46+
"offset": (self.page - 1) * self.per_page,
47+
"limit": self.per_page,
48+
"order_by": self.order_by_value,
49+
"order_direction": self.order_direction_value,
5650
}
5751

5852
response = None
5953
try:
6054
async with httpx.AsyncClient() as client:
6155
response = await client.get(
62-
url=f"{url}/v1/me/keys",
56+
url=f"{self.opengatellm_url}/v1/me/keys",
6357
params=params,
64-
headers={"Authorization": f"Bearer {api_key}"},
58+
headers={"Authorization": f"Bearer {self.api_key}"},
6559
timeout=configuration.settings.playground_opengatellm_timeout,
6660
)
61+
6762
response.raise_for_status()
6863
data = response.json()
69-
entities = [
70-
Key(
71-
id=k["id"],
72-
name=k["name"],
73-
token=k["token"],
74-
expires=dt.datetime.fromtimestamp(k["expires"]).strftime("%Y-%m-%d %H:%M") if k["expires"] else "never",
75-
created=dt.datetime.fromtimestamp(k["created"]).strftime("%Y-%m-%d %H:%M"),
76-
)
77-
for k in data.get("data", [])
78-
if k["name"] != "playground"
79-
]
80-
has_more = len(entities) == per_page
81-
async with self:
82-
self.entities = entities
83-
self.has_more_page = has_more
84-
yield
64+
self.entities = []
65+
66+
for key in data.get("data", []):
67+
if key["name"] != "playground":
68+
self.entities.append(self._format_key(key))
69+
70+
self.has_more_page = len(self.entities) == self.per_page
8571
except Exception as e:
86-
async with self:
87-
yield httpx_error_toast(exception=e, response=response)
72+
yield httpx_error_toast(exception=e, response=response)
8873
finally:
89-
async with self:
90-
self.entities_loading = False
91-
yield
74+
self.entities_loading = False
75+
yield
9276

9377
############################################################
9478
# Delete entity
@@ -128,7 +112,8 @@ async def delete_entity(self):
128112

129113
self.handle_delete_entity_dialog_change(is_open=False)
130114
yield rx.toast.success("Key deleted successfully", position="bottom-right")
131-
yield type(self).load_entities()
115+
async for _ in self.load_entities():
116+
yield
132117

133118
except Exception as e:
134119
yield httpx_error_toast(exception=e, response=response)
@@ -209,7 +194,8 @@ async def create_entity(self):
209194
self.is_created_dialog_open = True
210195

211196
yield rx.toast.success("Key created successfully", position="bottom-right")
212-
yield type(self).load_entities()
197+
async for _ in self.load_entities():
198+
yield
213199

214200
except Exception as e:
215201
yield httpx_error_toast(exception=e, response=response)
@@ -228,34 +214,38 @@ async def create_entity(self):
228214
order_direction_value: str = "asc"
229215
order_by_options: list[str] = ["id", "name", "created"]
230216

231-
@rx.event(background=True)
217+
@rx.event
232218
async def set_order_by(self, value: str):
233219
"""Set order by field and reload."""
234-
async with self:
235-
self.order_by_value = value
236-
self.page = 1
237-
self.has_more_page = False
238-
yield type(self).load_entities()
220+
self.order_by_value = value
221+
self.page = 1
222+
self.has_more_page = False
223+
yield
224+
async for _ in self.load_entities():
225+
yield
239226

240-
@rx.event(background=True)
227+
@rx.event
241228
async def set_order_direction(self, value: str):
242229
"""Set order direction and reload."""
243-
async with self:
244-
self.order_direction_value = value
245-
self.page = 1
246-
self.has_more_page = False
247-
yield type(self).load_entities()
230+
self.order_direction_value = value
231+
self.page = 1
232+
self.has_more_page = False
233+
yield
234+
async for _ in self.load_entities():
235+
yield
248236

249-
@rx.event(background=True)
237+
@rx.event
250238
async def prev_page(self):
251-
async with self:
252-
if self.page > 1:
253-
self.page -= 1
254-
yield type(self).load_entities()
239+
if self.page > 1:
240+
self.page -= 1
241+
yield
242+
async for _ in self.load_entities():
243+
yield
255244

256-
@rx.event(background=True)
245+
@rx.event
257246
async def next_page(self):
258-
async with self:
259-
if self.has_more_page:
260-
self.page += 1
261-
yield type(self).load_entities()
247+
if self.has_more_page:
248+
self.page += 1
249+
yield
250+
async for _ in self.load_entities():
251+
yield

0 commit comments

Comments
 (0)