
    yjj                       U d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 ddl
mZ ddlmZ ddlmZmZ dd	lmZ dd
lmZmZ ddlmZmZ ddlmZ ddlmZ ddlmZm Z  ddl!m"Z" ddl#m$Z$ ddl%m&Z& ddl'm(Z(m)Z) ddl*m+Z+m,Z, ddl-m.Z. ddl/m0Z0m1Z1m2Z2  ej3        e4          Z5d)dZ6e G d d                      Z7dhZ8 G d  d!e          Z9 G d" d#e9          Z: G d$ d%e9          Z; e:             e;            d&Z<d'e=d(<   dS )*a  
Agentic specialists for the dream cycle.

Each specialist is a fully autonomous agent that:
1. Receives probing questions as entry points
2. Uses tools to search for relevant observations
3. Creates new observations (deductive or inductive)
4. Can delete duplicates (deduction only)
    )annotationsN)ABCabstractmethod)Counter)Callable)	dataclass)Anycast)generate)crudschemas)ConfiguredModelSettingssettings)
tracked_dbValidationException)HonchoLLMCallResponsehoncho_llm_call)LLMTelemetryContext)ResolvedConfiguration)prometheus_metrics)DreamSpecialistEventemit)accumulate_metriclog_performance_metrics)
TokenTypes)DEDUCTION_SPECIALIST_TOOLSINDUCTION_SPECIALIST_TOOLScreate_tool_executormodel_configConfiguredModelSettings | Nonespecialist_namestrreturnr   c               .    | t          | d          | S )Nz) MODEL_CONFIG must be resolved before user   )r    r"   s     ?/DATA/AppData/hermes/projects/honcho/src/dreamer/specialists.py _require_specialist_model_configr'   +   s0    
 !III
 
 	
     c                  n    e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   d
ed<   ded<   ded<   dS )SpecialistResultz9Result of a specialist run for telemetry and aggregation.r#   run_idspecialist_typeint
iterationstool_calls_countinput_tokensoutput_tokensfloatduration_msboolsuccesscontentN)__name__
__module____qualname____doc____annotations__ r(   r&   r*   r*   7   sv         CCKKKOOOMMMLLLLLr(   r*   update_peer_cardc                      e Zd ZU dZdZded<   dZded<   eddd$d            Zed%d            Z	d&dZ
d&dZeddd'd            Ze	 d(d)d            Zd*dZ	 	 	 d+d,d#ZdS )-BaseSpecialistz#Base class for agentic specialists.baser#   namezCOnly update this with durable profile facts via `update_peer_card`.peer_card_update_instructionTpeer_card_enabledrD   r4   r$   list[dict[str, Any]]c                   dS )z+Get the tools available to this specialist.Nr<   selfrD   s     r&   	get_toolszBaseSpecialist.get_toolsS   	     	r(   r   c                    dS )z4Get the configured model to use for this specialist.Nr<   rH   s    r&   get_model_configzBaseSpecialist.get_model_configX   rJ   r(   r-   c                    dS )z*Get max output tokens for this specialist.i @  r<   rL   s    r&   get_max_tokenszBaseSpecialist.get_max_tokens]   s    ur(   c                    dS )zGet max tool iterations.   r<   rL   s    r&   get_max_iterationsz!BaseSpecialist.get_max_iterationsa   s    rr(   observedc                   dS )z,Build the system prompt for this specialist.Nr<   )rH   rS   rD   s      r&   build_system_promptz"BaseSpecialist.build_system_prompte   s	    
 	r(   Nhintslist[str] | None	peer_cardc                    dS )zLBuild the user prompt with optional exploration hints and current peer card.Nr<   )rH   rV   rX   s      r&   build_user_promptz BaseSpecialist.build_user_promptl   s	     	r(   c                d    |sdS d                     d |D                       }d| d| j         dS )z5Build the peer card context section for user prompts. 
c              3      K   | ]	}d | V  
dS z- Nr<   ).0facts     r&   	<genexpr>z:BaseSpecialist._build_peer_card_context.<locals>.<genexpr>y   s(      <<$+t++<<<<<<r(   z
## CURRENT PEER CARD

z

zN
If you update it, send the full deduplicated list and remove stale entries.

)joinrB   )rH   rX   factss      r&   _build_peer_card_contextz'BaseSpecialist._build_peer_card_contextu   s_     	2		<<)<<<<<  
 "   	r(   workspace_nameobserversession_name
str | NoneconfigurationResolvedConfiguration | Noneparent_run_idr*   c                  K   |pt                      }d| j         d| }	t          j                    }
d}d}d}d}d}d}d}d}t	                      }t	                      }	 t          d          4 d{V }t          j        ||t          j	        |                     d{V  ||k    r/t          j        ||t          j	        |                     d{V  |du p|j
        j        }d}|rt          j        ||||	           d{V }	 ddd          d{V  n# 1 d{V swxY w Y   d
|                     ||          dd|                     ||          dg}t          ||||dt           j        j        ||| j        d
  
         d{V }|                                 }|j        }|r|dk    r|n|                                 }d| j         }t-          |d||                     |          d||                                 |d| j         t3          ||d| j        |||          
  
         d{V }t          j                    |
z
  dz  }t5          |	d|d           t5          |	dt7          |j                  d           t5          |	d|j        d           t5          |	d|j        d           t           j        j         r`tC          j"        |j        | j        tF          j$        j%                   tC          j"        |j        | j        tF          j&        j%                   tN          (                    | j         d|dd t7          |j                   d!z   |j         d"|j         d#z              tS          d| j         |           h d$}|j        D ]}}|*                    d%          p|*                    d&          } |*                    d'          pi }!| |v r|d(z  }tW          |!tX                    rt[          tX          t\          t^          f         |!          }"|"*                    d)          pd}#|"*                    d*          pd}$|ta          |#          z  }|ta          |$          z  }|"*                    d+          rd}|"*                    d,          }%tW          |%tb                    r`t[          tb          t^                   |%          }&d- |&D             }'d)|"v r|2                    |'           dd*|"v r|2                    |'           d}tg          || j        |j4        t7          |j                  |j        |j        |d|j5        .	  	        	 |t7          |j                  nd}(||j        nd})||j        nd}*||j4        nd}+tm          to          d@i d/|d0| j        d1|d2|d3|d4|+d5|(d|)d|*d6|d7|d8|d9|d:|d+|d;|d<tY          |          d=tY          |                     S # tp          $ r tN          9                    d>d?           Y S w xY w# tt          $ r9},tw          |,          j<        }|dk    rt          j                    |
z
  dz  } d},~,ww xY w# 	 |t7          |j                  nd}(||j        nd})||j        nd}*||j4        nd}+tm          to          d@i d/|d0| j        d1|d2|d3|d4|+d5|(d|)d|*d6|d7|d8|d9|d:|d+|d;|d<tY          |          d=tY          |                     w # tp          $ r tN          9                    d>d?           Y w w xY wxY w)Aa  
        Run the specialist agent.

        Uses short-lived DB sessions to avoid holding connections during LLM calls.

        Args:
            workspace_name: Workspace identifier
            observer: The observing peer
            observed: The peer being observed
            session_name: Session identifier
            hints: Optional hints to guide exploration (specialists explore freely if None)
            configuration: Resolved configuration for checking feature flags (optional)
            parent_run_id: Optional run_id from orchestrator for correlation

        Returns:
            SpecialistResult with metrics and content
        dreamer__FNr   g        zdream.specialist.preflightrA   )rf   rg   rS   systemrC   )roler6   userTdream)
rf   rg   rS   rh   include_observation_idshistory_token_limitrj   r+   
agent_typeparent_categoryzdream.r\   zDreamer/)rf   call_purposerx   rw   r+   rg   rS   )
r    prompt
max_tokenstoolstool_choicetool_executormax_tool_iterationsmessages
track_name	telemetryi  total_durationms
tool_callscountr0   r1   )r   r"   
token_typez: Completed in z.0fzms, z tool calls, z in / z out>   search_memorysearch_messagessearch_messages_temporal	tool_namerA   tool_result_metadata   created_countdeleted_countpeer_card_updatedlevelsc                0    g | ]}|t          |          S N)r#   )r`   levels     r&   
<listcomp>z&BaseSpecialist.run.<locals>.<listcomp>\  s*     & & &+05CTCJJCTCTCTr(   )	r+   r,   r.   r/   r0   r1   r3   r5   r6   r+   r,   rf   rg   rS   r.   r/   r3   r5   error_classcreated_observation_countdeleted_observation_countsearch_tool_calls_countcreated_counts_by_leveldeleted_counts_by_levelz#Failed to emit DreamSpecialistEvent)exc_infor<   )=generate_nanoidrA   timeperf_counterr   r   r   get_peerr   
PeerCreaterX   createget_peer_cardrU   rZ   r   r   DREAMHISTORY_TOKEN_LIMITrM   max_output_tokensrO   r   rI   rR   r   r   lentool_calls_mader0   r1   METRICSENABLEDr   record_dreamer_tokensr   INPUTvalueOUTPUTloggerinfor   get
isinstancedictr
   r#   r	   r-   listupdater*   r.   r6   r   r   	ExceptiondebugBaseExceptiontyper7   )-rH   rf   rg   rS   rh   rV   rj   rl   r+   	task_name
start_timespecialist_successspecialist_error_classresponser   r   r   r   r3   r   r   dbrD   current_peer_cardr   r~   r    configured_maxeffective_max_tokenscall_purpose_slug_search_toolstctool_name_anymeta_any	meta_dictcreated_valdeleted_val
levels_anylevels_list
level_strsr/   r0   r1   r.   es-                                                r&   runzBaseSpecialist.run   sK     6 3/"3"33ty33633	&((
 #-16: %&!$%!!"#
 18		07		j	S!">??       2m(:(I(I(I         x''-NG,>H,M,M,M         "T)K]-D-K "
 7;!$ .2.@'5!)!)	/ / / ) ) ) ) ) )% #                          8 %#77 4E  8      ##55e=NOO .H  +-!!)(,$,N$F+9 '           0022L *;N "+&4q&8&8 ((** ! !5 4 4 -)/nn7HnII +$($;$;$=$=!1di11-#1!2$+#y!%%          H,  ,..;tCKi)9;MMM<X-E)F)F   i9NPWXXX?H,BG  
 ' 
"8"/$(I)/5   
 #8"0$(I)06    KK9BB[BBBB122AAAB*NN(2HNNNO   $$:ty$:$:FCCC  M
 .  G  G%'VVK%8%8%JBFF6NN "'= > > D" M11+q0+h-- G
 !%T#s(^X > >I'0}}_'E'E'JK'0}}_'E'E'JK-[1A1AA--[1A1AA- }}%899 1,0) '0mmH&=&=J!*d33 G&*49j&A&A& &4?& & &
 +i773:::FFFF,	993:::FFF!%# $	#.!$X%=!>!>%2&4' (
 
 
4"S5=5IC0111q ! 9A8Lx44RS:B:N 6 6TU4<4HX00a
(   %v(,		 (6~ "*	
 "* $.: *:)9 &2\ '4m %0K !3 2 %;$: 3L2K 3L2K  +<*;!" 1H0G#* 155L0M0M0M+, 155L0M0M0M-   4  S S SBTRRRRRSa  	 	 	
 &*!WW%5"c!!#022Z?4G	"S5=5IC0111q ! 9A8Lx44RS:B:N 6 6TU4<4HX00a
(   %v(,		 (6~ "*	
 "* $.: *:)9 &2\ '4m %0K !3 2 %;$: 3L2K 3L2K  +<*;!" 1H0G#* 155L0M0M0M+, 155L0M0M0M-   4  S S SBTRRRRRSs    X7 5BDX7 
D))X7 ,D)-P7X7 %B%X&X43X47
Y:4Y55Y::Y= =]?B%\%$]%&]]]]rD   r4   r$   rE   r$   r   r$   r-   rS   r#   rD   r4   r$   r#   r   rV   rW   rX   rW   r$   r#   )rX   rW   r$   r#   )NNN)rf   r#   rg   r#   rS   r#   rh   ri   rV   rW   rj   rk   rl   ri   r$   r*   )r7   r8   r9   r:   rA   r;   rB   r   rI   rM   rO   rR   rU   rZ   re   r   r<   r(   r&   r?   r?   J   s^        --D 	N !     59      ^    ^       :>     ^  '+    ^   * #'6:$(aS aS aS aS aS aS aSr(   r?   c                  p    e Zd ZU dZdZded<   dZded<   ddddZddZddZ	ddZ
ddddZ	 dddZdS ) DeductionSpecialista  
    Creates deductive observations from explicit observations.

    This specialist:
    1. Explores recent observations and messages to understand what's there
    2. Identifies logical implications, knowledge updates, and contradictions
    3. Creates new deductive observations with premise linkage
    4. Deletes outdated observations
    5. Updates peer card with biographical facts
    	deductionr#   rA   zOUpdate this with `update_peer_card` only for stable biographical/profile facts.rB   TrC   rD   r4   r$   rE   c               6    |rt           S d t           D             S )Nc                0    g | ]}|d          t           v|S rp   PEER_CARD_TOOL_NAMESr`   ts     r&   r   z1DeductionSpecialist.get_tools.<locals>.<listcomp>  2     
 
 
y 444 444r(   )r   rG   s     r&   rI   zDeductionSpecialist.get_tools  1     	.--
 
/
 
 
 	
r(   r   c                B    t          t          j        j        d          S )NzDREAM DEDUCTIONr"   )r'   r   r   DEDUCTION_MODEL_CONFIGrL   s    r&   rM   z$DeductionSpecialist.get_model_config  $    /N1-
 
 
 	
r(   r-   c                    dS Ni    r<   rL   s    r&   rO   z"DeductionSpecialist.get_max_tokens      tr(   c                    dS )N   r<   rL   s    r&   rR   z&DeductionSpecialist.get_max_iterations      rr(   rS   c                    d}|rd}d| d| dS )Nr\   a  

## PEER CARD (REQUIRED)

The peer card is a summary of stable biographical facts. You MUST update it when you learn:
- Name, age, location, occupation
- Family members and relationships
- Standing instructions ("call me X", "don't mention Y")
- Core preferences and traits

Never add temporary event summaries, one-off conclusions, reasoning traces, or contradiction notes.

Format entries as:
- Plain facts: "Name: Alice", "Works at Google", "Lives in NYC"
- `INSTRUCTION: ...` for standing instructions
- `PREFERENCE: ...` for preferences
- `TRAIT: ...` for personality traits

Call `update_peer_card` with the complete updated list when you have new biographical info.
Keep it concise (max 40 entries), deduplicated, and current.zAYou are a deductive reasoning agent analyzing observations about u  .

## YOUR JOB

Create deductive observations by finding logical implications in what's already known. Think like a detective connecting evidence.

## PHASE 1: DISCOVERY

Explore what's actually in memory. Use these tools freely:
- `get_recent_observations` - See what's been learned recently
- `search_memory` - Search for specific topics
- `search_messages` - See actual conversation content

Spend a few tool calls understanding the landscape before creating anything.

## PHASE 2: ACTION

Once you understand what's there, create observations and clean up:

### Knowledge Updates (HIGH PRIORITY)
When the same fact has different values at different times:
- "meeting Tuesday" [old] → "meeting moved to Thursday" [new]
- Create a deductive update observation
- DELETE the outdated observation immediately

### Logical Implications
Extract implicit information:
- "works as SWE at Google" → "has software engineering skills", "employed in tech"
- "has kids ages 5 and 8" → "is a parent", "has school-age children"

### Contradictions
When statements can't both be true (not just updates), flag them:
- "I love coffee" vs "I hate coffee" → contradiction observation
ao  

## CREATING OBSERVATIONS

Use `create_observations_deductive`.

```json
{
  "observations": [{
    "content": "The logical conclusion",
    "source_ids": ["id1", "id2"],
    "premises": ["premise 1 text", "premise 2 text"]
  }]
}
```

## RULES

1. Don't explain your reasoning - just call tools
2. Create observations based on what you ACTUALLY FIND, not what you expect
3. Always include source_ids linking to the observations you're synthesizing
4. Empty or missing source_ids will be rejected
5. Delete outdated observations - don't leave duplicates
6. Quality over quantity - fewer good deductions beat many weak onesr<   rH   rS   rD   peer_card_sections       r&   rU   z'DeductionSpecialist.build_system_prompt  sP      	@!@*8HU] 8H 8HB C8H 8H 8H 8	Hr(   NrV   rW   rX   c                    |                      |          }|r/d                    d |d d         D                       }| d| dS | dS )Nr]   c              3      K   | ]	}d | V  
dS r_   r<   r`   qs     r&   rb   z8DeductionSpecialist.build_user_prompt.<locals>.<genexpr>(  (      !>!>q(q((!>!>!>!>!>!>r(      z_Start by exploring recent observations and messages. These topics may be worth investigating:

z

But follow the evidence - if you find something more interesting, pursue that instead.

Begin with `get_recent_observations` to see what's there.ae  Explore the observation space and create deductive observations.

Start with `get_recent_observations` to see what's been learned recently, then investigate whatever seems most promising.

Look for:
1. Knowledge updates (same fact, different values over time)
2. Logical implications that haven't been made explicit
3. Contradictions that need flagging

Go.re   rc   rH   rV   rX   peer_card_context	hints_strs        r&   rZ   z%DeductionSpecialist.build_user_prompt   s    
 !99)DD 	=		!>!>E"1"I!>!>!>>>I) = =
= = = = & 	 	 	 		r(   r   r   r   r   r   r   r7   r8   r9   r:   rA   r;   rB   rI   rM   rO   rR   rU   rZ   r<   r(   r&   r   r     s         	 	 D(y yyyy59 
 
 
 
 
 

 
 
 
       ;?RH RH RH RH RH RHn '+      r(   r   c                  p    e Zd ZU dZdZded<   dZded<   ddddZddZddZ	ddZ
ddddZ	 dddZdS ) InductionSpecialistan  
    Creates inductive observations from explicit and deductive observations.

    This specialist:
    1. Explores observations to understand what's there
    2. Identifies patterns and generalizations across multiple observations
    3. Creates new inductive observations with source linkage
    4. Updates peer card with high-confidence traits and tendencies
    	inductionr#   rA   zUOnly add highly stable profile traits/preferences; do not copy transient conclusions.rB   TrC   rD   r4   r$   rE   c               6    |rt           S d t           D             S )Nc                0    g | ]}|d          t           v|S rp   r   r   s     r&   r   z1InductionSpecialist.get_tools.<locals>.<listcomp>N  r   r(   )r   rG   s     r&   rI   zInductionSpecialist.get_toolsK  r   r(   r   c                B    t          t          j        j        d          S )NzDREAM INDUCTIONr   )r'   r   r   INDUCTION_MODEL_CONFIGrL   s    r&   rM   z$InductionSpecialist.get_model_configT  r   r(   r-   c                    dS r   r<   rL   s    r&   rO   z"InductionSpecialist.get_max_tokensZ  r   r(   c                    dS )N
   r<   rL   s    r&   rR   z&InductionSpecialist.get_max_iterations]  r   r(   rS   c                    d}|rd}d| d| dS )Nr\   a  

## PEER CARD (REQUIRED)

After identifying patterns, only update the peer card for durable profile-level traits/preferences:
- `TRAIT: Analytical thinker`
- `TRAIT: Tends to reschedule when stressed`
- `PREFERENCE: Prefers detailed explanations`

Do NOT add temporary patterns, episode-specific conclusions, or reasoning summaries.
Call `update_peer_card` with the complete deduplicated list only when a durable profile update is warranted.
Keep it concise (max 40 entries).z@You are an inductive reasoning agent identifying patterns about u  .

## YOUR JOB

Create inductive observations by finding patterns across multiple observations. Think like a psychologist identifying behavioral tendencies.

## PHASE 1: DISCOVERY

Explore broadly to find patterns. Use these tools:
- `get_recent_observations` - Recent learnings
- `search_memory` - Topic-specific search
- `search_messages` - Actual conversation content

Look at BOTH explicit observations AND deductive ones. Patterns often emerge from synthesizing across both levels.

## PHASE 2: ACTION

Create inductive observations when you see patterns:

### Behavioral Patterns
- "Tends to reschedule meetings when stressed"
- "Makes decisions after consulting with partner"
- "Projects follow: enthusiasm → doubt → completion"

### Preferences
- "Prefers morning meetings"
- "Likes detailed technical explanations"

### Personality Traits
- "Generally optimistic about outcomes"
- "Detail-oriented in planning"

### Temporal Patterns
- "Career goals have remained consistent"
- "Living situation changes frequently"
a  

## CREATING OBSERVATIONS

Use `create_observations_inductive`.

```json
{
  "observations": [{
    "content": "The pattern or generalization",
    "source_ids": ["id1", "id2", "id3"],
    "sources": ["evidence 1", "evidence 2"],
    "pattern_type": "tendency", // preference|behavior|personality|tendency|correlation
    "confidence": "medium" // low (2 sources), medium (3-4), high (5+)
  }]
}
```

## RULES

1. Minimum 2 source observations required - patterns need evidence
2. Don't just restate a single fact as a pattern
3. Confidence based on evidence count: 2=low, 3-4=medium, 5+=high
4. Look for HOW things change over time, not just static facts
5. Include source_ids - always link back to evidence
6. Empty or missing source_ids will be rejectedr<   r   s       r&   rU   z'InductionSpecialist.build_system_prompt`  sG      	%!%<3T\ <3 <3F G<3 <3 <3 <	3r(   NrV   rW   rX   c                    |                      |          }|r/d                    d |d d         D                       }| d| dS | dS )Nr]   c              3      K   | ]	}d | V  
dS r_   r<   r   s     r&   rb   z8InductionSpecialist.build_user_prompt.<locals>.<genexpr>  r   r(   r   zDExplore and find patterns. These areas may be worth investigating:

zp

But follow the evidence - if you find patterns elsewhere, pursue those.

Start with `get_recent_observations`.zExplore the observation space and identify patterns.

Remember: patterns need 2+ sources. Look for tendencies, preferences, and behavioral regularities.

Go.r   r   s        r&   rZ   z%InductionSpecialist.build_user_prompt  s    
 !99)DD 	)		!>!>E"1"I!>!>!>>>I) ) )
) ) ) ) &    	r(   r   r   r   r   r   r   r   r<   r(   r&   r   r   =  s           D( 59 
 
 
 
 
 

 
 
 
       ;?N3 N3 N3 N3 N3 N3f '+      r(   r   )r   r   zdict[str, BaseSpecialist]SPECIALISTS)r    r!   r"   r#   r$   r   )>r:   
__future__r   loggingr   abcr   r   collectionsr   collections.abcr   dataclassesr   typingr	   r
   nanoidr   r   srcr   r   
src.configr   r   src.dependenciesr   src.exceptionsr   src.llmr   r   src.llm.typesr   src.schemasr   src.telemetryr   src.telemetry.eventsr   r   src.telemetry.loggingr   r    src.telemetry.prometheus.metricsr   src.utils.agent_toolsr   r   r   	getLoggerr7   r   r'   r*   r   r?   r   r   r  r;   r<   r(   r&   <module>r     s     # " " " " "   # # # # # # # #       $ $ $ $ $ $ ! ! ! ! ! !         . . . . . .         8 8 8 8 8 8 8 8 ' ' ' ' ' ' . . . . . . : : : : : : : : - - - - - - - - - - - - , , , , , , ; ; ; ; ; ; ; ; L L L L L L L L 7 7 7 7 7 7          
	8	$	$	 	 	 	         ++ [S [S [S [S [SS [S [S [S|
R R R R R. R R RjH H H H H. H H HZ %$&&$$&&* *      r(   