U
    DAf,                  	   @   s   d dl Z d dlZd dlmZ d dlmZmZmZmZm	Z	 d dl
Z
d dlmZmZ d dlmZ d dlmZ eeZedZd dlZW 5 Q R X eG dd	 d	ZdS )
    N)Path)AnyDictListOptionalUnion)	componentlogging)
ByteStream)
LazyImportzRun 'pip install jsonref'c                   @   s   e Zd ZdZdZdd Zejee	e
ef  ee	e
ef  deee
eef  e	e
ef dddZe	e
ef ee	e
ef  d	d
dZe	e
ef ee	e
ef  dddZde	e
ef eee
  e	e
ef dddZe
e	e
ef dddZdS )OpenAPIServiceToFunctionsa  
    Converts OpenAPI service definitions to a format suitable for OpenAI function calling.

    The definition must respect OpenAPI specification 3.0.0 or higher.
    It can be specified in JSON or YAML format.
    Each function must have:
        - unique operationId
        - description
        - requestBody and/or parameters
        - schema for the requestBody and/or parameters
    For more details on OpenAPI specification see the [official documentation](https://github.com/OAI/OpenAPI-Specification).
    For more details on OpenAI function calling see the [official documentation](https://platform.openai.com/docs/guides/function-calling).

    Usage example:
    ```python
    from haystack.components.converters import OpenAPIServiceToFunctions

    converter = OpenAPIServiceToFunctions()
    result = converter.run(sources=["path/to/openapi_definition.yaml"])
    assert result["functions"]
    ```
       c                 C   s   t   dS )z@
        Create an OpenAPIServiceToFunctions component.
        N)openapi_importscheck)self r   T/tmp/pip-unpacked-wheel-z752163x/haystack/components/converters/openapi_functions.py__init__1   s    z"OpenAPIServiceToFunctions.__init__	functionsZopenapi_specs)sourcesreturnc           
      C   sZ  g }g }|D ]0}d}t |ttfrtj|rz"t|d}| }W 5 Q R X W q tk
r } zt	j
d||d W 5 d}~X Y qX qt	
d|  n>t |tr|jd}|st	j
d|d nt	j
d	t|d
 q|rz,| |}| |}	||	 || W q tk
r< } zt	jd||d W 5 d}~X Y qX q|sPt	
d ||dS )a  
        Converts OpenAPI definitions in OpenAI function calling format.

        :param sources:
            File paths or ByteStream objects of OpenAPI definitions (in JSON or YAML format).

        :returns:
            A dictionary with the following keys:
            - functions: Function definitions in JSON object format
            - openapi_specs: OpenAPI specs in JSON/YAML object format with resolved references

        :raises RuntimeError:
            If the OpenAPI definitions cannot be downloaded or processed.
        :raises ValueError:
            If the source type is not recognized or no functions are found in the OpenAPI definitions.
        NrzAIO error reading OpenAPI specification file: {source}. Error: {e})sourceez&OpenAPI specification file not found: zutf-8zFInvalid OpenAPI specification content provided: {openapi_spec_content})openapi_spec_contentzKInvalid source type {source}. Only str, Path, and ByteStream are supported.)r   zDError processing OpenAPI specification from source {source}: {error})r   errorzYNo OpenAI function definitions extracted from the provided OpenAPI specification sources.r   )
isinstancestrr   ospathexistsopenreadIOErrorloggerwarningr
   datadecodetype_parse_openapi_spec_openapi_to_functionsextendappend	Exceptionr   )
r   r   Zall_extracted_fc_definitionsZall_openapi_specsr   r   fr   service_openapi_specr   r   r   r   run7   sV    
  
 


  
zOpenAPIServiceToFunctions.run)r0   r   c                 C   s   | d}|std| t|dd }|tjk rPtd| dtj dg }|d  D ]*}| D ]}| |}|rl|| qlq`|S )aE  
        OpenAPI to OpenAI function conversion.

        Extracts functions from the OpenAPI specification of the service and converts them into a format
        suitable for OpenAI function calling.

        :param service_openapi_spec: The OpenAPI specification from which functions are to be extracted.
        :type service_openapi_spec: Dict[str, Any]
        :return: A list of dictionaries, each representing a function. Each dictionary includes the function's
                 name, description, and a schema of its parameters.
        :rtype: List[Dict[str, Any]]
        Zopenapiz>Invalid OpenAPI spec provided. Could not extract version from .r   zInvalid OpenAPI spec version z. Must be at least paths)	get
ValueErrorintsplitr   !MIN_REQUIRED_OPENAPI_SPEC_VERSIONvalues_parse_endpoint_specr-   )r   r0   spec_versionZservice_openapi_spec_versionr   r3   	path_specZfunction_dictr   r   r   r+   u   s    


z/OpenAPIServiceToFunctions._openapi_to_functions)resolved_specr   c           
         sh  t |tstd i S |d}|dp6|dd}di d}|di d	i d
i di }d|kr|d  D ]\}}| ||d |< qzd|kr|dg |d  |dg D ]n d kr|  d }dddg}	|	 fdd|	D  ||d  d <  ddr|dg 
 d  q|rR|rR|d rR|||dS tjd|d i S d S )NzAInvalid OpenAPI spec format provided. Could not extract function.ZoperationIddescriptionsummary object)r)   
propertiesZrequestBodycontentzapplication/jsonschemarB   required
parameterspatternenumc                    s    i | ]}  |r| | qS r   )r4   ).0keyparamr   r   
<dictcomp>   s     
  zBOpenAPIServiceToFunctions._parse_endpoint_spec.<locals>.<dictcomp>nameF)rN   r>   rF   zLInvalid OpenAPI spec format provided. Could not extract function from {spec})spec)r   dictr%   r&   r4   items_parse_property_attributes
setdefaultr,   updater-   )
r   r=   Zfunction_namer>   rD   Zreq_body_schema	prop_nameZprop_schemaZschema_dictZuseful_attributesr   rK   r   r:      s8    



"
 z.OpenAPIServiceToFunctions._parse_endpoint_specN)property_schemainclude_attributesr   c           	         s    pdddg | d}|r$d|ini } D ]}||kr,|| ||< q,|dkr| di } fdd| D }||d< d	|kr|d	 |d	< n$|d
kr| di }| |d< |S )a  
        Parses the attributes of a property schema.

        Recursively parses the attributes of a property schema, including nested objects and arrays,
        and includes specified attributes like description, pattern, etc.

        :param property_schema: The schema of the property to parse.
        :param include_attributes: The list of attributes to include in the parsed schema.
        :return: The parsed schema of the property including the specified attributes.
        r>   rG   rH   r)   rA   rB   c                    s   i | ]\}}| | qS r   )rR   )rI   rU   proprW   r   r   r   rM      s    zHOpenAPIServiceToFunctions._parse_property_attributes.<locals>.<dictcomp>rE   arrayrQ   )r4   rQ   rR   )	r   rV   rW   Zschema_typeZparsed_schemaattrrB   Zparsed_propertiesrQ   r   rY   r   rR      s$    
z4OpenAPIServiceToFunctions._parse_property_attributes)rC   r   c              
   C   s   d}zt |}t|W S  t jk
rP } z| dr@|W 5 d}~X Y nX zt|}W n$ tj	k
r   d}t
||Y nX t|S )z
        Parses OpenAPI specification content, supporting both JSON and YAML formats.

        :param content: The content of the OpenAPI specification.
        :return: The parsed OpenAPI specification.
        N){[zbFailed to parse the OpenAPI specification. The content does not appear to be valid JSON or YAML.

)jsonloadsjsonrefZreplace_refsJSONDecodeErrorstrip
startswithyamlZ	safe_loadZ	YAMLErrorRuntimeError)r   rC   Zopen_api_spec_contentZ
json_errorerror_messager   r   r   r*      s    
z-OpenAPIServiceToFunctions._parse_openapi_spec)N)__name__
__module____qualname____doc__r8   r   r   Zoutput_typesr   r   r   r   r   r   r
   r1   r+   r   r:   rR   r*   r   r   r   r   r      s   $(=$$$) 
 

'r   )r^   r   pathlibr   typingr   r   r   r   r   rd   Zhaystackr   r	   Z haystack.dataclasses.byte_streamr
   Zhaystack.lazy_importsr   	getLoggerrg   r%   r   r`   r   r   r   r   r   <module>   s   

