diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..599aad4ca7b06415e7df9f54ed6c736a26371595 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_compat.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_compat.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85883d295532a91f5e1b4c2efd156c4352b404b7 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_compat.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_constants.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_constants.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41e051b83d713a72a8bc613975859d423689d2d3 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_constants.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a16a3de3fcb8db6653420c5a6e6cd66ed9f285c5 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_exceptions.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_files.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_files.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5e3d08e7fd497403af2ac2441f660fdf294f12e Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_files.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_models.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_models.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30f144508fe75fa73f468b036d881fb3b019fcaa Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_models.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_module_client.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_module_client.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd1f3466913592f5a19c869f40872e60a7aeb8a4 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_module_client.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_qs.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_qs.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2d2a2bc80dd8d9234c78e47ce57ea876e6a1afa Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_qs.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_response.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_response.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d881f756c5445e044962cb3a0d93d2fb11347f7 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_response.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/_types.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/_types.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..879b591153a576be7064533575ba3b77e3ba0270 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/_types.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/__pycache__/pagination.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/__pycache__/pagination.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c34b6d575e9cdc39f231d18c605fe5d30ce959f5 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/__pycache__/pagination.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6df914aa7bda9f62a2594312360b7eba7fbf6e9b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7385da265af973b5f96c12cdbc8b56291a1e12a4 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_logs.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..704d3d79460eb0ebcb4fd0bf34c8b56afb2e8ec4 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_proxy.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_reflection.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_reflection.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce7a66ca63888852e08883a2009fb4abe0b738dd Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_reflection.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6364ff3c61b207aa965101e29f12fc9afbe1c8c0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_streams.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a876b0672047211328446095c571b6e44cdecdf Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_sync.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8994b113814b65e07c0f6f8e59ca1746c7d08a81 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_transform.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2b8f8e158946dc8d8b799c3125659e0e58e4ee8 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_typing.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78318144d831da55f241af2a5164886b061cfd0b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/_utils/__pycache__/_utils.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_logs.py b/.venv/lib/python3.11/site-packages/openai/_utils/_logs.py new file mode 100644 index 0000000000000000000000000000000000000000..376946933cb7fd220a8192350a8948206aae3c11 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_logs.py @@ -0,0 +1,42 @@ +import os +import logging +from typing_extensions import override + +from ._utils import is_dict + +logger: logging.Logger = logging.getLogger("openai") +httpx_logger: logging.Logger = logging.getLogger("httpx") + + +SENSITIVE_HEADERS = {"api-key", "authorization"} + + +def _basic_config() -> None: + # e.g. [2023-10-05 14:12:26 - openai._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" + logging.basicConfig( + format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def setup_logging() -> None: + env = os.environ.get("OPENAI_LOG") + if env == "debug": + _basic_config() + logger.setLevel(logging.DEBUG) + httpx_logger.setLevel(logging.DEBUG) + elif env == "info": + _basic_config() + logger.setLevel(logging.INFO) + httpx_logger.setLevel(logging.INFO) + + +class SensitiveHeadersFilter(logging.Filter): + @override + def filter(self, record: logging.LogRecord) -> bool: + if is_dict(record.args) and "headers" in record.args and is_dict(record.args["headers"]): + headers = record.args["headers"] = {**record.args["headers"]} + for header in headers: + if str(header).lower() in SENSITIVE_HEADERS: + headers[header] = "" + return True diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_reflection.py b/.venv/lib/python3.11/site-packages/openai/_utils/_reflection.py new file mode 100644 index 0000000000000000000000000000000000000000..bdaca29e4a16c66db7689826be68213348d54da1 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_reflection.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), + description: str = "", +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError( + f"{len(errors)} errors encountered when comparing signatures{description}:\n\n" + "\n\n".join(errors) + ) diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_streams.py b/.venv/lib/python3.11/site-packages/openai/_utils/_streams.py new file mode 100644 index 0000000000000000000000000000000000000000..f4a0208f01c069616000eeed319029fc92c03b56 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_streams.py @@ -0,0 +1,12 @@ +from typing import Any +from typing_extensions import Iterator, AsyncIterator + + +def consume_sync_iterator(iterator: Iterator[Any]) -> None: + for _ in iterator: + ... + + +async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: + async for _ in iterator: + ... diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_sync.py b/.venv/lib/python3.11/site-packages/openai/_utils/_sync.py new file mode 100644 index 0000000000000000000000000000000000000000..5d9e2c2ac9dfa8c94c0132350b4925f30b830504 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_sync.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import sys +import asyncio +import functools +import contextvars +from typing import Any, TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +if sys.version_info >= (3, 9): + to_thread = asyncio.to_thread +else: + # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread + # for Python 3.8 support + async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs + ) -> Any: + """Asynchronously run function *func* in a separate thread. + + Any *args and **kwargs supplied for this function are directly passed + to *func*. Also, the current :class:`contextvars.Context` is propagated, + allowing context variables from the main thread to be accessed in the + separate thread. + + Returns a coroutine that can be awaited to get the eventual result of *func*. + """ + loop = asyncio.events.get_running_loop() + ctx = contextvars.copy_context() + func_call = functools.partial(ctx.run, func, *args, **kwargs) + return await loop.run_in_executor(None, func_call) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments. For python version 3.9 and above, it uses + asyncio.to_thread to run the function in a separate thread. For python version + 3.8, it uses locally defined copy of the asyncio.to_thread function which was + introduced in python 3.9. + + Usage: + + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result + + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + + ## Return + + An async function that takes the same positional and keyword arguments as the + original one, that when called runs the same original function in a thread worker + and returns the result. + """ + + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: + return await to_thread(function, *args, **kwargs) + + return wrapper diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_transform.py b/.venv/lib/python3.11/site-packages/openai/_utils/_transform.py new file mode 100644 index 0000000000000000000000000000000000000000..a6b62cad0cb130f7749e345d432b79a9e1fc8ce8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_transform.py @@ -0,0 +1,392 @@ +from __future__ import annotations + +import io +import base64 +import pathlib +from typing import Any, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints + +import anyio +import pydantic + +from ._utils import ( + is_list, + is_mapping, + is_iterable, +) +from .._files import is_base64_file_input +from ._typing import ( + is_list_type, + is_union_type, + extract_type_arg, + is_iterable_type, + is_required_type, + is_annotated_type, + strip_annotated_type, +) +from .._compat import model_dump, is_typeddict + +_T = TypeVar("_T") + + +# TODO: support for drilling globals() and locals() +# TODO: ensure works correctly with forward references in all cases + + +PropertyFormat = Literal["iso8601", "base64", "custom"] + + +class PropertyInfo: + """Metadata class to be used in Annotated types to provide information about a given type. + + For example: + + class MyParams(TypedDict): + account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] + + This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. + """ + + alias: str | None + format: PropertyFormat | None + format_template: str | None + discriminator: str | None + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + discriminator: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + self.discriminator = discriminator + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" + + +def maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `transform()` that allows `None` to be passed. + + See `transform()` for more details. + """ + if data is None: + return None + return transform(data, expected_type) + + +# Wrapper over _transform_recursive providing fake types +def transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = _transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +def _get_annotated_type(type_: type) -> type | None: + """If the given type is an `Annotated` type then it is returned, if not `None` is returned. + + This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` + """ + if is_required_type(type_): + # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` + type_ = get_args(type_)[0] + + if is_annotated_type(type_): + return type_ + + return None + + +def _maybe_transform_key(key: str, type_: type) -> str: + """Transform the given `data` based on the annotations provided in `type_`. + + Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata. + """ + annotated_type = _get_annotated_type(type_) + if annotated_type is None: + # no `Annotated` definition for this type, no transformation needed + return key + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.alias is not None: + return annotation.alias + + return key + + +def _transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + if is_typeddict(stripped_type) and is_mapping(data): + return _transform_typeddict(data, stripped_type) + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = _transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return _format_data(data, annotation.format, annotation.format_template) + + return data + + +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +def _transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) + return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_typing.py b/.venv/lib/python3.11/site-packages/openai/_utils/_typing.py new file mode 100644 index 0000000000000000000000000000000000000000..278749b1475df20265d460130007d0cfa9f458f2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_typing.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +import sys +import typing +import typing_extensions +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) + +from .._types import InheritsGeneric +from .._compat import is_union as _is_union + + +def is_annotated_type(typ: type) -> bool: + return get_origin(typ) == Annotated + + +def is_list_type(typ: type) -> bool: + return (get_origin(typ) or typ) == list + + +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + +def is_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +def is_typevar(typ: type) -> bool: + # type ignore is required because type checkers + # think this expression will always return False + return type(typ) == TypeVar # type: ignore + + +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + +# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +def strip_annotated_type(typ: type) -> type: + if is_required_type(typ) or is_annotated_type(typ): + return strip_annotated_type(cast(type, get_args(typ)[0])) + + return typ + + +def extract_type_arg(typ: type, index: int) -> type: + args = get_args(typ) + try: + return cast(type, args[index]) + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err + + +def extract_type_var_from_base( + typ: type, + *, + generic_bases: tuple[type, ...], + index: int, + failure_message: str | None = None, +) -> type: + """Given a type like `Foo[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(Foo[bytes]): + ... + + extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes + ``` + + And where a generic subclass is given: + ```py + _T = TypeVar('_T') + class MyResponse(Foo[_T]): + ... + + extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes + ``` + """ + cls = cast(object, get_origin(typ) or typ) + if cls in generic_bases: + # we're given the class directly + return extract_type_arg(typ, index) + + # if a subclass is given + # --- + # this is needed as __orig_bases__ is not present in the typeshed stubs + # because it is intended to be for internal use only, however there does + # not seem to be a way to resolve generic TypeVars for inherited subclasses + # without using it. + if isinstance(cls, InheritsGeneric): + target_base_class: Any | None = None + for base in cls.__orig_bases__: + if base.__origin__ in generic_bases: + target_base_class = base + break + + if target_base_class is None: + raise RuntimeError( + "Could not find the generic base class;\n" + "This should never happen;\n" + f"Does {cls} inherit from one of {generic_bases} ?" + ) + + extracted = extract_type_arg(target_base_class, index) + if is_typevar(extracted): + # If the extracted type argument is itself a type variable + # then that means the subclass itself is generic, so we have + # to resolve the type argument from the class itself, not + # the base class. + # + # Note: if there is more than 1 type argument, the subclass could + # change the ordering of the type arguments, this is not currently + # supported. + return extract_type_arg(typ, index) + + return extracted + + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/.venv/lib/python3.11/site-packages/openai/_utils/_utils.py b/.venv/lib/python3.11/site-packages/openai/_utils/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d6734e6b8faa53d20446140acb6f7cc0f81a0e23 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/_utils/_utils.py @@ -0,0 +1,430 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + TYPE_CHECKING, + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard + +import sniffio + +from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._compat import parse_date as parse_date, parse_datetime as parse_datetime + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + +if TYPE_CHECKING: + from ..lib.azure import AzureOpenAI, AsyncAzureOpenAI + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None)) + return files + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if isinstance(obj, NotGiven): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert_is_file_content(obj, key=flattened_key) + assert flattened_key is not None + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # We are at the last entry in the path so we must remove the field + if (len(path)) == index: + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + ) + for item in obj + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in it's place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +def deepcopy_minimal(item: _T) -> _T: + """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: + + - mappings, e.g. `dict` + - list + + This is done for performance reasons. + """ + if is_mapping(item): + return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) + if is_list(item): + return cast(_T, [deepcopy_minimal(entry) for entry in item]) + return item + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like Stainless-Event-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data + + +def is_azure_client(client: object) -> TypeGuard[AzureOpenAI]: + from ..lib.azure import AzureOpenAI + + return isinstance(client, AzureOpenAI) + + +def is_async_azure_client(client: object) -> TypeGuard[AsyncAzureOpenAI]: + from ..lib.azure import AsyncAzureOpenAI + + return isinstance(client, AsyncAzureOpenAI) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__init__.py b/.venv/lib/python3.11/site-packages/openai/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d453d5e17948a20250a79a2b4d81b6df74719418 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/__init__.py @@ -0,0 +1 @@ +from ._cli import main as main diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cdf57f442d16fc7ddc8700ae6121934a081d0d5 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_cli.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_cli.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e81881f415bdc04b023e5fb301762613b61f2444 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_cli.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_errors.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_errors.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9885c790694289e236bd84ff18a3ee096e71e42e Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_errors.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_models.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_models.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8ec6d617ed8eb784efa9b9cdfc6a6ccbdc9eab6 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_models.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_progress.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_progress.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c84f4e51af62a7cb969d566b44c44858351630a1 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_progress.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a32670c6d96e1a11df6cf546abdeec5522c5d27 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/__pycache__/_utils.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/_main.py b/.venv/lib/python3.11/site-packages/openai/cli/_api/_main.py new file mode 100644 index 0000000000000000000000000000000000000000..fe5a5e6fc0b033a55f01cb07557136b59cec3d09 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_api/_main.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from argparse import ArgumentParser + +from . import chat, audio, files, image, models, completions + + +def register_commands(parser: ArgumentParser) -> None: + subparsers = parser.add_subparsers(help="All API subcommands") + + chat.register(subparsers) + image.register(subparsers) + audio.register(subparsers) + files.register(subparsers) + models.register(subparsers) + completions.register(subparsers) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/audio.py b/.venv/lib/python3.11/site-packages/openai/cli/_api/audio.py new file mode 100644 index 0000000000000000000000000000000000000000..269c67df28d477273d408a33be90ca59b3f91427 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_api/audio.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING, Any, Optional, cast +from argparse import ArgumentParser + +from .._utils import get_client, print_model +from ..._types import NOT_GIVEN +from .._models import BaseModel +from .._progress import BufferReader +from ...types.audio import Transcription + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + # transcriptions + sub = subparser.add_parser("audio.transcriptions.create") + + # Required + sub.add_argument("-m", "--model", type=str, default="whisper-1") + sub.add_argument("-f", "--file", type=str, required=True) + # Optional + sub.add_argument("--response-format", type=str) + sub.add_argument("--language", type=str) + sub.add_argument("-t", "--temperature", type=float) + sub.add_argument("--prompt", type=str) + sub.set_defaults(func=CLIAudio.transcribe, args_model=CLITranscribeArgs) + + # translations + sub = subparser.add_parser("audio.translations.create") + + # Required + sub.add_argument("-f", "--file", type=str, required=True) + # Optional + sub.add_argument("-m", "--model", type=str, default="whisper-1") + sub.add_argument("--response-format", type=str) + # TODO: doesn't seem to be supported by the API + # sub.add_argument("--language", type=str) + sub.add_argument("-t", "--temperature", type=float) + sub.add_argument("--prompt", type=str) + sub.set_defaults(func=CLIAudio.translate, args_model=CLITranslationArgs) + + +class CLITranscribeArgs(BaseModel): + model: str + file: str + response_format: Optional[str] = None + language: Optional[str] = None + temperature: Optional[float] = None + prompt: Optional[str] = None + + +class CLITranslationArgs(BaseModel): + model: str + file: str + response_format: Optional[str] = None + language: Optional[str] = None + temperature: Optional[float] = None + prompt: Optional[str] = None + + +class CLIAudio: + @staticmethod + def transcribe(args: CLITranscribeArgs) -> None: + with open(args.file, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + + model = cast( + "Transcription | str", + get_client().audio.transcriptions.create( + file=(args.file, buffer_reader), + model=args.model, + language=args.language or NOT_GIVEN, + temperature=args.temperature or NOT_GIVEN, + prompt=args.prompt or NOT_GIVEN, + # casts required because the API is typed for enums + # but we don't want to validate that here for forwards-compat + response_format=cast(Any, args.response_format), + ), + ) + if isinstance(model, str): + sys.stdout.write(model + "\n") + else: + print_model(model) + + @staticmethod + def translate(args: CLITranslationArgs) -> None: + with open(args.file, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + + model = cast( + "Transcription | str", + get_client().audio.translations.create( + file=(args.file, buffer_reader), + model=args.model, + temperature=args.temperature or NOT_GIVEN, + prompt=args.prompt or NOT_GIVEN, + # casts required because the API is typed for enums + # but we don't want to validate that here for forwards-compat + response_format=cast(Any, args.response_format), + ), + ) + if isinstance(model, str): + sys.stdout.write(model + "\n") + else: + print_model(model) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/chat/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/_api/chat/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..252473b3d441898f46d5f5338f2db0a64f0e154a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/_api/chat/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/chat/__pycache__/completions.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/_api/chat/__pycache__/completions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3dc79e5d43412219db3c2992b292cda46fca42a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/_api/chat/__pycache__/completions.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/completions.py b/.venv/lib/python3.11/site-packages/openai/cli/_api/completions.py new file mode 100644 index 0000000000000000000000000000000000000000..cbdb35bf3a9f1f8db56abf78420a66cdc5140b83 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_api/completions.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING, Optional, cast +from argparse import ArgumentParser +from functools import partial + +from openai.types.completion import Completion + +from .._utils import get_client +from ..._types import NOT_GIVEN, NotGivenOr +from ..._utils import is_given +from .._errors import CLIError +from .._models import BaseModel +from ..._streaming import Stream + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("completions.create") + + # Required + sub.add_argument( + "-m", + "--model", + help="The model to use", + required=True, + ) + + # Optional + sub.add_argument("-p", "--prompt", help="An optional prompt to complete from") + sub.add_argument("--stream", help="Stream tokens as they're ready.", action="store_true") + sub.add_argument("-M", "--max-tokens", help="The maximum number of tokens to generate", type=int) + sub.add_argument( + "-t", + "--temperature", + help="""What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. + +Mutually exclusive with `top_p`.""", + type=float, + ) + sub.add_argument( + "-P", + "--top_p", + help="""An alternative to sampling with temperature, called nucleus sampling, where the considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10%% probability mass are considered. + + Mutually exclusive with `temperature`.""", + type=float, + ) + sub.add_argument( + "-n", + "--n", + help="How many sub-completions to generate for each prompt.", + type=int, + ) + sub.add_argument( + "--logprobs", + help="Include the log probabilities on the `logprobs` most likely tokens, as well the chosen tokens. So for example, if `logprobs` is 10, the API will return a list of the 10 most likely tokens. If `logprobs` is 0, only the chosen tokens will have logprobs returned.", + type=int, + ) + sub.add_argument( + "--best_of", + help="Generates `best_of` completions server-side and returns the 'best' (the one with the highest log probability per token). Results cannot be streamed.", + type=int, + ) + sub.add_argument( + "--echo", + help="Echo back the prompt in addition to the completion", + action="store_true", + ) + sub.add_argument( + "--frequency_penalty", + help="Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.", + type=float, + ) + sub.add_argument( + "--presence_penalty", + help="Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", + type=float, + ) + sub.add_argument("--suffix", help="The suffix that comes after a completion of inserted text.") + sub.add_argument("--stop", help="A stop sequence at which to stop generating tokens.") + sub.add_argument( + "--user", + help="A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.", + ) + # TODO: add support for logit_bias + sub.set_defaults(func=CLICompletions.create, args_model=CLICompletionCreateArgs) + + +class CLICompletionCreateArgs(BaseModel): + model: str + stream: bool = False + + prompt: Optional[str] = None + n: NotGivenOr[int] = NOT_GIVEN + stop: NotGivenOr[str] = NOT_GIVEN + user: NotGivenOr[str] = NOT_GIVEN + echo: NotGivenOr[bool] = NOT_GIVEN + suffix: NotGivenOr[str] = NOT_GIVEN + best_of: NotGivenOr[int] = NOT_GIVEN + top_p: NotGivenOr[float] = NOT_GIVEN + logprobs: NotGivenOr[int] = NOT_GIVEN + max_tokens: NotGivenOr[int] = NOT_GIVEN + temperature: NotGivenOr[float] = NOT_GIVEN + presence_penalty: NotGivenOr[float] = NOT_GIVEN + frequency_penalty: NotGivenOr[float] = NOT_GIVEN + + +class CLICompletions: + @staticmethod + def create(args: CLICompletionCreateArgs) -> None: + if is_given(args.n) and args.n > 1 and args.stream: + raise CLIError("Can't stream completions with n>1 with the current CLI") + + make_request = partial( + get_client().completions.create, + n=args.n, + echo=args.echo, + stop=args.stop, + user=args.user, + model=args.model, + top_p=args.top_p, + prompt=args.prompt, + suffix=args.suffix, + best_of=args.best_of, + logprobs=args.logprobs, + max_tokens=args.max_tokens, + temperature=args.temperature, + presence_penalty=args.presence_penalty, + frequency_penalty=args.frequency_penalty, + ) + + if args.stream: + return CLICompletions._stream_create( + # mypy doesn't understand the `partial` function but pyright does + cast(Stream[Completion], make_request(stream=True)) # pyright: ignore[reportUnnecessaryCast] + ) + + return CLICompletions._create(make_request()) + + @staticmethod + def _create(completion: Completion) -> None: + should_print_header = len(completion.choices) > 1 + for choice in completion.choices: + if should_print_header: + sys.stdout.write("===== Completion {} =====\n".format(choice.index)) + + sys.stdout.write(choice.text) + + if should_print_header or not choice.text.endswith("\n"): + sys.stdout.write("\n") + + sys.stdout.flush() + + @staticmethod + def _stream_create(stream: Stream[Completion]) -> None: + for completion in stream: + should_print_header = len(completion.choices) > 1 + for choice in sorted(completion.choices, key=lambda c: c.index): + if should_print_header: + sys.stdout.write("===== Chat Completion {} =====\n".format(choice.index)) + + sys.stdout.write(choice.text) + + if should_print_header: + sys.stdout.write("\n") + + sys.stdout.flush() + + sys.stdout.write("\n") diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/files.py b/.venv/lib/python3.11/site-packages/openai/cli/_api/files.py new file mode 100644 index 0000000000000000000000000000000000000000..5f3631b2840aa35511f9c26d550f1429f21c7155 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_api/files.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, cast +from argparse import ArgumentParser + +from .._utils import get_client, print_model +from .._models import BaseModel +from .._progress import BufferReader + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("files.create") + + sub.add_argument( + "-f", + "--file", + required=True, + help="File to upload", + ) + sub.add_argument( + "-p", + "--purpose", + help="Why are you uploading this file? (see https://platform.openai.com/docs/api-reference/ for purposes)", + required=True, + ) + sub.set_defaults(func=CLIFile.create, args_model=CLIFileCreateArgs) + + sub = subparser.add_parser("files.retrieve") + sub.add_argument("-i", "--id", required=True, help="The files ID") + sub.set_defaults(func=CLIFile.get, args_model=CLIFileCreateArgs) + + sub = subparser.add_parser("files.delete") + sub.add_argument("-i", "--id", required=True, help="The files ID") + sub.set_defaults(func=CLIFile.delete, args_model=CLIFileCreateArgs) + + sub = subparser.add_parser("files.list") + sub.set_defaults(func=CLIFile.list) + + +class CLIFileIDArgs(BaseModel): + id: str + + +class CLIFileCreateArgs(BaseModel): + file: str + purpose: str + + +class CLIFile: + @staticmethod + def create(args: CLIFileCreateArgs) -> None: + with open(args.file, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + + file = get_client().files.create( + file=(args.file, buffer_reader), + # casts required because the API is typed for enums + # but we don't want to validate that here for forwards-compat + purpose=cast(Any, args.purpose), + ) + print_model(file) + + @staticmethod + def get(args: CLIFileIDArgs) -> None: + file = get_client().files.retrieve(file_id=args.id) + print_model(file) + + @staticmethod + def delete(args: CLIFileIDArgs) -> None: + file = get_client().files.delete(file_id=args.id) + print_model(file) + + @staticmethod + def list() -> None: + files = get_client().files.list() + for file in files: + print_model(file) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/image.py b/.venv/lib/python3.11/site-packages/openai/cli/_api/image.py new file mode 100644 index 0000000000000000000000000000000000000000..3e2a0a90f1b41a1e0a39e401932e0f5ccfc3e3b0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_api/image.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, cast +from argparse import ArgumentParser + +from .._utils import get_client, print_model +from ..._types import NOT_GIVEN, NotGiven, NotGivenOr +from .._models import BaseModel +from .._progress import BufferReader + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("images.generate") + sub.add_argument("-m", "--model", type=str) + sub.add_argument("-p", "--prompt", type=str, required=True) + sub.add_argument("-n", "--num-images", type=int, default=1) + sub.add_argument("-s", "--size", type=str, default="1024x1024", help="Size of the output image") + sub.add_argument("--response-format", type=str, default="url") + sub.set_defaults(func=CLIImage.create, args_model=CLIImageCreateArgs) + + sub = subparser.add_parser("images.edit") + sub.add_argument("-m", "--model", type=str) + sub.add_argument("-p", "--prompt", type=str, required=True) + sub.add_argument("-n", "--num-images", type=int, default=1) + sub.add_argument( + "-I", + "--image", + type=str, + required=True, + help="Image to modify. Should be a local path and a PNG encoded image.", + ) + sub.add_argument("-s", "--size", type=str, default="1024x1024", help="Size of the output image") + sub.add_argument("--response-format", type=str, default="url") + sub.add_argument( + "-M", + "--mask", + type=str, + required=False, + help="Path to a mask image. It should be the same size as the image you're editing and a RGBA PNG image. The Alpha channel acts as the mask.", + ) + sub.set_defaults(func=CLIImage.edit, args_model=CLIImageEditArgs) + + sub = subparser.add_parser("images.create_variation") + sub.add_argument("-m", "--model", type=str) + sub.add_argument("-n", "--num-images", type=int, default=1) + sub.add_argument( + "-I", + "--image", + type=str, + required=True, + help="Image to modify. Should be a local path and a PNG encoded image.", + ) + sub.add_argument("-s", "--size", type=str, default="1024x1024", help="Size of the output image") + sub.add_argument("--response-format", type=str, default="url") + sub.set_defaults(func=CLIImage.create_variation, args_model=CLIImageCreateVariationArgs) + + +class CLIImageCreateArgs(BaseModel): + prompt: str + num_images: int + size: str + response_format: str + model: NotGivenOr[str] = NOT_GIVEN + + +class CLIImageCreateVariationArgs(BaseModel): + image: str + num_images: int + size: str + response_format: str + model: NotGivenOr[str] = NOT_GIVEN + + +class CLIImageEditArgs(BaseModel): + image: str + num_images: int + size: str + response_format: str + prompt: str + mask: NotGivenOr[str] = NOT_GIVEN + model: NotGivenOr[str] = NOT_GIVEN + + +class CLIImage: + @staticmethod + def create(args: CLIImageCreateArgs) -> None: + image = get_client().images.generate( + model=args.model, + prompt=args.prompt, + n=args.num_images, + # casts required because the API is typed for enums + # but we don't want to validate that here for forwards-compat + size=cast(Any, args.size), + response_format=cast(Any, args.response_format), + ) + print_model(image) + + @staticmethod + def create_variation(args: CLIImageCreateVariationArgs) -> None: + with open(args.image, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + + image = get_client().images.create_variation( + model=args.model, + image=("image", buffer_reader), + n=args.num_images, + # casts required because the API is typed for enums + # but we don't want to validate that here for forwards-compat + size=cast(Any, args.size), + response_format=cast(Any, args.response_format), + ) + print_model(image) + + @staticmethod + def edit(args: CLIImageEditArgs) -> None: + with open(args.image, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Image upload progress") + + if isinstance(args.mask, NotGiven): + mask: NotGivenOr[BufferReader] = NOT_GIVEN + else: + with open(args.mask, "rb") as file_reader: + mask = BufferReader(file_reader.read(), desc="Mask progress") + + image = get_client().images.edit( + model=args.model, + prompt=args.prompt, + image=("image", buffer_reader), + n=args.num_images, + mask=("mask", mask) if not isinstance(mask, NotGiven) else mask, + # casts required because the API is typed for enums + # but we don't want to validate that here for forwards-compat + size=cast(Any, args.size), + response_format=cast(Any, args.response_format), + ) + print_model(image) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_api/models.py b/.venv/lib/python3.11/site-packages/openai/cli/_api/models.py new file mode 100644 index 0000000000000000000000000000000000000000..017218fa6e3ace8d42ad745d4649bfd341535460 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_api/models.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from argparse import ArgumentParser + +from .._utils import get_client, print_model +from .._models import BaseModel + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("models.list") + sub.set_defaults(func=CLIModels.list) + + sub = subparser.add_parser("models.retrieve") + sub.add_argument("-i", "--id", required=True, help="The model ID") + sub.set_defaults(func=CLIModels.get, args_model=CLIModelIDArgs) + + sub = subparser.add_parser("models.delete") + sub.add_argument("-i", "--id", required=True, help="The model ID") + sub.set_defaults(func=CLIModels.delete, args_model=CLIModelIDArgs) + + +class CLIModelIDArgs(BaseModel): + id: str + + +class CLIModels: + @staticmethod + def get(args: CLIModelIDArgs) -> None: + model = get_client().models.retrieve(model=args.id) + print_model(model) + + @staticmethod + def delete(args: CLIModelIDArgs) -> None: + model = get_client().models.delete(model=args.id) + print_model(model) + + @staticmethod + def list() -> None: + models = get_client().models.list() + for model in models: + print_model(model) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_cli.py b/.venv/lib/python3.11/site-packages/openai/cli/_cli.py new file mode 100644 index 0000000000000000000000000000000000000000..fd165f48abf98611b30dd319a1e5052a96140bd8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_cli.py @@ -0,0 +1,233 @@ +from __future__ import annotations + +import sys +import logging +import argparse +from typing import Any, List, Type, Optional +from typing_extensions import ClassVar + +import httpx +import pydantic + +import openai + +from . import _tools +from .. import _ApiType, __version__ +from ._api import register_commands +from ._utils import can_use_http2 +from ._errors import CLIError, display_error +from .._compat import PYDANTIC_V2, ConfigDict, model_parse +from .._models import BaseModel +from .._exceptions import APIError + +logger = logging.getLogger() +formatter = logging.Formatter("[%(asctime)s] %(message)s") +handler = logging.StreamHandler(sys.stderr) +handler.setFormatter(formatter) +logger.addHandler(handler) + + +class Arguments(BaseModel): + if PYDANTIC_V2: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="ignore", + ) + else: + + class Config(pydantic.BaseConfig): # type: ignore + extra: Any = pydantic.Extra.ignore # type: ignore + + verbosity: int + version: Optional[str] = None + + api_key: Optional[str] + api_base: Optional[str] + organization: Optional[str] + proxy: Optional[List[str]] + api_type: Optional[_ApiType] = None + api_version: Optional[str] = None + + # azure + azure_endpoint: Optional[str] = None + azure_ad_token: Optional[str] = None + + # internal, set by subparsers to parse their specific args + args_model: Optional[Type[BaseModel]] = None + + # internal, used so that subparsers can forward unknown arguments + unknown_args: List[str] = [] + allow_unknown_args: bool = False + + +def _build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=None, prog="openai") + parser.add_argument( + "-v", + "--verbose", + action="count", + dest="verbosity", + default=0, + help="Set verbosity.", + ) + parser.add_argument("-b", "--api-base", help="What API base url to use.") + parser.add_argument("-k", "--api-key", help="What API key to use.") + parser.add_argument("-p", "--proxy", nargs="+", help="What proxy to use.") + parser.add_argument( + "-o", + "--organization", + help="Which organization to run as (will use your default organization if not specified)", + ) + parser.add_argument( + "-t", + "--api-type", + type=str, + choices=("openai", "azure"), + help="The backend API to call, must be `openai` or `azure`", + ) + parser.add_argument( + "--api-version", + help="The Azure API version, e.g. 'https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning'", + ) + + # azure + parser.add_argument( + "--azure-endpoint", + help="The Azure endpoint, e.g. 'https://endpoint.openai.azure.com'", + ) + parser.add_argument( + "--azure-ad-token", + help="A token from Azure Active Directory, https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id", + ) + + # prints the package version + parser.add_argument( + "-V", + "--version", + action="version", + version="%(prog)s " + __version__, + ) + + def help() -> None: + parser.print_help() + + parser.set_defaults(func=help) + + subparsers = parser.add_subparsers() + sub_api = subparsers.add_parser("api", help="Direct API calls") + + register_commands(sub_api) + + sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience") + _tools.register_commands(sub_tools, subparsers) + + return parser + + +def main() -> int: + try: + _main() + except (APIError, CLIError, pydantic.ValidationError) as err: + display_error(err) + return 1 + except KeyboardInterrupt: + sys.stderr.write("\n") + return 1 + return 0 + + +def _parse_args(parser: argparse.ArgumentParser) -> tuple[argparse.Namespace, Arguments, list[str]]: + # argparse by default will strip out the `--` but we want to keep it for unknown arguments + if "--" in sys.argv: + idx = sys.argv.index("--") + known_args = sys.argv[1:idx] + unknown_args = sys.argv[idx:] + else: + known_args = sys.argv[1:] + unknown_args = [] + + parsed, remaining_unknown = parser.parse_known_args(known_args) + + # append any remaining unknown arguments from the initial parsing + remaining_unknown.extend(unknown_args) + + args = model_parse(Arguments, vars(parsed)) + if not args.allow_unknown_args: + # we have to parse twice to ensure any unknown arguments + # result in an error if that behaviour is desired + parser.parse_args() + + return parsed, args, remaining_unknown + + +def _main() -> None: + parser = _build_parser() + parsed, args, unknown = _parse_args(parser) + + if args.verbosity != 0: + sys.stderr.write("Warning: --verbosity isn't supported yet\n") + + proxies: dict[str, httpx.BaseTransport] = {} + if args.proxy is not None: + for proxy in args.proxy: + key = "https://" if proxy.startswith("https") else "http://" + if key in proxies: + raise CLIError(f"Multiple {key} proxies given - only the last one would be used") + + proxies[key] = httpx.HTTPTransport(proxy=httpx.Proxy(httpx.URL(proxy))) + + http_client = httpx.Client( + mounts=proxies or None, + http2=can_use_http2(), + ) + openai.http_client = http_client + + if args.organization: + openai.organization = args.organization + + if args.api_key: + openai.api_key = args.api_key + + if args.api_base: + openai.base_url = args.api_base + + # azure + if args.api_type is not None: + openai.api_type = args.api_type + + if args.azure_endpoint is not None: + openai.azure_endpoint = args.azure_endpoint + + if args.api_version is not None: + openai.api_version = args.api_version + + if args.azure_ad_token is not None: + openai.azure_ad_token = args.azure_ad_token + + try: + if args.args_model: + parsed.func( + model_parse( + args.args_model, + { + **{ + # we omit None values so that they can be defaulted to `NotGiven` + # and we'll strip it from the API request + key: value + for key, value in vars(parsed).items() + if value is not None + }, + "unknown_args": unknown, + }, + ) + ) + else: + parsed.func() + finally: + try: + http_client.close() + except Exception: + pass + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_errors.py b/.venv/lib/python3.11/site-packages/openai/cli/_errors.py new file mode 100644 index 0000000000000000000000000000000000000000..7d0292dab267893a533eb3a5d8a634aa23c53b5f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_errors.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import sys + +import pydantic + +from ._utils import Colors, organization_info +from .._exceptions import APIError, OpenAIError + + +class CLIError(OpenAIError): ... + + +class SilentCLIError(CLIError): ... + + +def display_error(err: CLIError | APIError | pydantic.ValidationError) -> None: + if isinstance(err, SilentCLIError): + return + + sys.stderr.write("{}{}Error:{} {}\n".format(organization_info(), Colors.FAIL, Colors.ENDC, err)) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_models.py b/.venv/lib/python3.11/site-packages/openai/cli/_models.py new file mode 100644 index 0000000000000000000000000000000000000000..5583db26091d6514eab01b83ad98e8ab4c55286e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_models.py @@ -0,0 +1,17 @@ +from typing import Any +from typing_extensions import ClassVar + +import pydantic + +from .. import _models +from .._compat import PYDANTIC_V2, ConfigDict + + +class BaseModel(_models.BaseModel): + if PYDANTIC_V2: + model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore", arbitrary_types_allowed=True) + else: + + class Config(pydantic.BaseConfig): # type: ignore + extra: Any = pydantic.Extra.ignore # type: ignore + arbitrary_types_allowed: bool = True diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_progress.py b/.venv/lib/python3.11/site-packages/openai/cli/_progress.py new file mode 100644 index 0000000000000000000000000000000000000000..8a7f2525deb80aa9f24b22537cac725cefad4f4d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_progress.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import io +from typing import Callable +from typing_extensions import override + + +class CancelledError(Exception): + def __init__(self, msg: str) -> None: + self.msg = msg + super().__init__(msg) + + @override + def __str__(self) -> str: + return self.msg + + __repr__ = __str__ + + +class BufferReader(io.BytesIO): + def __init__(self, buf: bytes = b"", desc: str | None = None) -> None: + super().__init__(buf) + self._len = len(buf) + self._progress = 0 + self._callback = progress(len(buf), desc=desc) + + def __len__(self) -> int: + return self._len + + @override + def read(self, n: int | None = -1) -> bytes: + chunk = io.BytesIO.read(self, n) + self._progress += len(chunk) + + try: + self._callback(self._progress) + except Exception as e: # catches exception from the callback + raise CancelledError("The upload was cancelled: {}".format(e)) from e + + return chunk + + +def progress(total: float, desc: str | None) -> Callable[[float], None]: + import tqdm + + meter = tqdm.tqdm(total=total, unit_scale=True, desc=desc) + + def incr(progress: float) -> None: + meter.n = progress + if progress == total: + meter.close() + else: + meter.refresh() + + return incr + + +def MB(i: int) -> int: + return int(i // 1024**2) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/__init__.py b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..56a0260a6dc333995ff975158452eca83fc66012 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__init__.py @@ -0,0 +1 @@ +from ._main import register_commands as register_commands diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b51593e17fe4561f06a58f009a5647c70c7766df Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/_main.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/_main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..186009f9610fc3c7e731cf8e03d37bbf2db6fc87 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/_main.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/fine_tunes.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/fine_tunes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..907dc2ae855ab84e0c5ad2df5a82ea838256fffa Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/fine_tunes.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/migrate.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/migrate.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9673d97e07c88f502d00570be15ec3be42b7e179 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/cli/_tools/__pycache__/migrate.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/_main.py b/.venv/lib/python3.11/site-packages/openai/cli/_tools/_main.py new file mode 100644 index 0000000000000000000000000000000000000000..bd6cda408fdb92f0355476756551b29295ab2ade --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_tools/_main.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from argparse import ArgumentParser + +from . import migrate, fine_tunes + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register_commands(parser: ArgumentParser, subparser: _SubParsersAction[ArgumentParser]) -> None: + migrate.register(subparser) + + namespaced = parser.add_subparsers(title="Tools", help="Convenience client side tools") + + fine_tunes.register(namespaced) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/fine_tunes.py b/.venv/lib/python3.11/site-packages/openai/cli/_tools/fine_tunes.py new file mode 100644 index 0000000000000000000000000000000000000000..2128b8895224b1b16190bbe7b5a4bd98662e7f82 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_tools/fine_tunes.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING +from argparse import ArgumentParser + +from .._models import BaseModel +from ...lib._validators import ( + get_validators, + write_out_file, + read_any_format, + apply_validators, + apply_necessary_remediation, +) + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("fine_tunes.prepare_data") + sub.add_argument( + "-f", + "--file", + required=True, + help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing prompt-completion examples to be analyzed." + "This should be the local file path.", + ) + sub.add_argument( + "-q", + "--quiet", + required=False, + action="store_true", + help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", + ) + sub.set_defaults(func=prepare_data, args_model=PrepareDataArgs) + + +class PrepareDataArgs(BaseModel): + file: str + + quiet: bool + + +def prepare_data(args: PrepareDataArgs) -> None: + sys.stdout.write("Analyzing...\n") + fname = args.file + auto_accept = args.quiet + df, remediation = read_any_format(fname) + apply_necessary_remediation(None, remediation) + + validators = get_validators() + + assert df is not None + + apply_validators( + df, + fname, + remediation, + validators, + auto_accept, + write_out_file_func=write_out_file, + ) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_tools/migrate.py b/.venv/lib/python3.11/site-packages/openai/cli/_tools/migrate.py new file mode 100644 index 0000000000000000000000000000000000000000..841b777528069fa17181b1009e6a62e9d5b939f9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_tools/migrate.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +import os +import sys +import shutil +import tarfile +import platform +import subprocess +from typing import TYPE_CHECKING, List +from pathlib import Path +from argparse import ArgumentParser + +import httpx + +from .._errors import CLIError, SilentCLIError +from .._models import BaseModel + +if TYPE_CHECKING: + from argparse import _SubParsersAction + + +def register(subparser: _SubParsersAction[ArgumentParser]) -> None: + sub = subparser.add_parser("migrate") + sub.set_defaults(func=migrate, args_model=MigrateArgs, allow_unknown_args=True) + + sub = subparser.add_parser("grit") + sub.set_defaults(func=grit, args_model=GritArgs, allow_unknown_args=True) + + +class GritArgs(BaseModel): + # internal + unknown_args: List[str] = [] + + +def grit(args: GritArgs) -> None: + grit_path = install() + + try: + subprocess.check_call([grit_path, *args.unknown_args]) + except subprocess.CalledProcessError: + # stdout and stderr are forwarded by subprocess so an error will already + # have been displayed + raise SilentCLIError() from None + + +class MigrateArgs(BaseModel): + # internal + unknown_args: List[str] = [] + + +def migrate(args: MigrateArgs) -> None: + grit_path = install() + + try: + subprocess.check_call([grit_path, "apply", "openai", *args.unknown_args]) + except subprocess.CalledProcessError: + # stdout and stderr are forwarded by subprocess so an error will already + # have been displayed + raise SilentCLIError() from None + + +# handles downloading the Grit CLI until they provide their own PyPi package + +KEYGEN_ACCOUNT = "custodian-dev" + + +def _cache_dir() -> Path: + xdg = os.environ.get("XDG_CACHE_HOME") + if xdg is not None: + return Path(xdg) + + return Path.home() / ".cache" + + +def _debug(message: str) -> None: + if not os.environ.get("DEBUG"): + return + + sys.stdout.write(f"[DEBUG]: {message}\n") + + +def install() -> Path: + """Installs the Grit CLI and returns the location of the binary""" + if sys.platform == "win32": + raise CLIError("Windows is not supported yet in the migration CLI") + + _debug("Using Grit installer from GitHub") + + platform = "apple-darwin" if sys.platform == "darwin" else "unknown-linux-gnu" + + dir_name = _cache_dir() / "openai-python" + install_dir = dir_name / ".install" + target_dir = install_dir / "bin" + + target_path = target_dir / "grit" + temp_file = target_dir / "grit.tmp" + + if target_path.exists(): + _debug(f"{target_path} already exists") + sys.stdout.flush() + return target_path + + _debug(f"Using Grit CLI path: {target_path}") + + target_dir.mkdir(parents=True, exist_ok=True) + + if temp_file.exists(): + temp_file.unlink() + + arch = _get_arch() + _debug(f"Using architecture {arch}") + + file_name = f"grit-{arch}-{platform}" + download_url = f"https://github.com/getgrit/gritql/releases/latest/download/{file_name}.tar.gz" + + sys.stdout.write(f"Downloading Grit CLI from {download_url}\n") + with httpx.Client() as client: + download_response = client.get(download_url, follow_redirects=True) + if download_response.status_code != 200: + raise CLIError(f"Failed to download Grit CLI from {download_url}") + with open(temp_file, "wb") as file: + for chunk in download_response.iter_bytes(): + file.write(chunk) + + unpacked_dir = target_dir / "cli-bin" + unpacked_dir.mkdir(parents=True, exist_ok=True) + + with tarfile.open(temp_file, "r:gz") as archive: + if sys.version_info >= (3, 12): + archive.extractall(unpacked_dir, filter="data") + else: + archive.extractall(unpacked_dir) + + _move_files_recursively(unpacked_dir, target_dir) + + shutil.rmtree(unpacked_dir) + os.remove(temp_file) + os.chmod(target_path, 0o755) + + sys.stdout.flush() + + return target_path + + +def _move_files_recursively(source_dir: Path, target_dir: Path) -> None: + for item in source_dir.iterdir(): + if item.is_file(): + item.rename(target_dir / item.name) + elif item.is_dir(): + _move_files_recursively(item, target_dir) + + +def _get_arch() -> str: + architecture = platform.machine().lower() + + # Map the architecture names to Grit equivalents + arch_map = { + "x86_64": "x86_64", + "amd64": "x86_64", + "armv7l": "aarch64", + "arm64": "aarch64", + } + + return arch_map.get(architecture, architecture) diff --git a/.venv/lib/python3.11/site-packages/openai/cli/_utils.py b/.venv/lib/python3.11/site-packages/openai/cli/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..673eed613cc7d77a65674aef7221fea62c8ccc6b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/cli/_utils.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys + +import openai + +from .. import OpenAI, _load_client +from .._compat import model_json +from .._models import BaseModel + + +class Colors: + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" + + +def get_client() -> OpenAI: + return _load_client() + + +def organization_info() -> str: + organization = openai.organization + if organization is not None: + return "[organization={}] ".format(organization) + + return "" + + +def print_model(model: BaseModel) -> None: + sys.stdout.write(model_json(model, indent=2) + "\n") + + +def can_use_http2() -> bool: + try: + import h2 # type: ignore # noqa + except ImportError: + return False + + return True diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a1fdd4e489ad4e8a7900b22c5a11cedc5964ff0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c29487f67a6068822a03143968e91e2e2530f13b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_content_part_text_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b33ad41ef24f5b70d7ba306fc22a50d417ece127 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_developer_message_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..921f4077a8ba8de6477489b6566c0edf19b3a7a2 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_function_call_option_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_modality.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_modality.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ac08811e391ea37453ec72595b88236b9f415c4 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_modality.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bae4437006ea633272b07fa516d60ec208c8761 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_named_tool_choice_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2088d4cdbaa0bd8864c57a721842cd4fb90eafb Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_prediction_content_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1674ebdcc669a8d3dd634972ab9bcadd30908de Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_tool_message_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ff9d14ae0d1140ee91c14acd83a742082bb54c5 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_tool_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdf86239da40a89a96453fd82c2cc909029d5cd1 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/chat/__pycache__/chat_completion_user_message_param.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0fe45525a5e0d695b4d7ba6e2b0eed8d8b374b19 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba1d6908498cc657ccdab935e0785dc9fcf262c1 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/error_object.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d3356ce9966896575052a13b6159cedc31a779d Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/function_parameters.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/metadata.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/metadata.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3d7a358ca213ab8aac0b4f46fce8b2c0516392a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/metadata.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_json_object.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_json_object.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ba5f0c3ff2a35a9ae17cba089a1292c7f82596c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_json_object.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_json_schema.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_json_schema.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8f62482e56e41c52b2839599699f0781a2c8595 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_json_schema.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_text.cpython-311.pyc b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_text.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95e5c22eee9fa95bf44bf1b6f914b293c0e2583c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/openai/types/shared/__pycache__/response_format_text.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/openai/types/shared/error_object.py b/.venv/lib/python3.11/site-packages/openai/types/shared/error_object.py new file mode 100644 index 0000000000000000000000000000000000000000..32d7045e006a37eb761dff818da642a6cf5d9a24 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/types/shared/error_object.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["ErrorObject"] + + +class ErrorObject(BaseModel): + code: Optional[str] = None + + message: str + + param: Optional[str] = None + + type: str diff --git a/.venv/lib/python3.11/site-packages/openai/types/uploads/part_create_params.py b/.venv/lib/python3.11/site-packages/openai/types/uploads/part_create_params.py new file mode 100644 index 0000000000000000000000000000000000000000..9851ca41e96ae6019038c7ce9e26c6cc13d1e84e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/types/uploads/part_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import FileTypes + +__all__ = ["PartCreateParams"] + + +class PartCreateParams(TypedDict, total=False): + data: Required[FileTypes] + """The chunk of bytes for this Part.""" diff --git a/.venv/lib/python3.11/site-packages/openai/types/uploads/upload_part.py b/.venv/lib/python3.11/site-packages/openai/types/uploads/upload_part.py new file mode 100644 index 0000000000000000000000000000000000000000..e09621d8f93536a7d8ea66b26d0e1bf0bb873646 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/openai/types/uploads/upload_part.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["UploadPart"] + + +class UploadPart(BaseModel): + id: str + """The upload Part unique identifier, which can be referenced in API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the Part was created.""" + + object: Literal["upload.part"] + """The object type, which is always `upload.part`.""" + + upload_id: str + """The ID of the Upload object that this Part was added to."""