o
    2h	o                     @   s8  d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZ dd	lmZ e d
kr?ddlmZ n>e dkrLddlmZ n1e dkrYddlmZ n$e dkrfddlmZ ne dkrsddlmZ n
ede  ddZedG dd deZ	d"ddZd#ddZd d! ZdS )$z9Library for exporting SavedModel for Keras models/layers.    )backend)layers)tree)keras_export)get_input_signature)make_tf_tensor_spec)io_utils)
tensorflowr	   )TFExportArchivejax)JaxExportArchivetorch)TorchExportArchivenumpy)NumpyExportArchiveopenvino)OpenvinoExportArchivez	Backend 'z' must implement ExportArchive.servezkeras.export.ExportArchivec                       s   e Zd ZdZ fddZedd Zedd Zedd	 Zd
d Z	d fdd	Z
 fddZdd ZdddZdd Zdd Zdd Zdd Z  ZS ) ExportArchivea	  ExportArchive is used to write SavedModel artifacts (e.g. for inference).

    If you have a Keras model or layer that you want to export as SavedModel for
    serving (e.g. via TensorFlow-Serving), you can use `ExportArchive`
    to configure the different serving endpoints you need to make available,
    as well as their signatures. Simply instantiate an `ExportArchive`,
    use `track()` to register the layer(s) or model(s) to be used,
    then use the `add_endpoint()` method to register a new serving endpoint.
    When done, use the `write_out()` method to save the artifact.

    The resulting artifact is a SavedModel and can be reloaded via
    `tf.saved_model.load`.

    Examples:

    Here's how to export a model for inference.

    ```python
    export_archive = ExportArchive()
    export_archive.track(model)
    export_archive.add_endpoint(
        name="serve",
        fn=model.call,
        input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
    )
    export_archive.write_out("path/to/location")

    # Elsewhere, we can reload the artifact and serve it.
    # The endpoint we added is available as a method:
    serving_model = tf.saved_model.load("path/to/location")
    outputs = serving_model.serve(inputs)
    ```

    Here's how to export a model with one endpoint for inference and one
    endpoint for a training-mode forward pass (e.g. with dropout on).

    ```python
    export_archive = ExportArchive()
    export_archive.track(model)
    export_archive.add_endpoint(
        name="call_inference",
        fn=lambda x: model.call(x, training=False),
        input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
    )
    export_archive.add_endpoint(
        name="call_training",
        fn=lambda x: model.call(x, training=True),
        input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
    )
    export_archive.write_out("path/to/location")
    ```

    **Note on resource tracking:**

    `ExportArchive` is able to automatically track all `keras.Variables` used
    by its endpoints, so most of the time calling `.track(model)`
    is not strictly required. However, if your model uses lookup layers such
    as `IntegerLookup`, `StringLookup`, or `TextVectorization`,
    it will need to be tracked explicitly via `.track(model)`.

    Explicit tracking is also required if you need to be able to access
    the properties `variables`, `trainable_variables`, or
    `non_trainable_variables` on the revived archive.
    c                    s\   t    t dvrtdg | _i | _tj| _tj	j
 | _g | j_g | j_g | j_d S )N)r	   r   r   zK`ExportArchive` is only compatible with TensorFlow, JAX and Torch backends.)super__init__r   NotImplementedError_endpoint_names_endpoint_signaturestf__version__tensorflow_version__internal__trackingAutoTrackable_tf_trackable	variablestrainable_variablesnon_trainable_variablesself	__class__ W/var/www/html/chatgem/venv/lib/python3.10/site-packages/keras/src/export/saved_model.pyr   l   s   
zExportArchive.__init__c                 C      | j jS N)r    r!   r$   r(   r(   r)   r!   }      zExportArchive.variablesc                 C   r*   r+   )r    r"   r$   r(   r(   r)   r"      r,   z!ExportArchive.trainable_variablesc                 C   r*   r+   )r    r#   r$   r(   r(   r)   r#      r,   z%ExportArchive.non_trainable_variablesc                 C   s   t |tjr|jstdt |tjjjr#t	| dsg | _
| j
| t |tjr0| | dS t |tjjjsEtd| dt| ddS )a+  Track the variables (of a layer or model) and other assets.

        By default, all variables used by an endpoint function are automatically
        tracked when you call `add_endpoint()`. However, non-variables assets
        such as lookup tables need to be tracked manually. Note that lookup
        tables used by built-in Keras layers (`TextVectorization`,
        `IntegerLookup`, `StringLookup`) are automatically tracked by
        `add_endpoint()`.

        Args:
            resource: A layer, model or a TensorFlow trackable resource.
        JThe layer provided has not yet been built. It must be built before export._trackedzoInvalid resource type. Expected a Keras `Layer` or `Model` or a TensorFlow `Trackable` object. Received object z
 of type 'z'. N)
isinstancer   Layerbuilt
ValueErrorr   r   r   	Trackablehasattrr.   append_track_layertype)r%   resourcer(   r(   r)   track   s&   
zExportArchive.trackNc                    s   || j v rtd| dt dkr$d|v sd|v r$tdt  |du rRt|tjjjr?| s<td| d	|}ntd
t	| j
|| | j | |S tt|}t j|||fi |}|| j|< t	| j
|| | j | |S )a  Register a new serving endpoint.

        Args:
            name: `str`. The name of the endpoint.
            fn: A callable. It should only leverage resources
                (e.g. `keras.Variable` objects or `tf.lookup.StaticHashTable`
                objects) that are available on the models/layers tracked by the
                `ExportArchive` (you can call `.track(model)` to track a new
                model).
                The shape and dtype of the inputs to the function must be
                known. For that purpose, you can either 1) make sure that `fn`
                is a `tf.function` that has been called at least once, or 2)
                provide an `input_signature` argument that specifies the shape
                and dtype of the inputs (see below).
            input_signature: Optional. Specifies the shape and dtype of `fn`.
                Can be a structure of `keras.InputSpec`, `tf.TensorSpec`,
                `backend.KerasTensor`, or backend tensor (see below for an
                example showing a `Functional` model with 2 input arguments). If
                not provided, `fn` must be a `tf.function` that has been called
                at least once. Defaults to `None`.
            **kwargs: Additional keyword arguments:
                - Specific to the JAX backend:
                    - `is_static`: Optional `bool`. Indicates whether `fn` is
                        static. Set to `False` if `fn` involves state updates
                        (e.g., RNG seeds).
                    - `jax2tf_kwargs`: Optional `dict`. Arguments for
                        `jax2tf.convert`. See [`jax2tf.convert`](
                            https://github.com/google/jax/blob/main/jax/experimental/jax2tf/README.md).
                        If `native_serialization` and `polymorphic_shapes` are
                        not provided, they are automatically computed.

        Returns:
            The `tf.function` wrapping `fn` that was added to the archive.

        Example:

        Adding an endpoint using the `input_signature` argument when the
        model has a single input argument:

        ```python
        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
        )
        ```

        Adding an endpoint using the `input_signature` argument when the
        model has two positional input arguments:

        ```python
        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[
                keras.InputSpec(shape=(None, 3), dtype="float32"),
                keras.InputSpec(shape=(None, 4), dtype="float32"),
            ],
        )
        ```

        Adding an endpoint using the `input_signature` argument when the
        model has one input argument that is a list of 2 tensors (e.g.
        a Functional model with 2 inputs):

        ```python
        model = keras.Model(inputs=[x1, x2], outputs=outputs)

        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[
                [
                    keras.InputSpec(shape=(None, 3), dtype="float32"),
                    keras.InputSpec(shape=(None, 4), dtype="float32"),
                ],
            ],
        )
        ```

        This also works with dictionary inputs:

        ```python
        model = keras.Model(inputs={"x1": x1, "x2": x2}, outputs=outputs)

        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[
                {
                    "x1": keras.InputSpec(shape=(None, 3), dtype="float32"),
                    "x2": keras.InputSpec(shape=(None, 4), dtype="float32"),
                },
            ],
        )
        ```

        Adding an endpoint that is a `tf.function`:

        ```python
        @tf.function()
        def serving_fn(x):
            return model(x)

        # The function must be traced, i.e. it must be called at least once.
        serving_fn(tf.random.normal(shape=(2, 3)))

        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(name="serve", fn=serving_fn)
        ```

        Combining a model with some TensorFlow preprocessing, which can use
        TensorFlow resources:

        ```python
        lookup_table = tf.lookup.StaticHashTable(initializer, default_value=0.0)

        export_archive = ExportArchive()
        model_fn = export_archive.track_and_add_endpoint(
            "model_fn",
            model,
            input_signature=[tf.TensorSpec(shape=(None, 5), dtype=tf.float32)],
        )
        export_archive.track(lookup_table)

        @tf.function()
        def serving_fn(x):
            x = lookup_table.lookup(x)
            return model_fn(x)

        export_archive.add_endpoint(name="serve", fn=serving_fn)
        ```
        Endpoint name '' is already taken.r   jax2tf_kwargs	is_staticZ'jax2tf_kwargs' and 'is_static' are only supported with the jax backend. Current backend: NzThe provided tf.function 'z' has never been called. To specify the expected shape and dtype of the function's arguments, you must either provide a function that has been called at least once, or alternatively pass an `input_signature` argument in `add_endpoint()`.ao  If the `fn` argument provided is not a `tf.function`, you must provide an `input_signature` argument to specify the shape and dtype of the function arguments. Example:

export_archive.add_endpoint(
    name='call',
    fn=model.call,
    input_signature=[
        keras.InputSpec(
            shape=(None, 224, 224, 3),
            dtype='float32',
        )
    ],
))r   r2   r   r/   r   typesexperimentalGenericFunction_list_all_concrete_functionssetattrr    r5   r   map_structurer   r   add_endpointr   )r%   namefninput_signaturekwargsdecorated_fnr&   r(   r)   rE      s>    

	
zExportArchive.add_endpointc                    s   || j v rtd| dt|tjs td| dt| d|js'tdt dkr>d|v s5d	|v r>td
t  t	t
|}ttdsZ| | | j||j|fi |S t j|||fi |}|| j|< t| j|| | j | |S )a  Track the variables and register a new serving endpoint.

        This function combines the functionality of `track` and `add_endpoint`.
        It tracks the variables of the `resource` (either a layer or a model)
        and registers a serving endpoint using `resource.__call__`.

        Args:
            name: `str`. The name of the endpoint.
            resource: A trackable Keras resource, such as a layer or model.
            input_signature: Optional. Specifies the shape and dtype of `fn`.
                Can be a structure of `keras.InputSpec`, `tf.TensorSpec`,
                `backend.KerasTensor`, or backend tensor (see below for an
                example showing a `Functional` model with 2 input arguments). If
                not provided, `fn` must be a `tf.function` that has been called
                at least once. Defaults to `None`.
            **kwargs: Additional keyword arguments:
                - Specific to the JAX backend:
                    - `is_static`: Optional `bool`. Indicates whether `fn` is
                        static. Set to `False` if `fn` involves state updates
                        (e.g., RNG seeds).
                    - `jax2tf_kwargs`: Optional `dict`. Arguments for
                        `jax2tf.convert`. See [`jax2tf.convert`](
                            https://github.com/google/jax/blob/main/jax/experimental/jax2tf/README.md).
                        If `native_serialization` and `polymorphic_shapes` are
                        not provided, they are automatically computed.

        r:   r;   z^Invalid resource type. Expected an instance of a Keras `Layer` or `Model`. Received: resource=z
 (of type )r-   r   r<   r=   r>   track_and_add_endpoint)r   r2   r/   r   r0   r7   r1   r   r   rD   r   r4   BackendExportArchiver9   rE   __call__r   rL   r   rC   r    r5   )r%   rF   r8   rH   rI   rJ   r&   r(   r)   rL   r  sP   



z$ExportArchive.track_and_add_endpointc                 C   s   t |tttfstdt| dtdd |D s+tdttdd |D  t dkr;t	t
| j|}t| j|t| dS )	a  Register a set of variables to be retrieved after reloading.

        Arguments:
            name: The string name for the collection.
            variables: A tuple/list/set of `keras.Variable` instances.

        Example:

        ```python
        export_archive = ExportArchive()
        export_archive.track(model)
        # Register an endpoint
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
        )
        # Save a variable collection
        export_archive.add_variable_collection(
            name="optimizer_variables", variables=model.optimizer.variables)
        export_archive.write_out("path/to/location")

        # Reload the object
        revived_object = tf.saved_model.load("path/to/location")
        # Retrieve the variables
        optimizer_variables = revived_object.optimizer_variables
        ```
        zNExpected `variables` to be a list/tuple/set. Received instead object of type 'z'.c                 s   s"    | ]}t |tjtjfV  qd S r+   )r/   r   Variabler   .0vr(   r(   r)   	<genexpr>  s    
z8ExportArchive.add_variable_collection.<locals>.<genexpr>zgExpected all elements in `variables` to be `tf.Variable` instances. Found instead the following types: c                 s   s    | ]}t |V  qd S r+   )r7   rP   r(   r(   r)   rS     s    r   N)r/   listtuplesetr2   r7   allr   r   flattenrD   _convert_to_tf_variablerC   r    )r%   rF   r!   r(   r(   r)   add_variable_collection  s&   z%ExportArchive.add_variable_collectionTc                    s    j std   i } j D ]	} |||< qd j vr)  j d |d< tjj j|||d rQd fdd j D }t	
d| d	|  d
S d
S )a"  Write the corresponding SavedModel to disk.

        Arguments:
            filepath: `str` or `pathlib.Path` object.
                Path where to save the artifact.
            options: `tf.saved_model.SaveOptions` object that specifies
                SavedModel saving options.
            verbose: whether to print all the variables of an
                exported SavedModel.

        **Note on TF-Serving**: all endpoints registered via `add_endpoint()`
        are made visible for TF-Serving in the SavedModel artifact. In addition,
        the first endpoint registered is made visible under the alias
        `"serving_default"` (unless an endpoint with the name
        `"serving_default"` was already registered manually),
        since TF-Serving requires this endpoint to be set.
        z4No endpoints have been set yet. Call add_endpoint().serving_defaultr   )options
signaturesz

c                 3   s&    | ]}t t j||d V  qdS )verboseN)_print_signaturegetattrr    rQ   rF   r%   r_   r(   r)   rS     s    
z*ExportArchive.write_out.<locals>.<genexpr>zSaved artifact at 'z+'. The following endpoints are available:

N)r   r2   _filter_and_track_resources_get_concrete_fnr   saved_modelsaver    joinr   	print_msg)r%   filepathr\   r_   r]   rF   	endpointsr(   rc   r)   	write_out  s8   

zExportArchive.write_outc                 C   s@   t |tjstd| dt| dtj|j|j|j|j	dS )NzL`backend_variable` must be a `backend.Variable`. Recevied: backend_variable=z
 of type (rK   )dtype	trainablerF   )
r/   r   rO   	TypeErrorr7   r   valuerm   rn   rF   )r%   backend_variabler(   r(   r)   rY     s   z%ExportArchive._convert_to_tf_variablec                 C   s8   || j v rt| j|S t| j|d}t| d S )z&Workaround for some SavedModel quirks.rf   r   )r   ra   r    _trackable_childrenrT   values)r%   endpointtracesr(   r(   r)   re   ,  s   
zExportArchive._get_concrete_fnc                    s    fdd j D }t|S )Nc                       g | ]}  |qS r(   re   rb   r$   r(   r)   
<listcomp>7      zBExportArchive._get_variables_used_by_endpoints.<locals>.<listcomp>)r   _list_variables_used_by_fns)r%   fnsr(   r$   r)    _get_variables_used_by_endpoints6  s   z.ExportArchive._get_variables_used_by_endpointsc                    s    fdd j D }t|\}}t||  j_g  j_ddlm} t drF j	D ]}t
j| }|D ]}t||rD jj| q6q*dS dS )zBTrack resources used by endpoints / referenced in `track()` calls.c                    rv   r(   rw   rb   r$   r(   r)   rx   =  ry   z=ExportArchive._filter_and_track_resources.<locals>.<listcomp>r   )TrackableResourcer.   N)r   rz   rT   r    _all_variables_misc_assets#tensorflow.saved_model.experimentalr}   r4   r.   r   trainTrackableViewdescendantsr/   r5   )r%   r{   tvsntvsr}   rootr   	trackabler(   r$   r)   rd   :  s   


z)ExportArchive._filter_and_track_resourcesr+   )NT)__name__
__module____qualname____doc__r   propertyr!   r"   r#   r9   rE   rL   rZ   rl   rY   re   r|   rd   __classcell__r(   r(   r&   r)   r   )   s&    A


$ FD
26
r   Nc                 K   sJ   |du rd}t  }|du rt| }|jt| |fi | |j||d dS )a
  Export the model as a TensorFlow SavedModel artifact for inference.

    This method lets you export a model to a lightweight SavedModel artifact
    that contains the model's forward pass only (its `call()` method)
    and can be served via e.g. TensorFlow Serving. The forward pass is
    registered under the name `serve()` (see example below).

    The original code of the model (including any custom layers you may
    have used) is *no longer* necessary to reload the artifact -- it is
    entirely standalone.

    Args:
        filepath: `str` or `pathlib.Path` object. The path to save the artifact.
        verbose: `bool`. Whether to print a message during export. Defaults to
            `None`, which uses the default value set by different backends and
            formats.
        input_signature: Optional. Specifies the shape and dtype of the model
            inputs. Can be a structure of `keras.InputSpec`, `tf.TensorSpec`,
            `backend.KerasTensor`, or backend tensor. If not provided, it will
            be automatically computed. Defaults to `None`.
        **kwargs: Additional keyword arguments:
            - Specific to the JAX backend:
                - `is_static`: Optional `bool`. Indicates whether `fn` is
                    static. Set to `False` if `fn` involves state updates
                    (e.g., RNG seeds).
                - `jax2tf_kwargs`: Optional `dict`. Arguments for
                    `jax2tf.convert`. See [`jax2tf.convert`](
                        https://github.com/google/jax/blob/main/jax/experimental/jax2tf/README.md).
                    If `native_serialization` and `polymorphic_shapes` are not
                    provided, they are automatically computed.

    **Note:** This feature is currently supported only with TensorFlow, JAX and
    Torch backends. Support for the Torch backend is experimental.

    **Note:** The dynamic shape feature is not yet supported with Torch
    backend. As a result, you must fully define the shapes of the inputs using
    `input_signature`. If `input_signature` is not provided, all instances of
    `None` (such as the batch size) will be replaced with `1`.

    Example:

    ```python
    # Export the model as a TensorFlow SavedModel artifact
    model.export("path/to/location", format="tf_saved_model")

    # Load the artifact in a different process/environment
    reloaded_artifact = tf.saved_model.load("path/to/location")
    predictions = reloaded_artifact.serve(input_data)
    ```

    If you would like to customize your serving endpoints, you can
    use the lower-level `keras.export.ExportArchive` class. The
    `export()` method relies on `ExportArchive` internally.
    NTr^   )r   r   rL   DEFAULT_ENDPOINT_NAMErl   )modelrj   r_   rH   rI   export_archiver(   r(   r)   export_saved_modelN  s   9r   Tc                 C   sJ   |   d }|j|d}|d}d| dg|dd   }d|}|S )Nr   r^   
z* Endpoint ''   )rB   pretty_printed_signaturesplitrh   )rG   rF   r_   concrete_fnpprinted_signaturelinesrt   r(   r(   r)   r`     s   

r`   c           	      C   s   g }g }t  }t  }| D ]T}t|dr|j}nt|dr"| g}n|g}|D ]8}|jD ]}t||vr@|| |t| q,|jD ]}t||vr^t||vr^|| |t| qDq'q||fS )Nconcrete_functionsget_concrete_function)	rV   r4   r   r   r"   idr5   addr!   )	r{   r"   r#   trainable_variables_idsnon_trainable_variables_idsrG   r   r   rR   r(   r(   r)   rz     s2   





rz   )NN)T) r   	keras.srcr   r   r   keras.src.api_exportr   keras.src.export.export_utilsr   r   keras.src.utilsr   keras.src.utils.module_utilsr	   r   #keras.src.backend.tensorflow.exportr
   rM   keras.src.backend.jax.exportr   keras.src.backend.torch.exportr   keras.src.backend.numpy.exportr   !keras.src.backend.openvino.exportr   RuntimeErrorr   r   r   r`   rz   r(   r(   r(   r)   <module>   sB        )

E	