@@ -114,20 +114,6 @@ class FileUrl(ABC):
114114
115115 _ : KW_ONLY
116116
117- identifier : str
118- """The identifier of the file, such as a unique ID. generating one from the url if not explicitly set.
119-
120- This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument,
121- and the tool can look up the file in question by iterating over the message history and finding the matching `FileUrl`.
122-
123- This identifier is only automatically passed to the model when the `FileUrl` is returned by a tool.
124- If you're passing the `FileUrl` as a user message, it's up to you to include a separate text part with the identifier,
125- e.g. "This is file <identifier>:" preceding the `FileUrl`.
126-
127- It's also included in inline-text delimiters for providers that require inlining text documents, so the model can
128- distinguish multiple files.
129- """
130-
131117 force_download : bool = False
132118 """For OpenAI and Google APIs it:
133119
@@ -147,27 +133,48 @@ class FileUrl(ABC):
147133 compare = False , default = None
148134 )
149135
136+ _identifier : Annotated [str | None , pydantic .Field (alias = 'identifier' , default = None , exclude = True )] = field (
137+ compare = False , default = None
138+ )
139+
150140 def __init__ (
151141 self ,
152142 url : str ,
153143 * ,
154- force_download : bool = False ,
155- vendor_metadata : dict [str , Any ] | None = None ,
156144 media_type : str | None = None ,
157145 identifier : str | None = None ,
146+ force_download : bool = False ,
147+ vendor_metadata : dict [str , Any ] | None = None ,
158148 ) -> None :
159149 self .url = url
150+ self ._media_type = media_type
151+ self ._identifier = identifier
160152 self .force_download = force_download
161153 self .vendor_metadata = vendor_metadata
162- self ._media_type = media_type
163- self .identifier = identifier or _multi_modal_content_identifier (url )
164154
165155 @pydantic .computed_field
166156 @property
167157 def media_type (self ) -> str :
168158 """Return the media type of the file, based on the URL or the provided `media_type`."""
169159 return self ._media_type or self ._infer_media_type ()
170160
161+ @pydantic .computed_field
162+ @property
163+ def identifier (self ) -> str :
164+ """The identifier of the file, such as a unique ID.
165+
166+ This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument,
167+ and the tool can look up the file in question by iterating over the message history and finding the matching `FileUrl`.
168+
169+ This identifier is only automatically passed to the model when the `FileUrl` is returned by a tool.
170+ If you're passing the `FileUrl` as a user message, it's up to you to include a separate text part with the identifier,
171+ e.g. "This is file <identifier>:" preceding the `FileUrl`.
172+
173+ It's also included in inline-text delimiters for providers that require inlining text documents, so the model can
174+ distinguish multiple files.
175+ """
176+ return self ._identifier or _multi_modal_content_identifier (self .url )
177+
171178 @abstractmethod
172179 def _infer_media_type (self ) -> str :
173180 """Infer the media type of the file based on the URL."""
@@ -198,20 +205,21 @@ def __init__(
198205 self ,
199206 url : str ,
200207 * ,
208+ media_type : str | None = None ,
209+ identifier : str | None = None ,
201210 force_download : bool = False ,
202211 vendor_metadata : dict [str , Any ] | None = None ,
203- media_type : str | None = None ,
204212 kind : Literal ['video-url' ] = 'video-url' ,
205- identifier : str | None = None ,
206213 # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
207214 _media_type : str | None = None ,
215+ _identifier : str | None = None ,
208216 ) -> None :
209217 super ().__init__ (
210218 url = url ,
211219 force_download = force_download ,
212220 vendor_metadata = vendor_metadata ,
213221 media_type = media_type or _media_type ,
214- identifier = identifier ,
222+ identifier = identifier or _identifier ,
215223 )
216224 self .kind = kind
217225
@@ -273,20 +281,21 @@ def __init__(
273281 self ,
274282 url : str ,
275283 * ,
284+ media_type : str | None = None ,
285+ identifier : str | None = None ,
276286 force_download : bool = False ,
277287 vendor_metadata : dict [str , Any ] | None = None ,
278- media_type : str | None = None ,
279288 kind : Literal ['audio-url' ] = 'audio-url' ,
280- identifier : str | None = None ,
281289 # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
282290 _media_type : str | None = None ,
291+ _identifier : str | None = None ,
283292 ) -> None :
284293 super ().__init__ (
285294 url = url ,
286295 force_download = force_download ,
287296 vendor_metadata = vendor_metadata ,
288297 media_type = media_type or _media_type ,
289- identifier = identifier ,
298+ identifier = identifier or _identifier ,
290299 )
291300 self .kind = kind
292301
@@ -335,20 +344,21 @@ def __init__(
335344 self ,
336345 url : str ,
337346 * ,
347+ media_type : str | None = None ,
348+ identifier : str | None = None ,
338349 force_download : bool = False ,
339350 vendor_metadata : dict [str , Any ] | None = None ,
340- media_type : str | None = None ,
341351 kind : Literal ['image-url' ] = 'image-url' ,
342- identifier : str | None = None ,
343352 # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
344353 _media_type : str | None = None ,
354+ _identifier : str | None = None ,
345355 ) -> None :
346356 super ().__init__ (
347357 url = url ,
348358 force_download = force_download ,
349359 vendor_metadata = vendor_metadata ,
350360 media_type = media_type or _media_type ,
351- identifier = identifier ,
361+ identifier = identifier or _identifier ,
352362 )
353363 self .kind = kind
354364
@@ -392,20 +402,21 @@ def __init__(
392402 self ,
393403 url : str ,
394404 * ,
405+ media_type : str | None = None ,
406+ identifier : str | None = None ,
395407 force_download : bool = False ,
396408 vendor_metadata : dict [str , Any ] | None = None ,
397- media_type : str | None = None ,
398409 kind : Literal ['document-url' ] = 'document-url' ,
399- identifier : str | None = None ,
400410 # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
401411 _media_type : str | None = None ,
412+ _identifier : str | None = None ,
402413 ) -> None :
403414 super ().__init__ (
404415 url = url ,
405416 force_download = force_download ,
406417 vendor_metadata = vendor_metadata ,
407418 media_type = media_type or _media_type ,
408- identifier = identifier ,
419+ identifier = identifier or _identifier ,
409420 )
410421 self .kind = kind
411422
@@ -460,16 +471,6 @@ class BinaryContent:
460471 media_type : AudioMediaType | ImageMediaType | DocumentMediaType | str
461472 """The media type of the binary data."""
462473
463- identifier : str
464- """Identifier for the binary content, such as a unique ID.
465- This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument,
466- and the tool can look up the file in question by iterating over the message history and finding the matching `BinaryContent`.
467-
468- This identifier is only automatically passed to the model when the `BinaryContent` is returned by a tool.
469- If you're passing the `BinaryContent` as a user message, it's up to you to include a separate text part with the identifier,
470- e.g. "This is file <identifier>:" preceding the `BinaryContent`.
471- """
472-
473474 vendor_metadata : dict [str , Any ] | None = None
474475 """Vendor-specific metadata for the file.
475476
@@ -478,6 +479,10 @@ class BinaryContent:
478479 - `OpenAIChatModel`, `OpenAIResponsesModel`: `BinaryContent.vendor_metadata['detail']` is used as `detail` setting for images
479480 """
480481
482+ _identifier : Annotated [str | None , pydantic .Field (alias = 'identifier' , default = None , exclude = True )] = field (
483+ compare = False , default = None
484+ )
485+
481486 kind : Literal ['binary' ] = 'binary'
482487 """Type identifier, this is available on all parts as a discriminator."""
483488
@@ -489,10 +494,12 @@ def __init__(
489494 identifier : str | None = None ,
490495 vendor_metadata : dict [str , Any ] | None = None ,
491496 kind : Literal ['binary' ] = 'binary' ,
497+ # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
498+ _identifier : str | None = None ,
492499 ) -> None :
493500 self .data = data
494501 self .media_type = media_type
495- self .identifier = identifier or _multi_modal_content_identifier ( data )
502+ self ._identifier = identifier or _identifier
496503 self .vendor_metadata = vendor_metadata
497504 self .kind = kind
498505
@@ -518,6 +525,23 @@ def from_data_uri(cls, data_uri: str) -> Self:
518525 media_type , data = data_uri [len (prefix ) :].split (';base64,' , 1 )
519526 return cls (data = base64 .b64decode (data ), media_type = media_type )
520527
528+ @pydantic .computed_field
529+ @property
530+ def identifier (self ) -> str :
531+ """Identifier for the binary content, such as a unique ID.
532+
533+ This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument,
534+ and the tool can look up the file in question by iterating over the message history and finding the matching `BinaryContent`.
535+
536+ This identifier is only automatically passed to the model when the `BinaryContent` is returned by a tool.
537+ If you're passing the `BinaryContent` as a user message, it's up to you to include a separate text part with the identifier,
538+ e.g. "This is file <identifier>:" preceding the `BinaryContent`.
539+
540+ It's also included in inline-text delimiters for providers that require inlining text documents, so the model can
541+ distinguish multiple files.
542+ """
543+ return self ._identifier or _multi_modal_content_identifier (self .data )
544+
521545 @property
522546 def data_uri (self ) -> str :
523547 """Convert the `BinaryContent` to a data URI."""
0 commit comments