Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
AI Tutor
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Salma Mohammed Hamed
AI Tutor
Commits
b0bf570e
Commit
b0bf570e
authored
Oct 12, 2025
by
SalmaMohammedHamedMustafa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
handle multi workers
parent
6dc3cbe7
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
9 additions
and
100 deletions
+9
-100
game.tar
self_hosted_env/voice_agent/game.tar
+0
-0
session_response_manager.py
...sted_env/voice_agent/services/session_response_manager.py
+0
-94
audio-recorder.html
self_hosted_env/voice_agent/static/audio-recorder.html
+9
-6
No files found.
self_hosted_env/voice_agent/game.tar
0 → 100644
View file @
b0bf570e
File added
self_hosted_env/voice_agent/services/session_response_manager.py
deleted
100644 → 0
View file @
6dc3cbe7
import
redis
import
json
import
base64
import
time
from
typing
import
Optional
,
Dict
class
SessionResponseManager
:
"""
Manages response state in Redis, keyed by a session ID via cookies.
Implements the same interface as the old in-memory ResponseManager.
"""
def
__init__
(
self
,
redis_client
):
if
redis_client
is
None
:
raise
ConnectionError
(
"SessionResponseManager requires a valid Redis client."
)
self
.
redis
=
redis_client
# Redis TTL serves the same purpose as checking timestamps
self
.
default_ttl
=
600
# 10 minutes
def
_get_key
(
self
,
session_id
:
str
)
->
str
:
"""Helper to generate the Redis key."""
return
f
"session_response:{session_id}"
def
store_response
(
self
,
session_id
:
str
,
text
:
str
,
audio_filename
:
Optional
[
str
]
=
None
,
audio_bytes
:
Optional
[
bytes
]
=
None
)
->
None
:
"""Store response in Redis with TTL."""
key
=
self
.
_get_key
(
session_id
)
# Base64 encode binary data for JSON storage
encoded_audio
=
base64
.
b64encode
(
audio_bytes
)
.
decode
(
'utf-8'
)
if
audio_bytes
else
None
payload
=
{
"text"
:
text
,
"audio_filename"
:
audio_filename
,
"audio_bytes_b64"
:
encoded_audio
,
# We store the timestamp to perfectly match the old object structure,
# even though Redis handles expiration automatically.
"timestamp"
:
time
.
time
()
}
value
=
json
.
dumps
(
payload
)
# setex sets the value and the expiration (TTL) atomically
self
.
redis
.
setex
(
key
,
self
.
default_ttl
,
value
)
def
get_response
(
self
,
session_id
:
str
)
->
Dict
:
"""
Atomically retrieves and deletes ('pops') the response from Redis.
Returns the dictionary structure expected by the service layer.
"""
key
=
self
.
_get_key
(
session_id
)
# Use a pipeline to get and delete atomically
pipe
=
self
.
redis
.
pipeline
()
pipe
.
get
(
key
)
pipe
.
delete
(
key
)
# Ensure it's only read once
results
=
pipe
.
execute
()
json_value
=
results
[
0
]
# Default empty structure if nothing found (matches old manager behavior)
empty_response
=
{
"text"
:
None
,
"audio_filename"
:
None
,
"audio_bytes"
:
None
,
"timestamp"
:
0
}
if
not
json_value
:
return
empty_response
try
:
payload
=
json
.
loads
(
json_value
)
# Decode Base64 audio back to raw bytes
if
payload
.
get
(
"audio_bytes_b64"
):
payload
[
"audio_bytes"
]
=
base64
.
b64decode
(
payload
[
"audio_bytes_b64"
])
else
:
payload
[
"audio_bytes"
]
=
None
# Remove internal base64 key before returning
payload
.
pop
(
"audio_bytes_b64"
,
None
)
return
payload
except
(
TypeError
,
json
.
JSONDecodeError
):
return
empty_response
def
clear_response
(
self
,
session_id
:
str
)
->
None
:
"""Manually deletes the response key from Redis."""
key
=
self
.
_get_key
(
session_id
)
self
.
redis
.
delete
(
key
)
def
is_response_fresh
(
self
,
session_id
:
str
,
max_age_seconds
:
int
=
300
)
->
bool
:
"""
Checks if data exists in Redis.
Note: max_age_seconds is ignored because Redis handles TTL automatically,
but kept in signature for compatibility with the old interface.
"""
key
=
self
.
_get_key
(
session_id
)
# redis.exists returns > 0 if key exists
return
self
.
redis
.
exists
(
key
)
>
0
\ No newline at end of file
self_hosted_env/voice_agent/static/audio-recorder.html
View file @
b0bf570e
...
...
@@ -267,9 +267,12 @@
}
}
async
fetchAudioResponse
()
{
async
fetchAudioResponse
(
studentId
)
{
try
{
const
response
=
await
fetch
(
Config
.
AUDIO_RESPONSE_URL
,
{
// Build URL with student_id
const
urlWithParam
=
`
${
Config
.
AUDIO_RESPONSE_URL
}
?student_id=
${
encodeURIComponent
(
studentId
)}
`
;
const
response
=
await
fetch
(
urlWithParam
,
{
method
:
'GET'
,
mode
:
'cors'
,
credentials
:
'omit'
...
...
@@ -537,7 +540,7 @@
const
response
=
await
this
.
apiClient
.
sendFormData
(
Config
.
BACKEND_URL
,
formData
);
if
(
response
.
status
===
'success'
)
{
await
this
.
getAgentResponse
();
await
this
.
getAgentResponse
(
studentId
);
return
true
;
}
else
{
throw
new
Error
(
response
.
message
||
'Unknown error'
);
...
...
@@ -562,7 +565,7 @@
const
response
=
await
this
.
apiClient
.
sendFormData
(
Config
.
BACKEND_URL
,
formData
);
if
(
response
.
status
===
'success'
)
{
await
this
.
getAgentResponse
();
await
this
.
getAgentResponse
(
studentId
);
return
true
;
}
else
{
throw
new
Error
(
response
.
message
||
'Unknown error'
);
...
...
@@ -573,11 +576,11 @@
}
}
async
getAgentResponse
()
{
async
getAgentResponse
(
studentId
)
{
try
{
this
.
uiManager
.
showStatus
(
'جاري جلب رد المساعد...'
,
StatusType
.
PROCESSING
);
const
{
agentText
,
audioBlob
}
=
await
this
.
apiClient
.
fetchAudioResponse
();
const
{
agentText
,
audioBlob
}
=
await
this
.
apiClient
.
fetchAudioResponse
(
studentId
);
if
(
!
agentText
||
agentText
===
"لا يوجد رد متاح"
)
{
throw
new
Error
(
'لم يتم استلام رد صالح من المساعد'
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment