
    4AjG              	          d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
 ddlmZmZmZmZ dddd	d	d
d
ddZddZ G d d          Z e            ZdS )zTool registry with status, stability, and support-envelope reporting.

The registry discovers all registered tools, reports their availability,
and lets the orchestrator/agents query capabilities by tier, status, etc.
    )annotationsN)
ModuleType)AnyOptional)BaseTool
ToolStatusToolTierToolStabilityz---'"z...)u   —u   –u   −u   ‘u   ’u   “u   ”u   …valuer   returnc                   t          | t                    r=| }t                                          D ]\  }}||v r|                    ||          } |S t          | t
                    rd | D             S t          | t                    rt          d | D                       S t          | t                    rd |                                 D             S | S )zRecursively normalize unicode punctuation in str leaves to ASCII.

    Used to keep `provider_menu_summary()` output readable on Windows cp1252
    stdout. Does NOT modify dict/list structure or non-string values.
    c                ,    g | ]}t          |          S  _scrub_unicode_dashes.0items     B/app/working/workspaces/default/OpenMontage/tools/tool_registry.py
<listcomp>z)_scrub_unicode_dashes.<locals>.<listcomp>/   s!    >>>%d++>>>    c              3  4   K   | ]}t          |          V  d S Nr   r   s     r   	<genexpr>z(_scrub_unicode_dashes.<locals>.<genexpr>1   s+      CCT*400CCCCCCr   c                4    i | ]\  }}|t          |          S r   r   r   kvs      r   
<dictcomp>z)_scrub_unicode_dashes.<locals>.<dictcomp>3   s'    FFF1(++FFFr   )
isinstancestr_UNICODE_DASH_REPLACEMENTSitemsreplacelisttupledict)r   outneedlerepls       r   r   r   "   s     % 6<<>> 	0 	0LFD}}kk&$//
% ?>>>>>>% DCCUCCCCCC% GFFFFFFLr   c                      e Zd ZdZd7dZd8dZd7dZd9dZed7d            Z	d:d;dZ
d:d<dZd=dZd>dZd?dZd@dZdAdZdBd!ZdCd"ZdCd#ZdDd&Zd@d'ZdEd)ZdFd+ZdGd-ZdGd.ZdHd0ZdId2ZdFd3Zd>d4Zd>d5Zd6S )JToolRegistryz*Central registry of all OpenMontage tools.r   Nonec                :    i | _         t                      | _        d S r   )_toolsset_discovered_packagesselfs    r   __init__zToolRegistry.__init__:   s    +-.1ee!!!r   toolr   c                P    |j         st          d          || j        |j         <   dS )zRegister a tool instance.zTool must have a non-empty nameN)name
ValueErrorr2   )r6   r8   s     r   registerzToolRegistry.register>   s0    y 	@>???!%DIr   c                j    | j                                          | j                                         dS )z+Clear registered tools and discovery state.N)r2   clearr4   r5   s    r   r>   zToolRegistry.clearD   s1    !'')))))r   moduler   	list[str]c                N   g }t          j        |t           j                  D ]\  }}|t          u st	          |t                    s$|j        |j        k    st          j        |          rI |            }|                     |           |	                    |j
                   |S )z>Register all concrete BaseTool subclasses defined in a module.)inspect
getmembersisclassr   
issubclass
__module____name__
isabstractr<   appendr:   )r6   r?   
registered_clsr8   s         r   register_modulezToolRegistry.register_moduleI   s     "
(AA 	) 	)FAshjh&?&?~00G4Fs4K4K0355DMM$di((((r   c                 8   ddl m}  ddl} | t                                                    j        j        dz  }|                                sdS ddl}t          |dd          5 }|D ]}|	                                }|r|
                    d          sd	|vr3|                    d	          \  }}}|	                                }|	                                }|dd
         dv r9|d         }	|                    |	d
          }
|
dk    r
|d
|
         n	|d
d         }nH|                    d|          }|r|d|                                         }|	                                }|r||j        vr
||j        |<   	 ddd           dS # 1 swxY w Y   dS )zFLoad .env file into os.environ if present, so tools can find API keys.r   )PathNz.envzutf-8ignore)encodingerrors#=   )r   r   z(^|\s)#)pathlibrO   os__file__resolveparentis_filereopenstrip
startswith	partitionfindsearchstartenviron)rO   rX   env_pathr]   flinekeyrK   r   quoteendmatchs               r   _load_dotenvzToolRegistry._load_dotenvV   s    	!     			4>>))++29FB!! 	F			(WX>>> 	,! , ,zz|| ts33 s$ $s 3 3Qiikk!9
**!!HE**UA..C,/2IIE!C%LL59EE IIj%88E 7 %oo 6!KKMME ,3bj00&+BJsO),	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	, 	,s   &DFFFtoolspackage_namer$   c                
   |                                   t          j        |          }g }t          |dd          }||                     |          S t          j        ||j         d          D ]x}|j        	                    d          s|j        	                    d          r7t          j        |j                  }|
                    |                     |                     y| j                            |           |S )zAImport a package tree and register any concrete tools it defines.__path__N.z
.base_toolz.tool_registry)rm   	importlibimport_modulegetattrrM   pkgutilwalk_packagesrG   r:   endswithextendr4   add)r6   ro   package
discoveredpackage_pathsmodule_infor?   s          r   discoverzToolRegistry.discoverv   s   ),77 "
T:: ''000"07CS@V@V@VWW 	< 	<K((66 +:J:S:STd:e:e ,[-=>>Fd226::;;;;!%%l333r   c                F    || j         vr|                     |           dS dS )z5Load tool modules once before reporting capabilities.N)r4   r   )r6   ro   s     r   ensure_discoveredzToolRegistry.ensure_discovered   s0    t888MM,''''' 98r   r:   Optional[BaseTool]c                6    | j                             |          S )zGet a tool by name.)r2   get)r6   r:   s     r   r   zToolRegistry.get   s    {t$$$r   c                N    t          | j                                                  S )zList all registered tool names.)r(   r2   keysr5   s    r   list_allzToolRegistry.list_all   s    DK$$&&'''r   tierr	   list[BaseTool]c                N    fd| j                                         D             S )zGet all tools in a given tier.c                *    g | ]}|j         k    |S r   )r   )r   tr   s     r   r   z,ToolRegistry.get_by_tier.<locals>.<listcomp>   s     BBBa16T>>>>>r   r2   values)r6   r   s    `r   get_by_tierzToolRegistry.get_by_tier   s+    BBBB4;--//BBBBr   
capabilityc                N    fd| j                                         D             S )z;Get all tools registered for a top-level capability family.c                *    g | ]}|j         k    |S r   )r   r   r   r   s     r   r   z2ToolRegistry.get_by_capability.<locals>.<listcomp>   s%    NNNa1<:3M3M3M3M3Mr   r   r6   r   s    `r   get_by_capabilityzToolRegistry.get_by_capability   s+    NNNN4;--//NNNNr   providerc                N    fd| j                                         D             S )z,Get all tools backed by a specific provider.c                *    g | ]}|j         k    |S r   r   )r   r   r   s     r   r   z0ToolRegistry.get_by_provider.<locals>.<listcomp>   s%    JJJa1:3I3I3I3I3Ir   r   )r6   r   s    `r   get_by_providerzToolRegistry.get_by_provider   s+    JJJJ4;--//JJJJr   statusr   c                N    fd| j                                         D             S )z"Get all tools with a given status.c                D    g | ]}|                                 k    |S r   )
get_status)r   r   r   s     r   r   z.ToolRegistry.get_by_status.<locals>.<listcomp>   s+    LLLa1<<>>V3K3K3K3K3Kr   r   )r6   r   s    `r   get_by_statuszToolRegistry.get_by_status   +    LLLL4;--//LLLLr   c                @    |                      t          j                  S )z+Get all tools that are currently available.)r   r   	AVAILABLEr5   s    r   get_availablezToolRegistry.get_available   s    !!*"6777r   c                @    |                      t          j                  S )z-Get all tools that are currently unavailable.)r   r   UNAVAILABLEr5   s    r   get_unavailablezToolRegistry.get_unavailable   s    !!*"8999r   	stabilityr
   c                N    fd| j                                         D             S )z)Get all tools at a given stability level.c                *    g | ]}|j         k    |S r   )r   )r   r   r   s     r   r   z1ToolRegistry.get_by_stability.<locals>.<listcomp>   s%    LLLa1;)3K3K3K3K3Kr   r   )r6   r   s    `r   get_by_stabilityzToolRegistry.get_by_stability   r   r   c                N    fd| j                                         D             S )z+Find tools that declare a given capability.c                &    g | ]}|j         v |S r   )capabilitiesr   s     r   r   z3ToolRegistry.find_by_capability.<locals>.<listcomp>   s0     
 
 
Q^++ +++r   r   r   s    `r   find_by_capabilityzToolRegistry.find_by_capability   s<    
 
 
 
{))++
 
 
 	
r   	tool_namec                <   |                      |          }|dS t          |j        pg           }|j        r#|j        |vr|                    |j                   |D ]?}|                      |          }|r&|                                t          j        k    r|c S @dS )zCFind the fallback tool for a given tool, if declared and available.N)r   r(   fallback_toolsfallbackrI   r   r   r   )r6   r   r8   
candidatesr:   fbs         r   find_fallbackzToolRegistry.find_fallback   s    xx	""<4$-344
= 	-T]*<<dm,,, 	 	D$B bmmoo)===			tr   dict[str, Any]c                    |                                   i }| j                                        D ]\  }}|                                }|||<   |S )zGenerate a full support-envelope report for all tools.

        Returns a dict mapping tool name to its contract info + live status.
        This is the primary report the orchestrator uses to understand
        what the system can and cannot do.
        )r   r2   r&   get_info)r6   reportr:   r8   infos        r   support_envelopezToolRegistry.support_envelope   sY     	   !#+++-- 	  	 JD$==??DF4LLr   dict[str, list[dict[str, Any]]]c                   |                                   i }| j                                        D ]B}|                    |j        g                               |                                           C|                                D ]}|                    d            t          t          |
                                                    S )z3Group the support envelope by top-level capability.c                "    | d         | d         fS Nr   r:   r   r   s    r   <lambda>z1ToolRegistry.capability_catalog.<locals>.<lambda>   s    j)94<(H r   ri   )r   r2   r   
setdefaultr   rI   r   sortr*   sortedr&   r6   groupedr8   r&   s       r   capability_catalogzToolRegistry.capability_catalog   s       35K&&(( 	L 	LDt33::4==??KKKK^^%% 	J 	JEJJHHJIIIIF7==??++,,,r   c                   |                                   i }| j                                        D ]B}|                    |j        g                               |                                           C|                                D ]}|                    d            t          t          |
                                                    S )z'Group the support envelope by provider.c                "    | d         | d         fS )Nr   r:   r   r   s    r   r   z/ToolRegistry.provider_catalog.<locals>.<lambda>   s    l);T&\(J r   r   )r   r2   r   r   r   rI   r   r   r*   r   r&   r   s       r   provider_catalogzToolRegistry.provider_catalog   s       35K&&(( 	J 	JDt}b1188IIII^^%% 	L 	LEJJJJJKKKKF7==??++,,,r   dict[str, dict[str, int]]c                    i }t           D ]c}|                     |          }dddd}|D ]7}|                                j        }|                    |d          dz   ||<   8|r
|||j        <   d|S )zSummarize tool counts by tier and status.

        Returns:
            {"core": {"available": 5, "unavailable": 2, "degraded": 0}, ...}
        r   )	availableunavailabledegradedrU   )r	   r   r   r   r   )r6   summaryr   
tier_toolscountsr   r   s          r   tier_summaryzToolRegistry.tier_summary   s     .0 	- 	-D))$//J#$QAFFF ; ;-!'FA!6!6!:v -&,
#r   dict[str, dict[str, Any]]c                t   |                                   i }d | j                                        D             }|D ]}|j        }||vr
g g ddd||<   |                                }|                                }|j        |j        |j        j	        |j
        |j        |j	        d}dD ]}||v r||         ||<   |t          j        k    r8||         d                             |           ||         dxx         dz  cc<   n!||         d	                             |           ||         d
xx         dz  cc<   |                                D ]<}	|	d                             d            |	d	                             d            =t!          t#          |                                                    S )uy  Generate a capability-grouped provider menu for user-facing display.

        Returns a dict like:
        {
            "video_generation": {
                "available": [{"name": ..., "provider": ..., "best_for": ...}],
                "unavailable": [{"name": ..., "provider": ..., "install_instructions": ...}],
                "total": 12,
                "configured": 2,
            },
            ...
        }

        This powers the agent's preflight provider menu — the agent reads this
        output and presents it to the user.  Adding a new tool to tools/ is
        enough; this method auto-discovers it.
        c                (    g | ]}|j         d k    |S )selectorr   r   r   s     r   r   z.ToolRegistry.provider_menu.<locals>.<listcomp>  s$    MMMqAJ*4L4L4L4L4Lr   r   )r   r   total
configured)r:   r   runtimebest_forinstall_instructionsr   )source_provider_menusource_provider_summaryrender_enginesremotion_noteprovider_matrixr   r   rU   r   r   c                "    | d         | d         fS r   r   entrys    r   r   z,ToolRegistry.provider_menu.<locals>.<lambda>2  s    j8I5QW=7Y r   r   c                "    | d         | d         fS r   r   r   s    r   r   z,ToolRegistry.provider_menu.<locals>.<lambda>3  s    %
:KUSY]9[ r   )r   r2   r   r   r   r   r:   r   r   r   r   r   r   r   rI   r   r*   r   r&   )
r6   menurn   r8   capr   r   r   	extra_keybuckets
             r   provider_menuzToolRegistry.provider_menu   s   $ 	   *, NMDK..00MMM 	$ 	$D/C$*,R![\]]S	==??D__&&F	 M<- M(,(A , E 7 7	 $$'+IE)$---S	+&--e444S	,'''1,''''S	-(//666Ig!#kkmm 	] 	]F;$$)Y)Y$ZZZ=!&&+[+[&\\\\F4::<<(()))r   c           
        |                                   |                                 }i }g }| j                            d          }|I|                                }|                    d          pi }d |                                D             }| j                            d          }|]|                                }|                    d          pi }	|	                    d          pg D ]}
|                    d|
            g }|                                D ]\  }}d	 |                    d
g           D             dhz
  }d |                    dg           D             dhz
  |z
  }|                    ||                    dd          |                    dd          t          |          t          |          d           g }|                                D ]\  }}|                    dg           D ]t}|                    d          pdt          fddD                       r@|                    ||                    d          |                    d          d           u||||d}t          |          S )u  Compact, human-ready rollup of provider_menu() for onboarding/preflight.

        Returns a dict shaped for the "N of M configured" capability menu the
        agent is supposed to present to the user per AGENT_GUIDE.md → "Provider
        Menu (Mandatory at Preflight)". Collapses the firehose of
        support_envelope() into something the agent can paraphrase in plain
        language in a few lines.

        Example output (abbreviated):
        {
          "composition_runtimes": {
            "ffmpeg": True,
            "remotion": True,
            "hyperframes": True,
          },
          "capabilities": [
            {"capability": "video_generation", "configured": 10, "total": 16,
             "available_providers": ["fal", "heygen", ...],
             "unavailable_providers": ["openai", ...]},
            ...
          ],
          "setup_offers": [
             {"capability": "music_generation", "tool": "suno_music",
              "install_instructions": "Add SUNO_API_KEY to .env"},
             ...
          ],
          "runtime_warnings": [
             "hyperframes: npm package `hyperframes` not resolvable: ...",
             ...
          ],
        }

        Agents should use this as the source for the preflight capability
        menu rather than rendering `support_envelope()` or `provider_menu()`
        raw. See AGENT_GUIDE.md > "Provider Menu (Mandatory at Preflight)".
        video_composeNr   c                4    i | ]\  }}|t          |          S r   )boolr   s      r   r"   z6ToolRegistry.provider_menu_summary.<locals>.<dictcomp>g  s$    DDDDAqQQDDDr   hyperframes_composehyperframes_runtimereasonszhyperframes: c                8    h | ]}|                     d           S r   r   r   es     r   	<setcomp>z5ToolRegistry.provider_menu_summary.<locals>.<setcomp>y  s1     # # #&'j!!# # #r   r   c                8    h | ]}|                     d           S r   r   r   s     r   r   z5ToolRegistry.provider_menu_summary.<locals>.<setcomp>}  s$    JJJqz""JJJr   r   r   r   r   )r   r   r   available_providersunavailable_providersr    c              3  D   K   | ]}|                                 v V  d S r   )lower)r   r    hints     r   r   z5ToolRegistry.provider_menu_summary.<locals>.<genexpr>  s0      VVQqDJJLL(VVVVVVr   )zapi keyenvz_key=_apir:   r   )r   r8   r   r   )composition_runtimesr   setup_offersruntime_warnings)
r   r   r2   r   r   r&   rI   r   anyr   )r6   r   comp_runtimesr  vcr   engineshfhf_inforcreasonr   r   r   r   r   r  r   resultr   s                      @r   provider_menu_summaryz"ToolRegistry.provider_menu_summary7  s   J 	   !!## *,&([___-->;;==Dhh/006BGDDGMMOODDDM [__233>kkmmG2339rB&&++1r B B ''(@(@(@AAAA .0::<< 	 	KC# #+1::k2+F+F# # ## KJFJJ}b,I,IJJJ&%& "
 "%"(**\1"="=#ZZ33+12E+F+F-34I-J-J     .0::<< 	 	KCM266  yy!788>BVVVV2UVVVVV  ''*-$)IIf$5$5(-		*(=(=48	   	 %2(( 0	
 
 %V,,,r   c                H    d | j                                         D             S )z'List tools that require GPU (VRAM > 0).c                <    g | ]}|j         j        d k    |j        S )r   )resource_profilevram_mbr:   r   s     r   r   z3ToolRegistry.gpu_required_tools.<locals>.<listcomp>  s5     
 
 
!)A-- F---r   r   r5   s    r   gpu_required_toolszToolRegistry.gpu_required_tools  1    
 
 K..00
 
 
 	
r   c                H    d | j                                         D             S )z'List tools that require network access.c                4    g | ]}|j         j        |j        S r   )r  network_requiredr:   r   s     r   r   z7ToolRegistry.network_required_tools.<locals>.<listcomp>  s6     
 
 
!2
F
 
 
r   r   r5   s    r   network_required_toolsz#ToolRegistry.network_required_tools  r  r   N)r   r0   )r8   r   r   r0   )r?   r   r   r@   )rn   )ro   r$   r   r@   )ro   r$   r   r0   )r:   r$   r   r   )r   r@   )r   r	   r   r   )r   r$   r   r   )r   r$   r   r   )r   r   r   r   )r   r   )r   r
   r   r   )r   r$   r   r   )r   r   )r   r   )r   r   )r   r   )rG   rF   __qualname____doc__r7   r<   r>   rM   staticmethodrm   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r   r   r   r/   r/   7   sR       444 4 4 4& & & &* * * *
    , , , \,>    $( ( ( ( (
% % % %( ( ( (C C C CO O O OK K K KM M M M8 8 8 8: : : :M M M M
 
 
 
      - - - -- - - -   "<* <* <* <*|r- r- r- r-n
 
 
 

 
 
 
 
 
r   r/   )r   r   r   r   )r  
__future__r   rs   rB   rv   typesr   typingr   r   tools.base_toolr   r   r	   r
   r%   r   r/   registryr   r   r   <module>r!     s    # " " " " "                             I I I I I I I I I I I I 	 	    *C
 C
 C
 C
 C
 C
 C
 C
N <>>r   