
    KiOB                        d dl mZ d dlmZ d dlmZ d dlmZmZ ddZ	dddZ
	 d	 	 	 	 	 	 	 	 	 ddZdd	Zdd
Z	 	 	 d	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 ddZy)    )annotations)defaultdict)Any)JsonRefErrorreplace_refsc                :   	 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        $ 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.

    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"}}}
    F)proxies	lazy_load$defs)	r   get_merge_ref_siblings
isinstancedictitems_strip_discriminator_mappingsr   resolve_root_ref)schemadereferenceddefsmergedkvs         i/home/jay/workspace/scripts/.codegraph-venv/lib/python3.12/site-packages/fastmcp/utilities/json_schema.pydereference_refsr   	   s    :( $FEUK zz'2&$V\4@&$''' l"-9-?-?-ARTQQ'\AqDRLR 	&l3 S  (  ''(s*   AB A=(A=-B =B BBc                @   |dkD  st        | t              syd| v r(t        | d   t              r| d   j                  dd       | j                         D ]I  }t        |t              rt	        ||dz          #t        |t
              s4|D ]  }t	        ||dz           K y)al  Remove discriminator.mapping entries whose values are $defs references.

    Pydantic emits discriminator.mapping with plain-string references like
    ``"#/$defs/Cat"`` that become dangling after $defs are removed by
    dereference_refs(). The oneOf/anyOf variants already carry their own
    const fields, so the mapping is redundant once refs are inlined.
    2   Ndiscriminatormapping   )r   r   popvaluesr   list)r   depthvalueitems       r   r   r   C   s     rzFD1& Z0G%N##It4 ?eT")%;t$ ?-dEAI>?	?    Nc           
     "   |
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
    N$ref)r(   r   #/$defs//F)strict)setr   r   r   str
startswithsplitr   updater"   minlenzip)originalr   r   visitedrefr   r   siblingsdef_namer   resultkeyr$   min_lenods                   r   r   r   W   s   * %(D!jt&DX6"C)1)9XAQFW=W1XH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
1  1dG4
 "# 	#
 I Y>
s   	FF!Fc                    d| v r`d| v r\d| vrX| d   }t        |t              rC|j                  d      r2|j                  d      d   }| d   }||v rt	        ||         }||d<   |S | S )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(   r   typer)   r*   r+   )r   r.   r/   r0   r   )r   r7   r9   r   resolveds        r   r   r      s}    0 Gv-&2FVnc3CNN:$>yy~b)H'?D4X/$(!Mr&   c                    | 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`.
    
propertiesNrequired)r   r    remove)r   parampropsremoveds       r   _prune_paramrI      sy     JJ|R(Eiit$G !F<

:r**z!!%(j!JJz"Mr&   c                  	
 sss| S t               	t        t              | j                  d      }	 	 	 d	 	 	 	 	 	 	 	 	 d		
fd
 
| d       ry|rw|j	                         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}
    r   Nc                    |dkD  ryt         t              rrn j                  d      }t        |t              rM|j	                  d      r<|j                  d      d   }|r	|   j                  |       nj                  |       r)d v r%t         fdd	D              r j                  d       
r$ j                  d
      du r j                  d
        j                         D ]E  \  }}|r|dk(  r|dv r&t        |t              r|D ]  } |||dz           8 |||dz          G yt         t              r D ]  } |||dz           yy)zATraverse schema tree, collecting $ref info and applying cleanups.r   Nr(   r)   r*   r+   titlec              3  &   K   | ]  }|v  
 y wN ).0r   nodes     r   	<genexpr>zD_single_pass_optimize.<locals>.traverse_and_clean.<locals>.<genexpr>0  s       Is   )r@   rC   r(   r   allOfoneOfanyOfrD   additionalPropertiesFr   )rS   rT   rU   r   )r#   )r   r   r   r.   r/   r0   appendaddanyr    r   r"   )rQ   current_def_nameskip_defs_sectionr#   r7   referenced_defr;   r$   r%   def_dependenciesprune_additional_properties
prune_defsprune_titles	root_refstraverse_and_cleans   `        r   rb   z1_single_pass_optimize.<locals>.traverse_and_clean  sp    2:dD!hhv&c3'CNN:,F%(YYs^B%7N'(8??@PQ "n5 4 	  HHW% ,HH34=/0 #jjl 	Q
U$ 55*UD:Q % T*41AQRST 'u.>eaiP	Q d# L"4)9KL $r&   T)r[   )rZ   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   r-   )r9   visitingreferencing_defsreferencing_defr]   is_def_usedra   s       r   rg   z*_single_pass_optimize.<locals>.is_def_used^  s{    9$  033HbA#"uH x' #xj0 (8 $O&h6;'<  $	$ r&   )NFr   )
rQ   objectrZ   z
str | Noner[   boolr#   intreturnNonerN   )r9   r.   rd   set[str] | Nonerk   ri   )r-   r   r"   r   r   keysr    )r   r`   r^   r_   r   r9   
def_schemar]   rg   ra   rb   s    ```   @@@@r   _single_pass_optimizerp      s   d ,*E %I4?5 ::gD (,"'	@L@L$@L  @L 	@L
 
@L @LF v6 d$(JJL 	F HjzHE	F	 	4 TYY[) 	#Hx("	#
 JJw%Mr&   c                z    |xs g D ]  }t        | |      }  |s|s|rt        | |||      } |rt        |       } | S )a  
    Remove the given parameters from the schema.

    Args:
        schema: The schema to compress
        prune_params: List of parameter names to remove from properties
        prune_defs: Whether to remove unused definitions
        prune_additional_properties: Whether to remove additionalProperties: false
        prune_titles: Whether to remove title fields from the schema
        dereference: Whether to inline $ref references for client compatibility
    )rF   )r`   r^   r_   )rI   rp   r   )r   prune_paramsr_   r^   r`   dereferencerF   s          r   compress_schemart     sX    ( # 3fE23 2j&%(C!	
 !&)Mr&   )r   dict[str, Any]rk   ru   )r   )r   r   r#   rj   rk   rl   rN   )
r5   r   r   r   r   ru   r6   rm   rk   r   )r   ru   rF   r.   rk   ru   )FFT)
r   ru   r`   ri   r^   ri   r_   ri   rk   ru   )NTTFF)r   ru   rr   zlist[str] | Noner_   ri   r^   ri   r`   ri   rs   ri   rk   ru   )
__future__r   collectionsr   typingr   jsonrefr   r   r   r   r   r   rI   rp   rt   rO   r&   r   <module>rz      s   " #  .7(t?0  $	@@@ @ 	@
 	@F$N. (-	jjj "&j 	j
 j^ &*(,$$"$ $ "&	$
 $ $ $r&   