Add client service metadata sanitization and debug source check
This commit is contained in:
@@ -71,6 +71,32 @@ class Session:
|
|||||||
"configVersionId",
|
"configVersionId",
|
||||||
"config_version_id",
|
"config_version_id",
|
||||||
}
|
}
|
||||||
|
_CLIENT_SERVICE_OVERRIDES = {
|
||||||
|
"llm": {
|
||||||
|
"provider",
|
||||||
|
"model",
|
||||||
|
"apiKey",
|
||||||
|
"baseUrl",
|
||||||
|
},
|
||||||
|
"asr": {
|
||||||
|
"provider",
|
||||||
|
"model",
|
||||||
|
"apiKey",
|
||||||
|
"baseUrl",
|
||||||
|
"interimIntervalMs",
|
||||||
|
"minAudioMs",
|
||||||
|
},
|
||||||
|
"tts": {
|
||||||
|
"enabled",
|
||||||
|
"provider",
|
||||||
|
"model",
|
||||||
|
"apiKey",
|
||||||
|
"baseUrl",
|
||||||
|
"voice",
|
||||||
|
"speed",
|
||||||
|
"mode",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -853,13 +879,52 @@ class Session:
|
|||||||
def _sanitize_client_metadata(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
|
def _sanitize_client_metadata(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Apply client metadata whitelist and remove forbidden secrets."""
|
"""Apply client metadata whitelist and remove forbidden secrets."""
|
||||||
sanitized = self._sanitize_untrusted_runtime_metadata(metadata)
|
sanitized = self._sanitize_untrusted_runtime_metadata(metadata)
|
||||||
if isinstance(metadata.get("services"), dict):
|
services = metadata.get("services")
|
||||||
|
if isinstance(services, dict):
|
||||||
|
if self._is_debug_metadata_source(metadata):
|
||||||
|
sanitized_services = self._sanitize_client_services(services)
|
||||||
|
if sanitized_services:
|
||||||
|
sanitized["services"] = sanitized_services
|
||||||
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Session {} provided metadata.services from client; client-side service config is ignored",
|
"Session {} provided metadata.services from client; client-side service config is ignored",
|
||||||
self.id,
|
self.id,
|
||||||
)
|
)
|
||||||
return sanitized
|
return sanitized
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_debug_metadata_source(metadata: Dict[str, Any]) -> bool:
|
||||||
|
source = str(metadata.get("source") or "").strip().lower()
|
||||||
|
if source == "debug":
|
||||||
|
return True
|
||||||
|
|
||||||
|
history = metadata.get("history")
|
||||||
|
if isinstance(history, dict):
|
||||||
|
history_source = str(history.get("source") or "").strip().lower()
|
||||||
|
if history_source == "debug":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sanitize_client_services(cls, services: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
if not isinstance(services, dict):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
sanitized_services: Dict[str, Any] = {}
|
||||||
|
for service_name, allowed_keys in cls._CLIENT_SERVICE_OVERRIDES.items():
|
||||||
|
payload = services.get(service_name)
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
sanitized_payload = {
|
||||||
|
key: payload[key]
|
||||||
|
for key in allowed_keys
|
||||||
|
if key in payload
|
||||||
|
}
|
||||||
|
if sanitized_payload:
|
||||||
|
sanitized_services[service_name] = sanitized_payload
|
||||||
|
return sanitized_services
|
||||||
|
|
||||||
def _build_config_resolved(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
|
def _build_config_resolved(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Build public resolved config payload (secrets removed)."""
|
"""Build public resolved config payload (secrets removed)."""
|
||||||
system_prompt = str(metadata.get("systemPrompt") or self.pipeline.conversation.system_prompt or "")
|
system_prompt = str(metadata.get("systemPrompt") or self.pipeline.conversation.system_prompt or "")
|
||||||
|
|||||||
@@ -2296,10 +2296,10 @@ export const DebugDrawer: React.FC<{
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) return;
|
if (!isOpen) return;
|
||||||
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
|
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
|
||||||
// If core TTS-related settings changed while drawer stays open,
|
// If core runtime settings changed while drawer stays open,
|
||||||
// reset the active WS session so the next launch uses new metadata.
|
// reset the active WS session so the next launch uses new metadata.
|
||||||
closeWs();
|
closeWs();
|
||||||
}, [isOpen, assistant.id, assistant.voice, assistant.speed]);
|
}, [isOpen, assistant.id, assistant.voice, assistant.speed, assistant.asrModelId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!textTtsEnabled) {
|
if (!textTtsEnabled) {
|
||||||
|
|||||||
Reference in New Issue
Block a user