[Bugfix] Reject instructions changes on locked native duplex sessions (#6318)
Signed-off-by: ANURAG KANADE <anuragkanade6@gmail.com>
This commit is contained in:
1 parent
926e7fbfa2
commit
91dd7fcaec
8 files changed
+309
-7
No files matched your search
@@ -14,6 +14,7 @@ from vllm_omni.experimental.fullduplex.minicpmo45.policy import MiniCPMO45Duplex
|
||||
from vllm_omni.experimental.fullduplex.openai.protocol import DuplexSessionConfig
|
||||
from vllm_omni.experimental.fullduplex.openai.runtime_adapter import (
|
||||
ServingRuntimeConfigError,
|
||||
reject_changed_runtime_value,
|
||||
)
|
||||
|
||||
|
||||
@@ -69,7 +70,13 @@ class MiniCPMO45NativeDuplexServingAdapter:
|
||||
current: object,
|
||||
) -> dict[str, object]:
|
||||
runtime_config = deepcopy(dict(current)) if isinstance(current, dict) else {}
|
||||
runtime_config["instructions"] = config.instructions
|
||||
reject_changed_runtime_value(
|
||||
config.instructions,
|
||||
runtime_config.get("instructions"),
|
||||
message="instructions cannot be changed after the session is created",
|
||||
code="instructions_update_unsupported",
|
||||
error_cls=MiniCPMO45ClientRuntimeConfigError,
|
||||
)
|
||||
stage_max_tokens = runtime_config.get("duplex_stage_max_tokens")
|
||||
stage_max_tokens = deepcopy(stage_max_tokens) if isinstance(stage_max_tokens, dict) else {}
|
||||
stage_max_tokens["0"] = (
|
||||
|
||||
@@ -16,6 +16,7 @@ class MiniCPMO45ServingSessionState:
|
||||
audio_buffer: MiniCPMO45PcmAppendBuffer = field(default_factory=MiniCPMO45PcmAppendBuffer)
|
||||
input_since_commit: bool = False
|
||||
speech_since_commit: bool = False
|
||||
native_context_locked: bool = False
|
||||
committed_audio_payload: dict[str, object] | None = None
|
||||
committed_audio_operation_id: str | None = None
|
||||
committed_audio_reserved_bytes: int = 0
|
||||
|
||||
@@ -14,6 +14,18 @@ class ServingRuntimeConfigError(ValueError):
|
||||
self.code = code
|
||||
|
||||
|
||||
def reject_changed_runtime_value(
|
||||
new_value: object,
|
||||
current_value: object,
|
||||
*,
|
||||
message: str,
|
||||
code: str,
|
||||
error_cls: type[ServingRuntimeConfigError] = ServingRuntimeConfigError,
|
||||
) -> None:
|
||||
if new_value != current_value:
|
||||
raise error_cls(message, code=code)
|
||||
|
||||
|
||||
class PcmAppendReservation(Protocol):
|
||||
operation_id: str
|
||||
payload: dict[str, object] | None
|
||||
@@ -64,6 +76,7 @@ class ServingRuntimeSessionState(Protocol):
|
||||
audio_buffer: PcmAppendBuffer
|
||||
input_since_commit: bool
|
||||
speech_since_commit: bool
|
||||
native_context_locked: bool
|
||||
committed_audio_payload: dict[str, object] | None
|
||||
committed_audio_operation_id: str | None
|
||||
committed_audio_reserved_bytes: int
|
||||
|
||||
@@ -1156,6 +1156,18 @@ class OmniDuplexSessionHandler(
|
||||
) -> dict[str, object] | None:
|
||||
if not self._uses_native_input_append(session):
|
||||
return None
|
||||
if (
|
||||
candidate_config.instructions != session.config.instructions
|
||||
and self._runtime_session_state(session).native_context_locked
|
||||
):
|
||||
return {
|
||||
"type": "error",
|
||||
"session_id": session.session_id,
|
||||
"code": "instructions_update_unsupported",
|
||||
"error": "session.update cannot change instructions after the native duplex context is initialized",
|
||||
}
|
||||
if session.config.extra_body.get("minicpmo45_native_duplex") is not True:
|
||||
return None
|
||||
if not self._config_requests_audio_output(candidate_config):
|
||||
return None
|
||||
if "ref_audio_data" in session.runtime_config:
|
||||
@@ -1326,6 +1338,19 @@ class OmniDuplexSessionHandler(
|
||||
"code": "ref_audio_update_unsupported",
|
||||
"error": "session.update cannot change ref_audio after the native duplex runtime is open",
|
||||
}
|
||||
extra_body_payload = payload.get("extra_body")
|
||||
if isinstance(extra_body_payload, dict) and "minicpmo45_native_duplex" in extra_body_payload:
|
||||
current_native_duplex = session.config.extra_body.get("minicpmo45_native_duplex")
|
||||
if (
|
||||
current_native_duplex is not None
|
||||
and extra_body_payload["minicpmo45_native_duplex"] != current_native_duplex
|
||||
):
|
||||
return {
|
||||
"type": "error",
|
||||
"session_id": session.session_id,
|
||||
"code": "native_duplex_mode_update_unsupported",
|
||||
"error": "session.update cannot change minicpmo45_native_duplex after the session is created",
|
||||
}
|
||||
if isinstance(payload.get("instructions"), str):
|
||||
session.config.instructions = str(payload["instructions"])
|
||||
elif "instructions" in payload and payload.get("instructions") is None:
|
||||
|
||||
@@ -28,6 +28,7 @@ from vllm_omni.experimental.fullduplex.openai.realtime_session import (
|
||||
)
|
||||
from vllm_omni.experimental.fullduplex.openai.runtime_adapter import (
|
||||
PcmAppendReservation,
|
||||
ServingRuntimeConfigError,
|
||||
ServingRuntimeSessionState,
|
||||
payload_turn_id,
|
||||
)
|
||||
@@ -357,6 +358,7 @@ class DuplexSessionRunnerMixin:
|
||||
expected_epoch=append_epoch,
|
||||
)
|
||||
if append_ok:
|
||||
native.native_context_locked = True
|
||||
if pcm_reservation is not None:
|
||||
pcm_reservation.commit()
|
||||
session.release_input_bytes(pcm_reservation.byte_count)
|
||||
@@ -1072,10 +1074,21 @@ class DuplexSessionRunnerMixin:
|
||||
if runtime_update_error is not None:
|
||||
await emit_event(runtime_update_error)
|
||||
continue
|
||||
candidate_runtime_config = self._runtime_config_for_session_update(
|
||||
session,
|
||||
candidate_config,
|
||||
)
|
||||
try:
|
||||
candidate_runtime_config = self._runtime_config_for_session_update(
|
||||
session,
|
||||
candidate_config,
|
||||
)
|
||||
except ServingRuntimeConfigError as exc:
|
||||
await emit_event(
|
||||
{
|
||||
"type": "error",
|
||||
"session_id": session.session_id,
|
||||
"code": exc.code,
|
||||
"error": str(exc),
|
||||
}
|
||||
)
|
||||
continue
|
||||
if not await self._signal_runtime_session(
|
||||
session,
|
||||
turn_event,
|
||||
|
||||
@@ -15,6 +15,7 @@ from vllm_omni.experimental.fullduplex.openai.protocol import (
|
||||
)
|
||||
from vllm_omni.experimental.fullduplex.openai.runtime_adapter import (
|
||||
ServingRuntimeConfigError,
|
||||
reject_changed_runtime_value,
|
||||
)
|
||||
from vllm_omni.experimental.fullduplex.personaplex.config import DEFAULT_PERSONA
|
||||
from vllm_omni.experimental.fullduplex.personaplex.data_plane import (
|
||||
@@ -31,6 +32,7 @@ _PRIVATE_RUNTIME_CONFIG_KEYS = frozenset(
|
||||
{
|
||||
"personaplex_prefill_slots",
|
||||
"personaplex_model_path",
|
||||
"minicpmo45_native_duplex",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -40,6 +42,7 @@ class PersonaPlexServingSessionState:
|
||||
audio_buffer: PersonaPlexPcmAppendBuffer = field(default_factory=PersonaPlexPcmAppendBuffer)
|
||||
input_since_commit: bool = False
|
||||
speech_since_commit: bool = False
|
||||
native_context_locked: bool = False
|
||||
committed_audio_payload: dict[str, object] | None = None
|
||||
committed_audio_operation_id: str | None = None
|
||||
committed_audio_reserved_bytes: int = 0
|
||||
@@ -190,9 +193,23 @@ class PersonaPlexServingRuntimeAdapter:
|
||||
current: Mapping[str, object],
|
||||
) -> dict[str, object]:
|
||||
cls.validate_client_extra_body(getattr(config, "extra_body", None))
|
||||
new_persona = str(getattr(config, "instructions", None) or DEFAULT_PERSONA)
|
||||
reject_changed_runtime_value(
|
||||
new_persona,
|
||||
current.get("personaplex_persona"),
|
||||
message="PersonaPlex persona (instructions) cannot be changed after the session is created",
|
||||
code="persona_update_unsupported",
|
||||
)
|
||||
new_voice = cls._voice_name(getattr(config, "voice", None))
|
||||
reject_changed_runtime_value(
|
||||
new_voice,
|
||||
current.get("personaplex_voice_prompt"),
|
||||
message="PersonaPlex voice cannot be changed after the session is created",
|
||||
code="voice_update_unsupported",
|
||||
)
|
||||
runtime_config = deepcopy(dict(current))
|
||||
runtime_config["personaplex_voice_prompt"] = cls._voice_name(getattr(config, "voice", None))
|
||||
runtime_config["personaplex_persona"] = str(getattr(config, "instructions", None) or DEFAULT_PERSONA)
|
||||
runtime_config["personaplex_voice_prompt"] = new_voice
|
||||
runtime_config["personaplex_persona"] = new_persona
|
||||
return runtime_config
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in new issue
Block a user