
    'i&                        d Z ddlmZ ddlZddlZddlmZmZmZ ddlm	Z	m
Z
  ej                  e      Z ed      ZdZd	Z G d
 d      Zy)u  
core/injection/system_prompt_injector.py
=========================================
Story 7.06 — SystemPromptInjector Builder Function
Story 7.07 — SystemPromptInjector Telnyx API Push

Assembles the full system prompt injection block from recent memory
+ AIVA persona. Called before every LLM context window is populated.
Story 7.07 adds push_to_telnyx() to update the Telnyx AI Assistant
system prompt via PATCH /v2/ai_assistants/{assistant_id} before a call.

# VERIFICATION_STAMP
# Story: 7.06
# Verified By: parallel-builder
# Verified At: 2026-02-25
# Tests: tests/track_a/test_story_7_06.py
# Coverage: 100%

# VERIFICATION_STAMP
# Story: 7.07
# Verified By: parallel-builder
# Verified At: 2026-02-25
# Tests: tests/track_a/test_story_7_07.py
# Coverage: 100%
    )annotationsN)datetimetimezone	timedelta)AnyOptional
   )hourszhttps://api.telnyx.com/v2z=== AIVA MEMORY INJECTION ===
{recent_conversations}

=== KING'S ACTIVE DIRECTIVES ===
{kinan_directives}

=== CURRENT SESSION STATE ===
Caller: {caller_info}
Time: {local_time} AEST
Active Tasks: {open_tasks}
=== END MEMORY INJECTION ===c                  d    e Zd ZdZ	 	 	 d	 ddZddZddZddZddZddZ	dd	Z
edd
       Zy)SystemPromptInjectoruZ  
    Assembles the full system prompt injection block and pushes it to Telnyx.

    Parameters
    ----------
    conversation_engine:
        Object with ``get_recent_summary(n: int) -> str`` method.
        Pass ``None`` to disable conversation history lookup (fallback text used).
    redis_client:
        Redis client for reading directives and session state.
        Pass ``None`` to disable Redis lookups (fallback text used for all fields).
    http_client:
        Optional HTTP client for making API requests. Supports dependency
        injection for testing — pass a mock to avoid real network calls.
        Must expose an async ``patch(url, json, headers)`` method that returns
        a response object with a ``.status`` (or ``.status_code``) attribute.
        Pass ``None`` to use ``aiohttp.ClientSession`` at call time (production).
    Nc                .    || _         || _        || _        y )N)conversation_engineredishttp)selfr   redis_clienthttp_clients       >/mnt/e/genesis-system/core/injection/system_prompt_injector.py__init__zSystemPromptInjector.__init__H   s     $7 !
	    c                   K   | j                         }| j                         }| j                  |      }| j                         }| j	                         }t
        j                  |||||      S w)u  
        Build and return the formatted memory injection block.

        Steps
        -----
        1. Get recent_conversations from conversation_engine (last 3 summaries).
        2. Get kinan_directives from Redis key ``kinan:directives:active``.
        3. Get caller_info from Redis key ``aiva:state:{session_id}``.
        4. Get open_tasks from Redis key ``aiva:tasks:active``.
        5. Format INJECTION_TEMPLATE with AEST time.
        6. Return formatted string (always non-empty — fallbacks ensure this).

        Parameters
        ----------
        session_id:
            Unique identifier for the current AIVA session / call leg.

        Returns
        -------
        str
            Populated injection block. Never empty.
        )recent_conversationskinan_directivescaller_info
local_time
open_tasks)_get_recent_conversations_get_directives_get_caller_info_get_open_tasks_get_aest_timeINJECTION_TEMPLATEformat)r   
session_idrecent
directivescallertasksr   s          r   build_injectionz$SystemPromptInjector.build_injectionV   sw     . //1))+
&&z2$$&((*
!((!''! ) 
 	
s   A,A.c                  K   t         j                  j                  dd      }|st        j	                  d       yt
         d| }d| dd}	 | j                  |       d	{   }d
|i}| j                  K| j                  j                  |||       d	{   }t        |d      r|j                  n|j                  }	nfdd	l}
|
j                         4 d	{   }|j                  |||      4 d	{   }|j                  }	d	d	d	      d	{    d	d	d	      d	{    d	cxk  rdk  rn nt        j                  d||	       yt        j	                  d|	|       y7 7 7 7 u7 [# 1 d	{  7  sw Y   kxY w7 b# 1 d	{  7  sw Y   rxY w# t        $ r t        j!                  d|       Y yw xY ww)u0  
        Build the memory injection block and push it to the Telnyx AI Assistant.

        Steps
        -----
        1. Call ``build_injection(session_id)`` to get the full injection block.
        2. PATCH ``/v2/ai_assistants/{assistant_id}`` with JSON body
           ``{"system_prompt": <injection_block>}``.
        3. Return ``True`` on HTTP 200/204, ``False`` on any error.

        Parameters
        ----------
        session_id:
            Unique identifier for the current AIVA session / call leg.
            Passed through to ``build_injection`` for caller-context lookup.
        assistant_id:
            The Telnyx AI Assistant UUID to update.
            Example: ``"assistant-9c42d3ce-e05a-4e34-8083-c91081917637"``

        Returns
        -------
        bool
            ``True`` if the PATCH succeeded (HTTP 2xx).
            ``False`` on any HTTP error or network failure — never raises.

        Auth
        ----
        Bearer token read from ``TELNYX_API_KEY`` environment variable.
        The key is NOT hardcoded and NOT logged.
        TELNYX_API_KEY uE   push_to_telnyx: TELNYX_API_KEY not set in environment — cannot pushFz/ai_assistants/zBearer zapplication/json)AuthorizationzContent-TypeNsystem_prompt)jsonheadersstatusr      i,  z;push_to_telnyx: successfully updated assistant %s (HTTP %s)Tz<push_to_telnyx: Telnyx API returned HTTP %s for assistant %sz6push_to_telnyx: unexpected error updating assistant %s)osenvirongetloggererrorTELNYX_API_BASEr)   r   patchhasattrr1   status_codeaiohttpClientSessioninfo	Exception	exception)r   r$   assistant_idapi_keyurlr0   r.   payloadresponser1   r<   sessionresps                r   push_to_telnyxz#SystemPromptInjector.push_to_telnyx{   s    > **..!126LLW  !?&wi0.

+	"&"6"6z"BBM&6Gyy$ "&7G!TT x2 OO!--  "002 - -g&}}'7  -   - -!%- -- - f"s"Q 
 LLN
 I C U-- - - - -- - - -*  	H, 		s   AGF #E'$5F E*AF E,F F	8E.9F	<E2	F	E0F	F $F%)F GF &G'F *F ,F .F	0F	2F	8E;9F	 F	F 	FFFF G =G?G  Gc                    | j                   y	 | j                   j                  d      S # t        $ r t        j	                  d       Y yw xY w)z=Fetch last 3 conversation summaries from conversation_engine.zNo prior conversations   z$Failed to fetch recent conversations)r   get_recent_summaryr?   r6   r@   )r   s    r   r   z.SystemPromptInjector._get_recent_conversations   sN    ##++	,++>>qAA 	,CD+	,s   * A
Ac                    | j                   y	 | j                   j                  d      }|yt        |t              r|j	                  d      S t        |      S # t        $ r t        j                  d       Y yw xY w)z)Fetch active Kinan directives from Redis.zNo active directiveszkinan:directives:activeutf-8z+Failed to fetch Kinan directives from Redis	r   r5   
isinstancebytesdecodestrr?   r6   r@   r   vals     r   r   z$SystemPromptInjector._get_directives   sq    ::)	***..!:;C{-*4S%*@3::g&Nc#hN 	*JK)	*   A  A 
A A:9A:c                   | j                   y	 | j                   j                  d|       }|yt        |t              r|j	                  d      S t        |      S # t        $ r t        j                  d|       Y yw xY w)z-Fetch caller info for the session from Redis.Unknownzaiva:state:rM   z5Failed to fetch caller info from Redis for session %srN   )r   r$   rT   s      r   r   z%SystemPromptInjector._get_caller_info   sz    ::	**..;zl!;<C{ *4S%*@3::g&Nc#hN 	TV`a	s    A  A 
A A>=A>c                    | j                   y	 | j                   j                  d      }|yt        |t              r|j	                  d      S t        |      S # t        $ r t        j                  d       Y yw xY w)z"Fetch active task list from Redis.Nonezaiva:tasks:activerM   z%Failed to fetch open tasks from RedisrN   rS   s     r   r    z$SystemPromptInjector._get_open_tasks   sq    ::	**..!45C{*4S%*@3::g&Nc#hN 	DE	rU   c                 |    t        j                  t        j                        } | t        z   }|j                  d      S )z6Return current AEST time (UTC+10) as formatted string.z%Y-%m-%d %H:%M:%S)r   nowr   utcAEST_OFFSETstrftime)utc_nowaest_nows     r   r!   z#SystemPromptInjector._get_aest_time	  s2     ,,x||,[(  !455r   )NNN)r   zOptional[Any])r$   rR   returnrR   )r$   rR   rA   rR   ra   bool)ra   rR   )__name__
__module____qualname____doc__r   r)   rH   r   r   r   r    staticmethodr!    r   r   r   r   4   sV    * !%)	  #	 #
JWz,* 6 6r   r   )rf   
__future__r   loggingr3   r   r   r   typingr   r   	getLoggerrc   r6   r]   r8   r"   r   rh   r   r   <module>rm      sS   2 #  	 2 2  			8	$b!-
  Z6 Z6r   