
    kKjs                    `   d dl mZ d dlZd dlmZ d dlmZ d dlmZm	Z	 ddZ
ddZddZdd	Zdd
ZddZ	 d	 	 	 	 	 	 	 	 	 ddZddZddZ eh d      Z eh d      Z eh d      Z eh d      Z eh d      Z eh d      Z	 	 	 d	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 d 	 	 	 	 	 	 	 	 	 	 	 d!dZy)"    )annotationsN)defaultdict)Any)JsonRefErrorreplace_refsc                    	  syt        t              dfd j                         D ]  \  }} ||        d\  t        t              	d 	fdt	        	fd D              S )uJ  Check whether any definitions in ``$defs`` form a reference cycle.

    A cycle means a definition directly or transitively references itself
    (e.g. Node → children → Node, or A → B → A).  ``jsonref.replace_refs``
    silently produces Python-level object cycles for these, which Pydantic's
    serializer rejects.
    Fc                V   t        | t              rw| j                  d      }t        |t              r7|j	                  d      r&|   j                  |j                  d      d          | j                         D ]  } ||        y t        | t              r| D ]  } ||        y y )N$ref#/$defs//)	
isinstancedictgetstr
startswithaddsplitvalueslist)objsourcerefvitem_collect_refsedgess        n/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/utilities/json_schema.pyr   z(_defs_have_cycles.<locals>._collect_refs   s    c4 ''&/C#s#z(Bf!!#))C."45ZZ\a( "T"dF+  #    )r         c                    | <   j                  | d      D ]$  }|vr|   k(  r y|   k(  s |      s$ y | <   y)N TF)r   )	nodeneighborDONEIN_STACK	UNVISITED
_has_cycledefsr   states	     r   r)   z%_defs_have_cycles.<locals>._has_cycle*   s_    d		$+Ht#X(*X)+
80D , dr   c              3  B   K   | ]  }|   k(  xr  |        y wNr#   ).0namer(   r)   r+   s     r   	<genexpr>z$_defs_have_cycles.<locals>.<genexpr>6   s(     NuT{i'<Jt,<<s   )r   r   r   r   returnNone)r$   r   r1   bool)r   setitemsintany)
r*   r/   
definitionr&   r'   r(   r   r)   r   r+   s
   `  @@@@@@@r   _defs_have_cyclesr9   
   sr      "-S!1E	, !JJLjj$' ) !(Ix',E
 
 NNNNr   c                   t        | t              r| j                  d      }t        |t              rD|j	                  d      s3| j                         D ci c]  \  }}|dk7  s|t        |       c}}S | j                         D ci c]  \  }}|t        |       c}}S t        | t              r| D cg c]  }t        |       c}S | S c c}}w c c}}w c c}w )a  Return a deep copy of *obj* with non-local ``$ref`` values removed.

    Local refs (starting with ``#``) are kept intact.  Remote refs
    (``http://``, ``https://``, ``file://``, or any other URI scheme) are
    stripped so that ``jsonref.replace_refs`` never attempts to fetch an
    external resource.  This prevents SSRF / LFI when proxying schemas
    from untrusted servers.
    r
   #)r   r   r   r   r   r5   _strip_remote_refsr   )r   r   kr   r   s        r   r<   r<   9   s     #tggfoc3s(;9<TAVA)!,,TT58YY[A[TQ%a(([AA#t589ST"4(S99J	 UA9s   C$C	C7Cc                D   t        | t              r[d| v xr
 d| v xs d| v }|rt        |       } h d}| j                         D ci c]  \  }}|dk(  r|s|||v r|n
t	        |        c}}S t        | t
              r| D cg c]  }t	        |       c}S | S c c}}w c c}w )uo  Recursively remove OpenAPI ``discriminator`` keys from a schema.

    Pydantic emits ``discriminator.mapping`` with values like
    ``#/$defs/ClassName``.  After ``$defs`` are inlined and removed by
    ``dereference_refs``, those mapping entries dangle.  The keyword is an
    OpenAPI extension — the ``anyOf`` variants already carry ``const`` on
    the discriminant field, so the mapping is redundant.

    Only strips ``discriminator`` when it appears alongside ``anyOf`` or
    ``oneOf``, which is where the OpenAPI keyword lives.  A property
    *named* ``discriminator`` (inside ``properties``) is left alone.
    discriminatoranyOfoneOf>   enumconstdefaultexamples)r   r   require_discriminator_propertyr5   _strip_discriminatorr   )r   skip
_DATA_KEYSr=   r   r   s         r   rG   rG   M   s     #t#%L7c>+KW^05C=
 		
#1(T Q*_*>q*AA#
 	

 #t7:;st$T*s;;J
 <s   #B Bc                ~    | j                  d      }|	i | d|giS t        |t              r||vri | dg ||iS | S )z?Return a copy of *schema* with *property_name* in ``required``.required)r   r   r   )schemaproperty_namerK   s      r   _require_propertyrN   j   s]    zz*%H6&6*}o66(D!m8&CA&A*&@&@-&@AAMr   c                l   | j                  d      }t        |t              s| S |j                  d      }t        |t              s| S | j	                         }dD ]S  }|j                  |      }t        |t
              s%|D cg c]   }t        |t              rt        ||      n|" c}||<   U |S c c}w )a  Keep an OpenAPI discriminator's tag mandatory after the keyword is dropped.

    Returns a copy of *schema* with ``discriminator.propertyName`` added to each
    ``anyOf``/``oneOf`` variant's ``required`` list. A Pydantic discriminated
    union whose tag has a default omits that tag from ``required``; without this,
    an untagged payload passes the generated schema but fails later in the source
    model with ``union_tag_not_found``. No-op if there is no string
    ``propertyName``.
    r?   propertyName)r@   rA   )r   r   r   r   copyr   rN   )rL   r?   rM   resultkeyvariantsvariants          r   rF   rF   t   s     JJ/MmT*!%%n5MmS)[[]F!::c?(D)
 $	
 $ '4( g}5 $	
s	 " M
s   %B1c                   t        |       } t        | j                  di             rt        |       S 	 t	        | dd      }| j                  di       }t        | ||      }t        |t              sJ |}d|v r)|j                         D ci c]  \  }}|dk7  s|| }}}t        |      }|S c c}}w # t        t        f$ r t        |       cY S w xY w)a  Resolve all $ref references in a JSON schema by inlining definitions.

    This function resolves $ref references that point to $defs, replacing them
    with the actual definition content while preserving sibling keywords (like
    description, default, examples) that Pydantic places alongside $ref.

    This is necessary because some MCP clients (e.g., VS Code Copilot) don't
    properly handle $ref in tool input schemas.

    For self-referencing/circular schemas where full dereferencing is not possible,
    this function falls back to resolving only the root-level $ref while preserving
    $defs for nested references.

    Only local ``$ref`` values (those starting with ``#``) are resolved.
    Remote URIs (``http://``, ``file://``, etc.) are stripped before
    resolution to prevent SSRF / local-file-inclusion attacks when proxying
    schemas from untrusted servers.

    Args:
        schema: JSON schema dict that may contain $ref references

    Returns:
        A new schema dict with $ref resolved where possible and $defs removed
        when no longer needed

    Example:
        >>> schema = {
        ...     "$defs": {"Category": {"enum": ["a", "b"], "type": "string"}},
        ...     "properties": {"cat": {"$ref": "#/$defs/Category", "default": "a"}}
        ... }
        >>> resolved = dereference_refs(schema)
        >>> # Result: {"properties": {"cat": {"enum": ["a", "b"], "type": "string", "default": "a"}}}
    $defsF)proxies	lazy_load)r<   r9   r   resolve_root_refr   _merge_ref_siblingsr   r   r5   rG   r   RecursionError)rL   dereferencedr*   mergedr=   r   s         r   dereference_refsr_      s    F  'F GR01''!( $FEUK zz'2&$V\4@&$''' l"-9-?-?-AR-ATQQ'\AqD-ALR ,L9 S .) (  ''(s*   AB4 B.B.B4 .B4 4CCc           
     "   |
t               }t        | t              rt        |t              rd| v r| d   }| j                         D ci c]  \  }}|dvs|| }}}t        |t              rB|j                  d      r1|j                  d      d   }||v r||vrt        ||   ||||hz        }|rt        |      }	|	j                  |       |	S |S i }
|j                         D ]#  \  }}|| v rt        | |   |||      |
|<   ||
|<   % |
S t        | t              rgt        |t              rWt        t        |       t        |            }t        | d| |d| d      D cg c]  \  }}t        ||||       c}}||d z   S |S c c}}w c c}}w )	a  Merge sibling keywords from original $ref nodes into dereferenced schema.

    When jsonref resolves $ref, it replaces the entire node with the referenced
    definition, losing any sibling keywords like description, default, or examples.
    This function walks both trees in parallel and merges those siblings back.

    Args:
        original: The original schema with $ref and potential siblings
        dereferenced: The schema after jsonref processing
        defs: The $defs from the original schema, for looking up referenced definitions
        visited: Set of definition names already being processed (prevents cycles)

    Returns:
        The dereferenced schema with sibling keywords restored
    Nr
   )r
   rW   r   r   r   F)strict)r4   r   r   r5   r   r   r   r[   updater   minlenzip)originalr]   r*   visitedr   r=   r   siblingsdef_namer^   rR   rS   valuemin_lenods                   r   r[   r[      s   * %(D!jt&DX6"C)1)9X)9AQFW=W1)9HX #s#z(B99S>"-t#(?#6XdGxj<P$L l+h' &,,.JCh1(3-gVs#s	 /
 	Hd	#
<(Fc(mS%67 HXg.Xg0FuU
U1  1dG4U
 "# 	#
 I Y>
s   	FF!Fc                H   d| v rd| v rd| vr| d   }t        |t              rz|j                  d      ri|j                  d      d   }| d   }||v rLt	        ||         }|j                  | j                         D ci c]  \  }}|dvr|| c}}       ||d<   |S | S c c}}w )a  Resolve $ref at root level to meet MCP spec requirements.

    MCP specification requires outputSchema to have "type": "object" at the root level.
    When Pydantic generates schemas for self-referential models, it uses $ref at the
    root level pointing to $defs. This function resolves such references by inlining
    the referenced definition while preserving $defs for nested references.

    Args:
        schema: JSON schema dict that may have $ref at root level

    Returns:
        A new schema dict with root-level $ref resolved, or the original schema
        if no resolution is needed

    Example:
        >>> schema = {
        ...     "$defs": {"Node": {"type": "object", "properties": {...}}},
        ...     "$ref": "#/$defs/Node"
        ... }
        >>> resolved = resolve_root_ref(schema)
        >>> # Result: {"type": "object", "properties": {...}, "$defs": {...}}
    r
   rW   typer   r   r   >   r
   rW   )r   r   r   r   r   rb   r5   )rL   r   ri   r*   resolvedrS   rj   s          r   rZ   rZ   &  s    0 Gv-&2FVnc3CNN:$>yy~b)H'?D4X/  +1,,.*8JC&77 U
*8 %)!Ms   <B
c                   t        j                  |       } | j                  di       }|j                  |d      }|| S || d<   || j                  dg       v r*| d   j	                  |       | d   s| j                  d       | S )zwReturn a new schema with *param* removed from `properties`, `required`,
    and (if no longer referenced) `$defs`.
    
propertiesNrK   )rQ   deepcopyr   popremove)rL   parampropsremoveds       r   _prune_paramry   [  s     ]]6"F JJ|R(Eiit$G !F<

:r**z!!%(j!JJz"Mr   >   ro   allOfr@   r5   rA   rK   rr   r
   >   titlereadOnly	writeOnly
deprecateddescription>   rB   rC   rD   examplerE   >   rr   definitionsdependenciesdependentSchemaspatternPropertiesrW   >   ifnotelsethenr5   containscontentSchemapropertyNamesadditionalItemsunevaluatedItemsadditionalPropertiesunevaluatedProperties>   rz   r@   rA   prefixItemsc                  	
 sss| S t               	t        t              | j                  d      }	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 d		
fd
 
| dd       rz|rx|j	                         D ]  \  }} 
||d        d
d	fdt        |j                               D ]  } |      r|j                  |        |s| j                  dd       | S )aK  
    Optimize JSON schemas in a single traversal for better performance.

    This function combines three schema cleanup operations that would normally require
    separate tree traversals:

    1. **Remove unused definitions** (prune_defs): Finds and removes `$defs` entries
       that aren't referenced anywhere in the schema, reducing schema size.

    2. **Remove titles** (prune_titles): Strips `title` fields throughout the schema
       to reduce verbosity while preserving functional information.

    3. **Remove restrictive additionalProperties** (prune_additional_properties):
       Removes `"additionalProperties": false` constraints to make schemas more flexible.

    **Performance Benefits:**
    - Single tree traversal instead of multiple passes (2-3x faster)
    - Immutable design prevents shared reference bugs
    - Early termination prevents runaway recursion on deeply nested schemas

    **Algorithm Overview:**
    1. Traverse main schema, collecting $ref references and applying cleanups
    2. Traverse $defs section to map inter-definition dependencies
    3. Remove unused definitions based on reference analysis

    Args:
        schema: JSON schema dict to optimize (not modified)
        prune_titles: Remove title fields for cleaner output
        prune_additional_properties: Remove "additionalProperties": false constraints
        prune_defs: Remove unused $defs entries to reduce size

    Returns:
        A new optimized schema dict

    Example:
        >>> schema = {
        ...     "type": "object",
        ...     "title": "MySchema",
        ...     "additionalProperties": False,
        ...     "$defs": {"UnusedDef": {"type": "string"}}
        ... }
        >>> result = _single_pass_optimize(schema, prune_titles=True, prune_defs=True)
        >>> # Result: {"type": "object", "additionalProperties": False}
    rW   NTc                    |dkD  ryt         t              rrn j                  d      }t        |t              rM|j	                  d      r<|j                  d      d   }|r|   j                  |       nj                  |       |rzrRd v rNt         d   t              r;t         fdt        D              st        d	  D              r j                  d       r$ j                  d
      du r j                  d
        j                         D ]  \  }}|r|dk(  r|s |||dz   d        |t        v r5t        |t              r%|j                         D ]  }	 |	||dz   d        ]|t        v rf|t         v r't        |t"              r|D ]  }
 |
||dz   d        |t$        v r |||dz   d        |||dz   d        yt         t"              r D ]  }
 |
||dz   |        yy)uC  Traverse schema tree, collecting $ref info and applying cleanups.

        The `in_schema` flag tracks whether the current node is reached via a
        known JSON-Schema-valued position (root, `properties` value, `items`,
        `allOf` element, etc.). When False — e.g. we descended through a user
        extension key like `x-ui` whose payload is opaque to us — we still
        collect `$ref` references (they may point at `$defs` the user cares
        about) but we skip all cleanups so we don't mutate user data that
        happens to look metadata-shaped.
        2   Nr
   r   r   r   r{   c              3  &   K   | ]  }|v  
 y wr-   r#   )r.   r=   r$   s     r   r0   zD_single_pass_optimize.<locals>.traverse_and_clean.<locals>.<genexpr>0  s     @/?!AI/?s   c              3  ,   K   | ]  }|t         v   y wr-   )_METADATA_KEYS)r.   r=   s     r   r0   zD_single_pass_optimize.<locals>.traverse_and_clean.<locals>.<genexpr>1  s     ADqqN2Ds   r   FrW   r    )depth	in_schemaT)r   r   r   r   r   r   appendr   r7   _SCHEMA_KEYWORDSallrt   r5   _SUBSCHEMA_MAP_KEYSr   _LITERAL_KEYWORDS_SUBSCHEMA_LIST_KEYSr   _SUBSCHEMA_VALUE_KEYS)r$   current_def_nameskip_defs_sectionr   r   r   referenced_defrS   rj   
sub_schemar   def_dependenciesprune_additional_properties
prune_defsprune_titles	root_refstraverse_and_cleans   `          r   r   z1_single_pass_optimize.<locals>.traverse_and_clean  s#   " 2:dD! hhv&c3'CNN:,F%(YYs^B%7N'(8??@PQ "n5  !4"4=#6@/?@@ADAA HHW% 0!78EAHH34 #jjl
U$ !&/uqyE  --*UD2I&+lln
*&,"'!)&*	 '5  ++ ..:eT3J %* ,"'!)&*	 !& //&(#ai"&	 
 #+519w +~ d#"*%!)y  $r   )r   r   )r   r   c                    | v ryj                  | g       }|r.|
t               }| |v ry|| hz  }|D ]  }||vs ||      s y y)z<Check if a definition is used, handling circular references.TF)r   r4   )ri   visitingreferencing_defsreferencing_defr   is_def_usedr   s       r   r   z*_single_pass_optimize.<locals>.is_def_used  sv    9$  033HbA#"uH x' #xj0 (8O&h6;'<  $	 (8 r   )NFr   T)r$   objectr   z
str | Noner   r3   r   r6   r   r3   r1   r2   r-   )ri   r   r   set[str] | Noner1   r3   )r4   r   r   r   r5   keysrt   )rL   r   r   r   r*   ri   
def_schemar   r   r   r   s    ```   @@@@r   _single_pass_optimizer     s   d ,*E %I4?5 ::gD (,"'CC$C  C 	C
 C 
C CL vF d$(JJL HjzHPTU %1	 	4 TYY[)Hx(" *
 JJw%Mr   c                    |rt        |       } t        |       } |xs g D ]  }t        | |      }  t        | ||d      } | S )a  
    Compress and optimize a JSON schema for MCP compatibility.

    Args:
        schema: The schema to compress
        prune_params: List of parameter names to remove from properties
        prune_additional_properties: Whether to remove additionalProperties: false.
            Defaults to False to maintain MCP client compatibility, as some clients
            (e.g., Claude) require additionalProperties: false for strict validation.
        prune_titles: Whether to remove title fields from the schema
        dereference: Whether to dereference $ref by inlining definitions.
            Defaults to False; dereferencing is typically handled by
            middleware at serve-time instead.
    )rv   T)r   r   r   )r_   rZ   ry   r   )rL   prune_paramsr   r   dereferencerv   s         r   compress_schemar     s\    * !&) f%F ##fE2 $
 #!$?	F Mr   )r*   dict[str, Any]r1   r3   )r   r   r1   r   )rL   r   rM   r   r1   r   )rL   r   r1   r   r-   )
rf   r   r]   r   r*   r   rg   r   r1   r   )rL   r   rv   r   r1   r   )FFT)
rL   r   r   r3   r   r3   r   r3   r1   r   )NFFF)rL   r   r   zlist[str] | Noner   r3   r   r3   r   r3   r1   r   )
__future__r   rQ   collectionsr   typingr   jsonrefr   r   r9   r<   rG   rN   rF   r_   r[   rZ   ry   	frozensetr   r   r   r   r   r   r   r   r#   r   r   <module>r      ss   "  #  .,O^(:>M(h  $	@@@ @ 	@
 	@F2j0 	    QR   	  " $ !!KL 
 (-	mmm "&m 	m
 md &*(-(("( "&( 	(
 ( (r   