o
    i@                     @  s,  U d Z ddlmZ ddlZddlmZmZmZ ddlm	Z	m
Z
mZmZmZmZmZmZ ddlmZ ddlmZ e	r?ddlmZ G d	d
 d
Ze Zd
ed< 	 d ddZedddZeG dd dZeG dd deZeG dd deZeG dd deZeG dd deZ eG dd deZ!dS )!u@  Settings infrastructure for Pipecat AI services.

Each service type has a settings dataclass (``LLMSettings``, ``TTSSettings``,
``STTSettings``, or a service-specific subclass).  The same class is used in
two distinct modes:

**Store mode** — the service's ``self._settings`` object that holds the full
current state.  Every field must have a real value; ``NOT_GIVEN`` is never
valid here.  Services that don't support an inherited field should set it to
``None``.  ``validate_complete()`` (called automatically in
``AIService.start()``) enforces this invariant.

**Delta mode** — a sparse update object carried by an
``*UpdateSettingsFrame``.  Only the fields the caller wants to change are set;
all others remain at their default of ``NOT_GIVEN``.  ``apply_update()``
merges a delta into a store, skipping any ``NOT_GIVEN`` fields.

Key helpers:

- ``NOT_GIVEN`` / ``is_given()`` — sentinel and check for "field not provided
  in this delta".
- ``apply_update(delta)`` — merge a delta into a store, returning changed
  fields.
- ``from_mapping(dict)`` — build a delta from a plain dict (for backward
  compatibility with dict-based ``*UpdateSettingsFrame``).
- ``validate_complete()`` — assert that a store has no ``NOT_GIVEN`` fields.
- ``extra`` dict — overflow for service-specific keys that don't map to a
  declared field.
    )annotationsN)	dataclassfieldfields)TYPE_CHECKINGAnyClassVarDictMappingOptionalTypeTypeVar)logger)Language)UserTurnCompletionConfigc                      sD   e Zd ZU dZdZded< d fddZdd	d
ZdddZ  Z	S )	_NotGivenu  Sentinel meaning "this field was not included in the delta".

    ``NOT_GIVEN`` is distinct from ``None`` (which is a valid stored value,
    typically meaning "this service doesn't support this field").  Every
    settings field defaults to ``NOT_GIVEN`` so that delta-mode objects are
    sparse by default and ``apply_update`` can skip untouched fields.

    ``NOT_GIVEN`` must never appear in a store-mode object — see
    ``validate_complete()``.
    NzOptional[_NotGiven]	_instancereturnc                   s   | j d u rt | | _ | j S N)r   super__new__)cls	__class__ M/home/ubuntu/.local/lib/python3.10/site-packages/pipecat/services/settings.pyr   F   s   
z_NotGiven.__new__strc                 C     dS )N	NOT_GIVENr   selfr   r   r   __repr__K      z_NotGiven.__repr__boolc                 C  r   )NFr   r   r   r   r   __bool__N   r"   z_NotGiven.__bool__)r   r   )r   r   )r   r#   )
__name__
__module____qualname____doc__r   __annotations__r   r!   r$   __classcell__r   r   r   r   r   8   s   
 
r   r   valuer   r   r#   c                 C  s   t | t S )a  Check whether a delta field was explicitly provided.

    Typically used when processing a delta to decide whether a field
    should be applied::

        if is_given(delta.voice):
            # caller wants to change the voice
            ...

    For store-mode objects this always returns ``True`` (since
    ``validate_complete`` ensures no ``NOT_GIVEN`` fields remain).

    Args:
        value: The value to check.

    Returns:
        ``True`` if *value* is anything other than ``NOT_GIVEN``.
    )
isinstancer   )r+   r   r   r   is_givenZ   s   r-   _SServiceSettings)boundc                   @  s   e Zd ZU dZedd dZded< 	 eedZded< 	 i Z	d	ed
< 	 dddZ
d ddZed!ddZd"ddZd#ddZdS )$r/   u  Base class for runtime-updatable service settings.

    These settings capture the subset of a service's configuration that can
    be changed **while the pipeline is running** (e.g. switching the model or
    changing the voice).  They are *not* meant to capture every constructor
    parameter — only those that support live updates via
    ``*UpdateSettingsFrame``.

    Every AI service type (LLM, TTS, STT) extends this with its own fields.
    Each instance operates in one of two modes (see module docstring):

    - **Store mode** (``self._settings``): holds the full current state.
      Every field must be a real value — ``NOT_GIVEN`` is never valid.
      Use ``None`` for inherited fields the service doesn't support.
      Enforced at runtime by ``validate_complete()``.
    - **Delta mode** (``*UpdateSettingsFrame``): a sparse update.
      Only fields the caller wants to change are set; all others stay at
      the default ``NOT_GIVEN`` and are skipped by ``apply_update()``.

    Parameters:
        model: The model identifier used by the service.  Set to ``None``
            in store mode if the service has no model concept.
        extra: Overflow dict for service-specific keys that don't map to a
            declared field.
    c                   C     t S r   r   r   r   r   r   <lambda>       zServiceSettings.<lambda>default_factorystr | None | _NotGivenmodelDict[str, Any]extraClassVar[Dict[str, str]]_aliasesr   c                 C  sL   i }t | D ]}|jdkrqt| |j}t|r|||j< q|| j |S )a  Return a dict of only the fields that are not ``NOT_GIVEN``.

        Primarily useful for delta-mode objects to inspect which fields were
        set.  For a store-mode object this returns all declared fields (since
        none should be ``NOT_GIVEN``).

        Skips the ``extra`` field itself but merges its entries into the
        returned dict at the top level.

        Returns:
            Dictionary mapping field names to their provided values.
        r:   )r   namegetattrr-   updater:   )r    resultfvalr   r   r   given_fields   s   

zServiceSettings.given_fieldsr    r.   deltac                 C  s   i }t | D ]*}|jdkrqt||jt}t|sqt| |j}||kr0t| |j| |||j< q|j D ]\}}| j|t}||krN|| j|< |||< q6|S )a  Merge a delta-mode object into this store-mode object.

        Only fields in *delta* that are **given** (i.e. not ``NOT_GIVEN``)
        are considered.  A field is "changed" if its new value differs from
        the current value.

        The ``extra`` dicts are merged: keys present in the delta overwrite
        keys in the target.

        Args:
            delta: A delta-mode settings object of the same type.

        Returns:
            A dict mapping each changed field name to its **pre-update** value.
            Use ``changed.keys()`` for the set of names, or index with
            ``changed["field"]`` to inspect the old value.

        Examples::

            # store-mode object (all fields given)
            current = TTSSettings(voice="alice", language="en")
            # delta-mode object (only voice is set)
            delta = TTSSettings(voice="bob")
            changed = current.apply_update(delta)
            # changed == {"voice": "alice"}
            # current.voice == "bob", current.language == "en"
        r:   )	r   r=   r>   r   r-   setattrr:   itemsget)r    rD   changedrA   new_valold_valkeyr   r   r   apply_update   s&   


zServiceSettings.apply_updater   Type[_S]settingsMapping[str, Any]c           	      C  sr   dd t | D dh }i }i }| D ]\}}| j||}||v r(|||< q|||< q| di |}||_|S )a  Build a **delta-mode** settings object from a plain dictionary.

        This exists for backward compatibility with code that passes plain
        dicts via ``*UpdateSettingsFrame(settings={...})``.  The returned
        object is a delta: only the keys present in *settings* are set;
        all other fields remain ``NOT_GIVEN``.

        Keys are matched to dataclass fields by name.  Keys listed in
        ``_aliases`` are translated to their canonical name first.  Any
        remaining unrecognized keys are placed into ``extra``.

        Args:
            settings: A dictionary of setting names to values.

        Returns:
            A new delta-mode settings instance.

        Examples::

            delta = TTSSettings.from_mapping({"voice_id": "alice", "speed": 1.2})
            # delta.voice == "alice"  (via alias)
            # delta.language is NOT_GIVEN  (not in the dict)
            # delta.extra == {"speed": 1.2}
        c                 S  s   h | ]}|j qS r   )r=   .0rA   r   r   r   	<setcomp>  s    z/ServiceSettings.from_mapping.<locals>.<setcomp>r:   Nr   )r   rF   r<   rG   r:   )	r   rN   field_nameskwargsr:   rK   r+   	canonicalinstancer   r   r   from_mapping   s   

zServiceSettings.from_mappingNonec                   sH    fddt  D }|r"d|}tt j d| d dS dS )uQ  Check that this is a valid store-mode object (no ``NOT_GIVEN`` fields).

        Called automatically by ``AIService.start()`` to catch fields that a
        service forgot to initialize in its ``__init__``.  Can also be called
        manually after constructing a store-mode settings object.

        Logs a warning for each uninitialized field.  Failure to initialize
        all fields may or may not cause runtime issues — it depends on
        whether and how the service actually reads the field — but it indicates
        a deviation from expectations and should be fixed.
        c                   s.   g | ]}|j d krtt |j tr|j qS )r:   )r=   r,   r>   r   rP   r   r   r   
<listcomp>(  s
    z5ServiceSettings.validate_complete.<locals>.<listcomp>z, z&: the following fields are NOT_GIVEN: zh. All settings fields should be initialized in the service's __init__ (use None for unsupported fields).N)r   joinr   errortyper%   )r    missingnamesr   r   r   validate_complete  s   

z!ServiceSettings.validate_completec                 C  s
   t | S )zReturn a deep copy of this settings instance.

        Returns:
            A new settings object with the same field values.
        )copydeepcopyr   r   r   r   r`   5  s   
zServiceSettings.copyN)r   r9   )r    r.   rD   r.   r   r9   )r   rM   rN   rO   r   r.   )r   rX   )r    r.   r   r.   )r%   r&   r'   r(   r   r8   r)   dictr:   r<   rC   rL   classmethodrW   r_   r`   r   r   r   r   r/   w   s   
 

1
)c                   @     e Zd ZdZdS )ImageGenSettingsu   Runtime-updatable settings for image generation services.

    Used in both store and delta mode — see ``ServiceSettings``.

    Parameters:
        model: Image generation model identifier.
    Nr%   r&   r'   r(   r   r   r   r   re   C      re   c                   @  rd   )VisionSettingsu   Runtime-updatable settings for vision services.

    Used in both store and delta mode — see ``ServiceSettings``.

    Parameters:
        model: Vision model identifier.
    Nrf   r   r   r   r   rh   N  rg   rh   c                   @  s   e Zd ZU dZedd dZded< edd dZded	< ed
d dZded< edd dZ	ded< edd dZ
ded< edd dZded< edd dZded< edd dZded< edd dZded< edd dZded< dS )LLMSettingsu  Runtime-updatable settings for LLM services.

    Used in both store and delta mode — see ``ServiceSettings``.

    These fields are common across LLM providers.  Not every provider supports
    every field; in store mode, set unsupported fields to ``None`` (e.g. a
    service that doesn't support ``seed`` should initialize it as
    ``seed=None``).

    Parameters:
        model: LLM model identifier.
        system_instruction: System instruction/prompt for the model.
        temperature: Sampling temperature.
        max_tokens: Maximum tokens to generate.
        top_p: Nucleus sampling probability.
        top_k: Top-k sampling parameter.
        frequency_penalty: Frequency penalty.
        presence_penalty: Presence penalty.
        seed: Random seed for reproducibility.
        filter_incomplete_user_turns: Enable LLM-based turn completion detection
            to suppress bot responses when the user was cut off mid-thought.
            See ``examples/foundational/22-filter-incomplete-turns.py`` and
            ``UserTurnCompletionLLMServiceMixin``.
        user_turn_completion_config: Configuration for turn completion behavior
            when ``filter_incomplete_user_turns`` is enabled. Controls timeouts
            and prompts for incomplete turns.
    c                   C  r1   r   r2   r   r   r   r   r3   w  r4   zLLMSettings.<lambda>r5   r7   system_instructionc                   C  r1   r   r2   r   r   r   r   r3   x  r4   zfloat | None | _NotGiventemperaturec                   C  r1   r   r2   r   r   r   r   r3   y  r4   zint | None | _NotGiven
max_tokensc                   C  r1   r   r2   r   r   r   r   r3   z  r4   top_pc                   C  r1   r   r2   r   r   r   r   r3   {  r4   top_kc                   C  r1   r   r2   r   r   r   r   r3   |  r4   frequency_penaltyc                   C  r1   r   r2   r   r   r   r   r3   }  r4   presence_penaltyc                   C  r1   r   r2   r   r   r   r   r3   ~  r4   seedc                   C  r1   r   r2   r   r   r   r   r3     r4   zbool | None | _NotGivenfilter_incomplete_user_turnsc                   C  r1   r   r2   r   r   r   r   r3     r4   z+UserTurnCompletionConfig | None | _NotGivenuser_turn_completion_configN)r%   r&   r'   r(   r   rj   r)   rk   rl   rm   rn   ro   rp   rq   rr   rs   r   r   r   r   ri   Y  s   
 ri   c                   @  sN   e Zd ZU dZedd dZded< edd dZded	< d
diZded< dS )TTSSettingsuP  Runtime-updatable settings for TTS services.

    Used in both store and delta mode — see ``ServiceSettings``.

    In store mode, set unsupported fields to ``None`` (e.g. ``language=None``
    if the service doesn't expose a language setting).

    Parameters:
        model: TTS model identifier.
        voice: Voice identifier or name.
        language: Language for speech synthesis.  The union type reflects the
            *input* side: callers may pass a ``Language`` enum or a raw string
            in a delta.  However, the **stored** value (in store mode) is
            always a service-specific string or ``None`` —
            ``TTSService._update_settings`` converts ``Language`` enums via
            ``language_to_service_language()`` before writing, and
            ``__init__`` methods do the same at construction time.
    c                   C  r1   r   r2   r   r   r   r   r3     r4   zTTSSettings.<lambda>r5   zstr | _NotGivenvoicec                   C  r1   r   r2   r   r   r   r   r3     r4   !Language | str | None | _NotGivenlanguagevoice_idr;   r<   N)	r%   r&   r'   r(   r   ru   r)   rw   r<   r   r   r   r   rt     s
   
 rt   c                   @  s(   e Zd ZU dZedd dZded< dS )STTSettingsu  Runtime-updatable settings for STT services.

    Used in both store and delta mode — see ``ServiceSettings``.

    In store mode, set unsupported fields to ``None`` (e.g. ``language=None``
    if the service auto-detects language).

    Parameters:
        model: STT model identifier.
        language: Language for speech recognition.  The union type reflects the
            *input* side: callers may pass a ``Language`` enum or a raw string
            in a delta.  However, the **stored** value (in store mode) is
            always a service-specific string or ``None`` —
            ``STTService._update_settings`` converts ``Language`` enums via
            ``language_to_service_language()`` before writing, and
            ``__init__`` methods do the same at construction time.
    c                   C  r1   r   r2   r   r   r   r   r3     r4   zSTTSettings.<lambda>r5   rv   rw   N)r%   r&   r'   r(   r   rw   r)   r   r   r   r   ry     s   
 ry   )r+   r   r   r#   )"r(   
__future__r   r`   dataclassesr   r   r   typingr   r   r   r	   r
   r   r   r   logurur   pipecat.transcriptions.languager   (pipecat.turns.user_turn_completion_mixinr   r   r   r)   r-   r.   r/   re   rh   ri   rt   ry   r   r   r   r   <module>   s6   (
 L

+