diff --git a/minio/api.py b/minio/api.py index f46e40f7..7c438475 100644 --- a/minio/api.py +++ b/minio/api.py @@ -28,13 +28,14 @@ from __future__ import absolute_import, annotations import itertools +import json import os import tarfile from collections.abc import Iterable from datetime import datetime, timedelta from io import BytesIO from random import random -from typing import BinaryIO, Iterator, TextIO, Tuple, Union, cast +from typing import Any, BinaryIO, Iterator, TextIO, Tuple, Union, cast from urllib.parse import urlunsplit from xml.etree import ElementTree as ET @@ -50,7 +51,7 @@ from urllib3.util import Timeout -from . import __title__, __version__, time +from . import time from .commonconfig import (COPY, REPLACE, ComposeSource, CopySource, SnowballObject, Tags) from .credentials import Credentials, StaticProvider @@ -1145,7 +1146,7 @@ def fget_object( progress.set_meta(object_name=object_name, total_length=length) with open(tmp_file_path, "wb") as tmp_file: - for data in response.stream(amt=1024*1024): + for data in response.stream(amt=1024 * 1024): size = tmp_file.write(data) if progress: progress.update(size) @@ -1258,6 +1259,70 @@ def get_object( preload_content=False, ) + def prompt_object( + self, + bucket_name: str, + object_name: str, + prompt: str, + lambda_arn: str | None = None, + request_headers: DictType | None = None, + ssec: SseCustomerKey | None = None, + version_id: str | None = None, + **kwargs: Any | None, + ) -> BaseHTTPResponse: + """ + Prompt an object using natural language. + + :param bucket_name: Name of the bucket. + :param object_name: Object name in the bucket. + :param prompt: Prompt the Object to interact with the AI model. + request. + :param lambda_arn: Lambda ARN to use for prompt. + :param request_headers: Any additional headers to be added with POST + :param ssec: Server-side encryption customer key. + :param version_id: Version-ID of the object. + :param kwargs: Extra parameters for advanced usage. + :return: :class:`urllib3.response.BaseHTTPResponse` object. + + Example:: + # prompt an object. + response = None + try: + response = client.get_object( + "my-bucket", "my-object", + "Describe the object for me") + # Read data from response. + finally: + if response: + response.close() + response.release_conn() + """ + check_bucket_name(bucket_name, s3_check=self._base_url.is_aws_host) + check_object_name(object_name) + check_ssec(ssec) + + headers = cast(DictType, ssec.headers() if ssec else {}) + headers.update(request_headers or {}) + + extra_query_params = {"lambdaArn": lambda_arn or ""} + + if version_id: + extra_query_params["versionId"] = version_id + + prompt_body = kwargs + prompt_body["prompt"] = prompt + + body = json.dumps(prompt_body) + return self._execute( + "POST", + bucket_name, + object_name, + headers=cast(DictType, headers), + query_params=cast(DictType, extra_query_params), + body=body.encode(), + preload_content=False, + ) + def copy_object( self, bucket_name: str, @@ -1592,11 +1657,11 @@ def compose_object( part_number += 1 if src.length is not None: headers["x-amz-copy-source-range"] = ( - f"bytes={offset}-{offset+src.length-1}" + f"bytes={offset}-{offset + src.length - 1}" ) elif src.offset is not None: headers["x-amz-copy-source-range"] = ( - f"bytes={offset}-{offset+size-1}" + f"bytes={offset}-{offset + size - 1}" ) etag, _ = self._upload_part_copy( bucket_name, @@ -1756,20 +1821,20 @@ def _upload_part_task(self, args): return args[5], self._upload_part(*args) def put_object( - self, - bucket_name: str, - object_name: str, - data: BinaryIO, - length: int, - content_type: str = "application/octet-stream", - metadata: DictType | None = None, - sse: Sse | None = None, - progress: ProgressType | None = None, - part_size: int = 0, - num_parallel_uploads: int = 3, - tags: Tags | None = None, - retention: Retention | None = None, - legal_hold: bool = False + self, + bucket_name: str, + object_name: str, + data: BinaryIO, + length: int, + content_type: str = "application/octet-stream", + metadata: DictType | None = None, + sse: Sse | None = None, + progress: ProgressType | None = None, + part_size: int = 0, + num_parallel_uploads: int = 3, + tags: Tags | None = None, + retention: Retention | None = None, + legal_hold: bool = False ) -> ObjectWriteResult: """ Uploads data from a stream to an object in a bucket. @@ -1909,7 +1974,7 @@ def put_object( parts = [Part(0, "")] * part_count while not result.empty(): part_number, etag = result.get() - parts[part_number-1] = Part(part_number, etag) + parts[part_number - 1] = Part(part_number, etag) upload_result = self._complete_multipart_upload( bucket_name, object_name, cast(str, upload_id), parts, @@ -2074,10 +2139,10 @@ def stat_object( ) def remove_object( - self, - bucket_name: str, - object_name: str, - version_id: str | None = None + self, + bucket_name: str, + object_name: str, + version_id: str | None = None ): """ Remove an object. @@ -3047,7 +3112,7 @@ def upload_snowball_objects( object_name, cast(BinaryIO, fileobj), length, - metadata=cast(Union[DictType, None], metadata), + metadata=cast(Union[DictType, None], metadata), sse=sse, tags=tags, retention=retention, @@ -3066,7 +3131,7 @@ def _list_objects( max_keys: int | None = None, # all prefix: str | None = None, # all start_after: str | None = None, - # all: v1:marker, versioned:key_marker + # all: v1:marker, versioned:key_marker version_id_marker: str | None = None, # versioned use_api_v1: bool = False, include_version: bool = False,