voicesmith-mcp 1.0.2 → 1.0.4
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.
- package/package.json +1 -1
- package/server.py +21 -4
- package/session_registry.py +25 -6
package/package.json
CHANGED
package/server.py
CHANGED
|
@@ -206,7 +206,8 @@ class _VoiceHTTPHandler(BaseHTTPRequestHandler):
|
|
|
206
206
|
self._json_response(400, {"error": "invalid_json"})
|
|
207
207
|
return
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
default_name = _config.main_agent if _config else "Eric"
|
|
210
|
+
name = params.get("name", _session_info.get("name", default_name) if _session_info else default_name)
|
|
210
211
|
text = params.get("text", "")
|
|
211
212
|
speed = params.get("speed", 1.0)
|
|
212
213
|
|
|
@@ -806,12 +807,28 @@ def main():
|
|
|
806
807
|
|
|
807
808
|
|
|
808
809
|
def _start_preheat_intro():
|
|
809
|
-
"""Speak a brief intro after server starts. Preheats TTS engine.
|
|
810
|
+
"""Speak a brief intro after server starts. Preheats TTS engine.
|
|
811
|
+
|
|
812
|
+
Only speaks if this session got its preferred name. If the preferred name
|
|
813
|
+
was taken (e.g., during a session resume where the old server is still
|
|
814
|
+
running), skip the intro to avoid confusing double introductions.
|
|
815
|
+
"""
|
|
810
816
|
if _tts_engine is None or _audio_player is None:
|
|
811
817
|
return
|
|
812
818
|
|
|
813
|
-
|
|
814
|
-
|
|
819
|
+
default_name = _config.main_agent if _config else "Eric"
|
|
820
|
+
default_voice = _config.tts.default_voice if _config else "am_eric"
|
|
821
|
+
|
|
822
|
+
name = _session_info.get("name", default_name) if _session_info else default_name
|
|
823
|
+
voice = _session_info.get("voice", default_voice) if _session_info else default_voice
|
|
824
|
+
|
|
825
|
+
# Determine what name we wanted
|
|
826
|
+
preferred = (_config.last_voice_name or default_name) if _config else default_name
|
|
827
|
+
|
|
828
|
+
# Skip intro if we didn't get our preferred name — another session has it
|
|
829
|
+
if name != preferred:
|
|
830
|
+
logger.info(f"Skipping preheat intro: wanted '{preferred}' but got '{name}'")
|
|
831
|
+
return
|
|
815
832
|
|
|
816
833
|
def _intro():
|
|
817
834
|
# Wait for server to settle
|
package/session_registry.py
CHANGED
|
@@ -65,7 +65,7 @@ def _write_sessions(path: Path, sessions: list[dict]) -> None:
|
|
|
65
65
|
_STALE_ACTIVITY_THRESHOLD = 300 # 5 minutes
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _session_healthy(session: dict) -> bool:
|
|
68
|
+
def _session_healthy(session: dict, activity_threshold: int = _STALE_ACTIVITY_THRESHOLD) -> bool:
|
|
69
69
|
"""Check if a session is alive and actively used.
|
|
70
70
|
|
|
71
71
|
Three checks:
|
|
@@ -97,7 +97,7 @@ def _session_healthy(session: dict) -> bool:
|
|
|
97
97
|
# Check activity age — if server hasn't had MCP tool calls
|
|
98
98
|
# in a while, it's orphaned
|
|
99
99
|
age = data.get("last_tool_call_age_s")
|
|
100
|
-
if age is not None and age >
|
|
100
|
+
if age is not None and age > activity_threshold:
|
|
101
101
|
logger.info(
|
|
102
102
|
f"Session '{session.get('name')}' (pid {pid}) inactive "
|
|
103
103
|
f"for {age}s — treating as stale"
|
|
@@ -117,11 +117,18 @@ def _session_healthy(session: dict) -> bool:
|
|
|
117
117
|
return False
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
def _clean_stale(sessions: list[dict]) -> list[dict]:
|
|
121
|
-
"""Remove sessions that are dead or unresponsive.
|
|
120
|
+
def _clean_stale(sessions: list[dict], aggressive: bool = False) -> list[dict]:
|
|
121
|
+
"""Remove sessions that are dead or unresponsive.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
aggressive: If True, use a shorter activity threshold (10s instead of 5min).
|
|
125
|
+
Used during startup registration to quickly reclaim names from
|
|
126
|
+
orphaned servers that haven't fully shut down yet.
|
|
127
|
+
"""
|
|
128
|
+
threshold = 10 if aggressive else _STALE_ACTIVITY_THRESHOLD
|
|
122
129
|
alive = []
|
|
123
130
|
for s in sessions:
|
|
124
|
-
if _session_healthy(s):
|
|
131
|
+
if _session_healthy(s, activity_threshold=threshold):
|
|
125
132
|
alive.append(s)
|
|
126
133
|
else:
|
|
127
134
|
logger.info(f"Removed stale session: {s.get('name')} (pid {s.get('pid')})")
|
|
@@ -214,10 +221,22 @@ def register_session(
|
|
|
214
221
|
fcntl.flock(f, fcntl.LOCK_EX)
|
|
215
222
|
|
|
216
223
|
sessions = _read_sessions(path)
|
|
217
|
-
|
|
224
|
+
# Aggressive cleanup on startup — use short activity threshold
|
|
225
|
+
# to quickly reclaim names from orphaned servers
|
|
226
|
+
sessions = _clean_stale(sessions, aggressive=True)
|
|
218
227
|
|
|
219
228
|
taken_names = {s["name"] for s in sessions}
|
|
220
229
|
|
|
230
|
+
if preferred_name in taken_names:
|
|
231
|
+
# Wait briefly and retry — the old server may be shutting down
|
|
232
|
+
fcntl.flock(f, fcntl.LOCK_UN)
|
|
233
|
+
time.sleep(2)
|
|
234
|
+
fcntl.flock(f, fcntl.LOCK_EX)
|
|
235
|
+
sessions = _read_sessions(path)
|
|
236
|
+
sessions = _clean_stale(sessions, aggressive=True)
|
|
237
|
+
_write_sessions(path, sessions)
|
|
238
|
+
taken_names = {s["name"] for s in sessions}
|
|
239
|
+
|
|
221
240
|
if preferred_name in taken_names:
|
|
222
241
|
name, voice = _find_available_name(taken_names, preferred_name)
|
|
223
242
|
logger.warning(
|