
    %j;                         d Z ddlZddlZddlmZ ddlmZ ddlmZ ddl	m
Z
mZmZmZ  ej        e          Z G d d	          ZdS )
a  
Embedded Hindsight client with automatic daemon lifecycle management.

This module provides HindsightEmbedded, a client that uses the same daemon
management interface as hindsight-embed CLI, ensuring full compatibility.

Example:
    ```python
    from hindsight import HindsightEmbedded

    # Daemon starts automatically on first use
    client = HindsightEmbedded(
        profile="myapp",
        llm_provider="groq",
        llm_api_key="your-api-key",
    )

    # Use just like HindsightClient
    client.retain(bank_id="alice", content="Alice loves AI")
    results = client.recall(bank_id="alice", query="What does Alice like?")

    # Optional cleanup
    client.close()
    ```

Using context manager:
    ```python
    from hindsight import HindsightEmbedded

    with HindsightEmbedded(profile="myapp") as client:
        client.retain(bank_id="alice", content="Alice loves AI")
        # Daemon managed automatically
    ```
    N)Optional)	Hindsight)get_embed_manager   )BanksAPIDirectivesAPIMemoriesAPIMentalModelsAPIc                      e Zd ZdZ	 	 	 	 	 	 	 	 	 	 	 d*dededededee         dee         dedededee         defdZd Z	d+defdZ
d+defdZdefdZd Zd Zd  Zed!efd"            Zed!efd#            Zed!efd$            Zed!efd%            Zed!efd&            Zed!efd'            Zed!efd(            Zed!efd)            ZdS ),HindsightEmbeddeda  
    Hindsight client with automatic daemon lifecycle management.

    This client uses the same daemon management interface as hindsight-embed CLI,
    ensuring full compatibility and shared profiles. The daemon is started automatically
    on first use and manages profile-specific databases.

    Profile data is stored in: ~/.pg0/instances/hindsight-embed-{profile}/

    All methods from HindsightClient are available:
    - retain(), retain_batch()
    - recall()
    - reflect()
    - create_bank(), set_mission(), delete_bank()
    - create_mental_model(), list_mental_models(), etc.
    - create_directive(), list_directives(), etc.
    - And all async variants (aretain, arecall, areflect, etc.)

    Args:
        profile: Profile name for data isolation (default: "default")
        llm_provider: LLM provider ("groq", "openai", "ollama", "gemini", "anthropic", "lmstudio")
        llm_api_key: API key for the LLM provider
        llm_model: Model name to use
        llm_base_url: Optional custom base URL for LLM API
        database_url: Optional database URL override (default: profile-specific pg0)
        idle_timeout: Seconds before daemon auto-exits when idle (default: 0, disabled)
        log_level: Daemon log level (default: "info")
        ui: Whether to start the control plane web UI alongside the daemon (default: False)
        ui_port: Port for the UI. Defaults to daemon_port + 10000.
        ui_hostname: Hostname to bind the UI to. Defaults to "0.0.0.0".
    defaultgroq openai/gpt-oss-120bNr   infoF0.0.0.0profilellm_providerllm_api_key	llm_modelllm_base_urldatabase_urlidle_timeout	log_leveluiui_portui_hostnamec                 Z   || _         ||||t          |          d| _        |r
|| j        d<   |r
|| j        d<   |	| _        |
| _        || _        d| _        t          j                    | _	        d| _
        d| _        t                      | _        d| _        d| _        d| _        d| _        dS )a  
        Initialize the embedded client (daemon starts on first use).

        Args:
            profile: Profile name for data isolation
            llm_provider: LLM provider
            llm_api_key: API key for the LLM provider
            llm_model: Model name to use
            llm_base_url: Optional custom base URL for LLM API
            database_url: Optional database URL override
            idle_timeout: Seconds before daemon auto-exits when idle (0 = disabled)
            log_level: Daemon log level
            ui: Whether to start the control plane web UI alongside the daemon
            ui_port: Port for the UI (defaults to daemon_port + 10000)
            ui_hostname: Hostname to bind the UI to (defaults to "0.0.0.0")
        )HINDSIGHT_API_LLM_PROVIDERHINDSIGHT_API_LLM_API_KEYHINDSIGHT_API_LLM_MODELHINDSIGHT_API_LOG_LEVEL#HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUTHINDSIGHT_API_LLM_BASE_URL HINDSIGHT_EMBED_API_DATABASE_URLNF)r   strconfig_ui_ui_port_ui_hostname_client	threadingLock_lock_started_closedr   _manager
_banks_api_mental_models_api_directives_api_memories_api)selfr   r   r   r   r   r   r   r   r   r   r   s               Z/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight/embedded.py__init__zHindsightEmbedded.__init__Q   s    <  +7)4'0'036|3D3D
 
  	E8DDK45 	K>JDK:;',0^%%
)++ /3=A8<48    c                    | j         r| j        | j                            | j                  rdS t
                              d| j                   	 | j                                         n,# t          $ r t
          	                    dd           Y nw xY wd| _        d| _         | j
        5  | j         r| j        | j                            | j                  r	 ddd           dS t
                              d| j                   	 | j                                         n,# t          $ r t
          	                    dd           Y nw xY wd| _        d| _         | j        rt          d          t
                              d	| j         d
           | j                            | j        | j                  }|st          d| j         d          | j                            | j                  }t#          |          | _        d| _         t
                              d|            | j        rst
                              d| j         d
           | j                            | j        | j        | j                  }|s#t
                              d| j         d           ddd           dS # 1 swxY w Y   dS )z>Ensure daemon is running (thread-safe), restarting if crashed.Nz>Daemon for profile '%s' is no longer responsive, restarting...zError closing stale clientTexc_infoFzJDaemon for profile '%s' is no longer responsive (lock path), restarting...z5Cannot use HindsightEmbedded after it has been closedz(Ensuring daemon is running for profile ''...z$Failed to start daemon for profile '')base_urlzConnected to daemon at zStarting UI for profile 'z Failed to start UI for profile ')r/   r+   r1   
is_runningr   loggerwarningclose	Exceptiondebugr.   r0   RuntimeErrorr   ensure_runningr'   get_urlr   r(   start_uir)   r*   )r6   success
daemon_url
ui_starteds       r7   _ensure_startedz!HindsightEmbedded._ensure_started   sm   = 	"T\5}''55 NNP  J""$$$$ J J J9DIIIIIJDL!DMZ *	W *	W} &!9=++DL99 	*	W *	W *	W *	W *	W *	W *	W *	W
 `L  NL&&((((  N N NLL!=LMMMMMN# %| "K  
 KKU4<UUUVVVm224;MMG "J4<JJJ  
 ..t|<<J$j999DL DMKK>*>>??? x WJJJJKKK!]33L$-1B 
 " WNN#Udl#U#U#UVVVU*	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	W *	WsO   A+ +&BB,/J4( J4	D#"J4#&E	J4EEJ44J8;J8stop_daemon_on_closec                 `   | j         rdS | j                            d          }|s)t                              d| j                   d| _         dS 	 | j         r	 | j                                         dS | j        T	 | j                                         n2# t          $ r% t          
                    d| j        d           Y nw xY wd| _        | j        rO| j        rHt                              d| j         d	           | j                            | j        | j                   |rI| j        rBt                              d
| j         d	           | j                            | j                   d| _         | j                                         dS # | j                                         w xY w)z
        Cleanup client resources (idempotent).

        Args:
            stop_daemon_on_close: If True, stops the daemon. Otherwise, daemon continues
                running (it will auto-stop after idle timeout).
        Ng      @)timeoutzeCleanup lock acquisition timed out for profile '%s'; marking closed, daemon will idle-stop on its ownTz%Error closing client for profile '%s'r;   zStopping UI for profile 'r=   zStopping daemon for profile ')r0   r.   acquirerA   rB   r   releaser+   rC   rD   rE   r(   r/   r   r1   stop_uir)   stop)r6   rN   acquireds      r7   _cleanupzHindsightEmbedded._cleanup   s    < 	F:%%c%22 
	 NNC  
  DLF	!| 2 J     / |'L&&((((    LL?!% !       $ x CDM CJJJJKKK%%dlDMBBB $ 1 1NDLNNNOOO""4<000DLJ     DJ    s7   F 4F <B F ,CF CB2F F-stop_daemonc                 2    |                      |           dS )a  
        Explicitly close the client.

        Args:
            stop_daemon: If True, stops the daemon. Otherwise, daemon continues running
                and will auto-stop after idle timeout (default: False).

        Note:
            The daemon may be shared with other clients or the CLI, so stopping it
            might affect other users. By default, we rely on the daemon's idle timeout.
        )rN   NrV   )r6   rW   s     r7   rC   zHindsightEmbedded.close  s     	;77777r9   namec                 T    |                                   t          | j        |          S )z
        Proxy all method calls to the underlying Hindsight client.

        This allows HindsightEmbedded to expose all HindsightClient methods
        without manually wrapping each one.
        )rM   getattrr+   )r6   rZ   s     r7   __getattr__zHindsightEmbedded.__getattr__  s(     	t|T***r9   c                 .    |                                   | S )z2Context manager entry - ensures server is started.)rM   r6   s    r7   	__enter__zHindsightEmbedded.__enter__  s    r9   c                 .    |                                   dS )z(Context manager exit - stops the server.N)rC   )r6   exc_typeexc_valexc_tbs       r7   __exit__zHindsightEmbedded.__exit__!  s    

r9   c                 .    |                                   dS )zCleanup on garbage collection.NrY   r_   s    r7   __del__zHindsightEmbedded.__del__%  s    r9   returnc                 F    | j         t          |           | _         | j         S )a  
        Access bank management operations.

        Each method call ensures the daemon is running before executing.

        Example:
            ```python
            from hindsight import HindsightEmbedded

            embedded = HindsightEmbedded(profile="myapp", ...)

            # Create a bank
            embedded.banks.create(bank_id="test", name="Test Bank")

            # Set mission
            embedded.banks.set_mission(bank_id="test", mission="Help users")
            ```
        )r2   r   r_   s    r7   bankszHindsightEmbedded.banks)  s!    ( ?"&tnnDOr9   c                 F    | j         t          |           | _         | j         S )ab  
        Access mental model operations.

        Each method call ensures the daemon is running before executing.

        Example:
            ```python
            from hindsight import HindsightEmbedded

            embedded = HindsightEmbedded(profile="myapp", ...)

            # Create a mental model
            embedded.mental_models.create(
                bank_id="test",
                name="User Preferences",
                content="User prefers dark mode"
            )

            # List mental models
            models = embedded.mental_models.list(bank_id="test")
            ```
        )r3   r
   r_   s    r7   mental_modelszHindsightEmbedded.mental_modelsA  s&    0 "*&5d&;&;D#&&r9   c                 F    | j         t          |           | _         | j         S )a]  
        Access directive operations.

        Each method call ensures the daemon is running before executing.

        Example:
            ```python
            from hindsight import HindsightEmbedded

            embedded = HindsightEmbedded(profile="myapp", ...)

            # Create a directive
            embedded.directives.create(
                bank_id="test",
                name="Response Style",
                content="Always be concise and friendly"
            )

            # List directives
            directives = embedded.directives.list(bank_id="test")
            ```
        )r4   r   r_   s    r7   
directiveszHindsightEmbedded.directives]  s&    0 '#0#6#6D ##r9   c                 F    | j         t          |           | _         | j         S )a  
        Access memory listing operations.

        Each method call ensures the daemon is running before executing.

        Example:
            ```python
            from hindsight import HindsightEmbedded

            embedded = HindsightEmbedded(profile="myapp", ...)

            # List memories
            memories = embedded.memories.list(
                bank_id="test",
                type="world",
                limit=50
            )
            ```
        )r5   r	   r_   s    r7   memorieszHindsightEmbedded.memoriesy  s&    * %!,T!2!2D!!r9   c                 8    |                                   | j        S )a  
        Get the underlying Hindsight client for direct access.

        Ensures daemon is started (and restarts it if it has crashed) before
        returning the client.

        Returns:
            Hindsight: The underlying client instance

        Example:
            ```python
            from hindsight import HindsightEmbedded

            embedded = HindsightEmbedded(profile="myapp", ...)

            client = embedded.client
            banks = client.list_banks()
            ```
        )rM   r+   r_   s    r7   clientzHindsightEmbedded.client  s    * 	|r9   c                 h    |                                   | j                            | j                  S )z-Get the daemon URL (starts daemon if needed).)rM   r1   rH   r   r_   s    r7   urlzHindsightEmbedded.url  s.     	}$$T\222r9   c                 p    | j         o/| j         o'| j        duo| j                            | j                  S )z@Check if the client is initialized and the daemon is responsive.N)r/   r0   r+   r1   r@   r   r_   s    r7   r@   zHindsightEmbedded.is_running  sF     M 7L 7D(7 ((66		
r9   c                 @    | j                             | j                  S )z Get the UI URL for this profile.)r1   
get_ui_urlr   r_   s    r7   ui_urlzHindsightEmbedded.ui_url  s     }''555r9   )r   r   r   r   NNr   r   FNr   )F)__name__
__module____qualname____doc__r&   r   intboolr8   rM   rV   rC   r]   r`   re   rg   propertyr   rj   r
   rl   r   rn   r	   rp   r   rr   rt   r@   rx    r9   r7   r   r   0   s        D !".&*&*!%$=9 =9=9 =9 	=9
 =9 sm=9 sm=9 =9 =9 =9 #=9 =9 =9 =9 =9~;W ;W ;Wz3! 3!T 3! 3! 3! 3!j8 8 8 8 8 8
+ 
+ 
+ 
+ 
+  
     x    X. ' ' ' ' X'6 $M $ $ $ X$6 "+ " " " X"0 	    X. 3S 3 3 3 X3
 
D 
 
 
 X
 6 6 6 6 X6 6 6r9   r   )r|   loggingr,   typingr   hindsight_clientr   hindsight_embedr   api_namespacesr   r   r	   r
   	getLoggerry   rA   r   r   r9   r7   <module>r      s   ! !F            & & & & & & - - - - - - Q Q Q Q Q Q Q Q Q Q Q Q		8	$	$M6 M6 M6 M6 M6 M6 M6 M6 M6 M6r9   